Writing terraform with typescript

September 20, 2020

You may or may not have heard about the release of the terraform cdk (short for cloud development kit). It’s HashiCorps answer to the aws cdk. In the words of the projects readme:

CDK (Cloud Development Kit) for Terraform allows developers to use familiar programming languages to define cloud infrastructure and provision it through HashiCorp Terraform.

Let’s try this out shall we?

To get the full developer experience, make sure you have typescript support installed for your IDE


Generating the boilerplate

Let’s open our terminal and install install cdktf-cli:

npm install -g cdktf-cli

Next we’ll initialize the project

mkdir hello-cdktf
cd hello-cdktf
cdktf init --template="typescript" --local

Answer the two configuration questions and the project boilerplate will be generated.

Now we should see a main.ts file with the following contents in our folder:

import { Construct } from 'constructs';
import { App, TerraformStack } from 'cdktf';

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    // define resources here


const app = new App();
new MyStack(app, 'hello-cdktf2');

Adding a provider package and importing modules from it

After generation finishes you’ll see a message in the console listing instructions of what to do next. To add the prebuilt aws provider (which also gives us all the modules and types that we might want).

npm install -a @cdktf/provider-aws

Now you can import modules from @cdktf/provider-aws such as AwsProvider and others. We’ll go for the AwsProvider, LambdaFunction and IamRole. Add this at the top of your file:

import { AwsProvider, LambdaFunction, IamRole } from '@cdktf/provider-aws';

and then create an AwsProvider in your stack:

new AwsProvider(this, 'aws', {
  region: 'eu-west-2'

Adding a lambda function to our stack

To create a lambda we need to define an IAM role at first. Boring, but made easier by autocomplete of cours. Anyway here’s the default policy:

const roleForLambda = new IamRole(this, 'iam-role-for-lambda', {
  name: 'iam-role-for-lambda',
  assumeRolePolicy: JSON.stringify({
    "Version": "2012-10-17",
    "Statement": [
        "Action": "sts:AssumeRole",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        "Effect": "Allow"

Now we can add a lambda function to the stack like this:

new LambdaFunction(this, 'hello-world', {
  filename: process.cwd() + 'hello-world.zip'
  functionName: 'hello-world',
  handler: 'index.handler',
  runtime: 'nodejs12.x',
  role: roleForLambda.arn,

You will need to zip your lambda function - which is usually a separate step before running terraform. For example sake, let’s say you have a file in your project named hello-world.js:

export const handler = async function () {
  return { hello: world }

Then zip your lambda zip -r lambda.zip hello-world.js

Deploying your stack

Before you deploy don’t forget need to have your aws credentials in your path.

Now that you have everything ready you can deploy your stack with cdktf deploy. This command will display an execution plan and ask you if you want to deploy. Press the Y and Enter key to deploy.

Any errors at this stage should be farely self-explanatory. If they don’t make sense, google the error message - other people have likely run into the same problem.

If you’re a terraform user and you’ve used the lambda_function module before, you’ll notice that the configuration is exactly the same.

Ultimately, when you run cdktf synth cdktf compiles your javascript/typescript modules into terraforms alternative JSON configuration syntax.

This is an extremely powerful feature of terraform’s design since it can be a compile target not just for javascript and typescript, but any kind of language. The open source community could add it’s own language compilers.

Why not write one in rust? 😅

🙏 Thanks for reading

I help software teams build text based editing interfaces