Tại Sao Bạn Muốn Thay HCL bằng Python
Nếu bạn đã từng làm việc với Terraform, bạn biết HCL (HashiCorp Configuration Language) hoàn toàn ổn — cho đến khi bạn cần một vòng lặp làm gì đó hơi khác thường, hoặc muốn tái sử dụng logic giữa các stack mà không phải copy-paste block khắp nơi. HCL được thiết kế theo kiểu khai báo, rất tốt cho hạ tầng đơn giản, nhưng khi độ phức tạp tăng lên, bạn sẽ ước mình có thể viết một function.
Đây chính xác là khoảng trống mà CDK for Terraform lấp đầy. CDKTF cho phép bạn viết code hạ tầng bằng Python (hoặc TypeScript, Go, Java, C#) và tự động sinh ra cấu hình Terraform JSON bên dưới. State, provider và backend Terraform thực tế của bạn vẫn giữ nguyên — bạn chỉ đang dùng một ngôn ngữ lập trình thực sự để định nghĩa chúng.
Tôi đã áp dụng cách tiếp cận này trong môi trường production trên nhiều AWS environment, và kết quả luôn ổn định. Terraform JSON được tạo ra là hợp lệ, dự đoán được, và tương thích tốt với các CI/CD pipeline hiện có đang chạy terraform plan và terraform apply.
Khi Nào CDKTF Phù Hợp
- Bạn cần logic có điều kiện hoặc vòng lặp mà viết trong HCL cảm thấy vụng về
- Team của bạn đã quen Python nhưng thấy cú pháp HCL lạ lẫm
- Bạn muốn chia sẻ các pattern hạ tầng dưới dạng Python package
- Bạn đang xây dựng nền tảng nội bộ cho developer mà hạ tầng được sinh ra động
Nếu hạ tầng của bạn nhỏ và đơn giản, Terraform thuần là đủ. Nhưng khi bạn quản lý hàng chục environment với cấu hình dùng chung, CDKTF bắt đầu phát huy giá trị.
Cài Đặt
CDKTF có hai yêu cầu runtime: Node.js (cho CLI) và Python (cho code stack thực tế). Bạn cần cả hai dù đang viết Python — vì CLI là công cụ Node.js.
Bước 1: Cài Đặt Node.js
CLI CDKTF yêu cầu Node.js 18 trở lên. Nếu bạn đang dùng Ubuntu/Debian:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node --version # phải là v20.x hoặc mới hơn
Trên macOS với Homebrew:
brew install node
Bước 2: Cài Đặt CDKTF CLI
npm install -g cdktf-cli
cdktf --version # xác nhận cài đặt thành công
Bước 3: Cài Đặt Terraform
CDKTF sinh ra cấu hình Terraform, vì vậy bạn vẫn cần Terraform để thực hiện provisioning thực tế:
# Trên Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
terraform --version
Bước 4: Khởi Tạo Dự Án CDKTF Mới
Tạo thư mục mới và khởi tạo dự án Python:
mkdir my-cdktf-project && cd my-cdktf-project
cdktf init --template=python --local
Flag --local lưu Terraform state cục bộ (file terraform.tfstate). Cho môi trường production, bạn sẽ trỏ nó vào S3 hoặc Terraform Cloud, nhưng state cục bộ là đủ để học.
Sau khi khởi tạo, cấu trúc thư mục sẽ trông như thế này:
my-cdktf-project/
├── main.py ← định nghĩa stack của bạn ở đây
├── cdktf.json ← cấu hình CDKTF (providers, thư mục output)
├── requirements.txt
└── .gen/ ← provider bindings tự sinh (không chỉnh sửa)
Bước 5: Thiết Lập Python Virtual Environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Cấu Hình
Đây là phần thú vị — viết hạ tầng thực tế bằng Python. Ví dụ này triển khai một AWS S3 bucket với tính năng versioning được bật. Ví dụ nhỏ, nhưng minh họa đầy đủ pattern bạn sẽ dùng cho mọi tài nguyên.
Thêm AWS Provider
Chỉnh sửa cdktf.json để thêm AWS provider:
{
"language": "python",
"app": "pipenv run python main.py",
"terraformProviders": ["aws@~> 5.0"],
"terraformModules": [],
"output": "cdktf.out"
}
Sau đó sinh provider bindings. Lệnh này tải schema AWS provider và sinh ra các class Python có kiểu dữ liệu cho mọi tài nguyên AWS:
cdktf get
Quá trình này mất một đến hai phút. Khi hoàn tất, bạn sẽ thấy hàng nghìn class trong .gen/providers/aws/ — một class cho mỗi loại tài nguyên AWS.
Viết Stack bằng Python
Mở main.py và thay thế nội dung mặc định:
from constructs import Construct
from cdktf import App, TerraformStack, TerraformOutput
from cdktf_cdktf_provider_aws.provider import AwsProvider
from cdktf_cdktf_provider_aws.s3_bucket import S3Bucket
from cdktf_cdktf_provider_aws.s3_bucket_versioning import (
S3BucketVersioning,
S3BucketVersioningVersioningConfiguration,
)
class MyInfraStack(TerraformStack):
def __init__(self, scope: Construct, id: str, env: str):
super().__init__(scope, id)
# Cấu hình AWS provider
AwsProvider(self, "AWS", region="ap-northeast-1")
# Tạo S3 bucket
bucket = S3Bucket(
self,
"app-bucket",
bucket=f"my-app-assets-{env}",
tags={"Environment": env, "ManagedBy": "cdktf"},
)
# Bật versioning cho bucket đó
S3BucketVersioning(
self,
"bucket-versioning",
bucket=bucket.id,
versioning_configuration=S3BucketVersioningVersioningConfiguration(
status="Enabled"
),
)
# Xuất tên bucket để có thể tham chiếu sau này
TerraformOutput(
self,
"bucket_name",
value=bucket.bucket,
description="Tên của S3 bucket vừa tạo",
)
app = App()
MyInfraStack(app, "staging", env="staging")
MyInfraStack(app, "production", env="production")
app.synth()
Lưu ý điều đang xảy ra ở đây: tham số env cho phép bạn khởi tạo cùng một stack hai lần — một lần cho staging, một lần cho production — mà không cần trùng lặp bất cứ thứ gì. Đây chính là điểm mạnh của Python so với HCL; stack được tham số hóa chỉ là các instance của class.
Tổng Hợp Cấu Hình Terraform
CDKTF không triển khai trực tiếp — nó trước tiên sinh ra Terraform JSON, rồi bạn mới apply. Chạy:
cdktf synth
Lệnh này tạo ra cdktf.out/stacks/staging/ và cdktf.out/stacks/production/, mỗi thư mục chứa một file cdk.tf.json. Bạn có thể kiểm tra file đó để xác nhận những gì CDKTF đã sinh ra — đây là Terraform JSON chuẩn và bạn có thể đọc như bất kỳ cấu hình Terraform nào.
Triển Khai lên AWS
Đảm bảo thông tin xác thực AWS của bạn đã được cấu hình (biến môi trường, ~/.aws/credentials, hoặc IAM role), sau đó:
# Xem trước các thay đổi trước khi apply
cdktf plan staging
# Apply lên staging
cdktf deploy staging
# Apply lên cả hai stack cùng một lúc
cdktf deploy --all
Bạn sẽ thấy output quen thuộc của Terraform plan — CDKTF chỉ đơn giản chuyển tiếp sang lệnh terraform apply bên dưới cho mỗi stack.
Kiểm Tra và Giám Sát
Khi deployment hoàn tất, hãy xác nhận mọi thứ đã đúng và chuẩn bị để phát hiện sớm các thay đổi ngoài mong muốn.
Kiểm Tra Output của Stack
Sau khi cdktf deploy hoàn tất, các output được in trực tiếp trong terminal. Để truy vấn lại sau này:
cdktf output staging
Bạn sẽ thấy output bucket_name mà chúng ta đã định nghĩa — hữu ích để truyền giá trị giữa các stack hoặc vào cấu hình ứng dụng.
Xác Nhận Tài Nguyên trên AWS
# Liệt kê bucket và xác nhận bucket mới đã tồn tại
aws s3 ls | grep my-app-assets
# Kiểm tra versioning thực sự đã được bật
aws s3api get-bucket-versioning --bucket my-app-assets-staging
Output mong đợi khi kiểm tra versioning:
{
"Status": "Enabled"
}
Phát Hiện Configuration Drift
Nếu ai đó thay đổi thủ công một tài nguyên trên AWS console, CDKTF (qua Terraform) sẽ phát hiện sự thay đổi đó trong lần plan tiếp theo:
cdktf diff staging
Lệnh này chạy terraform plan so với state hiện tại và hiển thị những gì đã thay đổi ngoài code của bạn. Chạy lệnh này trong CI theo lịch định kỳ là cách rẻ để phát hiện các thay đổi trái phép trước khi chúng gây ra vấn đề.
Xóa Tài Nguyên Khi Xong
Cho các môi trường test bạn muốn dọn sạch:
cdktf destroy staging
Luôn kiểm tra kỹ lệnh này trước khi chạy trên bất cứ thứ gì gần production — nó không thể hoàn tác đối với các tài nguyên có trạng thái như database và S3 bucket chứa dữ liệu.
Tích Hợp với CI/CD
Một workflow GitHub Actions tối giản để plan khi có pull request và apply khi merge:
name: CDKTF Deploy
on:
push:
branches: [main]
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: npm install -g cdktf-cli
- run: pip install -r requirements.txt
- run: cdktf get
- name: Plan (chỉ khi PR)
if: github.event_name == 'pull_request'
run: cdktf plan --all
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Deploy (chỉ khi trên nhánh main)
if: github.ref == 'refs/heads/main'
run: cdktf deploy --all --auto-approve
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Pattern ở đây — plan khi có PR, apply khi merge — là cùng workflow mà các team dùng với Terraform thuần. CDKTF tích hợp vào mà không làm gián đoạn quy trình hiện có.
Một điều cần lưu ý: bước cdktf get tái sinh provider bindings. Ghim phiên bản provider trong cdktf.json để tránh bất ngờ khi HashiCorp phát hành phiên bản provider mới giữa chừng trong pipeline.

