Converting Full Terraform Programs to Pulumi

Posted on

Over the last 2 years, we’ve seen an increasing trend of cloud development teams migrating to Pulumi from Terraform. These teams often have experience with and meaningful investment in Terraform, but have also typically run into limits of expressivity, productivity, scalability, or reliability with their existing tools. One of the first questions we hear when they decide to move to Pulumi is “how will I migrate my existing Terraform projects over?”.

Today, we’re excited to announce new support for converting whole Terraform projects to Pulumi via the pulumi convert command in the Pulumi CLI. The new Terraform converter includes support for Terraform modules, core features of Terraform 1.4, and the majority of Terraform built-in functions, converting to Pulumi TypeScript, Python, Go, or C#. The new converter can significantly reduce the amount of time it takes to migrate Terraform to Pulumi. Let’s dig in to learn more about the new converter and how to use it.

Historically, we have offered a separate tf2pulumi tool to convert small snippets of Terraform to Pulumi. The new converter is no longer a separate tool. As of v3.71.0, you can run the new converter directly from the Pulumi CLI with the pulumi convert --from terraform command. And you can convert more than small snippets – the new converter supports converting full Terraform programs.

The new support in pulumi convert builds upon Pulumi’s CrossCode foundations for providing universal infrastructure as code support across a wide variety of programming languages and conversion tooling between them. It also introduces a new concept of converter plugin in the Pulumi engine, which allows conversion tools from other Infrastructure as Code platforms to be integrated into the same pulumi convert experience in the future, both as part of the core project, as well as by other ecosystem partners and contributors.

Several common use cases are supported via the new pulumi convert --from terraform support in the Pulumi CLI:

  • Converting your organization’s existing Terraform projects to Pulumi
  • Converting your organization’s existing Terraform modules to Pulumi, to be consumed as part of existing Pulumi projects
  • Converting 3rd party open source Terraform modules or projects which address a use case you want to incorporate into your existing Pulumi projects

Supported Terraform Features

The following major features are supported:

  • Variables, outputs, resources, and data sources
  • Terraform modules are converted to Pulumi components
  • Almost all HCL2 expression syntax

In cases where the converter does not yet support a feature, the pulumi convert command succeeds but generates a TODO in the form of a call to a notImplemented not_implemented notImplemented NotImplemented function that will need to be filled in manually. For most projects, the converter should be able to convert 90-95% of the code without any TODOs, with only a small percentage of items to address manually, significantly reducing migration time compared to doing an entire migration by hand. We are actively improving the converter by adding support for missing features and improving the overall quality of the converted code to reduce the amount of manual fix-ups required.

Converting a Real World Program

Let’s walk through converting a Terraform codebase to Pulumi. Avant Terraform Vault Setup is an open source project that provides a high-availability installation of Vault using a variety of managed AWS services. It defines a fairly complex installation with dozens of AWS resources in over 1,000 lines of Terraform HCL, including the main program and a Vault module. Let’s convert it to Pulumi.

First, clone the repo and cd into the directory containing the Terraform project:

$ git clone https://github.com/avantoss/vault-infra.git
$ cd vault-infra/terraform/main

Next, run the converter:

$ pulumi convert --from terraform --language typescript --out pulumi
$ pulumi convert --from terraform --language python --out pulumi
$ pulumi convert --from terraform --language go --out pulumi
$ pulumi convert --from terraform --language csharp --out pulumi

pulumi convert

The converted code is generated in the specified pulumi output directory. A complete Pulumi project is generated, including two primary code files specific to this this program’s conversion:

  • index.js index.ts __main__.py main.go Program.cs Program.fs Program.vb App.java Pulumi.yaml contains the converted code for the main program
  • vault.ts vault.py vault.go vault.cs contains the Vault Pulumi component, converted from the Terraform module

Addressing TODOs

The file for the Vault component makes up the bulk of the implementation and contains some TODOs emitted by the converter that must be addressed manually.

For example, the converter doesn’t yet support the replace, element, and split built-in functions.

const plainDomain = notImplemented("replace(element(split(\":\",var.vault_dns_address),1),\"////\",\"\")");
plain_domain = not_implemented("replace(element(split(\":\",var.vault_dns_address),1),\"////\",\"\")")
plainDomain := notImplemented("replace(element(split(\":\",var.vault_dns_address),1),\"////\",\"\")");
var plainDomain = NotImplemented("replace(element(split(\":\",var.vault_dns_address),1),\"////\",\"\")");

We can fill in an implementation. Note that we get to use the full expressiveness of the native and familiar string manipulation libraries in our target programming language, instead of the relatively constrained options of the Terraform DSL.

const plainDomain = pulumi.output(args.vaultDnsAddress)
    .apply(a => a.split(":")[1].replace("//", ""));
plain_domain = pulumi.Output.from_input(args["vaultDnsAddress"]) \
    .apply(lambda a: a.split(":")[1].replace("//", ""))
plainDomain := args.VaultDnsAddress.ToStringOutput().ApplyT(func(a string) string {
	return strings.ReplaceAll(strings.Split(a, ":")[1], "//", "")
}).(pulumi.StringOutput)
var plainDomain = args.VaultDnsAddress
    .Apply(a => a.Split(':')[1].Replace("//", ""));

Wrapping Up

After addressing the remaining TODOs and some other tweaks so that the code compiles, we can now run the converted program with pulumi up to provision the Vault installation with Pulumi.

The converter has saved us a ton of time, converting over 1,000 lines of Terraform to a modern Pulumi language, with only a small number of manual fix-ups required. From here, we can leverage our IDE and compiler to further refactor and improve the code, one of the many benefits of Pulumi!

Importing State

It’s great that the new converter can migrate Terraform projects to Pulumi for new deployments, but what if you want to import existing resource states from a .tfstate file to avoid unnecessarily recreating your infrastructure?

If you’re using TypeScript or Go, there is some additional code that can be added to your converted Pulumi program to import resource states from a .tfstate file. See the Importing Resources reference documentation for more details.

We’re working to make this even more seamless with built-in support for importing state from .tfstate files in a future update coming soon.

Get Started

Support for the new pulumi convert --from terraform command is now available in v3.71.0 of the Pulumi CLI. Download the latest Pulumi CLI and give the new converter a try today. If you run into any issues, please let us know or reach out in the Pulumi Community Slack with any questions!