[アップデート] CloudFormationのStack間のリソースの移動や論理IDの変更を簡単に行えるようになりました
もっと簡単にStack間のリソースの移動を行いたい
こんにちは、のんピ(@non____97)です。
皆さんはCloudFormationやAWS CDKを使っていて「もっと簡単にStack間のリソースの移動をしたい」や「論理IDを変更したい」と思ったことはありますか? 私はあります。
従来はそのような対応をする場合、以下のようなステップが必要でした。
- Deletion Policyで
Retain
を設定して、Stack上で削除されても、リソースを削除されないようにする - Stack上からリソースを削除する
- 別のStack or 別の論理IDとしてリソースをインポート
どうしても手間が掛かりますし、非常に神経を使います。
今回アップデートによって、 CloudFormationのStack間のリソースの移動や論理IDの変更を簡単に行えるようになりました。
AWS Blogsにも投稿されています。
個人的にはAWS CDKで論理ID名を後から変更したくなったり、別のカスタムConstructにまとめたくなることが1日に10回はあるので、これは非常にありがたいアップデートです。
実際に試して見ました。
いきなりまとめ
- リファクタリングをする際には以下の2つの情報が必要
- リファクタリング後のStackのテンプレートファイル
- リファクタリング後に変更する論理IDをマッピングした定義ファイル
- AWS CDKでもデプロイしたStackに対しても使用できる
- AWS CDKでデプロイしているリソースをリファクタリングをする際のステップは以下
- AWS CDKのコードの修正
npx cdk diff
で差分と修正後の論理IDの確認npx cdk synth
でテンプレートファイルを出力- 出力されたテンプレートファイル内で、リファクタリング対象のリソースのメタデータを修正し、現在の値と一致させる
- リファクタリングの定義と実行
npx cdk diff
でメタデータのみ差分が出力されることを確認npx cdk deploy
でメタデータを反映
- その他の注意点
- 新規でStackを作成する際には
Condition
とParameter
を指定できない - Construct名の変更などConstructの変更を伴うリファクタリングを行う場合は
AWS::CDK::Metadata
のAnalytics
の出力を無効にする必要がある
- 新規でStackを作成する際には
論理IDの変更 (AWS CDK)
初期状態
早速実際に試してみましょう。
「CloudFormationのアップデートならAWS CDKでもできるでしょ」ということで、AWS CDKから試します。
以下のようにIAM RoleとIAM Policyを作成するだけのStackをデプロイします。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export interface IamRoleConstructProps {
roleName: string;
}
export class IamRoleConstruct extends Construct {
constructor(scope: Construct, id: string, props: IamRoleConstructProps) {
super(scope, id);
const policy = new cdk.aws_iam.ManagedPolicy(this, "Policy", {
statements: [
new cdk.aws_iam.PolicyStatement({
effect: cdk.aws_iam.Effect.ALLOW,
resources: ["*"],
actions: ["ec2:DescribeTags"],
}),
],
});
new cdk.aws_iam.Role(this, "Default", {
roleName: props?.roleName,
assumedBy: new cdk.aws_iam.CompositePrincipal(
new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com")
),
managedPolicies: [policy],
});
}
}
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { IamRoleConstruct } from "./construct/iam-role-construct";
export class ReshapeCfnStackStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new IamRoleConstruct(this, "IamRoleConstructA", {
roleName: "test1",
});
}
}
npx cdk synth
すると以下のようなテンプレートを確認できます。
> npx cdk synth --no-version-reporting --no-path-metadata
Resources:
IamRoleConstructAPolicy45ACD7CB:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: ""
Path: /
PolicyDocument:
Statement:
- Action: ec2:DescribeTags
Effect: Allow
Resource: "*"
Version: "2012-10-17"
IamRoleConstructABEBB5F34:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- Ref: IamRoleConstructAPolicy45ACD7CB
RoleName: test1
Parameters:
BootstrapVersion:
Type: AWS::SSM::Parameter::Value<String>
Default: /cdk-bootstrap/hnb659fds/version
Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
npx cdk deploy
でデプロイします。
Stackデプロイ後のリソース一覧は以下のとおりです。
この後、IAM Policyの論理IDを変更します。
リファクタリングの実行 (1回目)
それではリファクタリングを行います。
Policy
をPolicy1
に変更します。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export interface IamRoleConstructProps {
roleName: string;
}
export class IamRoleConstruct extends Construct {
constructor(scope: Construct, id: string, props: IamRoleConstructProps) {
super(scope, id);
+ const policy = new cdk.aws_iam.ManagedPolicy(this, "Policy1", {
statements: [
new cdk.aws_iam.PolicyStatement({
effect: cdk.aws_iam.Effect.ALLOW,
resources: ["*"],
actions: ["ec2:DescribeTags"],
}),
],
});
new cdk.aws_iam.Role(this, "Default", {
roleName: props?.roleName,
assumedBy: new cdk.aws_iam.CompositePrincipal(
new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com")
),
managedPolicies: [policy],
});
}
}
npx cdk diff
で差分を確認します。
> npx cdk diff --no-change-set
Stack ReshapeCfnStackStack
IAM Policy Changes
┌───┬──────────────────────────────┬──────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼──────────────────────────────┼──────────────────────────────┤
│ - │ ${IamRoleConstructA/Default} │ ${IamRoleConstructA/Policy} │
├───┼──────────────────────────────┼──────────────────────────────┤
│ + │ ${IamRoleConstructA/Default} │ ${IamRoleConstructA/Policy1} │
└───┴──────────────────────────────┴──────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[-] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy IamRoleConstructAPolicy45ACD7CB destroy
[+] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy1 IamRoleConstructAPolicy138F2EF20
[~] AWS::IAM::Role IamRoleConstructA/Default IamRoleConstructABEBB5F34
└─ [~] ManagedPolicyArns
└─ @@ -1,5 +1,5 @@
[ ] [
[ ] {
[-] "Ref": "IamRoleConstructAPolicy45ACD7CB"
[+] "Ref": "IamRoleConstructAPolicy138F2EF20"
[ ] }
[ ] ]
✨ Number of stacks with differences: 1
論理IDが変わってしまうので、IAM Policyが再作成されそうです。IAM Roleならまだしも、これがRDS DBインスタンスなどデータを保持するリソースの場合、これは避けたいです。
それでは、リファクタリングをします。
まず、リファクタリング対象を定義します。
必要な情報は以下2つです。
- リファクタリング後のStackのテンプレートファイル
- リファクタリング後に変更する論理IDをマッピングした定義ファイル
厳密にはどちらもファイルである必要はないのですが、ファイルで渡した方がスッキリして見やすいでしょう。
前者の「リファクタリング後のStackのテンプレートファイル」は先ほどnpx cdk diff
の際に合成されたcdk.out/ReshapeCfnStackStack.template.json
を使用します。
後者の「リファクタリング後に変更する論理IDをマッピングした定義ファイル」は以下フォーマットで定義します。
[
{
"Source": {
"StackName": "string",
"LogicalResourceId": "string"
},
"Destination": {
"StackName": "string",
"LogicalResourceId": "string"
}
}
...
]
今回は以下のように定義しました。
[
{
"Source": {
"StackName": "ReshapeCfnStackStack",
"LogicalResourceId": "IamRoleConstructAPolicy45ACD7CB"
},
"Destination": {
"StackName": "ReshapeCfnStackStack",
"LogicalResourceId": "IamRoleConstructAPolicy138F2EF20"
}
}
]
それでは実際にやって見ましょう。
create-stack-refactorを叩きます。
> aws --version
aws-cli/2.23.15 Python/3.12.9 Darwin/24.1.0 source/arm64
> aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=ReshapeCfnStackStack,TemplateBody@=file://cdk.out/ReshapeCfnStackStack.template.json \
--no-enable-stack-creation \
--resource-mappings file://refactor1.json
{
"StackRefactorId": "37ae4f3c-9881-4a75-8271-3d9c91aedbb9"
}
> aws cloudformation describe-stack-refactor --stack-refactor-id 37ae4f3c-9881-4a75-8271-3d9c91aedbb9
{
"StackRefactorId": "37ae4f3c-9881-4a75-8271-3d9c91aedbb9",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf"
],
"ExecutionStatus": "UNAVAILABLE",
"Status": "CREATE_FAILED",
"StatusReason": "Resource IamRoleConstructAPolicy45ACD7CB in stack arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf does not match the destination resource's properties."
}
はい、Resource IamRoleConstructAPolicy45ACD7CB in stack arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf does not match the destination resource's properties.
とエラーになってしまいました。
エラー文を見るにリファクタリング対象のリソースが、リファクタリング先のテンプレートとパラメーターが一致しないためにエラーとなっているようです。
もしかすると、IAM Policyに物理名を指定していないためでしょうか。
IAM Policyは物理名を指定しない場合、論理IDから物理名を生成します。
今回、論理IDが変わってしまうに伴い、物理名も変化しようとしてしまい、エラーになるのかもしれません。
リファクタリングの実行 (IAM Policyの物理名を指定してトライ)
IAM Policyの物理名を指定してトライします。
以下のように物理名を指定しました。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export interface IamRoleConstructProps {
roleName: string;
}
export class IamRoleConstruct extends Construct {
constructor(scope: Construct, id: string, props: IamRoleConstructProps) {
super(scope, id);
const policy = new cdk.aws_iam.ManagedPolicy(this, "Policy", {
+ managedPolicyName: `${props.roleName}-policy`,
statements: [
new cdk.aws_iam.PolicyStatement({
effect: cdk.aws_iam.Effect.ALLOW,
resources: ["*"],
actions: ["ec2:DescribeTags"],
}),
],
});
new cdk.aws_iam.Role(this, "Default", {
roleName: props?.roleName,
assumedBy: new cdk.aws_iam.CompositePrincipal(
new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com")
),
managedPolicies: [policy],
});
}
}
一旦デプロイします。
> npx cdk diff --no-change-set
Stack ReshapeCfnStackStack
Resources
[~] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy IamRoleConstructAPolicy45ACD7CB replace
└─ [+] ManagedPolicyName (requires replacement)
└─ test1-policy
✨ Number of stacks with differences: 1
> npx cdk deploy
✨ Synthesis time: 8.59s
ReshapeCfnStackStack: start: Building 6f87046052f4fedec3ad90690831bd05199b5371d1246044a64081ea13f0f19f:current_account-current_region
.
.
(中略)
.
.
ReshapeCfnStackStack | 4/5 | 11:31:50 | UPDATE_COMPLETE | AWS::CloudFormation::Stack | ReshapeCfnStackStack
✅ ReshapeCfnStackStack
✨ Deployment time: 56.2s
Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf
✨ Total time: 64.79s
デプロイ後のリソース一覧は以下のとおりです。
それでは、この状態で論理IDを変更します。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export interface IamRoleConstructProps {
roleName: string;
}
export class IamRoleConstruct extends Construct {
constructor(scope: Construct, id: string, props: IamRoleConstructProps) {
super(scope, id);
+ const policy = new cdk.aws_iam.ManagedPolicy(this, "Policy1", {
managedPolicyName: `${props.roleName}-policy`,
statements: [
new cdk.aws_iam.PolicyStatement({
effect: cdk.aws_iam.Effect.ALLOW,
resources: ["*"],
actions: ["ec2:DescribeTags"],
}),
],
});
new cdk.aws_iam.Role(this, "Default", {
roleName: props?.roleName,
assumedBy: new cdk.aws_iam.CompositePrincipal(
new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com")
),
managedPolicies: [policy],
});
}
}
npx cdk diff
をして、論理IDが何に変わるか確認しておきます。
> npx cdk diff --no-change-set
Stack ReshapeCfnStackStack
IAM Policy Changes
┌───┬──────────────────────────────┬──────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼──────────────────────────────┼──────────────────────────────┤
│ - │ ${IamRoleConstructA/Default} │ ${IamRoleConstructA/Policy} │
├───┼──────────────────────────────┼──────────────────────────────┤
│ + │ ${IamRoleConstructA/Default} │ ${IamRoleConstructA/Policy1} │
└───┴──────────────────────────────┴──────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[-] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy IamRoleConstructAPolicy45ACD7CB destroy
[+] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy1 IamRoleConstructAPolicy138F2EF20
[~] AWS::IAM::Role IamRoleConstructA/Default IamRoleConstructABEBB5F34
└─ [~] ManagedPolicyArns
└─ @@ -1,5 +1,5 @@
[ ] [
[ ] {
[-] "Ref": "IamRoleConstructAPolicy45ACD7CB"
[+] "Ref": "IamRoleConstructAPolicy138F2EF20"
[ ] }
[ ] ]
✨ Number of stacks with differences: 1
確認した論理IDをrefactor1.json
で設定してリファクタリングの定義を行います。
> aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=ReshapeCfnStackStack,TemplateBody@=file://cdk.out/ReshapeCfnStackStack.template.json \
--no-enable-stack-creation \
--resource-mappings file://refactor1.json
{
"StackRefactorId": "f288e5be-e264-4041-9b8d-c7edd2916a24"
}
> aws cloudformation describe-stack-refactor --stack-refactor-id f288e5be-e264-4041-9b8d-c7edd2916a24
{
"StackRefactorId": "f288e5be-e264-4041-9b8d-c7edd2916a24",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf"
],
"ExecutionStatus": "UNAVAILABLE",
"Status": "CREATE_FAILED",
"StatusReason": "Resource IamRoleConstructAPolicy45ACD7CB in stack arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf does not match the destination resource's properties."
}
はい、同じエラーで失敗しました。
本当に差分はあるのでしょうか。
試しに出力された./cdk.out/ReshapeCfnStackStack.template.json
とマネジメントコンソール上で表示されている現在のStackのテンプレートをdiffしてみます。
> diff -u ./before.json ./cdk.out/ReshapeCfnStackStack.template.json
--- ./before.json 2025-02-07 11:49:35
+++ ./cdk.out/ReshapeCfnStackStack.template.json 2025-02-07 11:36:11
@@ -1,6 +1,6 @@
{
"Resources": {
- "IamRoleConstructAPolicy45ACD7CB": {
+ "IamRoleConstructAPolicy138F2EF20": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"Description": "",
\ No newline at end of file
@@ -16,9 +16,6 @@
],
"Version": "2012-10-17"
}
- },
- "Metadata": {
- "aws:cdk:path": "ReshapeCfnStackStack/IamRoleConstructA/Policy/Resource"
}
},
"IamRoleConstructABEBB5F34": {
\ No newline at end of file
@@ -38,292 +35,13 @@
},
"ManagedPolicyArns": [
{
- "Ref": "IamRoleConstructAPolicy45ACD7CB"
+ "Ref": "IamRoleConstructAPolicy138F2EF20"
}
],
"RoleName": "test1"
- },
- "Metadata": {
- "aws:cdk:path": "ReshapeCfnStackStack/IamRoleConstructA/Default/Resource"
}
- },
- "CDKMetadata": {
- "Type": "AWS::CDK::Metadata",
- "Properties": {
- "Analytics": "v2:deflate64:H4sIAAAAAAAA/1WOT4vCQ..(中略)..+EepVIuAQAA"
- },
- "Metadata": {
- "aws:cdk:path": "ReshapeCfnStackStack/CDKMetadata/Default"
- },
- "Condition": "CDKMetadataAvailable"
}
},
- "Conditions": {
- "CDKMetadataAvailable": {
.
.
(中略)
.
.
- }
- },
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
\ No newline at end of file
リソースの"Properties"
に差分はないようです。
リファクタリングの実行 (リファクタリング対象のリソースのメタデータを修正してトライ)
もしかすると、aws:cdk:path
のメタデータが異なることが原因でエラーになっているのでしょうか。
実際に試してみます。
diffしやすくするためにJSON形式のテンプレートファイルを合成します。
> npx cdk synth --json
{
"Resources": {
"IamRoleConstructAPolicy138F2EF20": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"Description": "",
"ManagedPolicyName": "test1-policy",
"Path": "/",
"PolicyDocument": {
"Statement": [
{
"Action": "ec2:DescribeTags",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
}
},
"Metadata": {
"aws:cdk:path": "ReshapeCfnStackStack/IamRoleConstructA/Policy1/Resource"
}
},
"IamRoleConstructABEBB5F34": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
{
"Ref": "IamRoleConstructAPolicy138F2EF20"
}
],
"RoleName": "test1"
},
"Metadata": {
"aws:cdk:path": "ReshapeCfnStackStack/IamRoleConstructA/Default/Resource"
}
},
"CDKMetadata": {
"Type": "AWS::CDK::Metadata",
"Properties": {
"Analytics": "v2:deflate64:H4sIAAAAAAAA/1WOT4vCQ..(中略)..EepVIuAQAA"
},
"Metadata": {
"aws:cdk:path": "ReshapeCfnStackStack/CDKMetadata/Default"
},
"Condition": "CDKMetadataAvailable"
}
},
"Conditions": {
"CDKMetadataAvailable": {
.
.
(中略)
.
.
}
},
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
}
}
diffで現在のStackのテンプレートとの差分を確認します。
diff -u cdk-iam-role-after.json before.json
--- cdk-iam-role-after.json 2025-02-07 14:54:21
+++ before.json 2025-02-07 14:56:23
@@ -1,6 +1,6 @@
{
"Resources": {
- "IamRoleConstructAPolicy138F2EF20": {
+ "IamRoleConstructAPolicy45ACD7CB": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"Description": "",
\ No newline at end of file
@@ -18,7 +18,7 @@
}
},
"Metadata": {
- "aws:cdk:path": "ReshapeCfnStackStack/IamRoleConstructA/Policy1/Resource"
+ "aws:cdk:path": "ReshapeCfnStackStack/IamRoleConstructA/Policy/Resource"
}
},
"IamRoleConstructABEBB5F34": {
\ No newline at end of file
@@ -38,7 +38,7 @@
},
"ManagedPolicyArns": [
{
- "Ref": "IamRoleConstructAPolicy138F2EF20"
+ "Ref": "IamRoleConstructAPolicy45ACD7CB"
}
],
"RoleName": "test1"
\ No newline at end of file
@@ -330,5 +330,32 @@
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
.
.
(中略)
.
.
+ }
}
}
\ No newline at end of file
他に怪しいところとすると、aws:cdk:path
のメタデータぐらいです。
合成されたテンプレートのaws:cdk:path
を修正して、現在のStackのテンプレート上と同じ値にします。
> diff -u cdk-iam-role-after.json before.json
--- cdk-iam-role-after.json 2025-02-07 14:56:57
+++ before.json 2025-02-07 14:56:23
@@ -1,6 +1,6 @@
{
"Resources": {
- "IamRoleConstructAPolicy138F2EF20": {
+ "IamRoleConstructAPolicy45ACD7CB": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"Description": "",
\ No newline at end of file
@@ -38,7 +38,7 @@
},
"ManagedPolicyArns": [
{
- "Ref": "IamRoleConstructAPolicy138F2EF20"
+ "Ref": "IamRoleConstructAPolicy45ACD7CB"
}
],
"RoleName": "test1"
\ No newline at end of file
@@ -330,5 +330,32 @@
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
.
.
(中略)
.
.
+ }
}
}
\ No newline at end of file
この状態でリファクタリングの定義を行います。
> aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=ReshapeCfnStackStack,TemplateBody@=file://cdk-iam-role-after.json \
--no-enable-stack-creation \
--resource-mappings file://refactor1.json
{
"StackRefactorId": "1faed814-a53b-4fe3-bd12-627613ebb1cb"
}
> aws cloudformation describe-stack-refactor --stack-refactor-id 1faed814-a53b-4fe3-bd12-627613ebb1cb
{
"StackRefactorId": "1faed814-a53b-4fe3-bd12-627613ebb1cb",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf"
],
"ExecutionStatus": "AVAILABLE",
"Status": "CREATE_COMPLETE"
}
無事完了しました。
list-stack-refactor-actionsで、これによりリファクタリングされる内容を確認します。
> aws cloudformation list-stack-refactor-actions --stack-refactor-id 1faed814-a53b-4fe3-bd12-627613ebb1cb
{
"StackRefactorActions": [
{
"Action": "MOVE",
"Entity": "RESOURCE",
"PhysicalResourceId": "arn:aws:iam::<AWSアカウントID>:policy/test1-policy",
"Description": "No configuration changes detected.",
"Detection": "MANUAL",
"TagResources": [],
"UntagResources": [],
"ResourceMapping": {
"Source": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"LogicalResourceId": "IamRoleConstructAPolicy45ACD7CB"
},
"Destination": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"LogicalResourceId": "IamRoleConstructAPolicy138F2EF20"
}
}
}
]
}
定義したとおりの変更が行われそうです。
それではリファクタリングの実行をします。
> aws cloudformation execute-stack-refactor --stack-refactor-id 1faed814-a53b-4fe3-bd12-627613ebb1cb
> aws cloudformation describe-stack-refactor --stack-refactor-id 1faed814-a53b-4fe3-bd12-627613ebb1cb
{
"StackRefactorId": "1faed814-a53b-4fe3-bd12-627613ebb1cb",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf"
],
"ExecutionStatus": "EXECUTE_COMPLETE",
"Status": "CREATE_COMPLETE"
}
一瞬で完了しました。
Stackのイベントを確認します。
IMPORT_IN_PROGRESS
やIMPORT_COMPLETE
、EXPORT_COMPLETE
とイベントがあることから内部的にImport/Exportが行われているようです。
リソース一覧を確認しましょう。
はい、ツリービューを展開してもリファクタリング対象のIAM Policyを確認できません。これはメタデータが本来と異なるためです。
実際はリソースは削除されておらず、フラットビューから確認することが可能です。
実際にIAM Roleにアクセスすると、リファクタリング対象のIAM Policyがアタッチされていることを確認できました。
リファクタリング後のStackの更新
ツリービューが正しく表示されなくなるのは気になります。また、リファクタリング後に正常にStackを更新できるかも気になります。
試しにnpx cdk diff
してみましょう。
> npx cdk diff --no-change-set
Stack ReshapeCfnStackStack
Resources
[~] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy1 IamRoleConstructAPolicy138F2EF20
└─ [~] Metadata
└─ [~] .aws:cdk:path:
├─ [-] ReshapeCfnStackStack/IamRoleConstructA/Policy/Resource
└─ [+] ReshapeCfnStackStack/IamRoleConstructA/Policy1/Resource
✨ Number of stacks with differences: 1
メタデータのみ更新してくれそうですね。リファクタリングを実行したタイミングでStackのテンプレートも更新されていそうです。
Stackを更新します。
> npx cdk deploy
✨ Synthesis time: 8.67s
ReshapeCfnStackStack: start: Building 2319551a001853907f524f0333755cf12c1b8ec17856895f066450576da4eb8c:current_account-current_region
.
.
(中略)
.
.
ReshapeCfnStackStack | 3/3 | 15:07:11 | UPDATE_COMPLETE | AWS::CloudFormation::Stack | ReshapeCfnStackStack
✅ ReshapeCfnStackStack
✨ Deployment time: 22.21s
Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf
✨ Total time: 30.88s
デプロイ後、ツリービューを確認すると問題なく表示されました。
ということで、AWS CDKでデプロイしているリソースをリファクタリングをする際には以下のステップが必要になります。
- AWS CDKのコードの修正
npx cdk diff
で差分と修正後の論理IDの確認npx cdk synth
でテンプレートファイルを出力- 出力されたテンプレートファイル内で、リファクタリング対象のリソースのメタデータを修正し、現在の値と一致させる
- リファクタリングの定義と実行
npx cdk diff
でメタデータのみ差分が出力されることを確認npx cdk deploy
でメタデータを反映
論理IDの変更 (CloudFormation)
初期状態
AWS CDKを介さず、純粋なCloudFormationでも試してみます。
テンプレートファイルは以下のとおりです。
Resources:
IamRoleConstructAPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: ""
Path: /
PolicyDocument:
Statement:
- Action: ec2:DescribeTags
Effect: Allow
Resource: "*"
Version: "2012-10-17"
IamRoleConstructABEBB5F34:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- Ref: IamRoleConstructAPolicy
RoleName: cfn-test1
今回はIAM Policyの物理名は指定していません。
Stackをデプロイします。
> aws cloudformation create-stack \
--stack-name IamRoleStacks \
--template-body file://iam-role-before.yml \
--capabilities CAPABILITY_NAMED_IAM
{
"StackId": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/IamRoleStacks/9dde0210-e50e-11ef-b1ce-123c895e04bb"
}
デプロイ後のリソース一覧は以下のとおりです。
リファクタリングの実行
リファクタリングを行います。
差分は以下のとおりで、IamRoleConstructAPolicy
をIamRoleConstructAPolicy1
に変更するのみです。
> diff -u iam-role-before.yml iam-role-after.yml
--- iam-role-before.yml 2025-02-07 13:47:07
+++ iam-role-after.yml 2025-02-07 13:49:29
@@ -1,5 +1,5 @@
Resources:
- IamRoleConstructAPolicy:
+ IamRoleConstructAPolicy1:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: ""
@@ -21,5 +21,5 @@
Service: ec2.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- - Ref: IamRoleConstructAPolicy
+ - Ref: IamRoleConstructAPolicy1
RoleName: cfn-test1
定義ファイルも以下のとおり、非常にシンプルです。
[
{
"Source": {
"StackName": "IamRoleStacks",
"LogicalResourceId": "IamRoleConstructAPolicy"
},
"Destination": {
"StackName": "IamRoleStacks",
"LogicalResourceId": "IamRoleConstructAPolicy1"
}
}
]
リファクタリングの定義を作成します。
> aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=IamRoleStacks,TemplateBody@=file://iam-role-after.yml \
--no-enable-stack-creation \
--resource-mappings file://refactor2.json
{
"StackRefactorId": "04af50ac-0e46-46cb-8407-c02bbb098c37"
}
> aws cloudformation describe-stack-refactor --stack-refactor-id 04af50ac-0e46-46cb-8407-c02bbb098c37
{
"StackRefactorId": "04af50ac-0e46-46cb-8407-c02bbb098c37",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/IamRoleStacks/9dde0210-e50e-11ef-b1ce-123c895e04bb"
],
"ExecutionStatus": "AVAILABLE",
"Status": "CREATE_COMPLETE"
}
> aws cloudformation list-stack-refactor-actions --stack-refactor-id 04af50ac-0e46-46cb-8407-c02bbb098c37
{
"StackRefactorActions": [
{
"Action": "MOVE",
"Entity": "RESOURCE",
"PhysicalResourceId": "arn:aws:iam::<AWSアカウントID>:policy/IamRoleStacks-IamRoleConstructAPolicy-e5y5tHCgdooi",
"Description": "No configuration changes detected.",
"Detection": "MANUAL",
"TagResources": [],
"UntagResources": [],
"ResourceMapping": {
"Source": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/IamRoleStacks/9dde0210-e50e-11ef-b1ce-123c895e04bb",
"LogicalResourceId": "IamRoleConstructAPolicy"
},
"Destination": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/IamRoleStacks/9dde0210-e50e-11ef-b1ce-123c895e04bb",
"LogicalResourceId": "IamRoleConstructAPolicy1"
}
}
}
]
}
正常にできました。
リファクタリングの実行をします。
> aws cloudformation execute-stack-refactor --stack-refactor-id 04af50ac-0e46-46cb-8407-c02bbb098c37
> aws cloudformation describe-stack-refactor --stack-refactor-id 04af50ac-0e46-46cb-8407-c02bbb098c37
{
"StackRefactorId": "04af50ac-0e46-46cb-8407-c02bbb098c37",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/IamRoleStacks/9dde0210-e50e-11ef-b1ce-123c895e04bb"
],
"ExecutionStatus": "EXECUTE_COMPLETE",
"Status": "CREATE_COMPLETE"
}
こちらも正常に完了しました。
Stackのイベントは以下のとおりです。
論理IDが正常に変わっていることを確認できました。
また、テンプレートもiam-role-after.yml
で指定したものに変更されていました。
Stack間のリソースの移動
下準備
Stack間のリソースの移動も試してみます。
IamRoleConstructA
を別Stackに移動させます。
このまま移動させてしまうとStack内のリソースが空になってしまい、エラーとなります。
> aws cloudformation describe-stack-refactor \
--stack-refactor-id 34f28269-d951-4341-a630-157998b44342 \
--query 'StatusReason'
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf: Empty [/Resources] values are not allowed in templates. CloudFormation does not support empty stack creation."
そのため、IamRoleConstructB
というConstructを一つ追加してデプロイしておきます。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { IamRoleConstruct } from "./construct/iam-role-construct";
export class ReshapeCfnStackStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new IamRoleConstruct(this, "IamRoleConstructA", {
roleName: "test1",
});
+ new IamRoleConstruct(this, "IamRoleConstructB", {
+ roleName: "test2",
+ });
}
}
変更箇所の確認
IamRoleConstructB
のデプロイが完了したら、Stack間のリソースの移動を行うための差分を確認します。
以下のようにReshapeCfnStackStack
のIamRoleConstructA
をReshapeCfnStack2Stack
のIamRoleConstructA2
として移動させます。
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";
import { ReshapeCfnStackStack } from "../lib/reshape-cfn-stack-stack";
+ import { ReshapeCfnStack2Stack } from "../lib/reshape-cfn-stack2-stack";
const app = new cdk.App();
new ReshapeCfnStackStack(app, "ReshapeCfnStackStack", {});
+ new ReshapeCfnStack2Stack(app, "ReshapeCfnStack2Stack", {});
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { IamRoleConstruct } from "./construct/iam-role-construct";
export class ReshapeCfnStack2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new IamRoleConstruct(this, "IamRoleConstructA2", {
roleName: "test1",
});
}
}
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { IamRoleConstruct } from "./construct/iam-role-construct";
export class ReshapeCfnStackStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
+ // new IamRoleConstruct(this, "IamRoleConstructA", {
+ // roleName: "test1",
+ // });
new IamRoleConstruct(this, "IamRoleConstructC", {
roleName: "test2",
});
}
}
npx cdk diff
で差分と論理IDを確認します。
> npx cdk diff --no-change-set
Stack ReshapeCfnStackStack
IAM Statement Changes
┌───┬──────────────────────────────────┬────────┬────────────────┬───────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼──────────────────────────────────┼────────┼────────────────┼───────────────────────────┼───────────┤
│ - │ ${IamRoleConstructA/Default.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
└───┴──────────────────────────────────┴────────┴────────────────┴───────────────────────────┴───────────┘
IAM Policy Changes
┌───┬──────────────────────────────┬──────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼──────────────────────────────┼──────────────────────────────┤
│ - │ ${IamRoleConstructA/Default} │ ${IamRoleConstructA/Policy1} │
└───┴──────────────────────────────┴──────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[-] AWS::IAM::ManagedPolicy IamRoleConstructA/Policy1 IamRoleConstructAPolicy138F2EF20 destroy
[-] AWS::IAM::Role IamRoleConstructA/Default IamRoleConstructABEBB5F34 destroy
Stack ReshapeCfnStack2Stack
IAM Statement Changes
┌───┬───────────────────────────────────┬────────┬────────────────┬───────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼───────────────────────────────────┼────────┼────────────────┼───────────────────────────┼───────────┤
│ + │ ${IamRoleConstructA2/Default.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
└───┴───────────────────────────────────┴────────┴────────────────┴───────────────────────────┴───────────┘
IAM Policy Changes
┌───┬───────────────────────────────┬───────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼───────────────────────────────┼───────────────────────────────┤
│ + │ ${IamRoleConstructA2/Default} │ ${IamRoleConstructA2/Policy1} │
└───┴───────────────────────────────┴───────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {.
.
(中略)
.
.}
Resources
[+] AWS::IAM::ManagedPolicy IamRoleConstructA2/Policy1 IamRoleConstructA2Policy1CFD08057
[+] AWS::IAM::Role IamRoleConstructA2/Default IamRoleConstructA296AD5240
✨ Number of stacks with differences: 2
リファクタリングの実行 (1回目)
それではリファクタリングをします。
npx cdk synth
でStackのテンプレートを合成して、ファイルとして保存しておきます。
> npx cdk synth ReshapeCfnStackStack
Resources:
IamRoleConstructBPolicy1A4484B55:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: ""
ManagedPolicyName: test2-policy
Path: /
PolicyDocument:
Statement:
- Action: ec2:DescribeTags
Effect: Allow
Resource: "*"
Version: "2012-10-17"
Metadata:
aws:cdk:path: ReshapeCfnStackStack/IamRoleConstructB/Policy1/Resource
IamRoleConstructBC974AF92:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- Ref: IamRoleConstructBPolicy1A4484B55
RoleName: test2
Metadata:
aws:cdk:path: ReshapeCfnStackStack/IamRoleConstructB/Default/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Analytics: v2:deflate64:H4sIAAAAAAAA/1WOT4vCQA..(中略)..+A+EepVIuAQAA
Metadata:
aws:cdk:path: ReshapeCfnStackStack/CDKMetadata/Default
Condition: CDKMetadataAvailable
Conditions:
CDKMetadataAvailable:
.
.
(中略)
.
.
Parameters:
BootstrapVersion:
Type: AWS::SSM::Parameter::Value<String>
Default: /cdk-bootstrap/hnb659fds/version
Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
> npx cdk synth ReshapeCfnStack2Stack
Resources:
IamRoleConstructA2Policy1CFD08057:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: ""
ManagedPolicyName: test1-policy
Path: /
PolicyDocument:
Statement:
- Action: ec2:DescribeTags
Effect: Allow
Resource: "*"
Version: "2012-10-17"
Metadata:
aws:cdk:path: ReshapeCfnStack2Stack/IamRoleConstructA2/Policy1/Resource
IamRoleConstructA296AD5240:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- Ref: IamRoleConstructA2Policy1CFD08057
RoleName: test1
Metadata:
aws:cdk:path: ReshapeCfnStack2Stack/IamRoleConstructA2/Default/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Analytics: v2:deflate64:H4sIAAAAAAAA/1WOT4vCQA..(中略)..+A+EepVIuAQAA
Metadata:
aws:cdk:path: ReshapeCfnStack2Stack/CDKMetadata/Default
Condition: CDKMetadataAvailable
Conditions:
CDKMetadataAvailable:
.
.
(中略)
.
.
Parameters:
BootstrapVersion:
Type: AWS::SSM::Parameter::Value<String>
Default: /cdk-bootstrap/hnb659fds/version
Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
論理IDをマッピングした定義ファイルは以下のようにしました。
[
{
"Source": {
"StackName": "ReshapeCfnStackStack",
"LogicalResourceId": "IamRoleConstructAPolicy138F2EF20"
},
"Destination": {
"StackName": "ReshapeCfnStack2Stack",
"LogicalResourceId": "IamRoleConstructBPolicy1A4484B55"
}
},
{
"Source": {
"StackName": "ReshapeCfnStackStack",
"LogicalResourceId": "IamRoleConstructABEBB5F34"
},
"Destination": {
"StackName": "ReshapeCfnStack2Stack",
"LogicalResourceId": "IamRoleConstructBC974AF92"
}
}
]
それでは、リファクタリングの定義を作成します。
> aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=ReshapeCfnStackStack,TemplateBody@=file://after-ReshapeCfnStackStack.yml \
StackName=ReshapeCfnStack2Stack,TemplateBody@=file://after-ReshapeCfnStack2Stack.yml \
--enable-stack-creation \
--resource-mappings file://refactor3.json
{
"StackRefactorId": "c4778c94-8f8d-4e49-946f-9050d09c46be"
}
> aws cloudformation describe-stack-refactor --stack-refactor-id c4778c94-8f8d-4e49-946f-9050d09c46be
{
"StackRefactorId": "c4778c94-8f8d-4e49-946f-9050d09c46be",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"ReshapeCfnStack2Stack"
],
"ExecutionStatus": "UNAVAILABLE",
"Status": "CREATE_FAILED",
"StatusReason": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/24778a97-9ddf-4de7-a192-82cf73941da9: Following template sections are not allowed when creating a stack while refactoring: Conditions"
}
新規Stackを作成するにあたって、Condition
は使用できないようです。
Condition
周りを削って再度リファクタリングの定義を作成したが、以下のようにエラーになります。
(aws cloudformation create-stack-refactorは省略)
> aws cloudformation describe-stack-refactor \
--stack-refactor-id 14b01a0b-651d-4bd4-82d0-be4971a63d93 \
--query 'StatusReason'
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/636c942d-c79b-41a9-a30c-142ad9b7b298: Following template sections are not allowed when creating a stack while refactoring: Parameters"
同様にStack作成時にParameters
は指定できないとのことです。
さらにParameter
周りも削りましたが、まだエラーになります。
(aws cloudformation create-stack-refactorは省略)
> aws cloudformation describe-stack-refactor \
--stack-refactor-id 9125c90e-672a-4319-b714-2dd70582dfe5 \
--query 'StatusReason'
"Found an action type that is not permitted during refactor operations: Modify"
はい、リファクタリング中にはModify
は許可されていないとエラーになりました。
npx cdk diff
ではModifyとなるようなものはありませんでした。
変更セットを作成して確認してみます。
はい、AWS::CDK::Metadata
のAnalytics
がModify
と判定されているようです。
こちらにはConstructのリストが含まれているため、今回のようにConstruct自体を変更する際にはAnalytics
に変更が発生します。
Analytics プロパティは、スタック内のコンストラクトのリストを gzip圧縮し、base64 エンコードして、プレフィックスエンコードしたものです。
リファクタリングの実行 (analyticsReportingをfalseにして再トライ)
Analytics
を含む場合必ずエラーとなるので、analyticsReporting: false
でメタデータとして出力しないようにします。
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";
import { ReshapeCfnStackStack } from "../lib/reshape-cfn-stack-stack";
import { ReshapeCfnStack2Stack } from "../lib/reshape-cfn-stack2-stack";
+const app = new cdk.App({ analyticsReporting: false });
new ReshapeCfnStackStack(app, "ReshapeCfnStackStack");
new ReshapeCfnStack2Stack(app, "ReshapeCfnStack2Stack", {});
この状態で、手元のテンプレートファイルを更新して、リファクタリングの定義を作成しようとしても以下のようにエラーになります。
(aws cloudformation create-stack-refactorは省略)
> aws cloudformation describe-stack-refactor \
--stack-refactor-id f4dfc043-1d4c-4201-bd71-3f562c6b3629 \
--query 'StatusReason'
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf: Stack Refactor does not support AWS::CDK::Metadata."
一度デプロイしてStack上にこのメタデータが含まれないようにする必要があります。
> npx cdk diff ReshapeCfnStackStack --no-change-set
Stack ReshapeCfnStackStack
Conditions
[-] Condition CDKMetadataAvailable: {.
.
(中略)
.
.}
✨ Number of stacks with differences: 1
> npx cdk deploy ReshapeCfnStackStack
✨ Synthesis time: 7.65s
ReshapeCfnStackStack: deploying... [1/1]
.
.
(中略)
.
.
ReshapeCfnStackStack | 3/3 | 18:45:44 | UPDATE_COMPLETE | AWS::CloudFormation::Stack | ReshapeCfnStackStack
✅ ReshapeCfnStackStack
✨ Deployment time: 22.89s
Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf
✨ Total time: 30.54s
この状態でリファクタリングの定義を作成しようとしても、まだダメです。以下のようにプロパティがマッチしないとエラーになります。
> aws cloudformation describe-stack-refactor \
--stack-refactor-id b07f65c2-72b9-4b00-a9d6-d2d8790f3fba \
--query 'StatusReason'
"Resource IamRoleConstructAPolicy138F2EF20 in stack arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf does not match the destination resource's properties."
この場合も以下のようにaws:cdk:path
をリファクタリング対象のリソースのものと同じ値にする必要があります。
Resources:
IamRoleConstructA2Policy1CFD08057:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: ""
ManagedPolicyName: test1-policy
Path: /
PolicyDocument:
Statement:
- Action: ec2:DescribeTags
Effect: Allow
Resource: "*"
Version: "2012-10-17"
Metadata:
+ aws:cdk:path: ReshapeCfnStackStack/IamRoleConstructA/Policy1/Resource
IamRoleConstructA296AD5240:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- Ref: IamRoleConstructA2Policy1CFD08057
RoleName: test1
Metadata:
+ aws:cdk:path: ReshapeCfnStackStack/IamRoleConstructA/Default/Resource
こちらでリファクタリングの定義の作成を行います。
> aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=ReshapeCfnStackStack,TemplateBody@=file://after-ReshapeCfnStackStack.yml \
StackName=ReshapeCfnStack2Stack,TemplateBody@=file://after-ReshapeCfnStack2Stack.yml \
--enable-stack-creation \
--resource-mappings file://refactor3.json
{
"StackRefactorId": "b871c83b-bcda-4e9c-9d5a-cf708151436b"
}
> aws cloudformation describe-stack-refactor --stack-refactor-id b871c83b-bcda-4e9c-9d5a-cf708151436b
{
"StackRefactorId": "b871c83b-bcda-4e9c-9d5a-cf708151436b",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/0b09cdf0-e524-11ef-9310-0affccb0cbdb"
],
"ExecutionStatus": "AVAILABLE",
"Status": "CREATE_COMPLETE"
}
作成できました。
リファクタリングの内容を確認すると、確かに2つのリソースに対してMOVE
が走るようです。
> aws cloudformation list-stack-refactor-actions --stack-refactor-id b871c83b-bcda-4e9c-9d5a-cf708151436b
{
"StackRefactorActions": [
{
"Action": "CREATE",
"Entity": "STACK",
"Description": "Stack arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/0b09cdf0-e524-11ef-9310-0affccb0cbdb created.",
"Detection": "MANUAL",
"TagResources": [],
"UntagResources": [],
"ResourceMapping": {
"Source": {},
"Destination": {}
}
},
{
"Action": "MOVE",
"Entity": "RESOURCE",
"PhysicalResourceId": "test1",
"Description": "Resource configuration changes will be validated during refactor execution.",
"Detection": "MANUAL",
"TagResources": [],
"UntagResources": [],
"ResourceMapping": {
"Source": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"LogicalResourceId": "IamRoleConstructABEBB5F34"
},
"Destination": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/0b09cdf0-e524-11ef-9310-0affccb0cbdb",
"LogicalResourceId": "IamRoleConstructA296AD5240"
}
}
},
{
"Action": "MOVE",
"Entity": "RESOURCE",
"PhysicalResourceId": "arn:aws:iam::<AWSアカウントID>:policy/test1-policy",
"Description": "No configuration changes detected.",
"Detection": "MANUAL",
"TagResources": [],
"UntagResources": [],
"ResourceMapping": {
"Source": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"LogicalResourceId": "IamRoleConstructAPolicy138F2EF20"
},
"Destination": {
"StackName": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/0b09cdf0-e524-11ef-9310-0affccb0cbdb",
"LogicalResourceId": "IamRoleConstructA2Policy1CFD08057"
}
}
}
]
}
あとは実行するだけです。
> aws cloudformation execute-stack-refactor --stack-refactor-id b871c83b-bcda-4e9c-9d5a-cf708151436b
> aws cloudformation describe-stack-refactor --stack-refactor-id b871c83b-bcda-4e9c-9d5a-cf708151436b
{
"StackRefactorId": "b871c83b-bcda-4e9c-9d5a-cf708151436b",
"StackIds": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStackStack/92282680-e4f5-11ef-8dae-12951cea36cf",
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/ReshapeCfnStack2Stack/0b09cdf0-e524-11ef-9310-0affccb0cbdb"
],
"ExecutionStatus": "EXECUTE_IN_PROGRESS",
"Status": "CREATE_COMPLETE"
}
なかなか完了しない and 文字数が5万文字近いので続きは別記事で書きます。
Stack間でリソースを移動させたい場合や論理IDに悩みがある人待望のアップデート
CloudFormationのStack間のリソースの移動や論理IDの変更を簡単に行えるようになったアップデートを紹介しました。
Stack間でリソースを移動させたい場合や論理IDに悩みがある人にとっては待望のアップデートですね。
Constructの分割方針に失敗して、リファクタリングに絶望することが少しは減りそうです。
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!