Converting Existing Terraform HCL to CDKTF Python
Migrating legacy HCL to programmatic infrastructure requires strict state preservation. Converting existing Terraform HCL to CDKTF Python eliminates configuration drift. It also enables deterministic CI/CD validation pipelines.
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 terraform force-unlock -force <LOCK_ID> # Use only if backend is stuck
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 out of the box. You must explicitly pin provider versions to prevent silent breaking changes.
CLI: Automated HCL Translation
cdktf init --template="python" --local cdktf convert --language python --provider-hashicorp/aws --provider-version "~> 5.0" < main.tf > 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 --no-color to validate the initial JSON payload.
Validation Steps:
- Execute
cdktf convert --language python --provider-hashicorp/aws - Audit
cdktf.jsonfor exact provider version constraints - Run
cdktf synth --no-colorand inspect the output directory
Python 3.9+ Type Enforcement & Construct Refactoring
Automated translation strips type safety. You must 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 import AwsProvider, s3
class MigratedStack(TerraformStack):
def __init__(
self,
scope: Construct,
ns: str,
config: Dict[str, Optional[Any]]
) -> None:
super().__init__(scope, ns)
# Secure credential handling via environment variables
access_key = os.environ.get("AWS_ACCESS_KEY_ID")
secret_key = os.environ.get("AWS_SECRET_ACCESS_KEY")
AwsProvider(
self,
"aws",
region=config.get("region", "us-east-1"),
access_key=access_key,
secret_key=secret_key
)
bucket_name = config.get("bucket_name")
if not bucket_name:
raise ValueError("bucket_name is required for S3 provisioning")
s3.Bucket(
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. Validate construct boundaries to prevent environment variable leakage.
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. You must map legacy IDs to the new construct tree safely. Use cdktf import to reconcile state files without triggering destructive replacements.
CLI: State Address Mapping & Import
terraform state list cdktf synth cdktf import aws_s3_bucket.data <state-file.json> --state=terraform.tfstate
When addressing provider-specific state mapping and ID translation, consult Terraform Provider Bridging. Execute cdktf diff --json to verify zero unexpected changes before deployment.
Validation Steps:
- Run
cdktf synthto generate updated JSON payloads - Execute
cdktf import <resource-id> <state-file>for each legacy resource - Validate
cdktf diff --jsonoutput for zero drift - Confirm no destructive actions appear in the execution plan
Production Rollback & CI/CD Pipeline Handoff
Safe deployment requires automated rollback triggers. Configure your CI/CD runner to execute cdktf test before any production deployment. Implement state snapshot restoration on failure to maintain infrastructure consistency.
import subprocess
import sys
from typing import Tuple
def validate_drift() -> int:
"""Programmatic drift validation for CI/CD integration."""
result = subprocess.run(
["cdktf", "diff", "--json"],
capture_output=True,
text=True,
check=False
)
if result.returncode != 0:
print("Drift detected or synth failed.", file=sys.stderr)
return 1
print("State matches synthetic output.")
return 0
if __name__ == "__main__":
sys.exit(validate_drift())
Establish pre-deploy validation hooks. Schedule post-deploy drift monitoring via cron jobs. Define explicit failure boundaries to prevent partial deployments.
Validation Steps:
- Configure pipeline to run
cdktf testbeforecdktf 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. - Overwriting remote state by deploying before executing
cdktf import. - 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. Run cdktf synth. Use cdktf import or terraform state mv 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.
How do I enforce Python 3.9+ typing for complex infrastructure variables?
Use typing.Dict[str, Any], typing.Optional, and typing.List for all 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. Run terraform state replace-provider if aliases changed. Revert to the legacy branch until drift is reconciled.