AWS CloudFormation変更セットの見方を整理する
はじめに
AWS CloudFormationの変更セットは、変更予定のリソースや変更内容を出力できるのですが、ときどき表示された内容の見方が分からなくなるので、簡単に整理しました。
変更セットとは
変更セットを作成すると、新規・既存のCloudFormationスタックに対する変更予定の内容を明示します。内容に問題がなければ変更セットを実行し、スタックに対する変更を適用します。
スタックを更新する必要がある場合は、変更の実装前に実行中のリソースに与える影響を理解することで、安心してスタックを更新できます。変更セットを使用すると、スタックの変更案が実行中のリソースに与える可能性がある影響 (たとえば、変更によって重要なリソースが削除されたり置き換えられたりしないか) を確認できます。変更セットの実行を確定したときのみ AWS CloudFormation によってスタックが変更されるため、変更案のまま続行するか別の変更セットを作成して他の変更を検討するかを決定できます。変更セットは、CloudFormation コンソール、AWS CLI、または CloudFormation API を使用して作成および管理できます。
describe-change-setの見方
変更セットの作成後にスタックへの変更予定内容を確認するには DescribeChangeSet APIにリクエストを送ります。AWS CLIの例から、どのようなレスポンスが返ってくるかを見てみます。
{
"Changes": [
{
"Type": "Resource",
"ResourceChange": {
"Action": "Modify",
"LogicalResourceId": "function",
"PhysicalResourceId": "my-function-SEZV4XMPL4S5",
"ResourceType": "AWS::Lambda::Function",
"Replacement": "False",
"Scope": [
"Properties"
],
"Details": [
{
"Target": {
"Attribute": "Properties",
"Name": "Timeout",
"RequiresRecreation": "Never"
},
"Evaluation": "Static",
"ChangeSource": "DirectModification"
}
]
}
}
],
"ChangeSetName": "my-change-set",
"ChangeSetId": "arn:aws:cloudformation:us-west-2:123456789012:changeSet/my-change-set/4eca1a01-e285-xmpl-8026-9a1967bfb4b0",
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-stack/d0a825a0-e4cd-xmpl-b9fb-061c69e99204",
"StackName": "my-stack",
"Description": null,
"Parameters": null,
"CreationTime": "2019-10-02T05:20:56.651Z",
"ExecutionStatus": "AVAILABLE",
"Status": "CREATE_COMPLETE",
"StatusReason": null,
"NotificationARNs": [],
"RollbackConfiguration": {},
"Capabilities": [
"CAPABILITY_IAM"
],
"Tags": null
}
※AWS CLIドキュメントより抜粋
ここでは変更予定の内容に着目するので 'Changes[].ResourceChange'
の部分のみ言及します。
-
Action
: CloudFormationがリソースに対して行うアクションを表示します。アクションの種類はAdd
Modify
Remove
Import
Dynamic
になります。 -
ResourceType
: AWSリソースのタイプ (AWS::EC2::Instance
など) -
Replacement
:Action
がModify
の場合に表示されます。CloudFormationへの変更適用時に新しいリソースを作成して既存リソースと置き換えるか否かを表示します。値はTrue
False
Conditionally
です。 -
Scope
:Action
がModify
の場合に表示されます。この更新のトリガーとなる変更がどの属性かを表示します。値はMetadata
Properties
Tags
です。 -
Details
:Action
がModify
の場合に表示されます。リソースに対しどのような変更が行われるかを表示します。-
Target
: リソースの変更される箇所と、リソースの再作成がされるかを表示します。-
Attribute
: この更新のトリガーとなる変更がどの属性かを表示します。値はMetadata
Properties
Tags
です。 -
Name
:Attribute
がProperties
の場合、変更するPropertiesの名前を表示します。 -
RequiresRecreation
:Attribute
がProperties
の場合、このリソースが再作成されるかどうか表示します。値はNever
Always
Conditionally
です。
-
-
Evaluation
: CloudFormationが変更する値を決定できるかどうか、ターゲットの値が変更されるかどうかを表示します。値はStatic
Dynamic
です。Static
の場合は変更前に値を決定できますが、Dynamic
の場合は決定できません。 -
ChangeSource
: テンプレートに対してどのような方法で変更が加えられたかを表示します (例:DirectModification
はテンプレートを直接修正した場合)。値はResourceReference
ParameterReference
ResourceAttribute
DirectModification
Automatic
です。
-
ここからいくつかの点を抜き出します。
-
Replacement
がFalse
以外の場合:True
の場合はリソースの置き換えが発生するのでもちろん注意が必要ですが、Conditionally
の場合は、リソースの置き換えが発生するか否か、利用者側で判断する必要があります。 -
Evaluation
がDynamic
の場合:Dynamic
で値が決定できないのはどういう場合かというと、CloudFormationテンプレートでRef
やFn::GetAtt
などを利用しており、値が組込み関数の結果に依存する時です。 -
ChangeSource
がDirectModification
以外の場合:DirectModification
以外の場合、Ref
やFn::GetAtt
などの組み込み関数の結果が変わったとき、あるいはAWS::CloudFormation::Stack
を利用する場合です。特にAWS::CloudFormation::Stack
の場合、子スタックの変更の有無にかかわらずAutomatic
とするため、実際に変更があるかは確認が必要です。
試してみる
ここではVPC/Subnetを使っています。事前に以下のファイルを使ってVPCを作成します。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
EnvName:
Type: String
Default: cfn-changeset-test
VPCCIDR:
Type: String
Default: "10.0.0.0/16"
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
Tags:
- Key: Name
Value: !Sub ${EnvName}-VPC
作成後、まずはファイルを修正してSubnetを追加します。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
EnvName:
Type: String
Default: cfn-changeset-test
VPCCIDR:
Type: String
Default: "10.0.0.0/16"
# 追加
PrivateSubnetCIDR:
Type: String
Default: "10.0.0.0/24"
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
Tags:
- Key: Name
Value: !Sub ${EnvName}-VPC
# 追加
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Ref PrivateSubnetCIDR
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvName}-PrivateSubnet
変更セットを作成します。変更セットの名称はここでは add-subnet
としました。
$ aws cloudformation create-change-set --stack-name cfn-changeset-test --change-set-name add-subnet --template-body file://cfn-changeset-test.yaml
{
"Id": "arn:aws:cloudformation:ap-northeast-1:123456789012:changeSet/add-subnet/0be22236-4617-434d-a1d1-82ac135acf3a",
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/cfn-changeset-test/165e80c0-401f-11ee-b005-0eb9feabba13"
}
この時の変更セットの内容は以下の通りです。想定通り PrivateSubnet
という名称のサブネットの追加 ( Add
) を認識しています。
$ aws cloudformation describe-change-set --stack-name cfn-changeset-test --change-set-name add-subnet
{
"Changes": [
{
"Type": "Resource",
"ResourceChange": {
"Action": "Add",
"LogicalResourceId": "PrivateSubnet",
"ResourceType": "AWS::EC2::Subnet",
"Scope": [],
"Details": []
}
}
],
"ChangeSetName": "add-subnet",
"ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:123456789012:changeSet/add-subnet/0be22236-4617-434d-a1d1-82ac135acf3a",
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/cfn-changeset-test/165e80c0-401f-11ee-b005-0eb9feabba13",
"StackName": "cfn-changeset-test",
"Description": null,
"Parameters": [
{
"ParameterKey": "PrivateSubnetCIDR",
"ParameterValue": "10.0.0.0/24"
},
{
"ParameterKey": "VPCCIDR",
"ParameterValue": "10.0.0.0/16"
},
{
"ParameterKey": "EnvName",
"ParameterValue": "cfn-changeset-test"
}
],
"CreationTime": "2023-08-21T12:41:23.976000+00:00",
"ExecutionStatus": "AVAILABLE",
"Status": "CREATE_COMPLETE",
"StatusReason": null,
"NotificationARNs": [],
"RollbackConfiguration": {
"RollbackTriggers": []
},
"Capabilities": [],
"Tags": null,
"ParentChangeSetId": null,
"IncludeNestedStacks": false,
"RootChangeSetId": null
}
なおAWSマネジメントコンソールでも同様の情報を確認できます。
上記変更セットを適用して PrivateSubnet
サブネットを作成します。
$ aws cloudformation execute-change-set --stack-name cfn-changeset-test --change-set-name add-subnet
$ aws cloudformation describe-stack-resources --stack-name cfn-changeset-test
{
"StackResources": [
{
"StackName": "cfn-changeset-test",
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/cfn-changeset-test/165e80c0-401f-11ee-b005-0eb9feabba13",
"LogicalResourceId": "PrivateSubnet",
"PhysicalResourceId": "subnet-06e42c89f18e0ef41",
"ResourceType": "AWS::EC2::Subnet",
"Timestamp": "2023-08-21T12:49:34.928000+00:00",
"ResourceStatus": "CREATE_COMPLETE",
"DriftInformation": {
"StackResourceDriftStatus": "NOT_CHECKED"
}
},
{
"StackName": "cfn-changeset-test",
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/cfn-changeset-test/165e80c0-401f-11ee-b005-0eb9feabba13",
"LogicalResourceId": "VPC",
"PhysicalResourceId": "vpc-0623ffb826dd2e9c1",
"ResourceType": "AWS::EC2::VPC",
"Timestamp": "2023-08-21T12:34:51.812000+00:00",
"ResourceStatus": "CREATE_COMPLETE",
"DriftInformation": {
"StackResourceDriftStatus": "NOT_CHECKED"
}
}
]
}
続いて、再びファイルを修正します。ここでは Subnetの削除
と VPCのDNSホスト名の有効化
という2つの変更をします。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
EnvName:
Type: String
Default: cfn-changeset-test
VPCCIDR:
Type: String
Default: "10.0.0.0/16"
#PrivateSubnetCIDR:
# Type: String
# Default: "10.0.0.0/24"
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true # 追加
EnableDnsHostnames: true # 追加
Tags:
- Key: Name
Value: !Sub ${EnvName}-VPC
#PrivateSubnet:
# Type: AWS::EC2::Subnet
# Properties:
# AvailabilityZone: !Select [0, !GetAZs ""]
# CidrBlock: !Ref PrivateSubnetCIDR
# VpcId: !Ref VPC
# Tags:
# - Key: Name
# Value: !Sub ${EnvName}-PrivateSubnet
上記ファイルを使って変更セットを作成します。ここでの変更セット名は delete-subnet-enable-dns-hostname
です。
$ aws cloudformation create-change-set --stack-name cfn-changeset-test --change-set-name delete-subnet-enable-dns-hostname --template-body file://cfn-changeset-test.yaml
{
"Id": "arn:aws:cloudformation:ap-northeast-1:123456789012:changeSet/delete-subnet-enable-dns-hostname/36d1a0e8-7705-4146-a485-7b6c3bb48dec",
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/cfn-changeset-test/165e80c0-401f-11ee-b005-0eb9feabba13"
}
作成した変更セットの内容を確認します。ここでも想定通りサブネットの削除 (Remove
) とVPCの変更 (Modify
) の2つの操作が認識されています。また Modify
のほうは変更内容も記載されています。
今回の変更のうち EnableDnsSupport
のみを取り上げると、 Detail
以降の内容は以下のように読み取れます。
- 変更対象を
Target
で確認すると、Properties
中のEnableDnsSupport
であることがわかります。またRequiresRecreation
を見ると、この変更によるリソースの再作成は発生しないことがわかります (これはCloudFormationのドキュメントを見ても一致する挙動です)。 -
Evaluation
を見るとStatic
であるため、CloudFormationが決定できる値になることがわかります。 -
ChangeSource
を見るとDirectModification
であるため、今回の変更はテンプレートを直接修正して発生したことがわかります。
$ aws cloudformation describe-change-set --stack-name cfn-changeset-test --change-set-name delete-subnet-enable-dns-hostname
{
"Changes": [
{
"Type": "Resource",
"ResourceChange": {
"Action": "Remove",
"LogicalResourceId": "PrivateSubnet",
"PhysicalResourceId": "subnet-06e42c89f18e0ef41",
"ResourceType": "AWS::EC2::Subnet",
"Scope": [],
"Details": []
}
},
{
"Type": "Resource",
"ResourceChange": {
"Action": "Modify",
"LogicalResourceId": "VPC",
"PhysicalResourceId": "vpc-0623ffb826dd2e9c1",
"ResourceType": "AWS::EC2::VPC",
"Replacement": "False",
"Scope": [
"Properties"
],
"Details": [
# "EnableDnsSupport" に関する記述はこのあたり
{
"Target": {
"Attribute": "Properties",
"Name": "EnableDnsSupport",
"RequiresRecreation": "Never"
},
"Evaluation": "Static",
"ChangeSource": "DirectModification"
},
{
"Target": {
"Attribute": "Properties",
"Name": "EnableDnsHostnames",
"RequiresRecreation": "Never"
},
"Evaluation": "Static",
"ChangeSource": "DirectModification"
}
]
}
}
],
"ChangeSetName": "delete-subnet-enable-dns-hostname",
"ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:123456789012:changeSet/delete-subnet-enable-dns-hostname/36d1a0e8-7705-4146-a485-7b6c3bb48dec",
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/cfn-changeset-test/165e80c0-401f-11ee-b005-0eb9feabba13",
"StackName": "cfn-changeset-test",
"Description": null,
"Parameters": [
{
"ParameterKey": "VPCCIDR",
"ParameterValue": "10.0.0.0/16"
},
{
"ParameterKey": "EnvName",
"ParameterValue": "cfn-changeset-test"
}
],
"CreationTime": "2023-08-21T12:53:09.492000+00:00",
"ExecutionStatus": "AVAILABLE",
"Status": "CREATE_COMPLETE",
"StatusReason": null,
"NotificationARNs": [],
"RollbackConfiguration": {
"RollbackTriggers": []
},
"Capabilities": [],
"Tags": null,
"ParentChangeSetId": null,
"IncludeNestedStacks": false,
"RootChangeSetId": null
}
AWSコンソールはこちら。
変更セットの作成・確認までを行うスクリプト
変更セットの作成と変更内容の確認は、AWS CLIコマンドを使えば済む話ですが、コマンドを毎回打つのがちょっと面倒でもあるので、簡単に試せそうなスクリプトを作成してみました。1点だけ補足すると、CloudFormationスタックがない状態から変更セットを作成するには、 --change-set-type
オプションに CREATE
を指定する必要があります。
#!/bin/bash
set -o errexit
STACK_NAME=$1
CHANGE_SET_NAME=$2
TEMPLATE_FILE=$3
NEW_OPTION=$4
CHANGESET_TYPE="UPDATE"
if [ $# = 4 ] && [ $4 = "CREATE" ]; then
CHANGESET_TYPE="CREATE"
fi
CREATE_STATUS=`aws cloudformation create-change-set \
--stack-name ${STACK_NAME} \
--change-set-name ${CHANGE_SET_NAME} \
--template-body file://${TEMPLATE_FILE} \
--change-set-type ${CHANGESET_TYPE}`
echo "Creating changeset is successful. Please wait a while..."
aws cloudformation wait change-set-create-complete \
--stack-name ${STACK_NAME} \
--change-set-name ${CHANGE_SET_NAME}
aws cloudformation describe-change-set \
--stack-name ${STACK_NAME} \
--change-set-name ${CHANGE_SET_NAME} \
--query 'Changes[].ResourceChange'
# 実行した例
$ ./test.sh cfn-changeset-test test cfn-changeset-test.yaml CREATE
Creating changeset is successful. Please wait a while...
[
{
"Action": "Add",
"LogicalResourceId": "VPC",
"ResourceType": "AWS::EC2::VPC",
"Scope": [],
"Details": []
}
]
Discussion