CloudFormationのスタックリファクタリングを使って、肥大化したスタックのリソースを別のスタックに移動できるかを検証していましたが、毎回 API Gateway だけ移行に失敗していました。ただ、失敗したスタックをロールバックして再度同じ手順で API Gateway のスタックリファクタリングを実行すると、今度は成功します。
ということで、なぜロールバックしたあとにスタックリファクタリングを実行すると成功するのか、一発でリファクタリングを成功させるためにはどうすればいいのかについて調査・検証してみました。
事象再現
まずは移動対象となるスタックとリソースを作成します。API Gateway は別のスタックに移動するので、リソース移動後に移動元のスタックのリソースが空にならないようにダミーのリソース(パラメータストア)も用意しておきます。
AWSTemplateFormatVersion: '2010-09-09'
Description: 'HTTP API Gateway CloudFormation Template'
Resources:
# HTTP API Gateway
HttpApi:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: test-apigw
ProtocolType: HTTP
Description: 'HTTP API Gateway created via CloudFormation.'
Tags:
Environment: dev
# Parameter Store - String Parameter
StringParameter:
Type: AWS::SSM::Parameter
Properties:
Name: /myapp/string-parameter
Type: String
Value: 'dummy'
このテンプレートを使用してスタックを作成します。
aws cloudformation create-stack \
--stack-name test-stack-refactor-apigw \
--template-url ${IAC_BUCKET}/beforeApigw.yaml
スタックリファクタリングを作成するために、移動後のスタックのテンプレートと移動先のスタックのテンプレート用意します。
AWSTemplateFormatVersion: '2010-09-09'
Description: 'HTTP API Gateway CloudFormation Template'
Resources:
# HTTP API Gateway
HttpApi:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: test-apigw
ProtocolType: HTTP
Description: 'HTTP API Gateway created via CloudFormation.'
Tags:
Environment: dev
AWSTemplateFormatVersion: '2010-09-09'
Description: 'HTTP API Gateway CloudFormation Template'
Resources:
# Parameter Store - String Parameter
StringParameter:
Type: AWS::SSM::Parameter
Properties:
Name: /myapp/string-parameter
Type: String
Value: 'dummy'
Tags:
Environment: dev
スタックリファクタリングを作成します。
aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=test-stack-refactor-apigw,TemplateURL=${IAC_BUCKET}/afterSsm.yaml \
StackName=test-apigw,TemplateURL=${IAC_BUCKET}/afterApigw.yaml \
--enable-stack-creation
スタックリファクタリングを実行します
aws cloudformation execute-stack-refactor --stack-refactor-id stack-refactor-id
スタックリファクタリングを実行すると、失敗します。
スタックのステータスを正常にするために、移動元のスタック(test-stack-refactor-apigw)を beforeApigw.yamlを使ってスタックを更新し、移動先のスタック(test-apigw)は削除します。
再度スタックリファクタリングを実行します。
# スタックリファクタリングを作成
aws cloudformation create-stack-refactor \
--stack-definitions \
StackName=test-stack-refactor-apigw,TemplateURL=${IAC_BUCKET}/afterSsm.yaml \
StackName=test-apigw,TemplateURL=${IAC_BUCKET}/afterApigw.yaml \
--enable-stack-creation
# スタックリファクタリングを実行
aws cloudformation execute-stack-refactor --stack-refactor-id stack-refactor-id
すると、今度はリファクタリングに成功します。
リソースに勝手に付与されるタグが原因?
このエラーの調査をしていると、CloudFormation でスタック作成後の API Gateway のタグの内容とスタックロールバック後のタグの内容に差異があるのを発見しました。
リソース作成直後のタグの内容
スタックロールバック後のタグの内容
タグに CloudFormation が勝手に付与したであろう内容がありますが、テンプレートの API Gateway の部分を適当に修正して(tagの箇所でもそれ以外、例えばdescriptionの部分でも修正して)スタックを更新すると、CloudFormation が付与したタグが削除されて、タグの内容はテンプレートの定義通りになります。
ということで、CloudFormation で新規でスタックを作成したあとに API Gateway の部分を修正してスタックを更新し、スタックリファクタリングを実行してみると、スタックリファクタリングは一発で成功しました。
CloudFormation で新規リソース作成後に API Gateway をスタックリファクタリングで移行しようとすると、内部的に差分があると判定されてスタックリファクタリングに失敗してしまうのでしょうか。リソース作成後にドリフトの検出を実行してみても、差分は検出されませんでした。
おわりに
CloudFormation で API Gateway を作成すると CloudFormation 側でタグを追加で付与しますが、API Gateway の内容を修正してスタックを更新すると、CloudFormation が付与したタグは削除されます。一方で、今回ダミーリソースとしてパラメータストアを作成していますが、こちらも API Gateway と同様に CloudFormation 側が追加でタグを設定していますが、パラメータストアの内容を修正してスタックを更新しても、CloudFormation が追加で設定したタグは削除されずに残っていました。
リソース作成直後
スタック更新後
パラメータストアをスタックリファクタリングで別のスタックに移行してみると、エラーが出ずに一発で移動ができました。
CloudFormation でのタグの付与の違い?によってスタックリファクタリングの挙動も変わってくるようです。
以上、API Gateway のスタックリファクタリングが毎回失敗することの調査と検証でした。