CDKやCloudFormationにて、変更したDeletionPolicy (RemovalPolicy)がStackに反映されない時の対処法を残します。
はじめに
おはよう。@bioerrorlogです。
先日、CDKで作成したリソースのDeleteionPolicyを変更してdeployした際に、その変更が実環境のStackに反映されないことがありました。
その原因と対処法をメモします。
DeletionPolicyの変更が反映されないときの対処法
起こったこと
まず、起こったことをおさらいします。
例えば以下のように、S3を定義したシンプルなCDKコードがあるとします(Pythonを例とします)。
from aws_cdk import ( aws_s3 as s3, core ) class SampleS3Stack(core.Stack): def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) s3_bucket = s3.Bucket(self, "TestBucket", bucket_name="deletion-policy-test-bucket") app = core.App() SampleS3Stack(app, "deletion-policy-test-stack") app.synth()
上記のようにS3 BucketをCDK Constructのデフォルトで作成した場合、DeletionPolicyおよびUpdateReplacePolicyにはRetain
が設定されます。
以下、生成されるCloudFormationテンプレート抜粋です。
Resources: TestBucket560B80BC: Type: AWS::S3::Bucket Properties: BucketName: deletion-policy-test-bucket UpdateReplacePolicy: Retain DeletionPolicy: Retain
ここで、DeletionPolicy / UpdateReplacePolicyをDelete
に変更するには、以下のようにapply_removal_policy()
を利用します。
from aws_cdk import ( aws_s3 as s3, core ) class SampleS3Stack(core.Stack): def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) s3_bucket = s3.Bucket(self, "TestBucket", bucket_name="deletion-policy-test-bucket") # DeletionPolicy / UpdateReplacePolicyを指定 s3_bucket.apply_removal_policy(core.RemovalPolicy.DESTROY) app = core.App() SampleS3Stack(app, "deletion-policy-test-stack") app.synth()
上記のようにapply_removal_policy()
を適用することで、生成されるCloudFormationテンプレートやcdk diff
の結果には変更が反映されます。
# cdk synth Resources: TestBucket560B80BC: Type: AWS::S3::Bucket Properties: BucketName: deletion-policy-test-bucket UpdateReplacePolicy: Delete DeletionPolicy: Delete
# cdk diff Stack deletion-policy-test-stack Resources [~] AWS::S3::Bucket TestBucket TestBucket560B80BC ├─ [~] DeletionPolicy │ ├─ [-] Retain │ └─ [+] Delete └─ [~] UpdateReplacePolicy ├─ [-] Retain └─ [+] Delete
しかし、上記変更後のCDKコードをデプロイしても、変更がStackに反映されませんでした。
以下のようにno changes
と表示され、変更はデプロイされなかったのです。
# cdk deploy deletion-policy-test-stack: deploying... deletion-policy-test-stack: creating CloudFormation changeset... ✅ deletion-policy-test-stack (no changes)
原因
少し調べた結果、上記の振る舞いはCloudFormationの仕様であることが分かりました。
以下ドキュメント抜粋です。
You can't update the CreationPolicy, DeletionPolicy. or UpdatePolicy attribute by itself. You can update them only when you include changes that add, modify, or delete resources.
CreationPolicy/DeletionPolicy/UpdatePolicyのみでは変更を更新できないため、他のリソース情報と一緒に更新する必要がある、とのことです。
仕様としてイケてないとは思いますが、DeletionPolicyが更新できないこと自体は仕様として想定通りということですね。
対処法
では、どのようにしてDeletionPolicyを更新すればよいのか。
それ単体では更新できないことが仕様である以上、他のリソース情報と一緒に更新する他なさそうです。
例えば、metadataやTagの変更を同時に行えば、リソースそのものにはあまり影響を与えずにDeletionPolicyを更新することが出来ます。
ドキュメントにも以下のように、metadataの更新を一緒に行うことが言及されています。
For example, you can add or modify a metadata attribute of a resource.
例えば、以下のようにmetadataを変更して再デプロイすればDeletionPolicyの変更が反映されます。
from aws_cdk import ( aws_s3 as s3, core ) class SampleS3Stack(core.Stack): def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) s3_bucket = s3.Bucket(self, "TestBucket", bucket_name="deletion-policy-test-bucket") # DeletionPolicy / UpdateReplacePolicyを指定 s3_bucket.apply_removal_policy(core.RemovalPolicy.DESTROY) # metadataを更新 cfn_bucket = s3_bucket.node.default_child cfn_bucket.cfn_options.metadata = { "DeletionPolicyUpdated": "DESTROY" } app = core.App() SampleS3Stack(app, "deletion-policy-test-stack") app.synth()
※Metadataの更新方法はドキュメントやGitHub issueを参考にしています。
Metadataの上書き操作になるので注意が必要です(もともとあったmetadata aws:cdk:path
が上書きされる)。
また以下のようにTagを追加して再デプロイしても、DeletionPolicyの変更が反映されます。
from aws_cdk import ( aws_s3 as s3, core ) class SampleS3Stack(core.Stack): def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) s3_bucket = s3.Bucket(self, "TestBucket", bucket_name="deletion-policy-test-bucket") # DeletionPolicy / UpdateReplacePolicyを指定 s3_bucket.apply_removal_policy(core.RemovalPolicy.DESTROY) # Tagを追加 core.Tags.of(s3_bucket).add("DeletionPolicyUpdated", "DESTROY") app = core.App() SampleS3Stack(app, "deletion-policy-test-stack") app.synth()
おわりに
今回は、CloudFormation/CDKにてDeletionPolicyの変更が反映されないときの対処法を書きました。
そもそもDeletionPolicy単体では更新できないのがCloudFormationの仕様、というのにはなかなか驚きました。
なるべくリソースそのものに影響のないTagやmetadataの更新を一緒に行うことを対処法として書きましたが、他にもっと適切なやり方があれば教えて頂けると嬉しいです。
[関連記事]
参考
Modifying a stack template - AWS CloudFormation
RemovalPolicy — AWS Cloud Development Kit 1.94.1 documentation
Escape hatches - AWS Cloud Development Kit (AWS CDK)
Identifiers - AWS Cloud Development Kit (AWS CDK)
Python: cfn_options.metadata cannot be mutated · Issue #6379 · aws/aws-cdk · GitHub
Add Support for adding Metadata to deal with 3rd party tools · Issue #8336 · aws/aws-cdk · GitHub