Emulating Terraform on Pulumi's Engine

cnunciato1 pts0 comments

Emulating Terraform on Pulumi's Engine | Pulumi BlogSkip to main content<br>Latest release: Full support for Terraform state, cross-language modules, and HCL as a first-class language.<br>See the release

25.6K<br>Contact us<br>Sign in<br>Dashboard<br>Get started<br>25.6K<br>Contact us<br>Sign in<br>Dashboard<br>Get started

Navigation

25.6K<br>Contact us<br>Sign in<br>Dashboard<br>Get started

Copy logo SVG<br>Download mark<br>Get other variants<br>Read brand guide

Blog

In this post<br>Providers<br>Resources<br>Modules<br>Conclusion

The core promise of Pulumi&rsquo;s HCL support is that you can bring your existing Terraform configuration and modules, and pulumi will run them. If it works in OpenTofu and doesn&rsquo;t work in Pulumi, we would like to fix that. Given that goal, our HCL interpreter needs to take HCL as input and emit instructions to the Pulumi engine that semantically match how tofu would interpret the same input. This is made harder by the fact that Pulumi and OpenTofu have fundamentally different engine semantics and provider ecosystems. This blog post will explore how we have implemented that mapping well enough to get 96%1 of our top Terraform modules working on Pulumi. We&rsquo;ll briefly walk through how Pulumi&rsquo;s HCL interpreter handles Terraform&rsquo;s resource semantics, providers, and modules. It will also call out where Pulumi&rsquo;s HCL support lets you do things that Terraform and OpenTofu will not allow.<br>Providers<br>Both Pulumi and Terraform have providers, but they don&rsquo;t have the same providers. While there are providers that Terraform does not have, Pulumi can always resolve a Terraform provider using Pulumi&rsquo;s confusingly named terraform-provider provider.2 This is the same provider that lets you consume Any Terraform Provider in another Pulumi program with pulumi package add terraform-provider .... The terraform-provider provider acts as a relay: it speaks Pulumi&rsquo;s protocol to the Pulumi engine, and speaks Terraform&rsquo;s provider protocol to the Terraform provider it stands up. Because Pulumi HCL needs to work with all Pulumi providers and because terraform-provider lets Pulumi HCL speak to Terraform providers via the Pulumi protocol, Pulumi HCL actually only speaks Pulumi protocols directly:

flowchart LR<br>subgraph n2Entry[" "]<br>n2["terraform-provider"]<br>end<br>subgraph providerBox["Pulumi Provider"]<br>direction TD<br>n2Entry<br>n3["Terraform Provider"]<br>end<br>n0["Pulumi HCL"] n1["Pulumi Engine"]<br>n1 n2Entry<br>n2 n3

n2@{ shape: rect}<br>n3@{ shape: rect}<br>n0@{ shape: rect}<br>n1@{ shape: rect}<br>style n2Entry fill:transparent,stroke:transparent<br>All internals of github.com/pulumi/pulumi-hcl are implemented in Pulumi&rsquo;s language protocol.<br>Because the terraform-provider natively understands Terraform version ranges and defaults to the OpenTofu registry, we can directly translate normal provider requests to the terraform-provider. It takes its arguments as an untyped list of strings, since the interface was originally intended for the command line. Let&rsquo;s walk through some simple examples:<br>resource "aws_s3_bucket" "example" {<br>bucket = "my-bucket-123"

Pulumi&rsquo;s HCL interpreter sees that there is no terraform.required_providers block, so it cuts the resource token at the first _ and uses the default registry and namespace. This is what the request that goes to the Pulumi engine looks like:<br>pulumirpc.PackageSpec{<br>Source: "terraform-provider",<br>Parameters: []string{"registry.opentofu.org/hashicorp/aws"},

Because no version was specified, we leave it to terraform-provider to determine the version. It will use the latest version.<br>Just like Terraform, you can override this with a required_providers block:<br>terraform {<br>required_providers {<br>example = {<br>source = "my.custom.registry/me/example"<br>version = "~> 5.0"

resource "example_resource" "another_example" {

We perform the same mechanical translation. The source is fully specified, and there is a version, so we pass it along to the Pulumi engine, which passes it along to terraform-provider:<br>pulumirpc.PackageSpec{<br>Source: "terraform-provider",<br>Parameters: []string{"my.custom.registry/me/example", "~> 5.0"},

Pulumi HCL will automatically translate almost all provider calls through the terraform-provider translator. Providers with the source pulumi/* will instead be routed directly. This is how you can use a native Pulumi provider in Pulumi HCL:<br>terraform {<br>required_providers {<br>kubernetes = {<br>source = "pulumi/kubernetes"<br>version = "4.33.0"

resource "kubernetes_yaml_config_file" "app" {<br>file = "app.yaml"

Our HCL interpreter routes this directly to the Pulumi Kubernetes package:<br>pulumirpc.PackageSpec{<br>Source: "kubernetes",<br>Version: "4.33.0",

Observe that the version moved from Parameters to the Version field. That&rsquo;s because providers written with source = "pulumi/*" are talking about the plugin directly.<br>Resources<br>Both Pulumi programs and Terraform config exist to express a resource graph. It is their primary purpose. I don&rsquo;t have the time or the pixels to explain...

pulumi terraform provider rsquo providers version

Related Articles