RabbitPeepers supports The Armed Forces of UkraineDonate
RabbitPeepers supports The Armed Forces of UkraineDonate
Calculate project
Contact us
RabbitPeepers supports The Armed Forces of UkraineDonate
Calculate project

Creating terraform infrastructure via cdktf

Finish date: 28 March 2020
Published: 03 Jul 2021
Oleh Tsymbal
Oleh Tsymbal
Founder, Cloud Solutions Architect

Part one: Introduction and Setup


Approximately 4 years ago I faced one very common but sometimes too overused pattern in terraform configuration - with a count.
You can see it in any community terraform module, like:

1variable "s3_buckets" { 2 type = list(string)s 3 default = ["1bucket", "2bucket", "3bucket"] 4} 5resource "aws_s3_bucket" "s3-module" { 6 count = length(var.s3_buckets) 7} 8

It could produce some undesired changes when we just changed elements order or deleted some of them.

If we had had for_each or cdktf that time... but 3 years ago we didn't have another way to do it. However, since Terraform version >= 12, we can use `fore_each in our configuration. It has made our life much better.

1variable "s3_buckets" { 2 type = map(object({ 3 acl = string 4 })) 5 default = { 6 1bucket = { 7 acl = "private" 8 } 9 } 10} 11resource "aws_s3_bucket" "s3-module" { 12 for_each = var.s3_buckets 13 acl = each.value.acl 14} 15

As a result, it will not break anything even you delete something or change the order.

The count is still not so bad if you just practice, or you don't have other variants (you have only arrays in some data sources, and conversion to an object is useless).

Better way

But what if I tell you that we have a better approach?


It was my first attempt to reimagine my IaC vision. I was so inspired to use it, made some test configurations but... Pulumi is not a true open-source project, it doesn't have any collaboration alternatives except their cloud. In case if my company can afford to buy an enterprise plan, some very small companies cannot. So it can be hard to recommend it to other people. I still think that Pulumi is a great product, but maybe not for everyone.


Approximately 1 year ago I was pleasantly surprised, that Terraform developed their own CDK (In collaboration with Aws if I am not mistaken). And you can simplify your terraform code (or make it worse :) ) without using terraform-specific functions.

Cdkf setup

Create project

Terraform guys recommend installing cdktf globally (I don't know why, because I want to keep my OS clean as much as possible) and don't use npx.

That's why I just copy-paste this package.json file across my projects:

1{ 2 "name": "terraform", 3 "version": "1.0.0", 4 "main": "main.js", 5 "types": "main.ts", 6 "license": "MIT", 7 "private": true, 8 "scripts": { 9 "get": "cdktf get", 10 "plan": "cdktf diff", 11 "build": "cdktf get && tsc", 12 "synth": "cdktf synth", 13 "apply": "cdktf deploy", 14 "terraform:apply": "terraform -chdir=cdktf.out apply", 15 "terraform:destroy": "terraform -chdir=cdktf.out destroy", 16 "terraform:plan": "terraform -chdir=cdktf.out plan", 17 "destroy": "cdktf destroy", 18 "compile": "tsc --pretty", 19 "watch": "tsc -w", 20 "upgrade": "npm i cdktf@latest cdktf-cli@latest", 21 "upgrade:next": "npm i cdktf@next cdktf-cli@next" 22 }, 23 "engines": { 24 "node": ">=10.12" 25 }, 26 "dependencies": { 27 "cdktf": "^0.2.0", 28 "constructs": "^3.3.68" 29 }, 30 "devDependencies": { 31 "@types/node": "^14.14.10", 32 "cdktf-cli": "^0.2.0", 33 "typescript": "^4.2.3" 34 } 35} 36

But if you prefer building it from scratch you can safely follow this link:


Then just copy additional commands from my package.json file.

Modify tsconfig.json

Also, I'd like to have absolute imports and separated directories for my code, that's why I will change tsconfig.json file (of you can copy-paste it from here).

1{ 2 "compilerOptions": { 3 "baseUrl": "src", // added for source code dir 4 "alwaysStrict": true, 5 "charset": "utf8", 6 "declaration": true, 7 "experimentalDecorators": true, 8 "inlineSourceMap": true, 9 "inlineSources": true, 10 "lib": ["es2018"], 11 "paths": { 12 "*": ["*"] 13 }, 14 "outDir": "./dist", // here we will have or js code 15 "allowSyntheticDefaultImports": true, 16 "esModuleInterop": true, 17 "module": "commonjs", 18 "moduleResolution": "node", 19 "noEmitOnError": true, 20 "noFallthroughCasesInSwitch": true, 21 "noImplicitAny": true, 22 "noImplicitReturns": true, 23 "noImplicitThis": true, 24 "noUnusedLocals": true, 25 "noUnusedParameters": true, 26 "resolveJsonModule": true, 27 "strict": true, 28 "strictNullChecks": true, 29 "strictPropertyInitialization": true, 30 "stripInternal": true, 31 "target": "es6" 32 }, 33 "include": ["src"], 34 "exclude": ["node_modules"], 35 "moduleDirectories": ["node_modules", "src"] 36} 37

Compare it with:


Final touches

In case if you prefer using src folder you also have to correspondingly change your cdktf.json file.

1{ 2 "language": "typescript", 3 "app": "npm run --silent compile && NODE_PATH=./dist node dist/main.js", 4 "terraformProviders": [ 5 "aws@~> 2.0" 6 ], 7 "context": { 8 "excludeStackIdFromLogicalIds": "true", 9 "allowSepCharsInLogicalIds": "true" 10 } 11} 12

Also, you can notice that I use main.js instead of index.js

Install required packages

After it we can run npm install and create src firectory.

In the next chapter, we will setup additional packages and make a simple configuration for AWS.

Clap if you like it!

Get in touch to discuss your business idea

Let's Start

Interested in our services?


What's next?

We contact you
We’ll contact you within 24 hours with more information on our next steps. In the meantime, you can check out our case studies and our blog
Collect requirements
Our next step will be to collect all the requirements for your project, clarify your business objectives, and expectations towards our cooperation
After that, we’ll develop a proposal for you.


  • Development
  • Advisory
  • Infrastructure
RabbitPeepers LLC
Kyiv, Ukraine
Delaware, US
PrivacyTerms © 2019-2023