Virtual Event
Join us for the next HashiConf Digital October 12-15, 2020 Register for Free

Write Terraform with Typescript and Python

beta

Build AWS Infrastructure with TypeScript

Now that you've installed cdktf, let's run an example on AWS that creates an AWS instance.

»Prerequisites

You'll need to install the cdktf tool and set up your environment with AWS credentials. Terraform and the cdktf tool will use credentials set in your environment or through other means as described in the Terraform documentation.

export AWS_ACCESS_KEY_ID=AAAAAA
export AWS_SECRET_ACCESS_KEY=AAAAA

»Initialize a project

Start by creating a directory for the project. Name it typescript-aws.

$ mkdir typescript-aws && cd $_

Inside the directory, run cdktf init with the TypeScript template. Use --local to store Terraform's state file on your machine instead of remotely in Terraform Cloud.

$ cdktf init --template="typescript" --local

This will initialize a brand new CDK for Terraform project in TypeScript using an interactive command. Accept the defaults when prompted.

»Write TypeScript code

Open the main.ts file to view your application code. The template creates a scaffold with no functionality.

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-terraform');
app.synth();

The scaffold pre-configures the configuration to use AWS. Run the get command to install the relevant dependencies.

$ cdktf get

Now, write a configuration in TypeScript that uses the AWS provider to create a compute instance in the us-west-1 region. Although it requires about 30 lines of code, most of the lines map to equivalents in HCL for traditional Terraform.

Copy the following code and paste it into main.ts, replacing the existing code.

import { Construct } from 'constructs';
import { App, TerraformStack, TerraformOutput } from 'cdktf';
import { AwsProvider, Instance } from './.gen/providers/aws';

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

    new AwsProvider(this, 'aws', {
      region: 'us-west-1'
    });

    const instance = new Instance(this, 'compute', {
      ami: "ami-01456a894f71116f2",
      instanceType: "t2.micro",
      tags: {
        Name: "TypeScript-Demo",
        fruit: "blueberry",
        Address: "123 Main St"
      }
    });

    new TerraformOutput(this, 'public_ip', {
      value: instance.publicIp
    });

  }
}

const app = new App();
new MyStack(app, 'typescript-aws');
app.synth();

»Examine the code

Most of the code is similar to concepts you've seen in a traditional Terraform configuration written in HCL, but there are a few notable differences. Let's look at each.

Unlike HCL, your TypeScript code must explicitly import any classes it uses. A major addition is TerraformOutput which you will use to print attributes of the resources that you create. The core Terraform classes are in the cdktf library and respond to IntelliSense if you are using Visual Studio Code. Start typing "Terraform" inside the curly braces and you'll see a list of available objects.

import { App, TerraformStack, TerraformOutput } from 'cdktf';

AWS resources must also be explicitly imported. In this case we need the top level AwsProvider and the Instance for our compute resource.

import { AwsProvider, Instance } from './.gen/providers/aws';

Speaking of which, the AwsProvider can be configured by passing keys and values that map to Terraform arguments as listed in the provider API. In the snippet below we use the region key to set the region to us-west-1.

The Terraform API documentation uses snake case (lowercase with underscores) but TypeScript uses camel case (mixed lower and uppercase with no underscores). So a key such as shared_credentials_file is sharedCredentialsFile in TypeScript.

new AwsProvider(this, 'aws', {
  region: 'us-west-1'
});

Next we define a compute instance with the Instance class. For this you'll also use camel case for properties that correspond to the Terraform API.

A significant difference from HCL is the need to store the new instance in a variable, shown as const instance here. We will use the instance attributes in an output later, so we must create a variable. In HCL, any resource can be accessed from any point in the configuration file, but in TypeScript you must use a variable if you want to access the resource elsewhere.

const instance = new Instance(this, 'compute', {
  ami: "ami-01456a894f71116f2",
  instanceType: "t2.micro",
  tags: {
    Name: "TypeScript-Demo",
    fruit: "blueberry",
    Address: "123 Main St"
  }
});

The instance variable is used in a TerraformOutput. We want to know the publicIp of the instance when it is created, and we might want to read this output value from another Terraform workspace.

When writing this code, you can use IntelliSense to view all the properties of the instance variable. In this case, publicIp the only one that is used.

new TerraformOutput(this, 'public_ip', {
  value: instance.publicIp
});

»Provision infrastructure

Now that you have initialized the project with the AWS provider and have written code to provision an instance, it's time to deploy it.

$ cdktf deploy

Deploying Stack: typescript-aws
Resources
 ✔ AWS_INSTANCE         compute

Summary: 1 created, 0 updated, 0 destroyed.

Output: typescriptaws_publicip_EEB4927B = 54.219.195.36

You will be prompted to confirm. After the instance is created, you'll see the output which displays the publicIp we specified earlier (listed here with the full name of the project and the name of the output, typescriptaws_publicip_EEB4927B).

AWS Console

»Destroy

In a full cycle development workflow, you would modify the code and run deploy again to update your infrastructure as specified in the code.

For this tutorial, we're done with the compute instance, so you can run the destroy command to de-provision it.

$ cdktf destroy

Destroying Stack: typescript-aws
Resources
 ✔ AWS_INSTANCE         compute             aws_instance.typescriptaws_compute_D9683B2F

Summary: 1 destroyed.

You'll be prompted to confirm and a message will be displayed that shows it was destroyed.

»Next Steps

Even in its current state of development, cdktf is capable of much more. The synth command will generate JSON which can be used by the standard terraform executable to provision infrastructure using terraform apply and related commands.

Any other Terraform provider can be used by listing it under terraformProviders in cdktf.json and running cdktf get.

TypeScript language features can be used to build configuration around class inheritance or to fetch data from other sources which can be used together with your Terraform configuration.

Or, you can use cdktf with Terraform Cloud for persistent storage of your state file and for team collaboration.

For other examples, see the documentation in the terraform-cdk repository.