Terraform to AWS CDK Converter (TypeScript)

Paste Terraform HCL and get an AWS CDK v2 stack in TypeScript back, with resource blocks rewritten as aws-cdk-lib constructs and references turned into typed properties.

Your codeTerraform (HCL)
Drop the file to load it
Tab indents · Esc, Tab exits
ResultAWS CDK (TypeScript)
Your AWS CDK (TypeScript) will appear hereClick Convert to CDK or press Ctrl Enter
stack.ts
Don't paste secrets

How to convert Terraform to AWS CDK in TypeScript

To convert Terraform to AWS CDK in TypeScript, paste a module or .tf file into the editor above and press Convert to CDK. Each resource block becomes an aws-cdk-lib construct inside a Stack class. Add the result to a CDK app, run cdk synth and cdk diff, and import existing resources before you deploy.

  1. Paste your TerraformPaste HCL into the Terraform (HCL) editor, use Open file for a .tf or .hcl file (up to 500 KB), or drop one onto the editor. Include the provider, variable and output blocks the resources use.
  2. Check for secretsThe page scans your code in the browser for AWS access keys, private keys and tokens before anything is sent. Variable defaults are a common place for them, so move real values to placeholders.
  3. Convert to CDKPress Convert to CDK or Ctrl + Enter. The TypeScript stack streams into the right-hand editor, and Stop cancels it. One conversion takes up to 60,000 characters, which fits most modules.
  4. Review and save stack.tsLook for TODO: comments where HCL has no direct CDK equivalent, such as ignore_changes. Then Copy the stack or Download it as stack.ts.
  5. Synth, diff and adoptPut the class in a project created with cdk init app --language typescript, run npx cdk synth and npx cdk diff, and bring live resources in with cdk import instead of creating them twice.
Four-step Terraform to AWS CDK TypeScript workflow: paste HCL, convert to a TypeScript stack, run cdk synth and cdk diff, then adopt live resources with cdk import
Converting the code is step two of four. The last step is what keeps your running resources safe.

Why teams move Terraform code to CDK TypeScript

The usual reason is consolidation. If your application code is TypeScript and the rest of your infrastructure already lives in CDK, keeping a few Terraform modules on the side means two toolchains, two state stores and two review styles. Moving them into CDK lets you type-check infrastructure with the same compiler, share constants and interfaces with the app, and use L2 constructs that write IAM policies for you.

Rewriting HCL by hand is slow because nothing maps one to one. Nested blocks become object literals, count becomes an array or loop, and three resources can collapse into a single construct. This converter produces a first draft of the whole stack so you can spend your review time on the decisions: which names to keep, which grants to trust, and how to adopt what's already deployed.

It's also a quick way to learn CDK if you know Terraform well: paste a module you understand and compare the two side by side. If your team is choosing Pulumi instead, the Terraform to Pulumi TypeScript converter does the same job for Pulumi programs.

Diagram showing aws_s3_bucket, aws_s3_bucket_versioning and aws_iam_role_policy Terraform blocks collapsing into one CDK s3.Bucket construct with versioned: true and a bucket.grantRead call
Three Terraform blocks, one L2 construct and one grant in TypeScript.

How Terraform concepts map to AWS CDK

Terraform describes infrastructure in HCL and records what it created in a state file. AWS CDK describes it in TypeScript and synthesizes a CloudFormation template, which a CloudFormation stack deploys and tracks. The converter maps each resource block to a construct, turns references such as aws_s3_bucket.uploads.id into properties like bucket.bucketName, and rewrites jsonencode() policies as TypeScript objects.

Expect the output to be shorter than the input. L2 constructs bundle related settings, so new s3.Bucket(this, "Uploads", { versioned: true }) replaces a bucket and its aws_s3_bucket_versioning resource. They also add defaults: a lambda.Function without a role creates its own execution role. Compare cdk synth output with what Terraform manages today.

Converting code doesn't move infrastructure. Deploying the new stack as-is tries to create second copies of your resources, and fails for anything with a fixed name, such as an S3 bucket. Adopt existing resources with cdk import, then remove them from Terraform state.

Comparison of deployment pipelines: Terraform applies main.tf and records resources in terraform.tfstate, while AWS CDK synthesizes stack.ts into a CloudFormation template that a CloudFormation stack deploys and tracks
Terraform tracks resources in its state file. CDK hands them to a CloudFormation stack.

Terraform to CDK TypeScript: quick reference

How common Terraform language features look in a CDK TypeScript stack.

Terraform language features and their AWS CDK TypeScript equivalents
TerraformExampleCDK TypeScript
Resourceresource "aws_sqs_queue" "jobs"new sqs.Queue(this, "Jobs", { … })
Referenceaws_sqs_queue.jobs.arnjobs.queueArn
Variablevariable "env" { default = "dev" }A stack prop, this.node.tryGetContext("env") or CfnParameter
Outputoutput "url" { value = … }new CfnOutput(this, "Url", { value: … })
Data sourcedata "aws_vpc" "main"ec2.Vpc.fromLookup(this, "Main", { … }) (needs account and region on the stack)
count / for_eachcount = 3A for loop or .map() over an array
depends_ondepends_on = [aws_iam_role.x]a.node.addDependency(b), rarely needed because references create dependencies
prevent_destroylifecycle { prevent_destroy = true }removalPolicy: RemovalPolicy.RETAIN
ignore_changeslifecycle { ignore_changes = [tags] }No direct equivalent. Flagged with a TODO: comment
Modulemodule "vpc" { source = "…" }A custom Construct class, or an existing L2 such as ec2.Vpc
Provider regionprovider "aws" { region = … }env: { region: … } on the stack

Key features

  • Resource blocks become L2 constructsBuckets, queues, tables, functions and roles are written with aws-cdk-lib constructs and camelCase props, not raw CloudFormation.
  • References become typed propertiesaws_dynamodb_table.orders.arn turns into orders.tableArn, so the TypeScript compiler catches a wrong attribute before cdk synth does.
  • Policies become TypeScript objectsjsonencode() policy documents are rewritten as iam.PolicyStatement objects or grant calls you can read and edit.
  • Variables, outputs and loops handledvariable, output and count are rewritten as stack props, CfnOutput and ordinary TypeScript loops.
  • Gaps marked with TODOWhere HCL has no CDK equivalent, such as ignore_changes or a registry module the converter can't see, the output says so in a TODO: comment.
  • Your names and comments keptResource names, logical groupings and comments carry over, so the new stack still reads like your module.
  • Secret check before sendingAccess keys, private keys and tokens are detected in your browser before any code leaves it.
  • Stream, copy or downloadThe stack appears as it's written. Copy it, or download it as stack.ts.

Examples: Terraform converted to CDK TypeScript

Two patterns that change shape in CDK. Each shows the Terraform input and the TypeScript it should become. Choose Try this example to load the Terraform into the converter. Your output may be formatted differently or use different construct IDs.

Variables, count and an output

The variable becomes a typed stack prop with the same default, count becomes .map(), and the splat expression in the output becomes Fn.join over the queue URLs.

Terraform (HCL)
variable "environment" {
  type    = string
  default = "staging"
}

resource "aws_sqs_queue" "jobs" {
  count                      = 3
  name                       = "jobs-${var.environment}-${count.index}"
  visibility_timeout_seconds = 120
}

output "queue_urls" {
  value = aws_sqs_queue.jobs[*].url
}
AWS CDK (TypeScript)
import { Stack, StackProps, Duration, CfnOutput, Fn } from "aws-cdk-lib";
import * as sqs from "aws-cdk-lib/aws-sqs";
import { Construct } from "constructs";

interface JobsStackProps extends StackProps {
  environment?: string;
}

export class JobsStack extends Stack {
  constructor(scope: Construct, id: string, props: JobsStackProps = {}) {
    super(scope, id, props);
    const environment = props.environment ?? "staging";

    const queues = [0, 1, 2].map(
      (i) =>
        new sqs.Queue(this, `Jobs${i}`, {
          queueName: `jobs-${environment}-${i}`,
          visibilityTimeout: Duration.seconds(120),
        })
    );

    new CfnOutput(this, "QueueUrls", {
      value: Fn.join(",", queues.map((q) => q.queueUrl)),
    });
  }
}

An IAM role with an inline S3 policy

Keeping the exact actions with addToPolicy preserves today's permissions. reports.grantRead(role) would be shorter, but it grants a broader set: s3:GetObject*, s3:GetBucket* and s3:List*.

Terraform (HCL)
resource "aws_s3_bucket" "reports" {
  bucket = "acme-reports"
}

resource "aws_iam_role" "exporter" {
  name = "report-exporter"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Action    = "sts:AssumeRole"
      Principal = { Service = "lambda.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy" "exporter_read" {
  role = aws_iam_role.exporter.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["s3:GetObject", "s3:ListBucket"]
      Resource = [aws_s3_bucket.reports.arn, "${aws_s3_bucket.reports.arn}/*"]
    }]
  })
}
AWS CDK (TypeScript)
import { Stack, StackProps } from "aws-cdk-lib";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";

export class ReportsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const reports = new s3.Bucket(this, "Reports", {
      bucketName: "acme-reports",
    });

    const exporter = new iam.Role(this, "Exporter", {
      roleName: "report-exporter",
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
    });

    exporter.addToPolicy(
      new iam.PolicyStatement({
        actions: ["s3:GetObject", "s3:ListBucket"],
        resources: [reports.bucketArn, reports.arnForObjects("*")],
      })
    );
  }
}

Tips for better results

  • Include provider and variable blocks. The region and variable defaults tell the converter which values to hard-code and which to expose as stack props.
  • Convert one module at a time. A Terraform module usually maps well to one CDK construct or stack, and a smaller input gives a more reliable draft.
  • Replace registry modules yourself. The converter only sees what you paste, so module "vpc" { source = "terraform-aws-modules/vpc/aws" } can't expand into a full VPC. Use the matching L2 construct, such as ec2.Vpc.
  • Swap zip files for assets. A Lambda filename archive becomes lambda.Code.fromAsset(). Point it at your source folder and let CDK package it.
  • Turn on strict mode. With "strict": true in tsconfig.json, the compiler flags wrong prop names and missing values in the converted stack. AWS's guide to working with CDK in TypeScript covers the project setup.

Common mistakes to avoid

  • Deploying before importing. A fresh cdk deploy creates new resources. For anything that already exists, run cdk import first, or the deploy fails on fixed names or leaves you with duplicates.
  • Leaving resources in Terraform state. After CDK has adopted a resource, remove it from Terraform with terraform state rm or a removed block. Otherwise the next terraform apply or destroy still acts on it.
  • Changing construct IDs later. The ID you pass as the second argument, such as "Uploads", becomes part of the CloudFormation logical ID. Renaming it makes CloudFormation replace the resource.
  • Trusting grants blindly. A grant* call can allow more actions than your hand-written Terraform policy. Check the AWS::IAM::Policy in cdk synth output.
  • Mixing CDK v1 imports. Everything in v2 comes from aws-cdk-lib and constructs. An @aws-cdk/aws-s3 import is CDK v1, which reached end-of-support on June 1, 2023.

Frequently asked questions

How do I convert Terraform to AWS CDK TypeScript?

Paste your Terraform HCL into the converter at the top of this page and press Convert to CDK. Each resource block becomes an aws-cdk-lib construct inside a Stack class. Add the class to a CDK app, run cdk synth and cdk diff, and use cdk import for resources that already exist.

Does the converter migrate my Terraform state?

No. It converts code only. Your resources stay in Terraform state, and a new CDK stack would try to create them again. Import them into the stack with cdk import or CloudFormation resource import, then remove them from Terraform with terraform state rm. For the trade-offs, see AWS CDK vs Terraform for existing AWS stacks. To hand deployed resources over, follow the guide to migrate a Terraform stack to AWS CDK TypeScript.

Will the CDK stack create exactly the same resources?

Close, but not always identical. L2 constructs add their own extras: a lambda.Function creates an execution role unless you pass one, and enforceSSL: true on a bucket adds a bucket policy. Compare cdk synth output with your Terraform resources before you switch.

Which version of AWS CDK does the output use?

CDK v2, where everything is imported from aws-cdk-lib and Construct comes from the constructs package. If you ever see @aws-cdk/core imports, that's v1 syntax and should be updated.

What happens to Terraform modules and data sources?

Local modules you paste are converted like any other code, often into a custom Construct. Registry modules can't be expanded because their source isn't in your paste, so the output marks them for you to replace. Data sources become lookups such as Vpc.fromLookup or references to existing resources by ARN or name.

Can I keep using Terraform for some resources?

Yes. Many teams migrate one stack at a time. Pass values between the two with SSM parameters or stack outputs until everything has moved.

Should I convert to TypeScript or Python?

Use the language your team writes. TypeScript is CDK's native language, so examples and types line up exactly. If your team works in Python, use the Terraform to CDK Python converter instead.

Is my Terraform or CDK code stored?

No. The code is sent to an AI model hosted on NVIDIA's API only to produce the conversion, and ChatWithCloud doesn't store or log it. Your latest draft stays in your browser. Before converting, replace account IDs you consider sensitive, and any secrets in variable defaults, with placeholders. For the full picture, see whether it's safe to paste AWS code into an AI converter.

Is it free, and how much can I paste?

Yes. Each conversion takes up to 60,000 characters, and you can open files up to 500 KB. Without an account you get 6 conversions a minute and 60 a day. A free account raises that to 12 a minute and 200 a day and lists your recent conversions on your dashboard. The guide to ChatWithCloud free converter limits shows how far they stretch for a real migration.

Related tools and next steps