mcpField
C
cloudformation-to-pulumi

cloudformation-to-pulumi

github.com/pulumi云与 DevOps78892 次安装3 周前更新
代码仓库

关于 cloudformation-to-pulumi

用于将 AWS CloudFormation 模板手动转换为 Pulumi 程序并导入现有资源的技能。重点是保留逻辑 ID、默认使用 aws-native,并通过无差异预览验证导入结果。

主要功能

  1. 01CloudFormation 模板分析
  2. 02逐项手动转换资源
  3. 03逻辑 ID 命名要求
  4. 04aws-native Provider 指引
  5. 05自定义资源处理
  6. 06导入与无差异验证

适用场景

  1. 01将 CloudFormation 堆栈转换为 Pulumi
  2. 02准备将现有堆栈资源导入 Pulumi
  3. 03映射模板参数和内置函数
  4. 04检查导入基础设施是否有意外变更

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pulumi/agent-skills --skill cloudformation-to-pulumi

安装后在 Claude Code 中输入/cloudformation-to-pulumi确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

cfn-importer.md
cloudformation-id-lookup.md
use_cases.yaml
agents/
openai.yaml

详细介绍

重要:必须调整计划

如果你在加载此技能之前已经生成迁移计划,则必须:

  1. 根据此技能中的工作流和要求检查现有计划
  2. 找出任何差距、遗漏步骤或错误假设
  3. 更新并修订计划,使其符合此技能的指导
  4. 在继续操作之前,将调整后的计划告知用户

关键成功要求

迁移输出必须满足以下所有要求:

  1. 完整覆盖资源

    • 每个 CloudFormation 资源都必须在 Pulumi 程序中有所体现,或在最终报告中明确说明理由。
  2. 将 CloudFormation 逻辑 ID 用作资源名称

    • **关键:**每个 Pulumi 资源都必须使用 CloudFormation 逻辑 ID 作为其资源名称。
    • 这样 cdk-importer 工具才能自动查找导入 ID。
    • 不要重命名资源。更改逻辑 ID 会导致自动导入失败。
  3. 成功部署

    • 生成的 Pulumi 程序必须在结构上有效,并能够成功运行 pulumi preview(假设配置正确)。
  4. 导入验证无差异(如果导入现有资源)

    • 导入后,pulumi preview 必须不显示任何更新、替换、创建或删除操作。
  5. 最终迁移报告

    • 始终输出适用于 Pull Request 的正式迁移报告。

缺少信息时

如果用户未提供 CloudFormation 模板,则必须使用堆栈名称从 AWS 获取模板。

迁移工作流

必须严格按照以下顺序遵循此工作流:

1. 信息收集
1.1 验证 AWS 凭证(ESC)

运行 AWS 命令需要通过 Pulumi ESC 加载凭证。

  • 如果用户已提供 ESC 环境,则使用该环境。
  • 如果未指定 ESC 环境,继续操作前先询问用户要使用哪个 ESC 环境。

**有关 ESC 的详细信息:**使用技能 pulumi-esc。

必须向用户确认 AWS 区域。

1.2 获取 CloudFormation 模板

**如果用户提供了模板文件:**直接读取模板。

**如果用户只提供了堆栈名称:**从 AWS 获取模板:

aws cloudformation get-template \
  --region <region> \
  --stack-name <stack-name> \
  --query 'TemplateBody' \
  --output json > template.json
1.3 构建资源清单

列出堆栈中的所有资源:

aws cloudformation list-stack-resources \
  --region <region> \
  --stack-name <stack-name> \
  --output json

这将提供:

  • LogicalResourceId - 将其用作 Pulumi 资源名称
  • PhysicalResourceId - 实际的 AWS 资源 ID
  • ResourceType - CloudFormation 资源类型
1.4 分析模板结构

从模板中提取:

  • 参数及其默认值
  • 映射
  • 条件
  • 输出
  • 资源依赖关系(Ref、GetAtt、DependsOn)
2. 代码转换(CloudFormation → Pulumi)

**重要:**CloudFormation 没有自动转换工具。必须手动转换每个资源。

2.1 资源命名约定(关键)

每个 Pulumi 资源都必须使用 CloudFormation 逻辑 ID 作为其名称。

// CloudFormation:
// "MyAppBucketABC123": { "Type": "AWS::S3::Bucket", ... }

// Pulumi - CORRECT:
const myAppBucket = new aws.s3.Bucket("MyAppBucketABC123", { ... });

// Pulumi - WRONG (DO NOT do this - import will fail):
const myAppBucket = new aws.s3.Bucket("my-app-bucket", { ... });

此命名约定是必需的,因为 cdk-importer 工具会根据名称匹配资源。

2.2 Provider 策略

⚠️ 关键:始终默认使用 aws-native ⚠️

  • 除非有特定原因必须使用 aws,否则所有资源都使用 aws-native。
  • CloudFormation 类型可直接映射到 aws-native(例如,AWS::S3::Bucket → aws-native.s3.Bucket)。
  • 只有在 aws-native 不支持所需功能时,才使用 aws(经典版)。

这是使用 cdk-importer 成功导入的强制要求。 cdk-importer 通过匹配 CloudFormation 资源和 Pulumi 资源来工作,而 CloudFormation 与 aws-native 是一一对应的。使用经典 aws Provider 会导致导入失败。

2.3 CloudFormation 内置函数

将 CloudFormation 内置函数映射为 Pulumi 等效项:

CloudFormationPulumi 等效项
!Ref(资源)资源输出(例如,bucket.id)
!Ref(参数)Pulumi 配置
!GetAtt Resource.Attr资源属性输出
!Sub "..."pulumi.interpolate
!Join [delim, [...]]pulumi.interpolate 或 .apply()
!If [cond, true, false]三元运算符
!Equals [a, b]=== 比较
!Select [idx, list]使用 .apply() 进行数组索引
!Split [delim, str].apply(v => v.split(...))
Fn::ImportValue堆栈引用或配置
示例:!Sub
// CloudFormation: !Sub "arn:aws:s3:::${MyBucket}/*"
// Pulumi:
const bucketArn = pulumi.interpolate`arn:aws:s3:::${myBucket.bucket}/*`;
示例:!GetAtt
// CloudFormation: !GetAtt MyFunction.Arn
// Pulumi:
const functionArn = myFunction.arn;
2.4 CloudFormation 条件

将 CloudFormation 条件转换为 TypeScript 逻辑:

// CloudFormation:
// "Conditions": {
//   "CreateProdResources": { "Fn::Equals": [{ "Ref": "Environment" }, "prod"] }
// }

// Pulumi:
const config = new pulumi.Config();
const environment = config.require("environment");
const createProdResources = environment === "prod";

if (createProdResources) {
  // Create production-only resources
}
2.5 CloudFormation 参数

将参数转换为 Pulumi 配置:

// CloudFormation:
// "Parameters": {
//   "InstanceType": { "Type": "String", "Default": "t3.micro" }
// }

// Pulumi:
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";
2.6 CloudFormation 映射

将映射转换为 TypeScript 对象:

// CloudFormation:
// "Mappings": {
//   "RegionMap": {
//     "us-east-1": { "AMI": "ami-12345" },
//     "us-west-2": { "AMI": "ami-67890" }
//   }
// }

// Pulumi:
const regionMap: Record<string, { ami: string }> = {
  "us-east-1": { ami: "ami-12345" },
  "us-west-2": { ami: "ami-67890" },
};
const ami = regionMap[aws.config.region!].ami;
2.7 自定义资源

CloudFormation 自定义资源(AWS::CloudFormation::CustomResource 或 Custom::*)需要特殊处理:

  1. 识别用途:阅读 Lambda 函数代码,了解其功能
  2. 寻找原生替代方案:检查 Pulumi 是否提供具备相同功能的原生资源
  3. 如果没有替代方案:在迁移报告中注明需要手动实现
2.8 TypeScript 输出处理

aws-native 输出通常包含 undefined。避免使用 ! 非空断言。始终使用 .apply() 安全地解包:

// WRONG
functionName: lambdaFunction.functionName!,

// CORRECT
functionName: lambdaFunction.functionName.apply(name => name || ""),
3. 资源导入

转换完成后,导入现有资源,以便由 Pulumi 管理。

3.0 导入前验证(必需)

导入前,请验证代码:

  1. 检查 Provider 的使用情况:扫描代码,确保所有资源都使用 aws-native
  2. 记录例外情况:必须说明使用 aws(经典)Provider 的理由
  3. 验证资源名称:确认所有资源都使用 CloudFormation 逻辑 ID 作为名称
3.1 使用 cdk-importer 自动导入

由于你使用了 CloudFormation 逻辑 ID 作为资源名称,因此可以使用 cdk-importer 工具自动导入资源。

有关详细的导入步骤,请参阅 cfn-importer.md。

3.2 手动导入失败的资源

对于自动导入失败的资源:

  1. 按照 cloudformation-id-lookup.md 查找导入 ID 格式
  2. 使用 pulumi import:
pulumi import <pulumi-resource-type> <logical-id> <import-id>
3.3 导入后运行预览

导入后,运行 pulumi preview。结果必须满足:

  • 不得有更新
  • 不得有替换
  • 不得有创建
  • 不得有删除

如果存在变更,请进行调查并更新程序,直到预览结果无变更。

输出格式(必需)

执行迁移时,始终生成以下内容:

  1. 概述(高层次描述)
  2. 迁移计划摘要
  3. Pulumi 代码输出(TypeScript;按文件整理)
  4. 资源映射表:
CloudFormation 逻辑 IDCFN 类型Pulumi 类型Provider
MyAppBucketABC123AWS::S3::Bucketaws-native.s3.Bucketaws-native
MyLambdaFunction456AWS::Lambda::Functionaws-native.lambda.Functionaws-native
  1. 自定义资源摘要(如有)
  2. 最终迁移报告(可直接用于 PR)
  3. 后续步骤(导入说明)

详细文档

从 Pulumi 官方文档获取内容:

评论

支持 Markdown