CloudFormation変更セットでスタック更新時の影響範囲を確認する

CloudFormation変更セットでスタック更新時の影響範囲を確認する

AWS SOAの出題範囲の1つである、デプロイ、プロビジョニング、およびオートメーションに関する内容です。

CloudFormationで安全にリソースを更新するために、変更セットが利用できます。

変更セットを使用すると、スタックの変更案が実行中のリソースに与える可能性がある影響 (たとえば、変更によって重要なリソースが削除されたり置き換えられたりしないか) を確認できます。

変更セットを使用したスタックの更新

本ページでは、CloudFormation変更セットの動作を確認します。

構築する環境

Diagram of check CloudFormation Change Set to see the scope of impact when updating stack

CloudFormationを使用して、Lambda関数を作成します。

関数作成後に変更セットを作成し、スタック更新時の影響の確認や、スタックの更新を実施します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下の2つのテンプレートファイルで構築します

soa-03-004.yaml

AWSTemplateFormatVersion: 2010-09-09

Description: A sample EC2 instance template for testing change sets.

Parameters:
  TemplateBucketName:
    Type: String
    Default: [bucket-name]

  Prefix:
    Type: String
    Default: soa-03-004

  Handler:
    Type: String
    Default: index.lambda_handler

  Runtime:
    Type: String
    Default: python3.9
    #Default: python3.8


Resources:
  LambdaStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub "https://${TemplateBucketName}.s3.${AWS::Region}.amazonaws.com/${Prefix}/${Prefix}-lambda.yaml"
      Parameters:
        Handler: !Ref Handler
        Prefix: !Ref Prefix
        Runtime: !Ref Runtime
Code language: YAML (yaml)

soa-03-004-lambda.yaml

AWSTemplateFormatVersion: 2010-09-09

Parameters:
  Handler:
    Type: String

  Prefix:
    Type: String

  Runtime:
    Type: String


Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          def lambda_handler(event, context):
            print('hello, awstut !')
      FunctionName: !Sub "${Prefix}-function"
      #FunctionName: !Sub "${Prefix}-function-updated"
      Handler: !Ref Handler
      Role: !GetAtt FunctionRole.Arn
      Runtime: !Ref Runtime

  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  #S3Permission:
  #  Type: AWS::Lambda::Permission
  #  Properties:
  #    FunctionName: !Ref Function
  #    Action: lambda:InvokeFunction
  #    Principal: s3.amazonaws.com
  #    #SourceArn: !Ref SNSTopicArn
Code language: YAML (yaml)

以下のURLにもテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-soa/tree/main/03/004

テンプレートファイルのポイント解説

特別な設定は行いません。
Lambda関数をインライン形式で記載します。

Lambda関数に関する詳細は以下のページをご確認ください。

あわせて読みたい
CloudFormationでLambdaを作成する3パータン(S3/インライン/コンテナ) 【CloudFormationでLambdaを作成する】 CloudFormationでLambdaを作成する場合、大別すると以下の3パターンあります。 S3バケットにコードをアップロードする インライ...

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • ルートスタック:soa-03-004
  • 子スタック:soa-03-004-LambdaStack-1WUKC1VUIJ2Q1
  • Lambda関数:soa-03-004-function

AWS Management Consoleから各リソースを確認します。

CloudFormationスタックを確認します。

Detail of CloudFormation 1.
Detail of CloudFormation 2.

2つのスタックが作成されていることがわかります。

前者がルートスタック、もう一方が子スタックです。

動作確認

準備が整いました。
これらのスタックの変更セットを作成し、どの動作を確認します。

変更セットを作成してリソース作成

変更セット作成

スタックの更新として、新しいリソースを定義します。

soa-03-004-lambda.yamlに修正を行います。

  S3Permission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref Function
      Action: lambda:InvokeFunction
      Principal: s3.amazonaws.com
Code language: YAML (yaml)

コメントアウトされていた箇所です。
コメントを外し、Lambdaのリソースベースポリシーを定義します。

変更したテンプレートファイルを所定の位置に配置した上で、変更セットを作成します。
今回はS3バケットにテンプレートファイルを配置します。
変更セットの作成は、AWS CLIを使用します。

$ aws cloudformation create-change-set \
--stack-name soa-03-004 \
--change-set-name SampleChangeSet \
--template-url https://[bucket-url]/soa-03-004.yaml \
--include-nested-stacks \
--capabilities CAPABILITY_IAM
Code language: Bash (bash)

ネストされたスタックの場合、include-nested-stacksオプションを有効化した上で、ルートスタックを指定して、変更セットを作成します。

AWS Management Consoleから作成された変更セットを確認します。

Detail of CloudFormation 3
Detail of CloudFormation 4.

先ほどの1回の操作で、ルートスタック・子スタックに、それぞれ変更セットが作成されました。
ルートスタックを見ると、子スタックのアクションが「Modify」とあります。
子スタックを見ると、S3Permission(Lambdaのリソースベースポリシー)が「Add」とあります。

このように変更セットを作成することによって、スタックの更新時の影響範囲を確認することができます。

ちなみにAWS CLIを使用して変更セットを確認する場合は、以下のコマンドを実行します。
ルートスタックの変更セットを確認します。

$ aws cloudformation list-change-sets --stack-name soa-03-004
{
    "Summaries": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:[account-id]:stack/soa-03-004/3e47fc40-8cf3-11ed-8f62-0651fa1f475b",
            "StackName": "soa-03-004",
            "ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:[account-id]:changeSet/SampleChangeSet/8e2bf911-7a1e-4951-9f39-fc94fe2ca7b6",
            "ChangeSetName": "SampleChangeSet",
            "ExecutionStatus": "AVAILABLE",
            "Status": "CREATE_COMPLETE",
            "CreationTime": "2023-01-05T12:30:44.145000+00:00",
            "IncludeNestedStacks": true
        }
    ]
}


$ aws cloudformation describe-change-set --change-set-name SampleChangeSet --stack-name soa-03-004
{
    "Changes": [
        {
            "Type": "Resource",
            "ResourceChange": {
                "Action": "Modify",
                "LogicalResourceId": "LambdaStack",
                "PhysicalResourceId": "arn:aws:cloudformation:ap-northeast-1:[account-id]:stack/soa-03-004-LambdaStack-1WUKC1VUIJ2Q1/40f541f0-8cf3-11ed-9f59-0e1182c6182d",
                "ResourceType": "AWS::CloudFormation::Stack",
                "Replacement": "False",
                "Scope": [
                    "Properties"
                ],
                "Details": [
                    {
                        "Target": {
                            "Attribute": "Properties",
                            "RequiresRecreation": "Never"
                        },
                        "Evaluation": "Dynamic",
                        "ChangeSource": "Automatic"
                    }
                ],
                "ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:[account-id]:changeSet/SampleChangeSet-LambdaStack-3O37LKJ61939/9f630ba8-f400-4914-bf1d-68ce009445c6"
            }
        }
    ],
    "ChangeSetName": "SampleChangeSet",
    "ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:[account-id]:changeSet/SampleChangeSet/8e2bf911-7a1e-4951-9f39-fc94fe2ca7b6",
    "StackId": "arn:aws:cloudformation:ap-northeast-1:[account-id]:stack/soa-03-004/3e47fc40-8cf3-11ed-8f62-0651fa1f475b",
    "StackName": "soa-03-004",
    "Description": null,
    "Parameters": [
        {
            "ParameterKey": "Runtime",
            "ParameterValue": "python3.9"
        },
        {
            "ParameterKey": "Handler",
            "ParameterValue": "index.lambda_handler"
        },
        {
            "ParameterKey": "Prefix",
            "ParameterValue": "soa-03-004"
        },
        {
            "ParameterKey": "TemplateBucketName",
            "ParameterValue": "awstut-bucket"
        }
    ],
    "CreationTime": "2023-01-05T12:30:44.145000+00:00",
    "ExecutionStatus": "AVAILABLE",
    "Status": "CREATE_COMPLETE",
    "StatusReason": null,
    "NotificationARNs": [],
    "RollbackConfiguration": {},
    "Capabilities": [
        "CAPABILITY_IAM"
    ],
    "Tags": null,
    "ParentChangeSetId": null,
    "IncludeNestedStacks": true,
    "RootChangeSetId": null
}
Code language: Bash (bash)
変更セットを使用してスタック更新

変更セットを作成して、変更内容が確認できました。
次は変更セットを使用して、スタックを更新します。
スタックの更新はAWS CLIを使用します。

$ aws cloudformation execute-change-set \
--stack-name soa-03-004 \
--change-set-name SampleChangeSet
Code language: Bash (bash)

更新状況をAWS Management Consoleで確認します。

Detail of CloudFormation 5.
Detail of CloudFormation 6.

2つのスタックの変更セットが実行されたことがわかります。

Detail of CloudFormation 7.

子スタックから生成されたリソースを見ると、確かにS3Permissionが作成されています。

このように変更セットを使用して、スタックを更新することもできます。

変更セットを作成してリソース更新

リソースを更新する際の変更セットを確認します。

テンプレートファイルを修正します。

soa-03-004.yaml
AWSTemplateFormatVersion: 2010-09-09

Description: A sample EC2 instance template for testing change sets.

Parameters:
  TemplateBucketName:
    Type: String
    Default: [bucket-name]

  Prefix:
    Type: String
    Default: soa-03-004

  Handler:
    Type: String
    Default: index.lambda_handler

  Runtime:
    Type: String
    #Default: python3.9
    Default: python3.8


Resources:
  LambdaStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub "https://${TemplateBucketName}.s3.${AWS::Region}.amazonaws.com/${Prefix}/${Prefix}-lambda.yaml"
      Parameters:
        Handler: !Ref Handler
        Prefix: !Ref Prefix
        Runtime: !Ref Runtime
Code language: YAML (yaml)

パラメータのデフォルト値を変更します。
Lambda関数のランタイム環境に関する内容です。

soa-03-004-lambda.yaml
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  Handler:
    Type: String

  Prefix:
    Type: String

  Runtime:
    Type: String


Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          def lambda_handler(event, context):
            print('hello, awstut !')
      #FunctionName: !Sub "${Prefix}-function"
      FunctionName: !Sub "${Prefix}-function-updated"
      Handler: !Ref Handler
      Role: !GetAtt FunctionRole.Arn
      Runtime: !Ref Runtime

  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  S3Permission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref Function
      Action: lambda:InvokeFunction
      Principal: s3.amazonaws.com
Code language: YAML (yaml)

関数名を変更する内容です。

先ほどと同様の手順で変更セットを作成します。

以下が作成された変更セットです。

Detail of CloudFormation 9.

ルートスタックの変更セットです。
パラメータのRuntimeの値が変更しましたが、こちらが反映されています。

Detail of CloudFormation 10.
Detail of CloudFormation 11.

こちらもパラメータの変更が反映されています。

注目すべきはスタック内の2つのリソース(Function, S3Permission)の置き換えの項目が「True」とあります。
つまり今回の更新を実行すると、リソースの置き換えが発生するということです。

リソースが置き換わるということの意味については、AWS公式で以下の通りに説明されています。

AWS CloudFormation は更新の際にリソースを再作成し、新しい物理 ID も生成されます。AWS CloudFormation は、通常まず置換リソースを作成し、他の従属するリソースからの参照が置換リソースを指すように変更してから、古いリソースを削除します。

スタックのリソースの更新動作

つまりリソースの再作成によって、リソースの使用が一時中断するということになります。

Detail of CloudFormation 12.

リソースの置き換えが発生することは分かりましたが、どういった理由で発生するのかを確認します。
詳細はJSON changesのタブで確認することができます。

logicalResourceIdの値が「Function」のデータがLambda関数に関するものです。
detailsで変更内容の詳細が確認できます。

RuntimeとFunctionNameの値が変更になったことが読み取れます。
Runtimeに関しては2つのデータがありますが、これはParametersセクションの値を組み込み関数Fn::Refを使用して参照しているためです。
こちらについては、以下のページをご確認ください。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets-samples.html

ポイントはrequiresRecreationの値です。
この値が「Always」の変更を実行すると、常にリソースの置き換えが発生することになります。
つまりFunctionNameを変更することによって、リソースの置き換えが発生します。

同様に、S3Permissionでも、requiresRecreationの値が「Always」です。
こちらもFunctionNameの値に変更が生じることによって、リソースの置き換えが発生します。
この変更は先述の関数の名前変更に伴うもので、テンプレートファイルの変更に直接起因するものではありませんでした。

このように変更セットを使用することによって、事前にリソース更新時の影響範囲を確認することができます。

変更セットを作成してリソース削除

リソースを更新する際の変更セットを確認します。

テンプレートファイルを修正します。

soa-03-004-lambda.yaml
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  Handler:
    Type: String

  Prefix:
    Type: String

  Runtime:
    Type: String


Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          def lambda_handler(event, context):
            print('hello, awstut !')
      FunctionName: !Sub "${Prefix}-function"
      #FunctionName: !Sub "${Prefix}-function-updated"
      Handler: !Ref Handler
      Role: !GetAtt FunctionRole.Arn
      Runtime: !Ref Runtime

  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  #S3Permission:
  #  Type: AWS::Lambda::Permission
  #  Properties:
  #    FunctionName: !Ref Function
  #    Action: lambda:InvokeFunction
  #    Principal: s3.amazonaws.com
Code language: YAML (yaml)

S3Permissionをコメントアウトします。
つまりこのリソースを削除するということになります。

先ほどと同様の手順で変更セットを作成します。

以下が作成された変更セットです。

Detail of CloudFormation 14.

親スタックを見ると、子スタックが「Modify」とあります。
子スタックを見ると、S3Permissionが「Remove」とあります。

このように変更セットを作成することによって、リソース削除時の影響範囲を確認することができます。

まとめ

CloudFormation変更セットの動作を確認しました。