4.16 IaC 进阶——Pulumi / CDKTF / Terragrunt 实战

预计阅读时间:16 分钟

📖 目录

4.15:Terraform 入门 介绍了 Terraform 的基础用法。当基础设施规模增长、多环境管理变复杂、团队对编程语言有更高诉求时,Terraform 原生的 HCL 可能不够灵活。本文覆盖四种进阶方案:Pulumi(Python/Go 替代 HCL)、CDKTF(CDK + Terraform 生态)、Terragrunt(多环境 DRY 管理)和 Terraform Cloud/Enterprise(远程执行与协作)。

学习目标

学完本章后,你将能够:

  • 用 Pulumi 以 Python/Go 等通用编程语言编写 IaC,并管理 Stack 的导出值与状态
  • 用 CDKTF 结合 TypeScript 与 Terraform Provider 生态定义基础设施
  • 用 Terragrunt 的根级配置、dependencymock_outputs 实现多环境 DRY 管理
  • 配置 Terraform Cloud 远程 Backend、State 锁与 Sentinel 策略
  • 对比 Pulumi / CDKTF / Terragrunt / Terraform Cloud 的适用场景并制定选型决策

前置知识

1. Pulumi——用通用编程语言写 IaC

Pulumi 支持 Python、TypeScript、Go、C# 和 Java 等通用编程语言定义基础设施,可利用循环、条件、函数组合和 IDE 智能提示。

# 安装
curl -fsSL https://get.pulumi.com | sh

# 初始化 Python 项目(AWS)
mkdir pulumi-demo && cd pulumi-demo
pulumi new aws-python

Python 示例——EC2 + Security Group

import pulumi
from pulumi_aws import ec2

sg = ec2.SecurityGroup("web-sg",
    description="Allow HTTP and SSH",
    ingress=[
        ec2.SecurityGroupIngressArgs(protocol="tcp", from_port=80, to_port=80, cidr_blocks=["0.0.0.0/0"]),
        ec2.SecurityGroupIngressArgs(protocol="tcp", from_port=22, to_port=22, cidr_blocks=["10.0.0.0/8"]),
    ],
    egress=[ec2.SecurityGroupEgressArgs(protocol="-1", from_port=0, to_port=0, cidr_blocks=["0.0.0.0/0"])]
)

# AMI 别硬编码:用 SSM 参数按区域查询最新 Ubuntu 24.04 镜像(示例 id 已退役):
# aws ssm get-parameter --name /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id --query Parameter.Value --output text
server = ec2.Instance("web-server",
    instance_type="t3.micro", ami="<上一步查询到的 ami-id>",
    vpc_security_group_ids=[sg.id], tags={"Name": "web-server"}
)

pulumi.export("public_ip", server.public_ip)

Go 示例——循环生成多环境 S3 Bucket

package main

import (
    "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        // 通用语言的循环能力——HCL 中需要 count 或复制粘贴
        for _, env := range []string{"dev", "staging", "prod"} {
            _, err := s3.NewBucket(ctx, env+"-assets", &s3.BucketArgs{
                Bucket: pulumi.String("myapp-" + env + "-assets"),
                Tags:   pulumi.StringMap{"env": pulumi.String(env)},
            })
            if err != nil {
                return err
            }
        }
        ctx.Export("bucketCount", pulumi.Int(3))
        return nil
    })
}

状态后端与 Stack 管理

# Pulumi 状态可存于 Pulumi Cloud、S3、Azure Blob 或本地
pulumi login s3://pulumi-state-bucket     # S3 后端(类似 Terraform 的 s3 backend)
pulumi login azureblob://pulumi-state     # Azure Blob 后端
pulumi login --local                      # 仅本地实验使用,禁止生产

# Stack 即「同名项目 + 不同状态 + 不同配置」的组合
pulumi stack init dev                     # 创建 stack(类似 terraform workspace)
pulumi stack select prod
pulumi stack ls

# 查看导出值与配置(密钥在 state 中加密存储)
pulumi stack output public_ip
pulumi config set aws:region ap-northeast-1
pulumi config set --secret db_password    # 交互式输入,绝不写进代码

Import 已有资源

# 手工创建的存量资源可以用 pulumi import 接管(类似 terraform import)
pulumi import aws:ec2/instance:Instance web-server i-0a1b2c3d4e5f67890

# import 生成的资源定义会追加到 __main__.py,确认无误后执行
pulumi up

# 状态管理常用命令
pulumi state list          # 列出当前 state 中的全部资源
pulumi state unprotect web-server   # 解除保护后可删除该资源
pulumi refresh --target aws:ec2/instance:Instance   # 按资源刷新漂移

与 Terraform 的核心区别

维度Terraform (HCL)Pulumi
语言HCL(专用 DSL)Python / Go / TypeScript
状态存储S3 / Terraform CloudPulumi Cloud / S3
生态Terraform Registry(最大 Provider 库)Pulumi Registry + 可直接调用 SDK
复用方式Module(HCL 封装)原生包管理(pip / go mod)
IDE 支持HCL 插件(有限)通用语言 LSP(完整智能提示)
学习曲线中(需掌握编程语言)
适用场景 Pulumi 适合有编程背景的团队,需要复杂逻辑(循环生成 N 个资源、条件分支、从 API 动态获取参数)时优势明显。已有 Terraform 代码库的团队无需为了 Pulumi 而迁移。

2. CDKTF——AWS CDK + Terraform 生态

CDKTF(Cloud Development Kit for Terraform)让开发者用 TypeScript/Python/Go 编写 IaC,生成 Terraform 配置并复用 Terraform Provider 生态。

# 安装与初始化
npm install -g cdktf-cli
cdktf init --template=typescript
cdktf get --force  # 生成 Provider bindings

TypeScript 示例——VPC + EC2

import { App, TerraformStack } from "cdktf";
import { Construct } from "constructs";
import { AwsProvider } from "./providers/aws";
import { Vpc } from "@cdktf/provider-aws/lib/vpc";
import { Instance } from "@cdktf/provider-aws/lib/instance";

class MyStack extends TerraformStack {
    constructor(scope: Construct, id: string) {
        super(scope, id);
        new AwsProvider(this, "aws", { region: "ap-northeast-1" });
        const vpc = new Vpc(this, "vpc", { cidrBlock: "10.0.0.0/16" });
        new Instance(this, "web", {
            ami: "ami-0c55b159cbfafe1f0", instanceType: "t3.micro"
        });
    }
}
new MyStack(new App(), "cdktf-demo");
注意 CDKTF 生成的是标准 Terraform JSON 配置(cdktf.out/terraform/)。它不是 Terraform 的替代品,而是上层抽象——底层仍由 Terraform CLI 执行。

与 Terraform Provider 的关系

CDKTF 本身不直接调用云厂商 API,而是通过 cdktf get 把 Terraform Provider 的 schema 编译成对应的语言绑定(bindings)。因此:① Provider 生态完全复用 Terraform Registry;② 每次升级 Provider 版本后必须重新执行 cdktf get,否则新资源类型不会出现在语言层类型提示中;③ 语言层与执行层之间存在「生成 → 转换」的间接层。

# cdktf.json——声明 Provider 来源与后端
{
  "language": "typescript",
  "app": "npx ts-node main.ts",
  "projectId": "demo",
  "terraformProviders": ["aws@~>5.0"],
  "terraformModules": [],
  "context": { "excludeStackIdFromLogicalIds": "true" }
}

# 本地调试流程:代码 → 生成 Terraform JSON → 交给 Terraform 执行
cdktf synth          # 生成 cdktf.out/ 下的 Terraform JSON 配置
cdktf diff           # 等价于 terraform plan
cdktf deploy         # 等价于 terraform apply
cdktf destroy

# 远程后端同样支持(与 Terraform 共用同一套 backend 配置)
# 在 cdktf.json 或每个 stack 的 synth 配置中声明 s3 backend 即可,
# state 与纯 Terraform 项目完全互通

CDKTF vs Pulumi

维度CDKTFPulumi
Provider 生态复用 Terraform Registry(最大)Pulumi Registry(较小,可桥接 Terraform)
输出格式生成 .tf JSON(兼容 Terraform 工具链)自有 State 格式
渐进迁移可与已有 Terraform 代码共存需完整迁移 State

3. Terragrunt——多环境 DRY 管理

Terragrunt 是 Terraform 的包装器,解决多环境、多模块场景下的 State 管理和配置复用问题。核心原则是 DRY。

典型目录结构

infrastructure/
├── terragrunt.hcl           # 根级公共配置
├── dev/
│   ├── env.hcl              # dev 环境变量
│   ├── vpc/terragrunt.hcl
│   ├── ecs/terragrunt.hcl
│   └── rds/terragrunt.hcl
├── staging/
│   ├── env.hcl
│   └── ...
└── prod/
    ├── env.hcl
    └── ...

根级配置与模块依赖

# infrastructure/terragrunt.hcl
remote_state {
  backend = "s3"
  generate = { path = "backend.tf", if_exists = "overwrite_terragrunt" }
  config = {
    bucket         = "my-terraform-state-${local.env}"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "terraform-locks"
  }
}

# infrastructure/dev/ecs/terragrunt.hcl
include "root" { path = find_in_parent_folders() }
terraform { source = "../../modules//ecs" }

dependency "vpc" { config_path = "../vpc" }

inputs = {
  env        = local.env
  vpc_id     = dependency.vpc.outputs.vpc_id
  subnet_ids = dependency.vpc.outputs.private_subnet_ids
}
# 常用命令
terragrunt apply                          # 单模块执行
cd infrastructure/dev && terragrunt run-all apply  # 按依赖顺序执行全部
terragrunt run-all plan                   # 批量预览
terragrunt run-all destroy                # 批量销毁

环境变量分层与依赖管理

# infrastructure/terragrunt.hcl(根级)——locals 为子目录提供公共默认值
locals {
  region = "ap-northeast-1"
}

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite"
  contents  = <<EOF
provider "aws" {
  region = "${local.region}"
  default_tags {
    tags = { managed_by = "terragrunt" }
  }
}
EOF
}

# infrastructure/dev/env.hcl——环境专用变量(新增环境 = 新增目录 + 此文件)
locals {
  env      = "dev"
  region   = "ap-northeast-1"
  vpc_cidr = "10.10.0.0/16"
  ec2_type = "t3.medium"
}

# infrastructure/dev/ecs/terragrunt.hcl——子模块消费环境变量
locals {
  env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
  env      = local.env_vars.locals.env
}

include "root" { path = find_in_parent_folders() }
terraform { source = "../../modules//ecs" }

dependency "vpc" {
  config_path = "../vpc"
  mock_outputs = {
    vpc_id             = "vpc-00000000"
    private_subnet_ids = ["subnet-00000000"]
  }
}

inputs = {
  env           = local.env
  vpc_id        = dependency.vpc.outputs.vpc_id
  subnet_ids    = dependency.vpc.outputs.private_subnet_ids
  instance_type = local.env_vars.locals.ec2_type
}
mock_outputs 的作用 依赖模块尚未 apply 时,terragrunt plan 会因拿不到 outputs 而失败。为 dependency 配置 mock_outputs 后可用占位值完成预览,首次 run-all apply 时 Terragrunt 会按依赖拓扑顺序(VPC → 子网 → ECS)自动执行。
DRY 收益 将重复的 backend 配置、provider 版本锁定、变量注入抽取到根级配置。新增环境只需创建 env.hcl + 引用模块,无需复制粘贴。

4. Terraform Cloud / Enterprise

Terraform Cloud 提供远程执行、State 锁定、团队协作和策略即代码能力。

核心功能对比

功能FreeTeam & GovernanceHCP Terraform(企业版)
远程执行 / State 远程存储支持支持支持
State 锁定与协作支持支持支持
VCS 集成(Git 触发)支持支持支持
Policy as Code(Sentinel)支持支持
Private Registry / 审计日志支持支持
Agent(私有网络执行)支持

远程 Backend 配置

terraform {
  cloud {
    organization = "my-org"
    workspaces { name = "prod-vpc" }
  }
}

Sentinel 策略示例

# 限制 EC2 实例类型必须是 t3 系列
import "tfplan/v2" as tfplan

ec2_instances = filter tfplan.resource_changes as _, rc {
    rc.type is "aws_instance" and (rc.change.actions contains "create")
}

main = rule {
    all ec2_instances as _, inst { inst.change.after.instance_type matches "t3\\..*" }
}
成本考量 Free 版最多 5 用户、500 资源。超出需升级 Team & Governance($20/用户/月起)。预算敏感可考虑 Atlantis(开源替代)实现 PR 驱动的远程执行。

5. 选型对比与适用场景

工具核心价值State 管理适用场景
Pulumi通用编程语言写 IaCPulumi Cloud / S3需要复杂逻辑、有编程背景的团队
CDKTFCDK 抽象 + Terraform 生态Terraform State已有 Terraform 代码库想渐进迁移
Terragrunt多环境 DRY 管理Terraform State多环境、多模块、配置复用
Terraform Cloud远程执行 + 协作 + 策略托管 State + 锁团队协作、合规审计
Atlantis开源 PR 驱动执行Terraform State预算敏感的团队协作

选型决策流程

  1. 已有 Terraform? 配远程 backend + State 锁(Terraform Cloud 或 S3 + DynamoDB)
  2. 多环境复杂? 引入 Terragrunt 抽取公共配置
  3. HCL 表达力不足? 选 Pulumi 或 CDKTF(已有 Terraform 生态优先 CDKTF)
  4. 需要合规审计? Terraform Cloud Enterprise 或 Sentinel / OPA
  5. 预算有限? Atlantis 替代 Terraform Cloud,Terragrunt 替代 Enterprise 的 DRY 能力

6. 三框架核心差异对比

维度PulumiCDKTFTerragrunt
编程范式命令式为主(代码执行后得出资源图)声明式(代码编译为 Terraform 配置)纯声明式(HCL + 目录结构)
状态管理Pulumi 自有 state(S3 / Pulumi Cloud)Terraform state(完全复用)Terraform state(每模块独立 key)
多云支持AWS/Azure/GCP/K8s/私有云 SDK全部 Terraform Provider全部 Terraform Provider
逻辑表达循环/条件/函数/第三方库全可用TypeScript/Python 语言级逻辑仅 HCL 表达式(循环能力有限)
适用团队规模10+ 人、有专职开发能力5+ 人、已有 Terraform 资产2+ 人即可,纯运维团队友好
调试方式语言调试器 + 堆栈跟踪查看生成的 JSON + terraform planterragrunt debug + 检查 HCL

选型经验:纯运维团队(不写代码)从 Terragrunt 开始;有开发背景且需要强逻辑(批量生成、动态查询)选 Pulumi;已有 Terraform 代码库又想升级到编程语言时选 CDKTF——它不破坏现有 state。

7. 迁移路径:从 Terraform 到 Terragrunt 目录重构

存量 Terraform 仓库迁移到 Terragrunt 不需要改模块代码,只重构目录与 backend 声明:

# 迁移前——每个环境复制一份完整配置,改一个参数要改三处
terraform/
├── dev/
│   ├── main.tf          # 与 staging 完全相同的模块引用
│   ├── backend.tf       # bucket 名不同
│   └── terraform.tfvars
├── staging/
│   ├── main.tf          # 复制粘贴
│   └── ...
└── prod/
    ├── main.tf
    └── ...

# 迁移后——模块只保留一份,环境目录只放差异
terraform/
├── modules/
│   ├── vpc/
│   └── ecs/
├── terragrunt.hcl       # 根级:backend + provider + 公共 locals
├── dev/
│   ├── env.hcl          # 环境差异(cidr、实例规格、副本数)
│   ├── vpc/terragrunt.hcl
│   └── ecs/terragrunt.hcl
├── staging/...
└── prod/...
  1. 第一步:抽取模块——把各环境 main.tf 里相同的资源定义抽到 modules/,子目录用 terraform.source 引用。
  2. 第二步:建立根级 terragrunt.hcl——把 backend.tf 的 S3 配置迁入 remote_state 块,各环境自动生成 backend.tf。
  3. 第三步:接管 state——每个模块目录执行 terragrunt init -reconfigure,state key 与旧 backend 保持一致即可无缝接管,无需 terraform state mv
  4. 第四步:验证依赖——terragrunt run-all plan 全量预览,确认依赖图输出与手工执行顺序一致后再 apply。
迁移顺序 先迁移非生产环境(dev/staging)观察 1-2 个发布周期,再迁移 prod。迁移期间锁定生产变更,避免 state 漂移。

Pulumi 与 Terraform 代码量对比

以下对比创建相同 AWS 基础设施(VPC + 2 个子网 + EC2 实例)的代码行数:

组件Terraform (HCL)Pulumi (Python)Pulumi (Go)
VPC5 行3 行8 行
子网 ×212 行6 行(循环)12 行
EC2 实例8 行5 行10 行
输出3 行1 行2 行
总计28 行15 行32 行
代码量不是唯一指标 Pulumi 在循环生成多资源时更简洁,但 Go 版本的样板代码较多。HCL 虽然行数更多,但声明式语法更直观,适合运维团队。选型应综合考虑团队技能、现有代码库和长期维护成本。

渐进式采用路径

阶段 1:评估期(1-2 周)
├─ 在现有 Terraform 项目中引入 Terragrunt 做 DRY 管理
├─ 保持现有 .tf 文件不变,仅重构目录结构
└─ 验证 terragrunt run-all plan 输出与原始 terraform plan 一致

阶段 2:试点期(1 个月)
├─ 选一个非生产模块(如 VPC)迁移到 Terragrunt 管理
├─ 团队学习 Terragrunt 语法和工作流
└─ 积累踩坑经验,建立内部文档

阶段 3:推广期(2-3 个月)
├─ 逐步将所有环境迁移到 Terragrunt
├─ 建立模块库,标准化新模块的目录结构
└─ 配置 CI/CD 流水线自动执行 terragrunt plan/apply

阶段 4:评估 Pulumi/CDKTF(可选)
├─ 对需要复杂逻辑的模块评估 Pulumi/CDKTF
├─ 选 1-2 个模块做 POC
└─ 团队投票决定是否迁移
关键原则 渐进式采用的核心是「不动存量、增量先行」。先用 Terragrunt 管理新模块,观察稳定后再逐步迁移旧模块。迁移期间保持新旧两套工具并行运行,避免一次性切换带来的风险。

常见错误补充

问题原因解决方案
Pulumi import 后资源属性不匹配import 生成的代码缺少某些 computed 属性执行 pulumi up 让 Pulumi 自动补齐缺失属性
CDKTF 生成的 Terraform JSON 语法错误cdktf get 后未更新 Provider 版本执行 cdktf get --force 重新生成绑定
Terragrunt apply 顺序与预期不符dependency 块配置错误terragrunt graph-dependencies 可视化依赖图
Terraform Cloud 超时Agent 在私有网络中无法连接 TFC部署 Terraform Cloud Agent 在私有网络内执行

State 管理安全检查清单

检查项要求验证方式
State 远程存储禁止本地 state 文件检查 backend 配置是否指向 S3/TFC
State 加密S3 后端启用 SSE-S3 或 SSE-KMSaws s3api head-object 检查加密配置
State 锁定启用 DynamoDB 锁尝试并发 apply 验证锁生效
访问控制限制 state 文件的 IAM 权限检查 IAM 策略是否遵循最小权限
版本控制S3 开启版本历史检查 S3 bucket 版本配置
Secret 不入代码库敏感变量使用 Vault/SSMgit grep -i password 检查代码库
State 文件是核心资产 Terraform/Pulumi 的 state 文件包含所有资源的元数据和敏感信息(如数据库密码、API Key)。一旦泄露或损坏,可能导致基础设施被攻击或无法恢复。务必加密存储、启用版本控制、严格限制访问权限。

常见错误

问题原因解决方案
Terraform apply 报 Error: State lock上一次 apply 异常中断,锁未释放确认无并发操作后执行 terraform force-unlock <LOCK_ID>;S3 后端需检查 DynamoDB 条目
Terragrunt dependency 循环依赖模块 A 依赖 B、B 又依赖 A重新划分模块边界,引入第三个模块解耦,或使用 mock_outputs 打破循环
CDKTF cdktf get 生成的 Provider bindings 过时Provider 版本升级后未重新生成执行 cdktf get --force 并锁定 Provider 版本号
Pulumi stack 导出值找不到pulumi.export() 名称拼写错误或 stack 未正确选中运行 pulumi stack ls 确认当前 stack,检查 pulumi stack output 输出
Sentinel 策略误拦截合法变更正则匹配规则过于宽泛(如 instance_type matches "t3\\..*" 未覆盖 t3a审查策略规则,补充边界条件;用 policies/ 目录下的测试用例验证后再上线

最佳实践

  • State 远程存储 + 锁定:生产环境必须使用远程 backend(S3 + DynamoDB / Terraform Cloud),禁止本地 state 文件。启用 State 锁防止并发修改导致数据丢失。
  • 模块版本固定:引用 Terraform 模块或 Pulumi 包时锁定版本号,避免上游变更导致基础设施漂移。定期用 terraform init -upgrade 评估升级影响。
  • 环境隔离:每个环境使用独立的 state 文件和变量文件(terraform.tfvars / Terragrunt 的 env.hcl)。生产环境的 state bucket 与开发环境分离并限制访问权限。
  • Secret 不入代码库:敏感变量(数据库密码、API Key)使用 Vault、AWS SSM Parameter Store 或 Pulumi 的 pulumi.Config().requireSecret() 注入,绝不硬编码在 .tf__main__.py 中。
  • 渐进式采用:已有 Terraform 代码库的团队不必整体迁移。可先引入 Terragrunt 做 DRY 管理,再评估 Pulumi/CDKTF 是否值得部分模块迁移,保持新旧共存。

故障排查案例:Terragrunt 循环依赖

现象:执行 terragrunt run-all plan 报错 Circular dependency detected: vpc → ecs → vpc

排查:检查各模块的 dependency 块,发现 vpc 模块中引用了 ecs 模块的输出(用于注入安全组规则),而 ecs 模块又依赖 vpc 的输出。

根因:vpc 模块不应该引用下游模块的输出。安全组规则的配置应放在 ecs 模块中,而非 vpc 模块。

修复:① 将安全组规则从 vpc 模块移出到独立的 security-group 模块;② 调整依赖拓扑为 vpc → security-group → ecs;③ 使用 mock_outputs 在 vpc 模块中引用安全组的输出时提供占位值。

Terragrunt 常用调试命令

# 查看依赖拓扑(确认是否有循环)
terragrunt graph-dependencies

# 只预览单个模块(不触发依赖执行)
cd infrastructure/dev/vpc && terragrunt plan

# 强制重新初始化 backend
terragrunt init -reconfigure

# 查看当前状态中的资源
terragrunt state list

# 批量执行但跳过依赖检查(紧急修复用)
terragrunt run-all apply --terragrunt-ignore-external-dependencies
调试建议 Terragrunt 的循环依赖往往源于模块边界划分不合理。遇到循环时先画出依赖图(terragrunt graph-dependencies),再检查是否违反「上游不应引用下游输出」的原则。

练习题

  1. 多环境 Terragrunt 项目:搭建一个包含 dev / staging / prod 三个环境的 Terragrunt 项目,每个环境有独立的 VPC 和 RDS 模块。要求:使用根级 terragrunt.hcl 统一配置 S3 backend 和 Provider 版本,通过 env.hcl 注入环境变量,用 dependency 块串联 VPC → RDS 的依赖关系。
  2. Pulumi 组件封装:用 Pulumi(Python)封装一个可复用的 WebServer 组件,支持传入 instance_typeamisubnet_idsecurity_group_ids 参数,返回实例 ID 和公网 IP。编写测试验证该组件在 dev 和 prod 环境下创建的资源属性不同。
  3. Sentinel 策略编写:为 Terraform Cloud 编写两条 Sentinel 策略:① 禁止创建 ami- 开头的 EC2 实例(要求使用固定 AMI ID);② 要求所有 S3 Bucket 必须启用服务端加密(sse_configuration)。用 sentinel test 命令验证策略的通过与失败场景。

本章总结

当 HCL 无法满足规模与协作需求时,Pulumi 与 CDKTF 让 IaC 回归通用编程语言,Terragrunt 用 DRY 结构消除多环境重复,Terraform Cloud 提供远程执行与策略门禁。四者互补而非互斥,渐进式引入——先 Terragrunt 管理多环境,再按需评估 Pulumi / CDKTF 迁移部分模块——是最稳妥的演进路径。

延伸阅读

↑ 回到顶部