Generating Configuration (CDKTF)
Most teams write HCL by hand. Some generate Terraform configuration programmatically instead, and the best-known tool for it is CDK for Terraform — CDKTF, which lets you define infrastructure in TypeScript, Python, Go, Java, or C# and synthesizes it down to Terraform JSON that the normal engine applies. HashiCorp deprecated CDKTF on December 10, 2025 and no longer maintains it, so it is no longer something to adopt for new work — but the pattern it represents, generating configuration from a real language, is worth understanding and lives on in Pulumi and the AWS CDK. You write loops, classes, and types in a real language; CDKTF turns them into the configuration Terraform runs.
The trade is direct. HCL is deliberately constrained, which is exactly what makes a plan diff reviewable and a module predictable. A general-purpose language hands you abstraction and logic that HCL doesn't have — and the freedom to write generation code that's harder to review than the HCL it replaced. This topic covers when that trade is worth it.
CDKTF and the Synth Step
CDKTF defines resources as objects in your chosen language. You instantiate a VPC, a subnet, an instance the same way you'd construct any object, wiring them together with normal references. Running cdktf synth evaluates that code and emits Terraform JSON — the machine-readable equivalent of HCL — into an output directory. From there it is ordinary Terraform: the same plan, the same apply, the same state. CDKTF is a front end that produces configuration; it is not a different engine.
const instance = new Instance(this, "web", { ami: "ami-0abc123", instanceType: "t3.micro", tags: { Name: "web" }, }); // a real language's loop — three instances from one block for (const az of ["a", "b", "c"]) { new Subnet(this, `subnet-${az}`, { vpcId: vpc.id, availabilityZone: `us-east-1${az}`, }); }
The instance block is just a wordier version of an HCL resource — no real gain there. The loop is where the case lives: a real for over a typed array, with whatever conditional logic you want, producing three subnets. HCL has for_each for the simple shape of this, but when the generation logic is genuinely complex, a programming language expresses it more naturally.
Why Generate Configuration
The reasons to reach for CDKTF are the things HCL can't do well: complex generation logic that needs real control flow, strong static typing with editor autocomplete and compile-time checks, packaging reusable constructs as language libraries that teams import like any dependency, and letting application developers describe infrastructure in the language they already write daily. For a platform team building higher-level abstractions over raw resources, those are real advantages.
The Cost
None of that is free. CDKTF adds a build-and-synth step before every plan, a larger toolchain — a language runtime, a package manager, the CDKTF CLI — and a layer of indirection: the thing you review is no longer the HCL that runs, it is the program that generates it. A teammate reading your repo sees TypeScript, not resources. The clever generation logic that justified CDKTF is also the logic that can be harder to review than the configuration it replaced. For standard infrastructure that HCL expresses cleanly, this is pure overhead.
Programmatic Generation Generally
CDKTF was the best-known tool (deprecated as of December 2025), but it was never the only way to generate configuration. Terraform reads JSON as readily as HCL, so any script or template that emits valid Terraform JSON is fair game, and teams sometimes template HCL with a tool they already use rather than adopt a whole framework. That's the pragmatic middle ground: when you need a small amount of generation and don't want CDKTF's toolchain, a focused template or script can produce the repetitive configuration without the full framework. Reserve real generation tooling for real generation problems.
CDKTF vs Pulumi
CDKTF and Pulumi look similar — both define infrastructure in general-purpose languages — but they differ underneath. CDKTF synthesizes to Terraform and then runs on Terraform's engine, reusing its providers, plan/apply model, and state. Pulumi is its own engine with its own state and SDKs; it is not a front end to Terraform. The choice is whether you want to stay on the Terraform ecosystem and just change the authoring language (CDKTF) or move to a different platform entirely (Pulumi). Picking between them on "both use a real language" alone misses the part that actually matters.
HCL — declarative, constrained, and directly reviewable, which is exactly why most teams and nearly every published module use it. Choose it by default; the plan diff maps straight to the configuration you wrote.
CDKTF — brings a real language's loops, types, and abstraction for genuinely complex generation, at the cost of a synth step and configuration that's no longer directly readable as HCL. Choose it when the logic truly needs a programming language, not as a default.
- Adopting CDKTF for standard infrastructure that HCL expresses more simply and reviewably, adding a build toolchain and a synth step for no real gain.
- Writing imperative, clever generation logic in the host language that's harder to review than the HCL it replaced, hiding what actually gets applied.
- Assuming CDKTF removes the need to understand Terraform's plan, apply, and state model — it synthesizes to Terraform and runs exactly the same way underneath.
- Confusing CDKTF (synthesizes to Terraform, runs on its engine) with Pulumi (its own engine and state) when choosing a programmatic tool.
- Reaching for a full generation framework when a small template or script emitting Terraform JSON would have produced the repetition you needed.
- Use HCL by default, and reach for CDKTF only when the configuration genuinely needs a programming language's logic or typing.
- Keep generated configuration reviewable and the generation logic as simple as the problem allows, so reviewers can still reason about what applies.
- Remember that CDKTF still relies on Terraform's plan/apply/state model — understand it the same as you would for hand-written HCL.
- Choose between CDKTF and Pulumi on whether you want to stay on Terraform's engine and ecosystem, not on the surface fact that both use real languages.
- For light repetition, prefer a focused template or script emitting Terraform JSON over adopting a whole framework's toolchain.
Knowledge Check
What does CDKTF actually produce?
- Terraform JSON that the normal Terraform engine applies, using the same providers, plan/apply, and state
- A separate standalone runtime that replaces Terraform's plan and apply engine entirely and talks to providers itself
- Compiled native machine code that links the cloud SDKs and talks to the provider APIs directly
- CloudFormation templates that AWS executes
When is reaching for CDKTF justified over HCL?
- When the configuration genuinely needs a real language's logic, typing, or abstraction — not for standard infrastructure HCL expresses cleanly
- Always, since a general-purpose programming language is strictly more capable than HCL for every kind of infrastructure config you could ever write
- Only when you can't install the Terraform CLI and need CDKTF to apply the config on its own instead
- Whenever you need to declare more than one resource of the same type in a single configuration
What cost does CDKTF carry compared to plain HCL?
- A build-and-synth step, a larger toolchain, and configuration that's no longer directly readable as HCL
- It can no longer use the AWS provider, since synthesized JSON drops access to the registry providers entirely
- It loses the ability to store state remotely and must keep state on the local disk
- It forces every resource to be destroyed and replaced on each and every apply
How does CDKTF differ from Pulumi?
- CDKTF synthesizes to Terraform and runs on its engine and state; Pulumi is its own engine with its own state
- CDKTF authors infrastructure in HCL, while Pulumi is the one that uses real general-purpose programming languages
- They are the exact same product shipped under two different brand names
- Pulumi synthesizes to Terraform JSON and runs on its engine, while CDKTF uses its own separate engine
You got correct