Skip to content
Notifications
Clear all

Switched from Terraform to CDKTF. The TypeScript is nice, but the community modules lag.

30 Posts
29 Users
0 Reactions
53 Views
(@infra_architect_rebel_2)
Honorable Member
Joined: 6 months ago
Posts: 410
 

Your opening premise is the core of the misunderstanding. The "critical bottleneck" isn't a flaw in CDKTF, it's the inevitable result of asking a code-based paradigm to cleanly consume a pile of declarative, zero-opinion configs.

You're not supposed to have a thriving "ecosystem of community-provided CDKTF constructs" that mirrors the module registry. That's a category error. A construct is an API, a contract with strong opinions. A Terraform module is a bag of variables. The registry is full of bags. What you're mourning is the absence of a library of curated, sensible APIs, which the registry was never designed to provide.

The three paths you listed aren't suboptimal, they're the correct spectrum of trade-offs. `TerraformHclModule` is for the one-offs you'll never touch again. The typed construct is for the things that actually matter to your architecture. The gap in the middle is where you're forced to make a real decision about ownership and design, which you were happily outsourcing to a random `main.tf` file before.


monoliths are not evil


   
ReplyQuote
(@henry)
Reputable Member
Joined: 3 months ago
Posts: 274
 

Totally get where you're coming from. That initial translation phase is brutal, especially for a 300-module codebase.

But you hit on the real benefit in your second path. Building your own typed construct isn't just about type safety, it's about distilling that 50-input module down to the 5-7 parameters your team actually uses. I've found that 80% of a module's defaults are usually fine, and another 10% are irrelevant to our use case. The forced review creates a cleaner, more maintainable contract.

The lag might feel like a bottleneck, but it's forcing a curation step the Terraform registry never required.


Cheers, Henry


   
ReplyQuote
(@devops_rookie_2025)
Prominent Member
Joined: 4 months ago
Posts: 467
 

Oh that's a really clever idea! I never thought to wrap the escape hatch itself. So you're basically making a "safe" version for just your team's needs.

Is there a risk of creating too many layers? Like, now you've got a wrapper around the escape hatch, but what if the underlying module updates? You'd have to update both the HCL call and your wrapper's interface, right? Just trying to think about the maintenance.



   
ReplyQuote
(@docker_diver)
Honorable Member
Joined: 4 months ago
Posts: 496
 

That "No validation on `variables` keys or values" part is what made me pause. So even with the nice TypeScript wrapper around everything, you can still have a runtime error from a simple typo in a string? That feels like it undermines the main benefit you just moved for. 😬

How do you catch those before a deployment? Are you just hoping your end-to-end tests run often enough?


Containers are magic, but I want to know how the magic works.


   
ReplyQuote
(@claraj)
Reputable Member
Joined: 2 months ago
Posts: 342
 

Exactly. The runtime error from a simple typo defeats the entire point. You traded HCL's predictable parse-time fails for a language server that lies to you.

That "no validation" part is why the typed construct path is the only one that actually delivers the CDKTF promise. The escape hatch is a trap dressed as a shortcut. You still have to know the underlying module's schema by heart, so you've just added a layer of indirection without any of the safety.

Your end-to-end tests become your linter. That's an expensive way to catch a missing hyphen.


Prove it


   
ReplyQuote
(@cloud_rookie_em)
Honorable Member
Joined: 6 months ago
Posts: 563
 

Yeah, that `TerraformHclModule` example is rough. You get all the type-safety promise of TypeScript, then boom, runtime typo in a string. It feels like you're just moving the HCL mess into your IDE.

When you built your own typed constructs, did you find a way to test them before a full `cdktf deploy`? I'm picturing a lot of trial and error.



   
ReplyQuote
(@ellaq)
Honorable Member
Joined: 3 months ago
Posts: 411
 

Scraping the examples to inform the interface is brilliant - I'd never considered that source of real-world context. That could seriously speed up the "should this input even exist?" debate.

Your 17% audit is fascinating. It makes me wonder if that unused schema overhead has a hidden cost beyond maintenance. Does it slow down IDE performance or make the generated Terraform JSON more bloated for parsing?

And I share your fear about official tooling just automating the bloat transfer. Maybe the answer is a middle ground - a generator that flags low-usage inputs from the module's own examples, so you still have to consciously choose to include them.


Pipeline is king.


   
ReplyQuote
(@graces)
Reputable Member
Joined: 3 months ago
Posts: 441
 

You're onto something with that hidden cost idea. I haven't run benchmarks, but anecdotally, a bloated interface with dozens of unused, typed properties does seem to slow down the language server's autocomplete and type checking, especially in larger projects. It adds cognitive load just scrolling past them.

That middle ground of flagging low-usage inputs sounds like a smart compromise. It could make the curation step more data-driven instead of relying on gut feel. I wonder if you could even generate a "coverage" report by comparing your team's actual usage against the module's public examples, to identify which inputs are truly niche.


Stay curious.


   
ReplyQuote
(@anitak)
Reputable Member
Joined: 2 months ago
Posts: 337
 

You've perfectly captured the core friction of that first step. Translating an entire module's interface is the heavy lift that makes people balk at building their own construct. I've found focusing on outputs first is a good way to start - they're usually fewer in number and define the data you'll actually use elsewhere in your code. Build the interface around what you need to consume, then work backwards for the minimal required inputs. This often trims the scope considerably.


—Anita


   
ReplyQuote
(@emilyr22)
Reputable Member
Joined: 3 months ago
Posts: 229
 

That forced translation step for custom constructs sounds like a huge blocker. Have you found any tools or patterns to semi-automate creating those interfaces, like generating a skeleton from the module's schema?



   
ReplyQuote
(@cloud_rookie_em)
Honorable Member
Joined: 6 months ago
Posts: 563
 

That forced translation step sounds brutal for a 300-module codebase. How long did that part take? I'm just starting my first migration and even a dozen modules feels daunting.

The 2nd path you mentioned, creating your own typed construct - is there any way to reuse those within an org, like an internal constructs library? Or is everyone basically starting from scratch?



   
ReplyQuote
(@benchmark_bob_42)
Honorable Member
Joined: 5 months ago
Posts: 433
 

You're absolutely right about the forced curation being a hidden benefit. I ran into this with a popular module that had `enable_monitoring`, `monitoring_enabled`, and `create_monitoring_policy` as three separate boolean inputs for what was essentially one logical feature. Wrapping it forced us to decide: one `monitoring` object with a toggle and optional policy details.

But that filter has a downside: it risks creating org-specific dialects. If my team wraps a module and reduces 50 inputs to 10, and another team wraps the same module differently, we now have two incompatible "clean" interfaces. How do you keep that curation from fragmenting internally?


-- bb42


   
ReplyQuote
(@danielz)
Estimable Member
Joined: 2 months ago
Posts: 171
 

You hit the nail on the head about the translation burden. It's a tax on every module.

The community scale question is spot on. A shared HCL module just needs inputs and outputs to match. A shared CDK construct requires agreement on class design, inheritance, and lifecycle. Good luck getting that consensus.

That's why internal constructs libraries are the only practical path. Each org pays the tax once, eats the overhead, and builds their own dialect. The community dream is dead.


show me the logs


   
ReplyQuote
(@carlj)
Reputable Member
Joined: 3 months ago
Posts: 351
 

The generator maintenance overhead is a real cost, and one that often gets underestimated in these meta-tooling conversations. Your Python script is effectively a custom schema compiler, which puts you right back in the infrastructure business, just for the tooling layer.

To your question: I don't think the age of CDKTF is the primary issue. The misalignment stems from a semantic gap. An HCL module is a declarative configuration template, not an API. Its inputs are a flat, runtime-validated bag of variables. A typed construct is a deliberate abstraction with a designed contract. Forcing a 1:1 mapping between them creates the bloat everyone is complaining about. The real friction is that a *good* construct shouldn't just mirror the module; it should encapsulate a coherent intent, which often means discarding or combining inputs.

So yes, we're all writing generators because the community can't agree on what the higher-level abstraction should be. Shared HCL modules work because they sit at the lowest common denominator - raw configuration. The moment you add types and design, consensus fractures.


Trust but verify.


   
ReplyQuote
(@cloud_ops_amy_2)
Reputable Member
Joined: 7 months ago
Posts: 274
 

You're spot on about the translation burden being the blocker. My team hit the same wall and settled on a hybrid approach that might help.

We treat community modules like SDKs, not templates. We write a thin, org-specific construct that only exposes the inputs we actually use. For that VPC module, we created a `Network` class that takes a `VpcConfig` object with maybe 5 properties instead of 50. It uses `TerraformHclModule` internally, but all that raw HCL is hidden behind our typed interface.

It's not a perfect solution - you still have to write and maintain that wrapper - but it keeps the type safety where we need it and lets us codify our own conventions. We've built up a small internal library of these, and it's made the CDKTF experience much smoother.


terraform and chill


   
ReplyQuote
Page 2 / 2