Converting Existing Terraform HCL to CDKTF Python
Migrating legacy HCL to programmatic infrastructure requires strict state preservation. Converting to CDKTF Python eliminates configuration drift and enables deterministic CI/CD validation pipelines. This task sits within Terraform Provider Bridging, which translates Terraform provider schemas into the typed Python classes your converted code will target.
This guide details the exact workflow for safe translation. We prioritize state integrity and secure credential handling. Every step includes testable Python IaC patterns.
Pre-Migration State Audit & Backend Locking
Before modifying infrastructure code, verify remote state integrity. Enforce backend locking immediately. Immutable state snapshots prevent catastrophic resource recreation during translation.
Always export a full state backup before executing synthesis commands. Cross-reference provider configurations against your target environment.
CLI: State Audit & Backup
terraform state list terraform state pull > state-backup.json
Validate the backend lock status. Confirm all resource addresses resolve correctly. For comprehensive backend compatibility and lock mechanisms, review CDKTF Workflows & Terraform Synthesis.
Validation Steps:
- Run
terraform state listto verify resource inventory - Export
terraform state pull > state-backup.jsonfor immutable rollback - Confirm backend lock status via provider dashboard or CLI output
Automated Translation via cdktf convert
The cdktf convert utility generates baseline Python constructs from legacy HCL files. It does not produce production-ready code. You must explicitly pin provider versions to prevent silent breaking changes.
CLI: Automated HCL Translation
cdktf init --template="python" --local # Convert a single HCL file to Python cat main.tf | cdktf convert --language python --provider "hashicorp/aws@~> 6.0" > main.py
After generation, verify cdktf.json provider constraints match your requirements.txt. Replace dynamic HCL blocks with native Python iteration patterns. Run cdktf synth to validate the initial JSON payload.
Validation Steps:
- Audit
cdktf.jsonfor exact provider version constraints - Run
cdktf synthand inspect the output directory - Compare synthesized JSON against the original
terraform planoutput for parity
Python 3.9+ Type Enforcement & Construct Refactoring
Automated translation strips type safety. Apply strict typing annotations to all construct parameters. Replace HCL count and for_each directives with Python list comprehensions or dictionary mappings.
from typing import Dict, List, Optional, Any
import os
from constructs import Construct
from cdktf import TerraformStack
from cdktf_cdktf_provider_aws.provider import AwsProvider
from cdktf_cdktf_provider_aws.s3_bucket import S3Bucket
class MigratedStack(TerraformStack):
def __init__(
self,
scope: Construct,
ns: str,
config: Dict[str, Optional[Any]],
) -> None:
super().__init__(scope, ns)
# Prefer OIDC/IAM roles over static credentials in production
AwsProvider(
self,
"aws",
region=config.get("region", "us-east-1"),
)
bucket_name = config.get("bucket_name")
if not bucket_name:
raise ValueError("bucket_name is required for S3 provisioning")
S3Bucket(
self,
"data",
bucket=bucket_name,
tags=config.get("tags", {}),
)
Enforce mypy --strict or pyright checks before synthesis. This catches AttributeError exceptions caused by untyped nested configurations.
Validation Steps:
- Add explicit type hints to all
__init__parameters - Replace HCL loops with Python comprehensions
- Execute
mypy --strict main.pyand resolve all warnings
State Reconciliation & Drift Detection
Legacy resource addresses rarely match CDKTF-generated identifiers. Map legacy IDs to the new construct tree safely. Use terraform import in the synthesized output directory to reconcile state files without triggering destructive replacements.
CLI: State Address Mapping & Import
# 1. Synthesize to generate the Terraform configuration cdktf synth # 2. Import existing resources into the synthesized Terraform state terraform -chdir=cdktf.out/stacks/import aws_s3_bucket.data # 3. Verify zero drift after import terraform -chdir=cdktf.out/stacks/ plan -detailed-exitcode
When addressing provider-specific state mapping and ID translation, consult Terraform Provider Bridging. If your original HCL declared aliased or multi-region providers, replicate that topology with the patterns in Using Multiple Terraform Providers in One CDKTF Stack so imported resources resolve to the correct provider. A -detailed-exitcode of 0 from terraform plan confirms zero drift.
Validation Steps:
- Run
cdktf synthto generate updated JSON payloads - Execute
terraform importfor each legacy resource in the synthesized stack directory - Validate
terraform plan -detailed-exitcodereturns0(no changes)
Production Rollback & CI/CD Pipeline Handoff
Safe deployment requires automated rollback triggers. Configure your CI/CD runner to execute plan validation before any production deployment. Implement state snapshot restoration on failure to maintain infrastructure consistency.
import sys
import subprocess
def validate_drift(stack_name: str) -> int:
"""Programmatic drift validation for CI/CD integration.
Returns 0 if no changes detected, 1 if drift found or plan failed.
Uses terraform plan --detailed-exitcode: 0=no changes, 2=changes pending, 1=error.
"""
cdktf_out_dir = f"cdktf.out/stacks/{stack_name}"
result = subprocess.run(
["terraform", "-chdir", cdktf_out_dir, "plan", "-detailed-exitcode"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0:
print("State matches synthesized output: no changes pending.")
return 0
elif result.returncode == 2:
print("Drift detected: changes are pending.", file=sys.stderr)
return 1
else:
print(f"Plan failed:\n{result.stderr}", file=sys.stderr)
return 1
if __name__ == "__main__":
stack = sys.argv[1] if len(sys.argv) > 1 else "MigratedStack"
sys.exit(validate_drift(stack))
Establish pre-deploy validation hooks. Schedule post-deploy drift monitoring. Define explicit failure boundaries to prevent partial deployments.
Validation Steps:
- Run
cdktf synththenterraform planbeforecdktf deploy - Implement automated state snapshot restore on pipeline failure
- Schedule drift detection jobs for continuous compliance monitoring
Common Mistakes
- Assuming
cdktf convertyields production-ready code without manual type annotation. - Failing to pin provider versions in
cdktf.json, causing silent synthesis drift. - Deploying before executing
terraform importfor existing resources, causing state divergence. - Ignoring Python typing boundaries, leading to runtime
AttributeErroron nested configs. - Skipping
cdktf synthvalidation in CI/CD, resulting in undetected JSON payload errors.
FAQ
How do I preserve existing Terraform state during CDKTF conversion?
Lock the remote backend. Export a state snapshot with terraform state pull. Run cdktf synth. Use terraform import in the synthesized output directory to map legacy addresses before deploying.
Does cdktf convert handle dynamic blocks and for_each correctly?
It generates baseline equivalents. Complex loops require manual refactoring into native Python comprehensions with strict type hints. Dynamic blocks typically become Python if statements or loops over construct IDs.
How do I enforce Python 3.9+ typing for complex infrastructure variables?
Use typing.Dict[str, Any], typing.Optional, and typing.List for heterogeneous inputs. Validate at instantiation with pydantic or isinstance checks to prevent runtime synthesis failures.
What is the safest rollback procedure if cdktf deploy fails?
Halt the pipeline immediately. Restore the pre-migration state snapshot via terraform state push. Revert to the legacy branch until drift is reconciled. Run terraform plan to verify the restored state before re-attempting the migration.
Conclusion
The cdktf convert tool gets you 60-70% of the way through a migration—the rest is manual type annotation, construct refactoring, and state import. The most critical step is the terraform import phase: skipping it and deploying directly will either fail (if resources already exist and names conflict) or create duplicate resources (if CDKTF uses different naming). Budget time for this and validate zero drift before declaring the migration complete.
Related
- Terraform Provider Bridging — the parent guide on translating provider schemas into typed Python classes.
- Using Multiple Terraform Providers in One CDKTF Stack — replicate aliased and multi-region provider topologies from your original HCL.
- State Backend Configuration for CDKTF — lock and back up remote state before running conversion and import.