【テクニカル・上級編】CloudFormation DeletionPolicyとUpdateReplacePolicyの厳密な挙動差異とリソースタイプ別の挙動マトリクス – インフラ構成管理(IaC)活用バイブル

【AWS CloudFormation】DeletionPolicyとUpdateReplacePolicyの深淵:本番環境を全損から守るリソース保護の完全調停

インフラストラクチャ・アイズ・コード(IaC)の最大の暴力性は、「1行の変更が、数年かけて蓄積されたクリティカルなデータを一瞬で灰にする」という非情なまでの機械的正確性にある。

AWS CloudFormationにおける `DeletionPolicy` と `UpdateReplacePolicy` は、その圧倒的な破壊力を持つ自動化エンジンの暴走を防ぐための、極めて重要かつ境界線が曖昧な防壁である。
多くのエンジニアは「なんとなくデータを消したくないから `Retain` を書く」というレベルでこの属性を扱っているが、大規模なSRE組織や金融系ミッションクリティカルなパイプラインにおいて、この2つの属性の挙動差異を完全に把握していないことは、時限爆弾を抱えてデプロイボタンを押す行為に等しい。

本稿では、これら2つのポリシーの内部アーキテクチャレベルでの差異を解き明かし、主要リソースにおける罠、そしてCI/CDパイプライン全体を堅牢化する実践的なパターンを、骨の髄まで解説する。

—

1. 2大ポリシーの根本思想と発動フェーズの決定的な違い

まず、大前提として心に刻むべきは、「誰が、どのライフサイクルイベントでその判断を下すのか」という点だ。

  • `DeletionPolicy`: スタック自体が削除(`DeleteStack`)される際、またはスタックの更新時にリソースが明示的/暗黙的に切り離される(Remove)際に発動する。
  • `UpdateReplacePolicy`: スタックの更新(`UpdateStack`)プロセスにおいて、プロパティの変更等により既存リソースの「置換(Replacement)」が発生する際、古いリソースをどう扱うかを決定する。

ライフサイクルマトリクス

| ライフサイクルイベント | 対象操作 | 適用されるポリシー |
| :— | :— | :— |
| `aws cloudformation delete-stack` | スタックの完全削除 | `DeletionPolicy` |
| `aws cloudformation update-stack` | プロパティ変更によるリソース再作成 (Replacement = True) | `UpdateReplacePolicy` |
| スタックテンプレートからのリソースブロックの削除 | 既存リソースのデタッチ&削除 | `DeletionPolicy` |

> ⚠️ エキスパートの知見:
> 多くの人が勘違いしているが、`UpdateReplacePolicy` は単独ではスタック削除時には一切機能しない。逆に、`DeletionPolicy` は、スタック更新時の「置換」に対してデフォルトでは影響を与えない(例外あり)。本番環境では、実質的にこの両方を同時に記述することがベストプラクティスとなるケースが大半である。

—

2. ポリシーに指定可能な3つの値と内部挙動

それぞれの属性には以下の3つが指定可能だ。

1. `Delete`: 通常の挙動。リソースは削除または置換に伴い消滅する。
2. `Retain`: リソースを保持する。スタック削除時はオーファン(孤児)リソースとなり、AWSアカウント内に残存する。更新時の置換であれば、古いリソースがそのまま残り、新しいリソースが別名または新規作成される。
3. `Snapshot`: RDSデータベースやElmチキャッシュなど、スナップショットをサポートするリソースでのみ使用可能。リソース削除/置換の直前に最終スナップショットを取得する。

—

3. リソースタイプ別の挙動と「絶対にやってはいけない罠」

AWSのマネージドサービスは、リソースタイプごとにCloudFormationのステートマシンとの統合方法が異なるため、ポリシーの挙動にも「クセ」が存在する。

S3バケット (`AWS::S3::Bucket`)

  • 罠: `DeletionPolicy: Retain` を指定していても、バケット内にオブジェクトが存在する場合、CloudFormationの削除プロセスはバケットの削除を試みて失敗(DELETE_FAILED)する。
  • 設計解: バケット自体は残るが、空っぽにするわけではない。完全自動化パイプラインでは、事前のLambdaカスタムリソースやライフサイクルポリシーによるオブジェクトの強制パージ、あるいはバージョニングの厳密な管理が不可欠となる。

RDSデータベース (`AWS::DBInstance`)

  • 推奨設定: 本番環境では常に `UpdateReplacePolicy: Snapshot` / `DeletionPolicy: Snapshot` を基本としつつ、スナップショット名の衝突(同名スナップショット存在時のエラー)を防ぐためのネーミング戦略を組む必要がある。
  • 注意: `Snapshot` を指定した場合、CloudFormationはスナップショットの作成完了までスタックの削除/更新をブロック(ポーリング)する。数TBに及ぶDBの場合、タイムアウト(CloudFormationのデフォルト制限やLambdaのタイムアウト)に直面するため、非同期な運用設計が求められる。

—

4. 完全自動構成:実戦的CloudFormationテンプレート

以下に、S3バケット、RDS、およびDynamoDBという、データ損失のインパクトが最大級となる3つのリソースを保護し、かつ冪等性とメンテナンス性を極限まで高めたテンプレートの断片を示す。

AWSTemplateFormatVersion: ‘2010-09-09’
Description: ‘Mission-Critical Data Tier with Strict Resource Protection Policies’

Parameters:
EnvironmentType:
Type: String
AllowedValues: [prod, stg, dev]
Default: prod

Resources:
# 1. S3 Bucket: 誤削除・置換からの完全保護
SecureDataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub “my-company-core-data-${EnvironmentType}-${AWS::AccountId}”
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
# 【重要】データ消失を防ぐため、削除時・置換時ともに保持(Retain)を指定
DeletionPolicy: Retain
UpdateReplacePolicy: Retain

# 2. RDS Instance: スナップショットによる安全網の確保
SecureDatabase:
Type: AWS::DBInstance
Properties:
DBInstanceIdentifier: !Sub “core-db-${EnvironmentType}”
Engine: postgres
EngineVersion: ‘15.4’
DBInstanceClass: db.m6i.large
AllocatedStorage: 100
MasterUsername: dbadmin
# 本番ではSecrets Manager連携が必須だが簡略化
MasterUserPassword: !Ref DBAdminPasswordParameter
MultiAZ: true
StorageEncrypted: true
# 【重要】RDSはSnapshotが利用可能。置換・削除ともに最終スナップショットを取得
DeletionPolicy: Snapshot
UpdateReplacePolicy: Snapshot

# 3. DynamoDB Table: Point-in-Time RecoveryとRetainの組み合わせ
SecureTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub “core-table-${EnvironmentType}”
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:

  • AttributeName: PK

AttributeType: S
KeySchema:

  • AttributeName: PK

KeyType: HASH
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
DeletionPolicy: Retain
UpdateReplacePolicy: Retain

Parameters:
DBAdminPasswordParameter:
Type: String
NoEcho: true
Description: ‘Master password for RDS’

—

5. 高度な運用:オーファン(孤児)リソースの検知とクリーンアップ自動化

`Retain` や `Snapshot` を多用すると、CloudFormationスタックの管理外となったリソース(オーファンリソース)がAWS環境内に野良化し、セキュリティリスクや無駄なコストの温床となる。

これを解決するため、SREチームは以下のCLI/APIスクリプトを定期実行(EventBridge + Lambdaなど)し、IaCのガバナンスを強制すべきである。

オーファンリソース検出・監査スクリプト(Python / Boto3)

以下のスクリプトは、CloudFormationスタックから切り離された(あるいは削除されたスタックに残された)特定タグを持つS3バケットを検出し、アラートを上げる仕組みのコアロジックである。

import boto3
from botocore.exceptions import ClientError

def audit_orphaned_s3_buckets():
cf_client = boto3.client(‘cloudformation’)
s3_client = boto3.client(‘s3’)

# 1. 現在存在するすべてのCloudFormationスタックから、リソースの物理ID(バケット名)を収集
managed_buckets = set()
paginator = cf_client.get_paginator(‘list_stacks’)

for page in paginator.paginate(StackStatusFilter=[‘CREATE_COMPLETE’, ‘UPDATE_COMPLETE’, ‘UPDATE_ROLLBACK_COMPLETE’]):
for stack in page[‘StackSummaries’]:
stack_name = stack[‘StackName’]
try:
res_paginator = cf_client.get_paginator(‘list_stack_resources’)
for res_page in res_paginator.paginate(StackName=stack_name):
for resource in res_page[‘StackResourceSummaries’]:
if resource[‘ResourceType’] == ‘AWS::S3::Bucket’:
managed_buckets.add(resource[‘PhysicalResourceId’])
except ClientError as e:
print(f”Error reading stack {stack_name}: {e}”)

# 2. AWSアカウント内の実際のS3バケット一覧を取得
all_buckets = s3_client.list_buckets()[‘Buckets’]

print(“=== Orphaned S3 Buckets Audit ===”)
for bucket in all_buckets:
bucket_name = bucket[‘Name’]
# 特定の命名規則やタグを持つものに絞るフィルタリングをここに挿入
if bucket_name.startswith(“my-company-core-data-“):
if bucket_name not in managed_buckets:
print(f”[WARNING] Orphaned Bucket Detected: {bucket_name}”)
# ここにSlack通知や自動タグ付け(Owner不明化対策)のロジックを組み込む

if __name__ == “__main__”:
audit_orphaned_s3_buckets()

—

6. 伝説的アーキテクトからの最終提言

`DeletionPolicy` と `UpdateReplacePolicy` は、ただの設定項目ではない。これらは「ビジネスの連続性とコードによるインフラ管理のトレードオフをどこで調停するか」という、アーキテクトの哲学そのものである。

1. 開発(Dev)環境: 速度とクリーンアップの容易さが最優先。ポリシーは原則指定しない(または `Delete`)。コストのゴミを残さない。
2. 本番(Prod)環境: 冗長性とデータ保護が絶対。すべてのステートフルリソースに `Retain` または `Snapshot` を強制し、CI/CDパイプラインのLinter(`cfn-lint` 等)でこれらが欠損しているテンプレートをビルドエラーとして弾くCIガードレールを構築せよ。

インフラストラクチャコードに魂を込めるとは、こうした「最悪のシナリオ(全削除の誤爆)」からシステムを守るセーフティネットを、コードのレイヤで完璧に織り込むことに他ならない。

タイトルとURLをコピーしました