Skip to content

Commit fb9d70b

Browse files
authored
Add cdk to pulumi migration guide (#16740)
* Add cdk to pulumi migration guide This PR restructures the AWS CDK → Pulumi migration docs to separate the two major flows (“keep using CDK with Pulumi CDK adapter” vs “migrate to a Pulumi program and import existing resources”) and introduces a shared AWS import-ID guide that both CDK and CloudFormation content can reference. **Key changes** - AWS CDK landing page now a hub - Simplified body to present the two main strategies: - Coexist with CDK-managed stacks. - Convert CDK to Pulumi (either via Pulumi CDK or full Pulumi migration). - New guide: “Using Pulumi with AWS CDK” (Pulumi CDK adapter flow) - Extracted the detailed content that used to live in from-cdk.md into a dedicated guide focused on staying on CDK constructs and - New guide: “Migrating existing AWS CDK applications to Pulumi” (full migration flow) - New shared guide: “AWS import IDs and special cases” - CloudFormation guide now references the shared AWS import-ID guide Restructures the 'Migrating existing CDK apps' guide to prioritize planning and strategy over tooling commands. Why: - I think it was confusing to have the 'Convert + Import' vs 'CDK Importer' distinction. - The previous flow jumped straight into tooling without establishing a migration strategy (Convert vs Rewrite, Import vs Rehydrate). - The distinction between 'Code Conversion' and 'State Migration' needed to be explicit. What: - Moves 'Planning your Migration' to the top level. - Defines clear strategies: Convert (Lift & Shift) vs Hybrid/Refactor vs Rewrite. - Promotes the 'Automated Path' (cdk2pulumi + cdk-importer) as the recommended workflow. - Explicitly adds 'Refactor' as a post-import step in the automated workflow. - Restores detailed examples for consolidating stacks and mapping constructs to components. - Clarifies that 'program import' is the execution step for the generated import file."
1 parent c144260 commit fb9d70b

File tree

5 files changed

+1111
-446
lines changed

5 files changed

+1111
-446
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
---
2+
title_tag: "Finding AWS import IDs and special cases"
3+
meta_desc: Learn how to find AWS resource IDs and handle CloudFormation-specific edge cases when importing resources into Pulumi.
4+
title: AWS import IDs and special cases
5+
h1: "Finding AWS import IDs and special cases"
6+
meta_image: /images/docs/meta-images/docs-meta.png
7+
aliases:
8+
- /docs/iac/guides/migration/migrating-to-pulumi/aws-import-ids/
9+
menu:
10+
iac:
11+
name: Finding AWS import IDs
12+
parent: iac-guides-migration
13+
weight: 3
14+
---
15+
16+
This guide explains how to discover the correct AWS resource IDs to use when importing existing CloudFormation- or CDK-managed resources into Pulumi, and calls out CloudFormation features that require special handling.
17+
18+
## Finding Resource IDs
19+
20+
## About Pulumi import
21+
22+
The [`pulumi import`](/docs/iac/cli/commands/pulumi_import/) command allows you to bring a resource created outside of Pulumi under Pulumi management. This includes resources created by clicking in the AWS Console, or by other infrastructure as code tools such as Terraform, CloudFormation, and AWS CDK. Each resource to be imported requires a resource ID, along with a name for the resource, and its type.
23+
24+
## Finding resource ids
25+
26+
Whether you are running `pulumi import` for a single resource or using a bulk `import.json` file (for example, generated with `pulumi preview --import-file import.json`), you need to supply an ID that uniquely identifies the existing AWS resource. Sometimes this ID is a single value like a resource `ARN`, but other times it is a composite value made up of a combination of property values. For example, to import a [Lambda Permission](https://www.pulumi.com/registry/packages/aws/api-docs/lambda/permission/) resource you need both the `functionName` and the Permission `id` and the id suppled to `pulumi import` must be in the format `functionName|id`.
27+
28+
The first place you should look for resource ids for Pulumi import is by querying the CloudFormation stack. Import ids for most Pulumi resource types can be extracted from the `PhysicalResourceId` property in CloudFormation:
29+
30+
```bash
31+
$ aws cloudformation list-stack-resources --stack-name my-stack \
32+
--query 'StackResourceSummaries[].{Type:ResourceType,LogicalResourceId:LogicalResourceId,Physical:PhysicalResourceId}'
33+
```
34+
35+
Optionally, you can use the `cdk2pulumi` tool to lookup information on the import ids. `cdk2pulumi` has an `ids` subcommand that returns information on the expected import id format:
36+
37+
```bash
38+
$ pulumi plugin run cdk2pulumi -- ids AWS::Lambda::Permission
39+
Resource: aws-native:lambda:Permission (CFN: AWS::Lambda::Permission, provider: aws-native)
40+
Format: {functionName}|{id}
41+
Parts:
42+
- functionName (Input): The name or ARN of the Lambda function, version, or alias.
43+
**Name formats**
44+
+ *Function name*``my-function`` (name-only), ``my-function:v1`` (with alias).
45+
+ *Function ARN*``arn:aws:lambda:us-west-2:123456789012:function:my-function``.
46+
+ *Partial ARN*``123456789012:function:my-function``.
47+
48+
You can append a version number or alias to any of the formats. The length constraint applies only to the full ARN. If you specify only the function name, it is limited to 64 characters in length.
49+
- id (Output)
50+
```
51+
52+
### Finding IDs example
53+
54+
Lets walk through an example of finding the import ids for a couple of resources.
55+
56+
1. List the stack resources:
57+
58+
```bash
59+
$ aws cloudformation list-stack-resources --stack-name test-app-dev \
60+
--query 'StackResourceSummaries[].{ResourceType:ResourceType,LogicalResourceId:LogicalResourceId,PhysicalResourceId:PhysicalResourceId}'
61+
[output]
62+
[
63+
{
64+
"ResourceType": "AWS::ApiGatewayV2::Api",
65+
"LogicalResourceId": "Api48B32C1D",
66+
"PhysicalResourceId": "uzelpdmlxi"
67+
},
68+
{
69+
"ResourceType": "AWS::ApiGatewayV2::Stage",
70+
"LogicalResourceId": "ApiDefaultStageB9B75A7A",
71+
"PhysicalResourceId": "$default"
72+
},
73+
{
74+
"ResourceType": "AWS::ApiGatewayV2::Route",
75+
"LogicalResourceId": "ApiGEThelloF5F722C0",
76+
"PhysicalResourceId": "2kaoey7"
77+
},
78+
{
79+
"ResourceType": "AWS::ApiGatewayV2::Integration",
80+
"LogicalResourceId": "ApiGEThellointegration392349BE",
81+
"PhysicalResourceId": "qwo3s38"
82+
}
83+
]
84+
85+
```
86+
87+
1. Find the import id format for each:
88+
89+
```bash
90+
$ pulumi plugin run cdk2pulumi -- ids AWS::ApiGatewayV2::Api
91+
[output]
92+
Resource: aws-native:apigatewayv2:Api (CFN: AWS::ApiGatewayV2::Api, provider: aws-native)
93+
Format: {apiId}
94+
Parts:
95+
- apiId (Output): The API identifier.
96+
97+
pulumi plugin run cdk2pulumi -- ids AWS::ApiGatewayV2::Stage
98+
Resource: aws:apigatewayv2/stage:Stage (CFN: AWS::ApiGatewayV2::Stage, provider: aws)
99+
Format: {apiId}/{name}
100+
Parts:
101+
- apiId (Segment)
102+
- name (Segment)
103+
Import doc: to import `aws_apigatewayv2_stage` using the API identifier and stage name.//{ to = aws_apigatewayv2_stage.example id = "aabbccddee/example-stage"}
104+
105+
$ pulumi plugin run cdk2pulumi -- ids AWS::ApiGatewayV2::Route
106+
[output]
107+
Resource: aws-native:apigatewayv2:Route (CFN: AWS::ApiGatewayV2::Route, provider: aws-native)
108+
Format: {apiId}|{routeId}
109+
Parts:
110+
- apiId (Input): The API identifier.
111+
- routeId (Output): The route ID.
112+
113+
pulumi plugin run cdk2pulumi -- ids AWS::ApiGatewayV2::Integration
114+
Resource: aws-native:apigatewayv2:Integration (CFN: AWS::ApiGatewayV2::Integration, provider: aws-native)
115+
Format: {apiId}|{integrationId}
116+
Parts:
117+
- apiId (Input): The API identifier.
118+
- integrationId (Output): The integration ID.
119+
```
120+
121+
Typically if the import id consists of a single value (e.g. `apiId` in the Api example) then it will be the `PhysicalResourceId` from CloudFormation. Similarly if it is a composite id where one of the values refers to the `id`, or `name` of itself (e.g. `routeId` of the Route resource) then this will also typically be the `PhysicalResourceId`.
122+
123+
We would then update the `id`s in the bulk import file based on the format and values.
124+
125+
```json
126+
{
127+
"resources": [
128+
{
129+
"type": "aws-native:apigatewayv2:Api",
130+
"name": "Api48B32C1D",
131+
"id": "uzelpdmlxi"
132+
},
133+
{
134+
"type": "aws-native:apigatewayv2:Integration",
135+
"name": "ApiGEThellointegration392349BE",
136+
"id": "uzelpdmlxi|qwo3s38"
137+
},
138+
{
139+
"type": "aws-native:apigatewayv2:Route",
140+
"name": "ApiGEThelloF5F722C0",
141+
"id": "uzelpdmlxi|2kaoey7"
142+
},
143+
{
144+
"type": "aws:apigatewayv2/stage:Stage",
145+
"name": "ApiDefaultStageB9B75A7A",
146+
"id": "uzelpdmlxi/$default"
147+
}
148+
]
149+
}
150+
```
151+
152+
### Looking up Resource IDs
153+
154+
For some resource types, the resource ID may not be the value of its `PhysicalResourceId` in CloudFormation. In these cases it is necessary to perform AWS API calls to look up the necessary information:
155+
156+
```bash
157+
$ aws cloudformation list-stack-resources --stack-name test-app-dev \
158+
--query 'StackResourceSummaries[].{ResourceType:ResourceType,Logical:LogicalResourceId,PhysicalResourceId:PhysicalResourceId}'
159+
[
160+
{
161+
"ResourceType": "AWS::EC2::EIP",
162+
"LogicalResourceId": "VpcPublicSubnet1EIPD7E02669",
163+
"PhysicalResourceId": "3.150.255.6"
164+
},
165+
{
166+
"ResourceType": "AWS::ApplicationAutoScaling::ScalingPolicy",
167+
"LogicalResourceId": "StorageDynamoTableReadScalingTargetTrackingDB061E27",
168+
"PhysicalResourceId": "arn:aws:autoscaling:us-east-2:12345678910:scalingPolicy:c54f759a-aa04-4c0e-afbc-d45a960593a4:resource/dynamodb/table/Example-Dev-StorageDynamoTable201FACD8-1KCPDDFW2WLP6:policyName/ExampleDevStorageDynamoTableReadScalingTargetTrackingDE82FE6D"
169+
}
170+
]
171+
172+
```
173+
174+
If we look up the information on these two resources we will see these are not as straightforward as the previous examples.
175+
176+
```bash
177+
$ pulumi plugin run cdk2pulumi -- ids AWS::ApplicationAutoScaling::ScalingPolicy
178+
Resource: aws-native:applicationautoscaling:ScalingPolicy (CFN: AWS::ApplicationAutoScaling::ScalingPolicy, provider: aws-native)
179+
Format: {arn}|{scalableDimension}
180+
Parts:
181+
- arn (Output): Returns the ARN of a scaling policy.
182+
- scalableDimension (Input): The scalable dimension. This string consists of the service namespace, resource type, and scaling property.
183+
+ ``dynamodb:table:ReadCapacityUnits`` - The provisioned read capacity for a DynamoDB table.
184+
+ ``dynamodb:table:WriteCapacityUnits`` - The provisioned write capacity for a DynamoDB table.
185+
+ ``dynamodb:index:ReadCapacityUnits`` - The provisioned read capacity for a DynamoDB global secondary index.
186+
+ ``dynamodb:index:WriteCapacityUnits`` - The provisioned write capacity for a DynamoDB global secondary index.
187+
+ ... # truncated for brevity
188+
189+
pulumi plugin run cdk2pulumi -- ids AWS::EC2::EIP
190+
Resource: aws-native:ec2:Eip (CFN: AWS::EC2::EIP, provider: aws-native)
191+
Format: {publicIp}|{allocationId}
192+
Parts:
193+
- publicIp (Output): The Elastic IP address.
194+
- allocationId (Output): The ID that AWS assigns to represent the allocation of the address for use with Amazon VPC. This is returned only for VPC elastic IP addresses. For example, `eipalloc-5723d13e` .
195+
196+
```
197+
198+
Starting with the `ScalingPolicy` resource we can see that we need the `arn` which matches the `PhysicalResourceId`, but we also need the `scalableDimension` which is not in the CloudFormation data. Based on the information provided from the `ids` command we might be able to look at that CloudFormation resource and figure out which `scalableDimension` to use, otherwise we can use the `cloudcontrol` CLI commands. The `aws cloudcontrol list-resources` command will list all the resources of that type, which we can filter using `jq` to only include entries that have the part of the identifier that we know.
199+
200+
```bash
201+
$ aws cloudcontrol list-resources --type-name AWS::ApplicationAutoScaling::ScalingPolicy --resource-model '{"ServiceNamespace": "dynamodb"}' --profile dev-admin | jq '.ResourceDescriptions[] | select(.Identifier | contains("arn:aws:autoscaling:us-east-2:12345678910:scalingPolicy:c54f759a-aa04-4c0e-afbc-d45a960593a4:resource/dynamodb/table/Example-Dev-StorageDynamoTable201FACD8-1KCPDDFW2WLP6:policyName/ExampleDevStorageDynamoTableReadScalingTargetTrackingDE82FE6D")) | .Identifier'
202+
203+
"arn:aws:autoscaling:us-east-2:12345678910:scalingPolicy:c54f759a-aa04-4c0e-afbc-d45a960593a4:resource/dynamodb/table/Example-Dev-StorageDynamoTable201FACD8-1KCPDDFW2WLP6:policyName/ExampleDevStorageDynamoTableReadScalingTargetTrackingDE82FE6D|dynamodb:table:ReadCapacityUnits"
204+
205+
206+
```
207+
208+
The `EIP` resource is an example of a resource which has an id containing a value that cannot be determined by either the CloudFormation data or the template. It requires the `publicIp`, which we have, and the `allocationId` which we do not. We can use the same `aws cloudcontrol list-resources` command to find the correct identifier.
209+
210+
```bash
211+
$ aws cloudcontrol list-resources --type-name AWS::EC2::EIP --profile dev-admin | jq '.ResourceDescriptions[] | select(.Identifier | contains("3.150.255.6")) | .Identifier'
212+
213+
"3.150.255.6|eipalloc-0a79aa2cb81750a49"
214+
```

0 commit comments

Comments
 (0)