
Cloudformation To Pulumi
- 701 installs
- 63 repo stars
- Updated August 3, 2026
- pulumi/agent-skills
The cloudformation-to-pulumi skill converts existing AWS CloudFormation stacks to Pulumi programs and imported state.
About
The cloudformation-to-pulumi skill converts existing AWS CloudFormation stacks to Pulumi programs and imported state. Agents export CloudFormation templates and stack resources, map types to Pulumi providers, create projects in-repo, import state, and iterate until pulumi preview shows no unexpected changes. Follows Pulumi migration constraints: no destructive shortcuts, pinned provider versions, and credential environments for secure imports. Use when teams standardize on Pulumi while retaining existing CloudFormation-provisioned infrastructure.
- CloudFormation template and stack export steps.
- Resource type mapping to Pulumi providers.
- State import with preview-verified parity.
- In-repo project scaffolding patterns.
- Provider version pinning during migration.
Cloudformation To Pulumi by the numbers
- 701 all-time installs (skills.sh)
- +37 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #338 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
cloudformation-to-pulumi capabilities & compatibility
- Capabilities
- cloudformation template and stack export steps. · resource type mapping to pulumi providers. · state import with preview verified parity. · in repo project scaffolding patterns.
- Use cases
- documentation
npx skills add https://github.com/pulumi/agent-skills --skill cloudformation-to-pulumiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 701 |
|---|---|
| repo stars | ★ 63 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 3, 2026 |
| Repository | pulumi/agent-skills ↗ |
How do I apply cloudformation-to-pulumi using the workflow in its SKILL.md?
Import CloudFormation stacks into Pulumi with resource mapping and preview-verified translation.
Who is it for?
Developers following the cloudformation-to-pulumi skill for the tasks it documents.
Skip if: Tasks outside the cloudformation-to-pulumi scope described in SKILL.md.
When should I use this skill?
User mentions cloudformation-to-pulumi or related triggers from the skill description.
What you get
Working cloudformation-to-pulumi setup aligned with the documented patterns and constraints.
- Pulumi program
- imported Pulumi stack state
Files
IMPORTANT: PLAN ADJUSTMENT REQUIRED
If you have already generated a migration plan before loading this skill, you MUST: 1. Review your existing plan against the workflows and requirements in this skill 2. Identify any gaps, missing steps, or incorrect assumptions 3. Update and revise your plan to align with this skill's guidance 4. Communicate the adjusted plan to the user before proceeding
CRITICAL SUCCESS REQUIREMENTS
The migration output MUST meet all of the following:
1. Complete Resource Coverage
- Every CloudFormation resource MUST be represented in the Pulumi program OR explicitly justified in the final report.
2. CloudFormation Logical ID as Resource Name
- CRITICAL: Every Pulumi resource MUST use the CloudFormation Logical ID as its resource name.
- This enables the
cdk-importertool to automatically find import IDs. - DO NOT rename resources. Automated import will FAIL if you change the logical IDs.
3. Successful Deployment
- The produced Pulumi program must be structurally valid and capable of a successful
pulumi preview(assuming proper config).
4. Zero-Diff Import Validation (if importing existing resources)
- After import,
pulumi previewmust show NO updates, replaces, creates, or deletes.
5. Final Migration Report
- Always output a formal migration report suitable for a Pull Request.
WHEN INFORMATION IS MISSING
If the user has not provided a CloudFormation template, you MUST fetch it from AWS using the stack name.
MIGRATION WORKFLOW
Follow this workflow exactly and in this order:
1. INFORMATION GATHERING
1.1 Verify AWS Credentials (ESC)
Running AWS commands requires credentials loaded via Pulumi ESC.
- If the user has already provided an ESC environment, use it.
- If no ESC environment is specified, ask the user which ESC environment to use before proceeding.
For detailed ESC information: Use skill pulumi-esc.
You MUST confirm the AWS region with the user.
1.2 Get the CloudFormation Template
If user provided a template file: Read the template directly.
If user only provided a stack name: Fetch the template from AWS:
aws cloudformation get-template \
--region <region> \
--stack-name <stack-name> \
--query 'TemplateBody' \
--output json > template.json1.3 Build Resource Inventory
List all resources in the stack:
aws cloudformation list-stack-resources \
--region <region> \
--stack-name <stack-name> \
--output jsonThis provides:
LogicalResourceId- Use this as the Pulumi resource namePhysicalResourceId- The actual AWS resource IDResourceType- The CloudFormation resource type
1.4 Analyze Template Structure
Extract from the template:
- Parameters and their defaults
- Mappings
- Conditions
- Outputs
- Resource dependencies (Ref, GetAtt, DependsOn)
2. CODE CONVERSION (CloudFormation → Pulumi)
IMPORTANT: There is NO automated conversion tool for CloudFormation. You MUST convert each resource manually.
2.1 Resource Name Convention (CRITICAL)
Every Pulumi resource MUST use the CloudFormation Logical ID as its name.
// 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", { ... });This naming convention is REQUIRED because the cdk-importer tool matches resources by name.
2.2 Provider Strategy
⚠️ CRITICAL: ALWAYS USE aws-native BY DEFAULT ⚠️
- Use
aws-nativefor all resources unless there's a specific reason to useaws. - CloudFormation types map directly to aws-native (e.g.,
AWS::S3::Bucket→aws-native.s3.Bucket). - Only use
aws(classic) when aws-native doesn't support a required feature.
This is MANDATORY for successful imports with cdk-importer. The cdk-importer works by matching CloudFormation resources to Pulumi resources, and CloudFormation maps 1:1 to aws-native. Using the classic aws provider will cause import failures.
2.3 CloudFormation Intrinsic Functions
Map CloudFormation intrinsic functions to Pulumi equivalents:
| CloudFormation | Pulumi Equivalent |
|---|---|
!Ref (resource) | Resource output (e.g., bucket.id) |
!Ref (parameter) | Pulumi config |
!GetAtt Resource.Attr | Resource property output |
!Sub "..." | pulumi.interpolate |
!Join [delim, [...]] | pulumi.interpolate or .apply() |
!If [cond, true, false] | Ternary operator |
!Equals [a, b] | === comparison |
!Select [idx, list] | Array indexing with .apply() |
!Split [delim, str] | .apply(v => v.split(...)) |
Fn::ImportValue | Stack references or config |
Example: !Sub
// CloudFormation: !Sub "arn:aws:s3:::${MyBucket}/*"
// Pulumi:
const bucketArn = pulumi.interpolate`arn:aws:s3:::${myBucket.bucket}/*`;Example: !GetAtt
// CloudFormation: !GetAtt MyFunction.Arn
// Pulumi:
const functionArn = myFunction.arn;2.4 CloudFormation Conditions
Convert CloudFormation conditions to TypeScript logic:
// 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 Parameters
Convert parameters to Pulumi config:
// CloudFormation:
// "Parameters": {
// "InstanceType": { "Type": "String", "Default": "t3.micro" }
// }
// Pulumi:
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";2.6 CloudFormation Mappings
Convert mappings to TypeScript objects:
// 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 Custom Resources
CloudFormation Custom Resources (AWS::CloudFormation::CustomResource or Custom::*) require special handling:
1. Identify the purpose: Read the Lambda function code to understand what it does 2. Find native replacement: Check if Pulumi has a native resource that provides the same functionality 3. If no replacement: Document in the migration report that manual implementation is needed
2.8 TypeScript Output Handling
aws-native outputs often include undefined. Avoid ! non-null assertions. Always safely unwrap with .apply():
// WRONG
functionName: lambdaFunction.functionName!,
// CORRECT
functionName: lambdaFunction.functionName.apply(name => name || ""),3. RESOURCE IMPORT
After conversion, import existing resources to be managed by Pulumi.
3.0 Pre-Import Validation (REQUIRED)
Before proceeding with import, verify your code:
1. Check Provider Usage: Scan your code to ensure all resources use aws-native 2. Document Exceptions: Any use of aws (classic) provider must be justified 3. Verify Resource Names: Confirm all resources use CloudFormation Logical IDs as names
3.1 Automated Import with cdk-importer
Because you used CloudFormation Logical IDs as resource names, you can use the cdk-importer tool to automatically import resources.
Follow cfn-importer.md for detailed import procedures.
3.2 Manual Import for Failed Resources
For resources that fail automatic import:
1. Follow cloudformation-id-lookup.md to find the import ID format 2. Use pulumi import:
pulumi import <pulumi-resource-type> <logical-id> <import-id>3.3 Running Preview After Import
After import, run pulumi preview. There must be:
- NO updates
- NO replaces
- NO creates
- NO deletes
If there are changes, investigate and update the program until preview is clean.
OUTPUT FORMAT (REQUIRED)
When performing a migration, always produce:
1. Overview (high-level description) 2. Migration Plan Summary 3. Pulumi Code Outputs (TypeScript; organized by file) 4. Resource Mapping Table:
| CloudFormation Logical ID | CFN Type | Pulumi Type | Provider |
|---|---|---|---|
MyAppBucketABC123 | AWS::S3::Bucket | aws-native.s3.Bucket | aws-native |
MyLambdaFunction456 | AWS::Lambda::Function | aws-native.lambda.Function | aws-native |
5. Custom Resources Summary (if any) 6. Final Migration Report (PR-ready) 7. Next Steps (import instructions)
FOR DETAILED DOCUMENTATION
Fetch content from official Pulumi documentation:
- https://www.pulumi.com/docs/iac/adopting-pulumi/migrating-to-pulumi/from-aws/
interface:
display_name: "CloudFormation to Pulumi Migration"
short_description: "Convert CloudFormation stacks/templates to Pulumi"
default_prompt: "Use $cloudformation-to-pulumi to migrate an AWS CloudFormation stack or template to Pulumi."
CloudFormation Stack Importer Tool
This tool imports existing AWS resources from CloudFormation stacks into Pulumi state.
Installation
pulumi plugin install tool cdk-importerCredentials
Running the cdk-importer tool requires credentials loaded via Pulumi ESC.
- If the user has already provided an ESC environment, use it.
- If no ESC environment is specified, ask the user which ESC environment to use before proceeding with using the tool.
You MUST confirm the AWS region with the user. The results may be incorrect if ran with the wrong AWS Region. The region can be set with the AWS_REGION environment variable
Commands
program import
Import into the selected Pulumi stack using an existing Pulumi program.
pulumi plugin run cdk-importer -- program import \
--program-dir ./generated \
--stack MyStackRequired flags:
--program-dir: Path to the Pulumi program (resource names must match CloudFormation Logical IDs)--stack: CloudFormation stack name (can be specified multiple times or comma-separated)
Optional flags:
--import-file: Path to write a Pulumi bulk import file with failing resources (defaults toimport.jsonwhen provided without a value)--debug: Enable line by line logging of imported resources
Behavior:
- Runs against the selected Pulumi stack.
- With
--import-file, writes the bulk import file after import. The file will only contain entries for resources that failed to import with<PLACEHOLDER>ids. - Can be run iteratively to progressively import resources.
Example Output:
[INFO] Getting stack resources component="cdk-importer" stack=NeoExample-Dev
[INFO] Starting up providers... component="cdk-importer"
[INFO] Importing stack... component="cdk-importer"
[INFO] Run complete component="cdk-importer" status="success" resourcesImported=50 resourcesFailedToImport=0 stack="NeoExample-Dev" importFile="/workspace/pulumi-example-app-neo/import.json" importFileExists=trueImport File Output
The generated import.json includes:
- Full AWS resource metadata (type, logical name, provider reference, component bit, provider version)
- Property subsets captured during provider interception
Resources with composite identifiers may show <PLACEHOLDER> IDs that need manual completion before running pulumi import --file import.json.
Unsupported Resources
Resources that cannot be imported:
- CloudFormation Custom Resources (
aws-native:cloudformation:CustomResourceEmulator)
Example Workflow
1. Convert your CloudFormation template to Pulumi (using CloudFormation Logical IDs as resource names)
2. Import into your Pulumi stack:
pulumi plugin run cdk-importer -- program import \
--program-dir ./pulumi-program-dir \
--stack MyStackHandling Failures
This tool may not support 100% of the CloudFormation resources in the stack. For unsupported resources it is necessary to find the import ID and import manually.
Example output:
[INFO] Getting stack resources component="cdk-importer" stack=NeoExample-Dev
[INFO] Starting up providers... component="cdk-importer"
[INFO] Importing stack... component="cdk-importer"
[INFO] Pulumi errors component="cdk-importer" details=urn:pulumi:dev::cdk-convert-example::aws:rds/proxyDefaultTargetGroup:ProxyDefaultTargetGroup::DatabaseDbClusterDbProxyProxyTargetGroupA552DCC1: Don't have an ID!: aws:rds/proxyDefaultTargetGroup:ProxyDefaultTargetGroup neo-example-dev-database-db-cluster-db-proxy-eede4daa urn:pulumi:dev::cdk-convert-example::aws:rds/proxyDefaultTargetGroup:ProxyDefaultTargetGroup::DatabaseDbClusterDbProxyProxyTargetGroupA552DCC1
update failed
[INFO] Run complete component="cdk-importer" status="failed" resourcesImported=69 resourcesFailedToImport=1 stack="NeoExample-Dev"
- operation failedExample Failure Workflow:
1. Import ran with error
2. Review failures and run pulumi preview.
- Any resources that fail to import should appear as creations in the preview.
- Optionally run
program importwith the--import-fileflag to generate aimport.jsonfile with the failing resources.
3. Manually import remaining resources using cloudformation-id-lookup.md
Pulumi Import ID Lookup (cdk2pulumi ids)
This tool looks up the required Pulumi import ID format for AWS resources, helping you understand what identifier shape is needed when importing existing AWS resources into Pulumi.
Prerequisites
- The tool must be installed:
pulumi plugin install tool cdk2pulumi - Run via:
pulumi plugin run cdk2pulumi -- ids <resource-type>
Usage
Look Up by Pulumi Resource Token or CloudFormation type
pulumi plugin run cdk2pulumi -- ids aws-native:s3:Bucket
pulumi plugin run cdk2pulumi -- ids AWS::S3::BucketUnderstanding the Output
The tool returns two key pieces of information:
1. Import ID Format
Shows the structure of the ID required by Pulumi's import command. Examples:
- Single-part ID:
<BucketName>- Just the bucket name - Composite ID:
<FunctionName>|<StatementId>- Multiple parts separated by delimiters - Complex ID:
<CertificateAuthorityArn>|<CertificateArn>- ARNs or other identifiers
2. Finding the ID Hint
Provides guidance on how to obtain the actual ID value from AWS:
- Single-part IDs: "Use the CloudFormation PhysicalResourceId"
- Find this in CloudFormation via
aws cloudformation describe-stack-resourcesoraws cloudformation list-stack-resources - Composite IDs: Shows an
aws cloudcontrol list-resourcescommand example - May include
--resource-model '{...}'when the Cloud Control API requires input parameters - Example:
aws cloudcontrol list-resources --type-name AWS::Lambda::Permission --resource-model '{"FunctionName":"my-function"}'
Examples
Simple Resource (S3 Bucket)
$ pulumi plugin run cdk2pulumi -- ids AWS::S3::Bucket
Import ID format: <BucketName>
Finding the ID: Use the CloudFormation PhysicalResourceIdComposite ID (Lambda Permission)
$ pulumi plugin run cdk2pulumi -- ids AWS::Lambda::Permission
Import ID format: <FunctionName>|<StatementId>
Finding the ID: aws cloudcontrol list-resources --type-name AWS::Lambda::Permission --resource-model '{"FunctionName":"<function-name>"}'Complex Resource (ACM PCA Certificate)
$ pulumi plugin run cdk2pulumi -- ids AWS::ACMPCA::Certificate
Import ID format: <CertificateAuthorityArn>|<CertificateArn>
Finding the ID: aws cloudcontrol list-resources --type-name AWS::ACMPCA::Certificate --resource-model '{"CertificateAuthorityArn":"<ca-arn>"}'Tips for Running
- Always use
--to separate Pulumi CLI arguments from plugin arguments - For composite IDs, pay attention to the delimiter (usually
|,/, or:) - When the hint shows
--resource-model, you'll need to provide known properties to list the resources - The PhysicalResourceId from CloudFormation is often the simplest way to find single-part IDs
- Some resources may require multiple API calls to construct the full composite ID
# Queries that should activate the cloudformation-to-pulumi skill
queries:
# Direct CloudFormation template conversion
- "Convert my CloudFormation template to Pulumi"
# Import ID lookups (cloudformation-id-lookup.md supplemental file)
- "What's the import ID format for an AWS::S3::Bucket?"
- "How do I find the physical resource ID for importing a Lambda function from cloudformation?"
- "Look up the import ID for AWS::RDS::DBInstance"
- "I need to import a CloudFormation resource, what ID format does Pulumi expect?"
- "What import ID do I need for an S3 bucket when importing from CloudFormation?"
# Import with ID lookup (combined workflow)
- "Import resources from a CloudFormation stack into Pulumi. I need help figuring out the right import IDs for each resource type"
Related skills
How it compares
Choose cloudformation-to-pulumi for in-place CloudFormation-to-Pulumi imports; use Terraform migration tooling when the source of truth is HCL rather than AWS stacks.
FAQ
What does cloudformation-to-pulumi do?
Import CloudFormation stacks into Pulumi with resource mapping and preview-verified translation.
When should I use cloudformation-to-pulumi?
Invoke when Import CloudFormation stacks into Pulumi with resource mapping and preview-verified translation.
Is cloudformation-to-pulumi safe to install?
Review the Security Audits panel on this page before installing in production.