DEV Community

Alex Spinov
Alex Spinov

Posted on

CDKTF Has a Free API: Write Terraform Infrastructure with TypeScript Instead of HCL

What is CDKTF?

CDKTF (Cloud Development Kit for Terraform) lets you write Terraform infrastructure using TypeScript, Python, Go, C#, or Java instead of HCL. You get the full Terraform ecosystem — thousands of providers — with real programming language features.

Quick Start

npm install -g cdktf-cli
cdktf init --template=typescript --providers=aws
cdktf deploy
Enter fullscreen mode Exit fullscreen mode

Define Infrastructure

import { Construct } from "constructs";
import { App, TerraformStack, TerraformOutput } from "cdktf";
import { AwsProvider } from "@cdktf/provider-aws/lib/provider";
import { S3Bucket } from "@cdktf/provider-aws/lib/s3-bucket";
import { Instance } from "@cdktf/provider-aws/lib/instance";
import { SecurityGroup } from "@cdktf/provider-aws/lib/security-group";

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

    new AwsProvider(this, "aws", { region: "us-east-1" });

    // S3 bucket
    const bucket = new S3Bucket(this, "my-bucket", {
      bucket: "my-app-assets-2026",
      tags: { Environment: "production" },
    });

    // Security group
    const sg = new SecurityGroup(this, "web-sg", {
      name: "web-server-sg",
      ingress: [
        { fromPort: 80, toPort: 80, protocol: "tcp", cidrBlocks: ["0.0.0.0/0"] },
        { fromPort: 443, toPort: 443, protocol: "tcp", cidrBlocks: ["0.0.0.0/0"] },
      ],
      egress: [
        { fromPort: 0, toPort: 0, protocol: "-1", cidrBlocks: ["0.0.0.0/0"] },
      ],
    });

    // EC2 instance
    const server = new Instance(this, "web-server", {
      ami: "ami-0c55b159cbfafe1f0",
      instanceType: "t3.micro",
      vpcSecurityGroupIds: [sg.id],
      tags: { Name: "Web Server" },
    });

    new TerraformOutput(this, "bucket-name", { value: bucket.bucket });
    new TerraformOutput(this, "server-ip", { value: server.publicIp });
  }
}

const app = new App();
new MyStack(app, "my-infrastructure");
app.synth();
Enter fullscreen mode Exit fullscreen mode

Real Programming — Loops and Functions

// Create multiple environments with a loop
const environments = ["dev", "staging", "production"];

for (const env of environments) {
  new S3Bucket(this, `bucket-${env}`, {
    bucket: `myapp-${env}-assets`,
    tags: { Environment: env },
  });
}

// Reusable function
function createVpc(stack: TerraformStack, name: string, cidr: string) {
  return new Vpc(stack, name, {
    cidrBlock: cidr,
    enableDnsHostnames: true,
    tags: { Name: name },
  });
}

const devVpc = createVpc(this, "dev-vpc", "10.0.0.0/16");
const prodVpc = createVpc(this, "prod-vpc", "10.1.0.0/16");
Enter fullscreen mode Exit fullscreen mode

Constructs (Reusable Components)

class WebService extends Construct {
  public readonly url: string;

  constructor(scope: Construct, id: string, props: {
    port: number;
    instanceType?: string;
    environment: string;
  }) {
    super(scope, id);

    const sg = new SecurityGroup(this, "sg", {
      ingress: [{
        fromPort: props.port,
        toPort: props.port,
        protocol: "tcp",
        cidrBlocks: ["0.0.0.0/0"],
      }],
    });

    const instance = new Instance(this, "server", {
      ami: "ami-0c55b159cbfafe1f0",
      instanceType: props.instanceType || "t3.micro",
      vpcSecurityGroupIds: [sg.id],
      tags: {
        Name: `${id}-server`,
        Environment: props.environment,
      },
    });

    this.url = instance.publicIp;
  }
}

// Use like a building block
new WebService(this, "api", { port: 3000, environment: "prod" });
new WebService(this, "admin", { port: 8080, environment: "prod", instanceType: "t3.small" });
Enter fullscreen mode Exit fullscreen mode

Testing Infrastructure

import { Testing } from "cdktf";

describe("MyStack", () => {
  it("should create an S3 bucket", () => {
    const app = Testing.app();
    const stack = new MyStack(app, "test");
    const synth = Testing.synth(stack);

    expect(Testing.toHaveResource(synth, "aws_s3_bucket")).toBeTruthy();
  });

  it("should use t3.micro for instances", () => {
    const app = Testing.app();
    const stack = new MyStack(app, "test");
    const synth = Testing.synth(stack);

    expect(
      Testing.toHaveResourceWithProperties(synth, "aws_instance", {
        instance_type: "t3.micro",
      })
    ).toBeTruthy();
  });
});
Enter fullscreen mode Exit fullscreen mode

Deploy

# Synthesize to Terraform JSON
cdktf synth

# Plan changes
cdktf diff

# Deploy
cdktf deploy

# Destroy
cdktf destroy
Enter fullscreen mode Exit fullscreen mode

CDKTF vs Terraform HCL vs Pulumi

Feature CDKTF Terraform HCL Pulumi
Language TS/Python/Go HCL TS/Python/Go
Ecosystem Terraform providers Terraform providers Own providers
State Terraform state Terraform state Pulumi state
Testing Jest/pytest Terratest Jest/pytest
Maturity GA Mature GA

Need cloud infrastructure automation or IaC consulting?

📧 spinov001@gmail.com
🔧 My tools on Apify Store

CDKTF, Pulumi, or plain Terraform? Let me know your pick!

Top comments (0)