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

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

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

One of the AWS SOA topics is related to deployment, provisioning, and automation.

Change sets are available to safely update resources in CloudFormation.

Change sets allow you to preview how proposed changes to a stack might impact your running resources, for example, whether your changes will delete or replace any critical resources

Updating stacks using change sets

This page will confirm the operation of the CloudFormation change set.

Environment

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

Create a Lambda function using CloudFormation.

Create a change set after the function is created, check the impact of stack updates, and perform stack updates.

CloudFormation template files

The above configuration is built with CloudFormation.
We will build it with the following two template files.

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)

The templates are also placed at the following URL

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

Explanation of key points of template files

No special configuration is required.
Lambda functions are described in inline format.

For more information on Lambda functions, please see the following page

https://awstut.com/en/2022/02/02/3-parterns-to-create-lambda-with-cloudformation-s3-inline-container

Architecting

Use CloudFormation to build this environment and check its actual behavior.

Create CloudFormation stacks and check the resources in the stacks

Create CloudFormation stacks.
For information on how to create stacks and check each stack, please refer to the following pages.

https://awstut.com/en/2021/12/11/cloudformations-nested-stack

After reviewing the resources in each stack, information on the main resources created in this case is as follows

  • Root Stack: soa-03-004
  • Child Stack: soa-03-004-LambdaStack-1WUKC1VUIJ2Q1
  • Lambda function: soa-03-004-function

Check each resource from the AWS Management Console.

Check the CloudFormation stacks.

Detail of CloudFormation 1.
Detail of CloudFormation 2.

You can see that two stacks have been created.

The former is the root stack and the other is the child stack.

Operation Check

You are ready to go.
Create a modified set of these stacks and see which work.

Create change sets and create resources

Change Set Creation

Define new resources as updates to the stack.

Make modifications to 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)

This is the part that was commented out.
Uncomment and define a resource-based policy for Lambda.

After placing the modified template file in place, create a change set.
In this case, we will place the template file in the S3 bucket.
The AWS CLI is used to create the change set.

$ 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)

For nested stacks, create a change set by specifying the root stack with the include-nested-stacks option enabled.

Check the change set created from the AWS Management Console.

Detail of CloudFormation 3
Detail of CloudFormation 4.

The previous single operation created a change set on the root and child stacks, respectively.
Looking at the root stack, the action for the child stack is “Modify”.
Looking at the child stack, the S3Permission (Lambda’s resource-based policy) is “Add”.

By creating a change set, you can see the extent of the impact when the stack is updated.

By the way, if you want to check the change set using the AWS CLI, execute the following command
Check the change set for the root stack.

$ 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)
Stack update using change sets

You have created a change set and verified your changes.
The next step is to update the stack using the change set.
To update the stack, use the AWS CLI.

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

Check the update status in the AWS Management Console.

Detail of CloudFormation 5.
Detail of CloudFormation 6.

You can see that two stack change sets have been executed.

Detail of CloudFormation 7.

Looking at the resource generated from the child stack, an S3Permission is indeed created.

You can also use change sets in this way to update the stack.

Create change sets and updae resources

Check the change set when updating resources.

Modify the template file.

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)

Change the default values of parameters.
This is related to the runtime environment of the Lambda function.

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)

The contents of the function name are to be changed.

Create a change set using the same procedure as before.

The following is the set of changes created

Detail of CloudFormation 9.

This is a set of changes to the root stack.
The value of the parameter Runtime has been changed, which is reflected here.

Detail of CloudFormation 10.
Detail of CloudFormation 11.

The parameter changes are reflected here as well.

It is noteworthy that the Replace two resources (Function, S3Permission) in the stack are marked “True”.
This means that resource replacement will occur when this update is executed.

The official AWS explanation of what it means for a resource to be replaced is as follows

AWS CloudFormation recreates the resource during an update, which also generates a new physical ID. AWS CloudFormation usually creates the replacement resource first, changes references from other dependent resources to point to the replacement resource, and then deletes the old resource.

Update behaviors of stack resources

This means that recreating the resource will cause the use of the resource to be suspended.

Now that we know that resource replacement occurs, let’s see how and why it is raw.
Details can be found on the JSON changes tab.

Detail of CloudFormation 12.

The data with a logicalResourceId value of “Function” is related to the Lambda function.
You can see the details of the changes in details.

It can be read that the values for Runtime and FunctionName have changed.
For Runtime, there are two sets of data, because the values in the Parameters section are referenced using the built-in function Fn::Ref.
Please see the following page for more information about this.

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

The key point is the value of requiresRecreation.
Performing a change with this value of “Always” will always result in resource replacement.
In other words, changing FunctionName will cause the resource to be replaced.

Similarly, in S3Permission, the value of requiresRecreation is “Always”.
Here, too, a change in the value of FunctionName causes the resource to be replaced.
This change was due to the aforementioned function renaming and was not directly attributable to the template file change.

By using change sets in this way, the scope of the impact of resource updates can be ascertained in advance.

Create change sets and delete resources

Check the change set when updating resources.

Modify the template file.

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)

Comment out S3Permission.
This means that this resource will be deleted.

Create a change set using the same procedure as before.

The following is the set of changes created

Detail of CloudFormation 14.

Looking at the parent stack, the child stack says “Modify”.
Looking at the child stack, the S3Permission is “Remove”.

By creating a change set in this manner, the scope of the impact of resource deletion can be ascertained.

Summary

CloudFormation change sets are now working.