# Getting Started: Terraform with Typescript

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1609502307247/RJ6O4knrN.png align="left")

Reference: [Github: hashicorp/terraform-cdk](https://github.com/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*:

```plaintext
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](https://developer.hashicorp.com/terraform/tutorials/docker-get-started/docker-build)

*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. *http://localhost:8000*

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678489912222/e81ed7cb-983c-4c8a-b0f5-6214106ba28c.png align="left")

*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*](https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-install)

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.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678491752568/baad1e69-f3da-45c6-aad2-16f813951a25.png align="left")
    
    The tool then offers a set of possible providers. We chose Docker (pressing the spacebar to check it) and hit enter.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678491952467/98dbaebe-66a6-42f1-b7e7-61474a46cc2a.png align="center")
    
3. When your project is initialized you will find a full directory with boilerplate code:
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678493629996/9ad7ff84-da86-4bde-a260-20d6db9bee06.png align="left")
    
    We are only interested in the main.ts file, which needs to be modified to be like this:
    
    ```typescript
    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.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678494703051/b43a3167-cd3e-485c-966a-8eba3294578a.png align="left")
    
    Hit enter and wait for it to complete. Also note which external port the example uses this time! You might check http://localhost:**8080**
    
6. ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678494823706/9df7c523-bd65-4e78-bd1a-ea395454bd5d.png align="left")
    
    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:

```typescript
    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!
