Product1 publisher3 min readPublished
Pulumi turns its Terraform-compatibility claim into a diff against tofu
The tfcompat harness runs the same HCL program through tofu and pulumi, then compares what the providers saw. The oracle is tofu, not a recorded expectation.
The Product Desk · Product desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- Pulumi states that Pulumi HCL has at its core a simple promise: a program that works for `tofu apply` will also work for `pulumi up`.
- Pulumi's stated definition of correctness: Pulumi HCL correctly interprets an HCL program when it generates the same set of provider steps as tofu does.
- Pulumi has created a framework called tfcompat to assert the compatibility property for Pulumi HCL.
- Pulumi argues that providers are the part of its model that generates user-observable behavior, so matching what providers see means matching what users see.
- Pulumi describes both Pulumi and Terraform as systems that translate actual state and desired state into a series of imperative actions so actual state can be reconciled to desired state, noting that how desired state is expressed and the underlying reconciliation engine can be radically different.
Compiled by The Product DeskSomething wrong?How this is made
Why it matters
Pulumi has published the mechanics behind the one sentence its Terraform migration story rests on: a program that works for `tofu apply` will also work for `pulumi up` [1]. The consequential part is not the promise but its testability, because Pulumi defines correctness for Pulumi HCL as generating the same set of provider steps that tofu does for the same program [2], and has built a harness, tfcompat, that checks the property by running both tools and comparing what the providers saw [3].
The reasoning is narrow in a useful way. Pulumi's argument is that providers are the part of its model that generates user-observable behavior, so if the two systems match what providers see, they match what users see [4]. Both engines are described as doing the same job, turning actual state and desired state into imperative actions so one can be reconciled to the other, however different the expression syntax and the reconciliation engine underneath [5]. Pulumi HCL dynamically bridges any Terraform provider in the registry in order to match semantics [6], and Pulumi frames the whole promise as a precondition for sharing Terraform modules between tofu configs and Pulumi programs [7]. Note the scope: the equivalence is asserted for the subset of Pulumi programs that are valid OpenTofu programs [8], which makes the guarantee one-directional [9].
Mechanically, a tfcompat case is two things: the files of the HCL program, and the providers that program uses [10]. RunCase starts each provider in memory, copies the test files into two separate temp directories, then runs `tofu plan` and `tofu apply` on one side and `pulumi preview` and `pulumi up` on the other, using TF_REATTACH_PROVIDERS and PULUMI_BRIDGE_REATTACH_PROVIDERS respectively to attach both runs to the same in-memory providers [11]. The harness records every provider gRPC call and the stack outputs from both invocations, then asserts that the outputs match and that the providers saw the same operations [12].
The design detail worth copying: nowhere in a test case does anyone write down what Pulumi HCL should do, because RunCase takes a scenario and not accepted behavior [13]. tofu is the oracle. That removes the standard failure mode of golden-file compatibility suites, where the recorded expectation quietly drifts away from the thing it was supposed to imitate. It also means the suite is worth exactly as much as its scenario list.
Which is where the published account thins. The worked example, TestL2SimpleResource, is one resource with two inputs and one output, run against a purpose-built in-memory provider named "simple" [14], and its assertions cover ConfigureProvider, the plan RPC issued during preview and plan, a single ApplyResourceChange that creates the resource, and the absence of any other provider RPCs [15]. Plan-and-create against a synthetic provider is the easy end of the problem. In the material available, Pulumi does not state how many cases tfcompat runs, what share of the HCL surface they cover, or which incompatibilities are currently known [16].
For anyone treating a migration as a verification exercise rather than a rewrite, the things to watch are whether cases exercise update, destroy and refresh rather than initial create; whether they run against real registry providers instead of in-memory doubles; and whether tfcompat is packaged so a team can point it at its own modules and get the same diff for itself.