CloudFormation変更セットでスタック更新時の影響範囲を確認する
AWS SOAの出題範囲の1つである、デプロイ、プロビジョニング、およびオートメーションに関する内容です。
CloudFormationで安全にリソースを更新するために、変更セットが利用できます。
変更セットを使用すると、スタックの変更案が実行中のリソースに与える可能性がある影響 (たとえば、変更によって重要なリソースが削除されたり置き換えられたりしないか) を確認できます。
変更セットを使用したスタックの更新
本ページでは、CloudFormation変更セットの動作を確認します。
構築する環境
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を使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- ルートスタック:soa-03-004
- 子スタック:soa-03-004-LambdaStack-1WUKC1VUIJ2Q1
- Lambda関数:soa-03-004-function
AWS Management Consoleから各リソースを確認します。
CloudFormationスタックを確認します。
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から作成された変更セットを確認します。
先ほどの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で確認します。
2つのスタックの変更セットが実行されたことがわかります。
子スタックから生成されたリソースを見ると、確かに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)
関数名を変更する内容です。
先ほどと同様の手順で変更セットを作成します。
以下が作成された変更セットです。
ルートスタックの変更セットです。
パラメータのRuntimeの値が変更しましたが、こちらが反映されています。
こちらもパラメータの変更が反映されています。
注目すべきはスタック内の2つのリソース(Function, S3Permission)の置き換えの項目が「True」とあります。
つまり今回の更新を実行すると、リソースの置き換えが発生するということです。
リソースが置き換わるということの意味については、AWS公式で以下の通りに説明されています。
AWS CloudFormation は更新の際にリソースを再作成し、新しい物理 ID も生成されます。AWS CloudFormation は、通常まず置換リソースを作成し、他の従属するリソースからの参照が置換リソースを指すように変更してから、古いリソースを削除します。
スタックのリソースの更新動作
つまりリソースの再作成によって、リソースの使用が一時中断するということになります。
リソースの置き換えが発生することは分かりましたが、どういった理由で発生するのかを確認します。
詳細はJSON changesのタブで確認することができます。
logicalResourceIdの値が「Function」のデータがLambda関数に関するものです。
detailsで変更内容の詳細が確認できます。
RuntimeとFunctionNameの値が変更になったことが読み取れます。
Runtimeに関しては2つのデータがありますが、これはParametersセクションの値を組み込み関数Fn::Refを使用して参照しているためです。
こちらについては、以下のページをご確認ください。
ポイントは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をコメントアウトします。
つまりこのリソースを削除するということになります。
先ほどと同様の手順で変更セットを作成します。
以下が作成された変更セットです。
親スタックを見ると、子スタックが「Modify」とあります。
子スタックを見ると、S3Permissionが「Remove」とあります。
このように変更セットを作成することによって、リソース削除時の影響範囲を確認することができます。
まとめ
CloudFormation変更セットの動作を確認しました。