Getting Started: Terraform with Typescript

Getting Started: Terraform with Typescript

image.png

Reference: Github: hashicorp/terraform-cdk

While working with Terraform I found that Hashicorp made it possible to write Infrastructure as Code not only with the Hashicorp Configuration Language HCL but with common other languages: Typescript and Python.

This works via the Terraform Cloud Development Kit which transforms any instruction into JSON - which is a valid input format for Terraform itself.

How does it work (Terraform only)?

Imagine doing all your configuration in Terraform, for example, and for the sake of simplicity, a Docker based Nginx Webserver. In that case, you would define a Docker provider, a Docker Image of Nginx and a Container in your Terraform file which we will name: main.tf:

terraform {
  required_providers {
    docker = {
      source = "kreuzwerker/docker"
      version = "~> 3.0.1"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "tutorial"
  ports {
    internal = 80
    external = 8000
  }
}

Reference: Build Infrastructure - Terraform Docker Example

The above example is taken from the Getting-Started Tutorial of Hashicorp Terraform itself. Please follow the link above if you want to get into Terraform first.

Your prerequisites are to have Terraform and Docker installed and Docker running in the background. By executing the commands terraform init first to get the required dependencies and then executing terraform apply, specific steps happen sequentially. Based on that configuration file, a Docker image of Nginx will be downloaded and started as a container on your computer, so you can access it on your port 8000, e.g. localhost:8000

You should see this if you follow the Tutorial from Hashicorp Terraform, referenced above.

How does it work (Terraform with Typescript)?

Now let us imagine you have a reason not to write HCL-based configuration files. You would rather stick to one of the mentioned programming languages and are already saturated with YAML and JSON as a set of descriptive language formats. I'm personally a fan of Typescript and would rather use it instead of HCL. Fortunately, Hashicorp is not unfamiliar with that thought and created the Cloud-Development Kit for Terraform, in short: CDKTF.

Reference: Install CDK for Terraform and Run a Quick Start Demo

If you follow that link, you get the original documentation. It is essential for this topic of rewriting the above Nginx example. CDKTF can be seen as a command line tool for bootstrapping and running our Typescript based infrastructure-application that we are going to set up. So you might do the following steps:

  1. Create a directory and name it terraform-with-typescript

  2. Initialize within that repository a new cdktf-project cdktf init and follow the instructions. The CLI tool will ask you a set of questions: for this example we don't want remote state management and decline, then choose typescript and name and describe the project as you like.

    The tool then offers a set of possible providers. We chose Docker (pressing the spacebar to check it) and hit enter.

  3. When your project is initialized you will find a full directory with boilerplate code:

    We are only interested in the main.ts file, which needs to be modified to be like this:

     import { Construct } from "constructs";
     import { App, TerraformStack } from "cdktf";
     import { DockerProvider } from "@cdktf/provider-docker/lib/provider";
     import { Image } from "@cdktf/provider-docker/lib/image";
     import { Container } from "@cdktf/provider-docker/lib/container";
    
     class MyStack extends TerraformStack {
       constructor(scope: Construct, id: string) {
         super(scope, id);
    
         new DockerProvider(this, "docker", {})
    
         const dockerNginxImage = new Image(this, 'docker-nginx', {
           name: 'nginx:latest'
         })
    
         new Container(this, 'docker-container', {
           image: dockerNginxImage.imageId,
           name: 'docker-container-nginx',
           ports: [
             { internal: 80, external: 8080 }
           ]
         })
       }
     }
    
     const app = new App();
     new MyStack(app, "terraform-with-typescript");
     app.synth();
    
  4. Do you see the similarities between the main.tf file at the beginning of this article and the main.ts file here? We do mostly the same but in a language-specific manner: instantiating objects of the DockerProvider, an Image from the dependency @cdktf/provider-docker and a Container from the dependency @cdktf/provider-docker. You also see that resource references in HCL like
    image = docker_image.nginx.image_id become image: dockerNginxImage.imageId in Typescript.

  5. Now execute the code. You need to install any dependencies first with npm install then run npm run build and finally cdktf apply . Check the command line, cdktf wants you to approve the infrastructure change you are going to make.

    Hit enter and wait for it to complete. Also note which external port the example uses this time! You might check localhost:8080

  6. Voila! In the end, you might want to shut down the Nginx container. You might run cdktf destroy and again approve your infrastructure change. Your Docker container will be gone.

Where to continue from here?

You could start experimenting with different containers, or different configurations. Here is an idea: modify the Nginx container so it uses a volume. The volume is a subfolder in your project which is called data and it contains your custom index.html instead of that default "Welcome to nginx!" page above. Add the volume in your main.ts file to your container, so the object will look like this:

    new Container(this, 'docker-container', {
      image: dockerNginxImage.imageId,
      name: 'docker-container-nginx',
      ports: [
        { internal: 80, external: 8080 }
      ],
      // The volume references to a folder called 'data' 
      // in your project directory.
      volumes: [{
        containerPath: "/usr/share/nginx/html",
        hostPath: path.resolve('data'),
        readOnly: true
      }]
    })

Why should I use Typescript or any other language with Terraform?

This is a valid point. If you are fine with writing HCL and using the terraform CLI, the additional overhead of CDKTF might not be an option for you. On the other hand, you might have realised what possibilities it offers to be able to describe, test and deploy your infrastructure as code in the language you are most familiar with.

Thank you for your time!