lesson ── terraform-advanced ── ~18 мин ── 7 шагов
CDKTF: you write infrastructure in TypeScript, and the output is HCL plus terraform.tfstate.
The image already ships cdktf-cli next to node and npm. In this lesson you'll generate a
stack, synthesize it, look at the resulting HCL, and deploy.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
cd /home/student/cdktf-demo
which cdktf
cdktf --version
node --version
cdktf-cli is baked into the image globally (/usr/local/bin/cdktf),
next to node 20 LTS and npm 10. No npm install -g, which saves a
minute of install time and removes a runtime dependency on the npm registry.
✓ cdktf-cli is in place, the version shows up.
cdktf init --template=typescript --local --project-name=cdktf-demo --project-description="demo" 2>&1 | tail -10
ls
cat cdktf.json
--local keeps state local (not in HCP Terraform). The project now
has main.ts, cdktf.json, package.json, and node_modules/.
cdktf.json is the main config: which language, and which providers
to pull in.
✓ The TypeScript project is ready.
# edit cdktf.json, add the aws provider
python3 - <<'EOF'
import json
with open('cdktf.json') as f:c = json.load(f)
c['terraformProviders'] = ['aws@~> 5.60']
with open('cdktf.json', 'w') as f:json.dump(c, f, indent=2)
EOF
cat cdktf.json
cdktf get 2>&1 | tail -5
ls .gen/providers/aws/
cdktf get generated TypeScript types for the AWS provider. They
live in .gen/. Without them, TS does not know about S3Bucket, IamRole, and
so on.
✓ AWS provider type bindings are ready.
cat > main.ts <<'EOF'
import { App, TerraformStack, TerraformOutput } from "cdktf";import { Construct } from "constructs";import { AwsProvider } from "./.gen/providers/aws/provider";import { S3Bucket } from "./.gen/providers/aws/s3-bucket";class CdktfDemoStack extends TerraformStack { constructor(scope: Construct, id: string) {super(scope, id);
new AwsProvider(this, "aws", {region: "us-east-1",
accessKey: "test",
secretKey: "test",
s3UsePathStyle: true,
skipCredentialsValidation: true,
skipMetadataApiCheck: "true",
skipRequestingAccountId: true,
endpoints: [{s3: "http://localstack:4566",
iam: "http://localstack:4566",
sts: "http://localstack:4566",
}],
});
const envs = ["dev", "stage", "prod"];
const buckets = envs.map((env) =>
new S3Bucket(this, `bucket-${env}`, { bucket: `linuxlab-cdktf-${env}`, tags: { Environment: env, ManagedBy: "cdktf" },})
);
new TerraformOutput(this, "bucket-ids", {value: buckets.map((b) => b.id),
});
}
}
const app = new App();
new CdktfDemoStack(app, "demo");
app.synth();
EOF
Three buckets through Array.map, and that is the advantage of CDKTF over
native HCL. In HCL you write for_each = toset(...); in TS, a plain
loop with types.
✓ The TypeScript stack is ready. Now we synthesize.
cdktf synth 2>&1 | tail -5
cat cdktf.out/stacks/demo/cdk.tf.json | jq '.resource.aws_s3_bucket | keys'
You see three keys: bucket-dev, bucket-stage, bucket-prod.
This is the plain JSON form of HCL, and Terraform understands it.
✓ HCL generated. 3 buckets from a single array.map.
cdktf deploy demo --auto-approve 2>&1 | tail -10
aws --endpoint-url=http://localstack:4566 s3 ls
You see three buckets created. Under the hood, cdktf deploy = synth +
terraform init + terraform apply in cdktf.out/stacks/demo/.
State is a plain terraform.tfstate in the same folder. This means
you can drop CDKTF, delete main.ts, and write HCL by hand:
the state is compatible, and you lose nothing.
✓ Three buckets deployed through TypeScript.
OpenTofu keeps the CLI and state compatible with Terraform for the commands
in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. But on the first switch,
back up the state and run on a feature branch, since the differences
concentrate in the newer features (variables in the backend,
state encryption, OCI registry-backed modules). See
tf-opentofu-parity for the full matrix.
When native HCL is shorter, it is the choice:
# HCL, 5 lines
resource "aws_s3_bucket" "envs" {for_each = toset(["dev", "stage", "prod"])
bucket = "linuxlab-${each.key}"}
When generation gets more complex, CDKTF wins:
// 100 tenants from JSON, different conf per tenant
const tenants = require("./tenants.json");tenants.forEach((t: any) => { if (t.tier === "premium") { new PremiumTenantStack(app, `tenant-${t.id}`, t); } else { new BasicTenantStack(app, `tenant-${t.id}`, t);}
});
In HCL this would take 3-4 modules and hairy count = condition ? 1 : 0 logic. In TS, it is linear.
Remember the smell:
forEach, if, classes) → CDKTF.Cleanup:
cdktf destroy demo --auto-approve 2>&1 | tail -3
✓ CDKTF used deliberately. Next, your own provider in Go.
The CDKTF equivalent of a Terraform module is a Construct:
import { Construct } from "constructs";import { S3Bucket } from "./.gen/providers/aws/s3-bucket";import { S3BucketVersioningA } from "./.gen/providers/aws/s3-bucket-versioning";interface AuditedBucketProps {name: string;
tags?: { [key: string]: string };}
export class AuditedBucket extends Construct {public readonly bucket: S3Bucket;
constructor(scope: Construct, id: string, props: AuditedBucketProps) {super(scope, id);
this.bucket = new S3Bucket(this, "bucket", {bucket: props.name,
tags: props.tags,
});
new S3BucketVersioningA(this, "versioning", {bucket: this.bucket.id,
versioningConfiguration: { status: "Enabled" },});
}
}
// usage
new AuditedBucket(this, "logs", { name: "linuxlab-logs" });Advantages over a Terraform module: type-safe props,
inheritance (extends), and composition through TypeScript.
cdktf is already in the image. cdktf init --template=typescript gives you a skeleton.
main.ts holds App + Stack + Constructs. cdktf get pulls type bindings for
providers. cdktf synth generates cdktf.out/stacks/<name>/cdk.tf.json.
cdktf deploy runs apply.
команды
cdktf --versioncdktf-cli is preinstalled in the sandbox image.cdktf init --template=typescript --localproject skeleton.cdktf getpull type bindings for providers.cdktf synthgenerate HCL.cdktf deploy <stack>synth + apply in one command.концепции