CDK for Terraform reaches general availability
CDKTF 0.12 adds full Go support while preserving Terraform's execution model through synthesized JSON, deferred tokens, and resource iterators.
Writing infrastructure in a general-purpose language does not make every infrastructure value available when the program runs. CDK for Terraform reaches general availability with version 0.12 on August 1, and that distinction explains much of the release. HashiCorp's announcement confirms full Go support alongside TypeScript, Python, C#, and Java. The program synthesizes Terraform JSON that Terraform can deploy directly.
This architecture gives developers another authoring environment while retaining Terraform providers and modules. Familiar language tools can help construct configuration, but the generated configuration still describes work Terraform must plan and apply. Understanding where one stage ends and the next begins is necessary for using the abstraction correctly.
I find Terraform's reusable DSL valuable because it gives different APIs a common operational model. CDKTF interests me where an existing programming language can express useful configuration structure without obscuring that model. The practical question is whether the authored program makes the resulting infrastructure relationships easier to understand.
The program constructs a configuration
The 0.12.0 module documentation describes using existing public and private Terraform modules from a CDKTF application. A module source goes into the application's configuration, and cdktf get generates language bindings. The application can then instantiate the module through those bindings.
This retains an investment in existing module interfaces. A module does not have to be rewritten in the application's language before it becomes usable. The calling code describes its inputs and can refer to its outputs, while Terraform remains responsible for applying the configuration the program synthesizes.
The versioned reference shows module bindings under the generated directory. It also notes that bindings for submodules are not generated automatically for performance reasons. A submodule needs an explicit source address identifying the path within its module repository. The module package and the language package are related, but they are not the same unit.
Generated bindings are one option. The documentation also provides TerraformHclModule for directly referencing a supported module without generating typed bindings. Both approaches synthesize equivalent Terraform configuration, but the direct form does not supply typed inputs and outputs. That is a concrete tradeoff between preparation and the assistance available while authoring code.
This matters when deciding what a language's type checking actually guarantees. Generated types can catch certain mistakes in the authored program. A less typed module reference can still represent a valid Terraform module call. In either case, the remote system's eventual results remain subject to Terraform evaluation and provider behavior.
A resource attribute can be a deferred expression
The versioned token documentation explains how CDKTF represents values unavailable until Terraform applies configuration. An identifier assigned when a resource is created cannot be obtained by simply reading an ordinary application variable during synthesis. The program instead carries a token that will resolve to Terraform expression syntax.
The reference gives a module-output example involving a list of subnet identifiers. The consuming module expects a list of strings, while the producing module's output represents a value Terraform will resolve. Token.asList communicates the intended Terraform-facing type.
This expression is a small example of the boundary. It assumes vpc is the already declared module whose output will supply the subnet list.
Token.asList(vpc.publicSubnetsOutput)
During synthesis, CDKTF converts that representation into an expression referring to the module output. It does not need to know the actual subnet identifiers to emit the reference. The generated configuration tells Terraform how to obtain them when the module has produced its results.
A host-language value that carries a token is therefore not evidence that the remote value already exists. The reference even shows temporary token representations that are later converted into Terraform expressions. Treating those placeholders as real resource data would confuse synthesis with apply.
This explains why choosing Go or TypeScript cannot remove Terraform's unknown values. The application can know the structure it intends to generate while leaving some values deferred. Keeping that distinction visible is essential when building abstractions over module outputs or computed resource attributes.
Iteration has two evaluation times
The 0.12 iterator reference distinguishes normal programming-language loops from Terraform-level iteration. If the collection is known before synthesis, the documentation recommends the language's own loops. If a collection is represented as Terraform data unavailable to the synthesizing program, CDKTF provides TerraformIterator.
The iterator can be created from a list or map. A resource, data source, or module receives it through forEach, and the iterator exposes values or attributes for use in the generated configuration. The mechanism lets the program describe repetition that Terraform evaluates later.
For an existing Terraform variable named list, the iterator construction can look like this. The resulting iterator is intended for a resource's forEach property.
const iterator = TerraformIterator.fromList(list.listValue);
The reference also explains why list indices are unavailable through that iteration interface. CDKTF converts lists to sets for this purpose. The documentation connects that choice to avoiding unintended deletion and recreation when list positions change. A collection's ordering and a resource's stable identity are different concerns.
The release's versioned changelog records both resource iteration and dynamic-block support. The latter handles repeated blocks inside a resource definition. Those additions address separate places where authors need to express Terraform-managed repetition instead of expanding everything through the host language.
These facilities should be understood as ways to represent Terraform operations. An iterator does not make a remote value available earlier, and a token does not turn an unknown attribute into an ordinary known string. The benefit is that the program can preserve the deferred relationship in the configuration it emits.
Generated APIs change as their representation improves
General availability does not mean existing CDKTF programs have no migration work. The 0.12.0 changelog records breaking changes, including a split of Fn.merge into Fn.mergeLists and Fn.mergeMaps. This change concerns representing results in a typed language. The authored API needs to communicate distinctions that a less constrained representation can leave implicit.
Another change simplifies interfaces generated for recursive attributes. Terraform provider schemas represent such structures through large explicit descriptions. Previous generated bindings could acquire extremely long names that repeated nested structure. The release introduces detection of underlying recursion and converts those cases to recursive data structures.
That reduces the number of generated interfaces, according to the changelog, but programs that mention the old names need updates. The example is especially relevant to languages where users explicitly name generated types. Better representation can improve future authoring while still requiring a source change at the upgrade boundary.
The changelog also changes cdktf get to exit successfully when its configuration contains no provider or module specifications. Previously that case returned an error. This is a workflow detail, but it demonstrates that adoption involves the authoring commands as well as the generated resource classes.
I prefer evaluating these details alongside the convenience of language familiarity. A programmatic interface introduces its own generated types and synthesis commands. Their behavior needs to remain understandable together with the Terraform configuration they produce, particularly when a reusable abstraction spans several modules.
Existing module boundaries remain useful
CDKTF offers two forms of reuse that should not be conflated. A Terraform module remains a configuration package with an interface. A language-level abstraction can construct calls to modules or resources. The versioned module documentation shows that an application can use the first without rewriting it as the second.
Module binding generation also preserves a separate version-selection decision. The documented module configuration includes a source and a version constraint, so choosing a host language does not eliminate the need to specify which module release the application accepts. Generated calls still depend on the interface of that selected configuration package.
That arrangement allows adoption around existing boundaries. A program can call a published module, pass references between modules, and expose a Terraform output. Generated bindings help express those calls in the chosen language. Tokens preserve output references that cannot be resolved during synthesis.
The resulting JSON remains the artifact that connects those authoring choices to Terraform execution. A useful code review therefore needs to understand which declarations the program generates and which values remain deferred. CDKTF 0.12 provides more direct ways to express those relationships, especially where repeated resources and generated module interfaces meet.