Documentation Index
Fetch the complete documentation index at: https://tfts.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
tfts is a drop-in replacement for CDKTF. Migration requires updating imports and regenerating provider bindings.
Migration Steps
Step 1: Update Dependencies
Remove CDKTF packages and add tfts:
# Remove CDKTF and pre-built providers
npm remove cdktf cdktf-cli @cdktf/provider-aws @cdktf/provider-google
# Add tfts
npm install tfts
Step 2: Update Core Imports
Change imports from cdktf to tfts:
// Before
import { App, TerraformStack, TerraformOutput, TerraformVariable, Fn } from "cdktf";
// After
import { App, TerraformStack, TerraformOutput, TerraformVariable, Fn } from "tfts";
Step 3: Update Provider Imports
CDKTF uses pre-built provider packages (@cdktf/provider-*). tfts generates providers locally in .gen/.
// Before (CDKTF pre-built providers)
import { GoogleProvider } from "@cdktf/provider-google/lib/provider";
import { ComputeInstance } from "@cdktf/provider-google/lib/compute-instance";
import * as google from "@cdktf/provider-google";
// After (tfts generated providers - paths relative to your file)
import { GoogleProvider } from "../../../.gen/providers/hashicorp/google/lib/provider/index.js";
import { ComputeInstance } from "../../../.gen/providers/hashicorp/google/lib/compute-instance/index.js";
import * as google from "../../../.gen/providers/hashicorp/google/index.js";
Note: The relative path depth (../../../) depends on your file’s location relative to the project root.
Step 4: Regenerate Provider Bindings
This reads cdktf.json (or tfts.json) and generates TypeScript classes in .gen/.
Step 5: Update Configuration (Optional)
Rename cdktf.json to tfts.json if desired (both are supported):
Automated Migration
Save this script as migrate-from-cdktf.ts and run with bun migrate-from-cdktf.ts .:
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { dirname, join, relative, resolve } from "node:path";
function migrateFile(filePath: string, projectRoot: string): { modified: boolean; changes: string[] } {
const content = readFileSync(filePath, "utf-8");
let newContent = content;
const changes: string[] = [];
const fileDir = dirname(filePath);
const relToGen = relative(fileDir, join(projectRoot, ".gen"));
const cdktfCorePattern = /from\s+["']cdktf["']/g;
if (cdktfCorePattern.test(newContent)) {
newContent = newContent.replace(cdktfCorePattern, 'from "tfts"');
changes.push('from "cdktf" -> from "tfts"');
}
const constructsPattern = /from\s+["']constructs["']/g;
if (constructsPattern.test(newContent)) {
newContent = newContent.replace(constructsPattern, 'from "tfts"');
changes.push('from "constructs" -> from "tfts"');
}
const namespaceImportPattern = /from\s+["']@cdktf\/provider-([^/"']+)["']/g;
newContent = newContent.replace(namespaceImportPattern, (_, provider) => {
const newPath = `${relToGen}/providers/hashicorp/${provider}/index.js`;
changes.push(`@cdktf/provider-${provider} -> ${newPath}`);
return `from "${newPath}"`;
});
const namedImportPattern = /from\s+["']@cdktf\/provider-([^/"']+)\/lib\/([^"']+)["']/g;
newContent = newContent.replace(namedImportPattern, (_, provider, modulePath) => {
const newPath = `${relToGen}/providers/hashicorp/${provider}/lib/${modulePath}/index.js`;
changes.push(`@cdktf/provider-${provider}/lib/${modulePath} -> ${newPath}`);
return `from "${newPath}"`;
});
if (content !== newContent) writeFileSync(filePath, newContent);
return { modified: content !== newContent, changes };
}
function findTypeScriptFiles(dir: string): string[] {
const files: string[] = [];
const skipDirs = new Set(["node_modules", ".gen", "cdktf.out", "dist", ".git"]);
function walk(currentDir: string) {
for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
const fullPath = join(currentDir, entry.name);
if (entry.isDirectory() && !skipDirs.has(entry.name)) walk(fullPath);
else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name)) files.push(fullPath);
}
}
walk(dir);
return files;
}
const targetDir = resolve(process.argv[2] || ".");
if (!existsSync(targetDir) || !statSync(targetDir).isDirectory()) {
console.error(`Error: "${targetDir}" is not a valid directory`);
process.exit(1);
}
console.log(`Migrating CDKTF -> tfts in: ${targetDir}\n`);
const files = findTypeScriptFiles(targetDir);
let totalModified = 0;
for (const file of files) {
const { modified, changes } = migrateFile(file, targetDir);
if (modified) {
totalModified++;
console.log(file);
for (const change of changes) console.log(` ${change}`);
}
}
console.log(`\n${totalModified} file(s) modified`);
if (totalModified > 0) console.log("\nNext: npx tfts get");
The script converts:
from "cdktf" → from "tfts"
from "constructs" → from "tfts"
@cdktf/provider-X → {relPath}/.gen/providers/hashicorp/X/index.js
@cdktf/provider-X/lib/Y → {relPath}/.gen/providers/hashicorp/X/lib/Y/index.js
Import Path Reference
| CDKTF Import | tfts Import |
|---|
from "cdktf" | from "tfts" |
from "constructs" | from "tfts" |
from "@cdktf/provider-google" | from "{relPath}/.gen/providers/hashicorp/google/index.js" |
from "@cdktf/provider-google/lib/provider" | from "{relPath}/.gen/providers/hashicorp/google/lib/provider/index.js" |
from "@cdktf/provider-google/lib/compute-instance" | from "{relPath}/.gen/providers/hashicorp/google/lib/compute-instance/index.js" |
from "@cdktf/provider-aws" | from "{relPath}/.gen/providers/hashicorp/aws/index.js" |
from "@cdktf/provider-aws/lib/s3-bucket" | from "{relPath}/.gen/providers/hashicorp/aws/lib/s3-bucket/index.js" |
{relPath} = relative path from your file to project root (e.g., ../../.. for src/stacks/my-stack/)
API Compatibility
tfts maintains API compatibility with CDKTF core constructs:
| Class | Status |
|---|
App | Compatible |
TerraformStack | Compatible |
TerraformResource | Compatible |
TerraformDataSource | Compatible |
TerraformOutput | Compatible |
TerraformVariable | Compatible |
TerraformLocal | Compatible |
TerraformProvider | Compatible |
Fn | Compatible |
TerraformIterator | Compatible |
| Backends (S3, GCS, etc.) | Compatible |
Verification
After migration, verify the generated Terraform JSON is correct:
# Synthesize
npx tfts synth
# Compare output (if you have previous output)
diff -r old-cdktf.out/stacks/my-stack cdktf.out/stacks/my-stack
# Or run terraform plan to check
cd cdktf.out/stacks/my-stack
terraform init
terraform plan
Troubleshooting
Import Errors
If you see import errors after migration, ensure:
- You ran
npx tfts get to generate providers
- Provider imports use
.js extension (ESM requirement)
- Provider paths match the structure in
.gen/
Missing Providers
Add any missing providers to your config:
{
"terraformProviders": [
"hashicorp/google@~>6.0",
"hashicorp/aws@~>5.0"
]
}
Then regenerate: npx tfts get