ハードコーディングしたCloudFormationテンプレートの更新時の動作を確認してみた
はじめに
CloudFormation使ってますか?
Resourcesセクションのパラメータをテンプレート内でハードコーディングした後、組み込み関数やParametersを追加してテンプレートを更新したいってことありますよね?
本日は、ハードコーディングされたCFnテンプレートを更新するときの動作を確認してみました。
今回の構成
以下のネットワークのみ構成を作成して動作確認していきます。
ハードコーディングされたテンプレート
CFnテンプレートを用意しました。 CidrBlockやAvailabilityZone、各リソースのタグでは環境(dev)、システム名(nkhr)をハードコーディングしています。
VPCテンプレート
--- AWSTemplateFormatVersion: "2010-09-09" Description: "Network Template." Resources: # ----- # VPC1 # ----- vpc1: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: dev-nkhr-vpc1 # ----- # VPC1 Internet gateway # ----- internetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: dev-nkhr-igw attachIgw: Type: "AWS::EC2::VPCGatewayAttachment" Properties: VpcId: !Ref vpc1 InternetGatewayId: !Ref internetGateway # ----- # VPC1 Public Subnet # ----- PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: 10.0.0.0/24 VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-public-subnet-1 PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c CidrBlock: 10.0.1.0/24 VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-public-subnet-2 # ----- # VPC1 Public RouteTable # ----- PublicRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-public-rtb-1 PublicRoutingA1: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref internetGateway RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable1 # ----- # VPC1 Private Subnet # ----- PrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: 10.0.10.0/24 VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-private-subnet-1 PrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c CidrBlock: 10.0.11.0/24 VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-private-subnet-2 # ----- # VPC1 Private RouteTable # ----- PrivateRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-private-rtb-1 PrivateRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTableA PrivateRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTableA
このテンプレートをデプロイするとスタックが作成されます。
AWSのCLI実行例
aws cloudformation create-stack \ --stack-name dev-network \ --template-body file://network.yml
ハードコーディングされたパラメータを修正することでstg環境やprod環境にもデプロイできますが、ハードコーディングされた値が多く修正を間違えると厄介です。
組み込み関数を使ったテンプレート
CloudFormationには組み込み関数が用意されており、サブネットのAvailabilityZoneとCidrBlockをハードコーディングすることなく意図したパラメータを代入できます。
まず、GetAZs関数を使って、スタックを作成するリージョンのアベイラビリティーゾーンをアルファベット順にリストした配列を返します。ap-northeast-1(東京)では、ap-northeast-1a
、ap-northeast-1c
、ap-northeast-1d
の順番でリストされます。リストした配列は、Select関数を使って指定した値を取り出せます。
抜粋した以下の記述では、GetAZs関数でリスト化された配列の0(ap-northeast-1a)の値が参照されます。なお、AWS::Region
はAWSで定義されたパラメータです。スタックを作成するリージョン名(ap-northeast-1)を返します。
AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region
次にCidr関数とSelect関数を使ってVPCのCidrBlockからアドレスブロックを生成し、リストされた配列から参照するように記述します。
抜粋した以下の記述では、論理IDvpc1
のCidrBlock(10.0.0.0/16)からサブネットマスク/24
のアドレスブロックを2つ生成(10.0.0.0/24、10.0.1.0/24)し、リストした配列の1(10.0.1.0/24)の値が参照されます。
CidrBlock: !Select [ 1, !Cidr [ !GetAtt vpc1.CidrBlock, 2, 8 ]]
Cidr関数についてわかりやすく解説しているブログがあったので貼り付けておきます。
先程のテンプレートに組み込み関数を追加したテンプレートが以下です。
VPCテンプレート(組み込み関数あり)
--- AWSTemplateFormatVersion: "2010-09-09" Description: "Network Template." Resources: # ----- # VPC1 # ----- vpc1: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: dev-nkhr-vpc1 # ----- # VPC1 Internet gateway # ----- internetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: dev-nkhr-igw attachIgw: Type: "AWS::EC2::VPCGatewayAttachment" Properties: VpcId: !Ref vpc1 InternetGatewayId: !Ref internetGateway # ----- # VPC1 Public Subnet # ----- PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 0, !Cidr [ !GetAtt vpc1.CidrBlock, 1, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-public-subnet-1 PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 1, !Cidr [ !GetAtt vpc1.CidrBlock, 2, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-public-subnet-2 # ----- # VPC1 Public RouteTable # ----- PublicRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-public-rtb-1 PublicRoutingA1: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref internetGateway RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable1 # ----- # VPC1 Private Subnet # ----- PrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 10, !Cidr [ !GetAtt vpc1.CidrBlock, 11, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-private-subnet-1 PrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 11, !Cidr [ !GetAtt vpc1.CidrBlock, 12, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-private-subnet-2 # ----- # VPC1 Private RouteTable # ----- PrivateRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: dev-nkhr-private-rtb-1 PrivateRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTableA PrivateRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTableA
このテンプレートで変更セットを作成します。
AWSのCLI実行例
aws cloudformation create-change-set \ --stack-name dev-network \ --change-set-name add-functions \ --template-body file://network.yml
変更セットのステータス確認と変更対象を確認してみます。
# ステータス確認 aws cloudformation describe-change-set \ --change-set-name add-functions \ --stack-name dev-network | \ jq '. | { Status: .Status, StatusReason: .StatusReason }' # 変更対象の確認 aws cloudformation describe-change-set \ --change-set-name add-functions \ --stack-name dev-network | \ jq '.Changes[] | {Action: .ResourceChange.Action, LogicalResourceId: .ResourceChange.LogicalResourceId }'
ステータスがCREATE_COMPLETE、変更対象のリソースとしてサブネット、ルートテーブル関連が出力されています。これは変更セット実行のタイミングでは、アドレスブロックが生成されておらずハードコーディングされたものとは異なる値の為、変更対象として出力されます。
出力結果
# ステータス確認 { "Status": "CREATE_COMPLETE", "StatusReason": null } # 変更対象の確認 { "Action": "Modify", "LogicalResourceId": "PrivateRouteTableAssociation1" } { "Action": "Modify", "LogicalResourceId": "PrivateRouteTableAssociation2" } { "Action": "Modify", "LogicalResourceId": "PrivateSubnet1" } { "Action": "Modify", "LogicalResourceId": "PrivateSubnet2" } { "Action": "Modify", "LogicalResourceId": "PublicRouteTableAssociation1" } { "Action": "Modify", "LogicalResourceId": "PublicRouteTableAssociation2" } { "Action": "Modify", "LogicalResourceId": "PublicSubnet1" } { "Action": "Modify", "LogicalResourceId": "PublicSubnet2" }
変更対象のリソースは再作成されるのか?答えは否です。組み込み関数で参照される値がハードコーディングされた値と同等であれば、リソースの変更はありません。
以下のコマンドで変更セットを実行します。
aws cloudformation execute-change-set \ --stack-name dev-network --change-set-name add-functions
スタックイベントを見ると、リソースの更新が無いことが確認できます。(先頭4件のイベントのTimestamp,ResourceStatusを表示)
aws cloudformation describe-stack-events \ --stack-name dev-network | \ jq '.StackEvents[:4] | .[] | {Timestamp: .Timestamp, ResourceStatus: .ResourceStatus }'
出力結果
{ "Timestamp": "2021-07-05T11:57:33.859Z", "ResourceStatus": "UPDATE_COMPLETE" } { "Timestamp": "2021-07-05T11:57:32.994Z", "ResourceStatus": "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS" } { "Timestamp": "2021-07-05T11:57:26.663Z", "ResourceStatus": "UPDATE_IN_PROGRESS" } { "Timestamp": "2021-07-05T09:24:21.982Z", "ResourceStatus": "CREATE_COMPLETE" }
Parametersを使ったテンプレート
今度はVPCのCidrBlock、Tagの環境名(dev)、システム名(nkhr)をParametersを使ってテンプレートを修正します。ParametersでVpcCidr、Env、SystemNameを追加し、Defaultに値を設定しました。
各リソースのプロパティではSub関数を使ってParametersを参照できるようにします。
vpc1: Type: AWS::EC2::VPC Properties: CidrBlock: !Sub ${VpcCidr} EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-vpc
先程のテンプレートにParametersを追加したテンプレートが以下です。
VPCテンプレート(組み込み関数、Parametersあり)
--- AWSTemplateFormatVersion: "2010-09-09" Description: "Network Template." Parameters: VpcCidr: Type: String Default: 10.0.0.0/16 Env: Type: String Default: dev SystemName: Type: String Default: nkhr Resources: # ----- # VPC1 # ----- vpc1: Type: AWS::EC2::VPC Properties: CidrBlock: !Sub ${VpcCidr} EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-vpc1 # ----- # VPC1 Internet gateway # ----- internetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-igw attachIgw: Type: "AWS::EC2::VPCGatewayAttachment" Properties: VpcId: !Ref vpc1 InternetGatewayId: !Ref internetGateway # ----- # VPC1 Public Subnet # ----- PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 0, !Cidr [ !GetAtt vpc1.CidrBlock, 1, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-public-subnet-1 PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 1, !Cidr [ !GetAtt vpc1.CidrBlock, 2, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-public-subnet-2 # ----- # VPC1 Public RouteTable # ----- PublicRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-public-rtb-1 PublicRoutingA1: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref internetGateway RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable1 # ----- # VPC1 Private Subnet # ----- PrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 10, !Cidr [ !GetAtt vpc1.CidrBlock, 11, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-private-subnet-1 PrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 11, !Cidr [ !GetAtt vpc1.CidrBlock, 12, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-private-subnet-2 # ----- # VPC1 Private RouteTable # ----- PrivateRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-private-rtb-1 PrivateRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTableA PrivateRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTableA
このテンプレートで変更セットを作成します。
AWSのCLI実行例
aws cloudformation create-change-set \ --stack-name dev-network \ --change-set-name add-parameters \ --template-body file://network.yml
変更対象を確認してみます。
# ステータス確認 aws cloudformation describe-change-set \ --change-set-name add-parameters \ --stack-name dev-network | \ jq '. | { Status: .Status, StatusReason: .StatusReason }' # 変更対象の確認 aws cloudformation describe-change-set \ --change-set-name add-parameters \ --stack-name dev-network | \ jq '.Changes[] | {Action: .ResourceChange.Action, LogicalResourceId: .ResourceChange.LogicalResourceId }'
ステータスがFAILED、変更対象リソースは何も表示されません。
出力結果
# ステータス確認 { "Status": "FAILED", "StatusReason": "The submitted information didn't contain changes. Submit different information to create a change set." }
StatusReasonには、変更対象が含まれていないことでFAILEDとなったと書かれています。ParametersはCidr関数のように生成されるパラメータではなく固有パラメータを設定するので、変更セット実行時にパラメータにある値と現在の値が同等であれば変更対象となりません。
変更セットから実行できないので、変更セットなしでスタックをアップデートします。
aws cloudformation update-stack \ --stack-name dev-network --template-body file://network.yml
スタックイベントを見ると、リソースの更新が無いことが確認できます。
aws cloudformation describe-stack-events \ --stack-name dev-network | \ jq '.StackEvents[:4] | .[] | {Timestamp: .Timestamp, ResourceStatus: .ResourceStatus }'
出力結果
{ "Timestamp": "2021-07-05T15:31:58.975Z", "ResourceStatus": "UPDATE_COMPLETE" } { "Timestamp": "2021-07-05T15:31:58.199Z", "ResourceStatus": "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS" } { "Timestamp": "2021-07-05T15:31:49.479Z", "ResourceStatus": "UPDATE_IN_PROGRESS" } { "Timestamp": "2021-07-05T15:18:03.495Z", "ResourceStatus": "UPDATE_COMPLETE" }
ただ、上記方法だと変更セットで変更リソースを確認できないのは怖いですよね。どうしても変更セットから確認したい場合は、任意のリソースのタグを追加または変更することで変更セットを作成できます。
以下のテンプレートでは、論理IDvpc1
にChangSetタグを追加します。
VPCテンプレート(組み込み関数、Parametersあり、タグ追加)
--- AWSTemplateFormatVersion: "2010-09-09" Description: "Network Template." Parameters: Env: Type: String Default: dev SystemName: Type: String Default: nkhr Resources: # ----- # VPC1 # ----- vpc1: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-vpc1 - Key: ChangSet Value: "True" # ----- # VPC1 Internet gateway # ----- internetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-igw attachIgw: Type: "AWS::EC2::VPCGatewayAttachment" Properties: VpcId: !Ref vpc1 InternetGatewayId: !Ref internetGateway # ----- # VPC1 Public Subnet # ----- PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 0, !Cidr [ !GetAtt vpc1.CidrBlock, 1, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-public-subnet-1 PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 1, !Cidr [ !GetAtt vpc1.CidrBlock, 2, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-public-subnet-2 # ----- # VPC1 Public RouteTable # ----- PublicRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-public-rtb-1 PublicRoutingA1: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref internetGateway RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable1 PublicRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable1 # ----- # VPC1 Private Subnet # ----- PrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 10, !Cidr [ !GetAtt vpc1.CidrBlock, 11, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-private-subnet-1 PrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select [ 11, !Cidr [ !GetAtt vpc1.CidrBlock, 12, 8 ]] VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-private-subnet-2 # ----- # VPC1 Private RouteTable # ----- PrivateRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpc1 Tags: - Key: Name Value: !Sub ${Env}-${SystemName}-private-rtb-1 PrivateRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTableA PrivateRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTableA
このテンプレートで変更セットを作成します。
AWSのCLI実行例
aws cloudformation create-change-set \ --stack-name dev-network \ --change-set-name add-parameters-tag \ --template-body file://network.yml
変更対象を確認してみます。
# ステータス確認 aws cloudformation describe-change-set \ --change-set-name add-parameters-tag \ --stack-name dev-network | \ jq '. | { Status: .Status, StatusReason: .StatusReason }' # 変更対象の確認 aws cloudformation describe-change-set \ --change-set-name add-parameters-tag \ --stack-name dev-network | \ jq '.Changes[] | {Action: .ResourceChange.Action, LogicalResourceId: .ResourceChange.LogicalResourceId }'
出力結果
# ステータス確認 { "Status": "CREATE_COMPLETE", "StatusReason": null } # 変更対象の確認 { "Action": "Modify", "LogicalResourceId": "PrivateRouteTableAssociation1" } { "Action": "Modify", "LogicalResourceId": "PrivateRouteTableAssociation2" } { "Action": "Modify", "LogicalResourceId": "PrivateSubnet1" } { "Action": "Modify", "LogicalResourceId": "PrivateSubnet2" } { "Action": "Modify", "LogicalResourceId": "PublicRouteTableAssociation1" } { "Action": "Modify", "LogicalResourceId": "PublicRouteTableAssociation2" } { "Action": "Modify", "LogicalResourceId": "PublicSubnet1" } { "Action": "Modify", "LogicalResourceId": "PublicSubnet2" } { "Action": "Modify", "LogicalResourceId": "vpc1" }
変更対象が出力されましたね。以下のコマンドで変更セットを実行します。
aws cloudformation execute-change-set \ --stack-name dev-network \ --change-set-name add-parameters-tag
スタックイベントを見ると、VPCリソースのみ更新されていることが確認できます。
aws cloudformation describe-stack-events \ --stack-name dev-network | \ jq '.StackEvents[:5] | .[] | {Timestamp: .Timestamp, ResourceStatus: .ResourceStatus }'
出力結果
{ "Timestamp": "2021-07-05T15:54:37.159Z", "ResourceStatus": "UPDATE_COMPLETE" } { "Timestamp": "2021-07-05T15:54:36.532Z", "ResourceStatus": "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS" } { "Timestamp": "2021-07-05T15:54:32.603Z", "ResourceStatus": "UPDATE_COMPLETE" } { "Timestamp": "2021-07-05T15:54:32.141Z", "ResourceStatus": "UPDATE_IN_PROGRESS" } { "Timestamp": "2021-07-05T15:54:26.913Z", "ResourceStatus": "UPDATE_IN_PROGRESS" }
さいごに
作成したテンプレートを更新していくときに不安要素となるリソースがどうなるのか、動作確認してみました。結果としては設定済みの値と同等であれば再作成されないということがわかりました。
とはいえ、予期せぬ事象が起きる可能性も考えて、検証することをオススメします。より良いテンプレート作りの参考となれば幸いです。