AWS CloudFormation(クラウドフォーメーション)は、VPCやEC2、S3バケットといったAWSリソースの構成をYAMLまたはJSONのテンプレートに書き、その内容どおりにリソースを作成・更新・削除するAWSのサービスです。インフラをコードで管理するIaC(Infrastructure as Code)を、AWS純正の仕組みで実現します。
実務でつまずくのはテンプレートの文法より、プロパティを1行変えただけでEC2インスタンスやDBが作り直される、スタックを消したらS3バケットの削除で失敗する、といった更新時の挙動です。本記事は書き方とスタック操作の基本に加えて、更新で何が置き換わるのかをリソース定義から確認し、消してはいけないリソースを守る設定と、大きくなったテンプレートの分け方までを扱います。仕様・上限値は2026年11月1日時点のAWS公式ドキュメントで確認したものです。
まとめ:CloudFormationの要点
- テンプレート(設計図)から作ったリソースのまとまりを「スタック」と呼び、作成・更新・削除はスタック単位で行います。更新前の差分確認には「変更セット」を使います。
- テンプレートで必須なのは
Resourcesセクションだけです。AWSTemplateFormatVersionに書ける値は2010-09-09のみです。 - AWS::*のリソースタイプの管理にCloudFormationの追加料金はかかりません。ただし、カスタムHooksなどの有料拡張を使う場合は、その操作料金が別途発生します。
- 更新で最も危ないのは「置換(Replacement)」です。EC2の
ImageId・SubnetId、VPCのCidrBlock、RDSのDBNameなどを変えると、リソースは新しいIDで作り直されます。 DeletionPolicyはスタック削除時と更新による定義削除時の処理を指定し、置換には適用されません。置換時に古いリソースを残すにはUpdateReplacePolicy: Retainを併記します。ただし、置換そのものや新リソースへの参照の切り替えは防げません。- テンプレートが大きくなったら、一緒に作って一緒に消す部品はネストされたスタック(
AWS::CloudFormation::Stack)、更新の頻度や担当が違う層は別スタックに分けて出力値を参照します。 - AWSだけを対象にするならCloudFormation、複数クラウドや外部SaaSまでまとめて管理するならTerraform、プログラミング言語で書きたいならCDKを選びます。
CloudFormationの仕組み:テンプレート・スタック・変更セットの関係
CloudFormationを理解するには、3つの用語の関係を押さえれば足ります。テンプレートに「あるべき構成」を書き、CloudFormationがそれを読んでAWSの各サービスAPIを呼び、できあがったリソース群をスタックとして記録します。サーバーやネットワークを用意する作業全般はプロビジョニングと呼ばれ、CloudFormationはそれをテンプレート駆動で自動化する道具です。
| 用語 | 実体 | 操作の単位 |
|---|---|---|
| テンプレート | YAML/JSONのテキストファイル | Gitでバージョン管理 |
| スタック | 1つのテンプレートから作られたリソース群 | 作成・更新・削除 |
| 変更セット | 更新前に計算される差分の一覧 | 確認してから実行 |
スタックは作成したリソースの物理IDを覚えています。テンプレートからリソースの定義を消して更新すると、原則として実体も削除されます。ただし、DeletionPolicyにRetainなどを指定したリソースは保持されます。コンソールで手作業した場合と違い、「何を作ったか」の記録が消えないのがCloudFormationの価値です。
料金:AWSリソースの管理は追加料金なし、カスタム拡張は従量課金
CloudFormationの料金ページには、AWS::* と Alexa::* 名前空間のリソースをCloudFormationで扱うことに追加料金はかからない、と書かれています。支払うのは、テンプレートで作ったEC2インスタンスやロードバランサーなどの通常料金だけです。AWSのリソースだけを扱い、自作のリソースタイプやHooksを使わない通常の使い方なら、CloudFormationそのものへの請求は発生しません。
課金対象になるのは、AWS::*・Alexa::*以外のリソースタイプ(自社作成のプライベートリソースタイプを含む)と、AWS::*以外のカスタムHooksです。ハンドラ操作1回あたり0.0009米ドル、1回の操作が30秒を超えた部分に1秒あたり0.00008米ドルがかかり、無料枠はアカウントあたり月1,000ハンドラ操作です。テンプレートの部品を再利用する「モジュール」の利用は無料です。
上限値:1テンプレート500リソース・本文51,200バイト
テンプレートを大きく育てると、次の上限に当たります。いずれもCloudFormationユーザーガイドのクォータのページに載っている値です。
| 項目 | 上限 |
|---|---|
| 1テンプレートのリソース数 | 500 |
| パラメータ数/出力数/マッピング数 | 各200 |
| テンプレート本文(リクエストに直接渡す場合) | 51,200バイト |
| テンプレート本文(S3に置いて渡す場合) | 1MB |
| アカウント・リージョンあたりの有効なスタック数 | 既定2,000(引き上げ可能) |
| パラメータ値の大きさ | 4,096バイト |
| 1回の操作で扱うリソース数(ネスト階層全体) | 2,500 |
51,200バイトは、AWS CLIの deploy コマンドでも判定に使われています。テンプレートがこれを超えると --s3-bucket の指定が必要になります。500リソースに近づいたら、ネットワーク・データベース・アプリケーションのようにライフサイクルの違う単位でスタックを分けるのが定石です(分け方は後述の「スタックの分け方」の章)。すでに肥大化したスタックは、2025年2月6日に提供が始まったスタックリファクタリングを使うと、リソースを作り直さずに別スタックへ移したり、論理IDを付け替えたりできます。
テンプレートの書き方:セクション構成とVPC・EC2・S3の記述例
テンプレートは、決められた名前のトップレベルセクションで構成します。実務でよく使うのは次の6つです。
| セクション | 必須 | 書く内容 |
|---|---|---|
AWSTemplateFormatVersion |
任意 | 値は 2010-09-09 のみ |
Parameters |
任意 | スタック作成時に渡す値 |
Mappings |
任意 | リージョン別などの固定表 |
Conditions |
任意 | 作る/作らないの条件 |
Resources |
必須 | 作成するリソースの定義 |
Outputs |
任意 | 他スタックや利用者に渡す値 |
次のテンプレートは、VPC・サブネット・EC2インスタンス・S3バケットを作る例です。環境名が prod のときだけインスタンスタイプを大きくし、AMIはSystems Managerのパブリックパラメータから最新のAmazon Linux 2023を引きます。cfn-lint 1.59.0(2026年10月28日公開の最新版)で東京リージョン向けに検査し、エラー・警告ともゼロでした。
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC, isolated subnet, EC2 and S3 bucket (sample)
Parameters:
EnvName:
Type: String
AllowedValues: [dev, prod]
Default: dev
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
Conditions:
IsProd: !Equals [!Ref EnvName, prod]
Resources:
AppVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
AppSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref AppVpc
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs '']
AppInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: !If [IsProd, t3.small, t3.micro]
SubnetId: !Ref AppSubnet
Tags:
- Key: Name
Value: !Sub '${EnvName}-app'
LogBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
VersioningConfiguration:
Status: Enabled
Outputs:
VpcId:
Value: !Ref AppVpc
Export:
Name: !Sub '${AWS::StackName}-VpcId'
BucketArn:
Value: !GetAtt LogBucket.Arn
AMI IDをテンプレートに直書きしないのは、AMI IDがリージョンごとに異なり、しかも定期的に新しいIDへ差し替わるためです。パブリックパラメータを参照すれば、同じテンプレートを東京でも大阪でもそのまま使えます。ただしこの書き方には更新時の落とし穴があり、後述の「置換」の章で扱います。構文ミスや存在しないプロパティ名は、デプロイ前にcfn-lintによるテンプレート検証で機械的に見つけられます。
組み込み関数:Ref・GetAtt・Sub・If・ImportValue の使い分け
リソース同士をつなぐのは組み込み関数です。上のテンプレートで使ったものを中心に、役割を整理します。
!Ref:パラメータの値、またはリソースの主ID(VPCならVPC ID)を返します。依存関係もこれで決まり、AppSubnetはAppVpcの作成完了を待ってから作られます。!GetAtt:主ID以外の属性を返します。S3バケットのARNは!GetAtt LogBucket.Arnで取ります。!Sub:文字列に${EnvName}や${AWS::StackName}を埋め込みます。!If/!Equals:Conditionsで定義した条件によって値を切り替えます。リソースごと作らない場合は、リソース直下にCondition: IsProdと書きます。!ImportValue:同じアカウント・同じリージョンの別スタックがOutputsのExportで公開した値を取り込みます。どこかのスタックに取り込まれている間は、エクスポートした値の変更も、エクスポート元スタックの削除もできません。
最後の制約はスタック分割の設計に効きます。アカウントやリージョンをまたぐ参照に使う Fn::GetStackOutput との違いは、後述の「スタックの分け方」の章で扱います。
Secrets Managerの動的参照:パスワードを埋め込まない記述
DBのパスワードやAPIキーをテンプレートやパラメータの既定値に書くと、Gitの履歴に平文で残ります。パラメータに NoEcho: true を付けても、コンソールやAPIの表示が伏せ字になるだけで、テンプレート本文に書いた値は隠れません。CloudFormationでは、Secrets ManagerやParameter Storeに保存した値を {{resolve:...}} 形式の動的参照で取り出します。
次のコードはResources配下に置く抜粋です。Secrets Managerにprod/app-dbというシークレットを作成し、usernameとpasswordをJSONキーとして保存しておきます。CloudFormationの実行主体には、このシークレットのsecretsmanager:GetSecretValue権限が必要です。
AppDb:
Type: AWS::RDS::DBInstance
DeletionPolicy: Snapshot
UpdateReplacePolicy: Snapshot
Properties:
Engine: mysql
DBInstanceClass: db.t4g.micro
AllocatedStorage: '20'
MasterUsername: '{{resolve:secretsmanager:prod/app-db:SecretString:username}}'
MasterUserPassword: '{{resolve:secretsmanager:prod/app-db:SecretString:password}}'
運用で効く制約は2つです。1つは、Secrets Manager側でパスワードだけを変えても、CloudFormationは新しい値を取りに行かないことです。値を取得するのは、動的参照を含むリソースを作成するときと、そのリソースを変更する更新のときだけだとSecrets Managerの動的参照のページに書かれています。パスワードの交換はSecrets Managerの自動ローテーションに任せ、テンプレートにはバージョンを指定しない参照を書くのが推奨形です。もう1つは、EC2の UserData と AWS::CloudFormation::Init では動的参照が使えず、1テンプレートに書ける動的参照は60個までという点です。SecureStringパラメータとSecrets Managerの使い分けは、パラメータストアとは?AWS Parameter Storeの料金・使い方とSecrets Managerとの違いで整理しています。
YAML・JSONとエディタの選び方
YAMLとJSONに機能差はありません。人が読み書きするならYAMLを選びます。コメントを書け、!Ref のような短縮記法が使えるためです。JSONは、スクリプトやCDKのようなツールが生成・加工する場合に向きます。1つのリポジトリ内でどちらかに揃えておけば、差分レビューが楽になります。
図を描いてテンプレートを作るツールは入れ替わっています。古い解説記事に出てくるCloudFormation Designerの改良版として提供されてきたAWS Infrastructure Composerは、2026年10月29日に新規受付を止め、2026年12月7日にサポートを終了します。CloudFormationコンソールに組み込まれていた図の編集画面は、コンソール直結の新しいビジュアルエディタに置き換わり、同じドラッグ&ドロップの編集画面はVS Code用のAWS Toolkitに残ります。スタックとテンプレートには影響しません。
スタックの作成・更新・削除:AWS CLIでの手順
スタックの操作はマネジメントコンソールからもできますが、手順を再現しやすいのはAWS CLIです。以下はAWS CLIの認証情報・実行権限・対象リージョンを設定し、先のテンプレートを template.yml として保存した状態で実行します。日常の作成と更新には aws cloudformation deploy を使います。
aws cloudformation deploy \
--stack-name my-app-dev \
--template-file template.yml \
--parameter-overrides EnvName=dev
deploy は、指定したスタック名が存在しなければ新規作成、存在すれば更新として動きます。内部では変更セットを作成して実行しており、完了まで待ってから終了します。テンプレートでIAMロールやポリシーを作る場合は --capabilities CAPABILITY_IAM(名前付きのIAMリソースなら CAPABILITY_NAMED_IAM)を付けないと InsufficientCapabilities エラーで止まります。
削除は次のコマンドです。スタックが作ったリソースは、後述の DeletionPolicy で保護したものを除いてまとめて削除されます。この例の LogBucket は Retain 指定のため残り、スタックの管理対象から外れます。保存データなどの料金は引き続き発生するため、検証後の保管・削除は別途判断してください。
aws cloudformation delete-stack --stack-name my-app-dev
変更セットで「置換」の有無を確認する手順
本番スタックの更新では、deploy に --no-execute-changeset を付けて、変更セットを作るだけで止めます。
aws cloudformation deploy \
--stack-name my-app-prod \
--template-file template.yml \
--parameter-overrides EnvName=prod \
--no-execute-changeset
aws cloudformation describe-change-set \
--stack-name my-app-prod \
--change-set-name '表示された変更セット名に置換' \
--query 'Changes[].ResourceChange.[LogicalResourceId,Action,Replacement]' \
--output table
見るべきは Replacement 列です。値は True・False・Conditional の3つで、True はリソースが新しいIDで作り直されることを意味します。Conditional は、変更後の値次第で置換になる場合です。データを持つリソースに True か Conditional が付いていたら、実行を止めて原因のプロパティを確認します。問題がなければ execute-change-set で適用します。
作成失敗時のROLLBACK_COMPLETEとスタックの再作成手順
新規作成が途中で失敗すると、CloudFormationは作りかけのリソースを消して ROLLBACK_COMPLETE 状態にします。この状態のスタックに許される操作は削除だけです。テンプレートを直して同じスタック名で deploy し直しても更新はできないので、いったん delete-stack してから作り直します。
失敗のたびに最初から作り直すのが遅い場合は、deploy に --disable-rollback を付けます。成功したリソースは残り、失敗したリソースだけが CREATE_FAILED や UPDATE_FAILED のまま残るので、テンプレートを直して再試行できます。ただし、置換が必要な更新で失敗した場合は、失敗状態を保持して再試行する方式には対応しません。開発環境向けの使い方で、本番では通常のロールバックを残すほうが安全です。
スタック更新で置換されるプロパティとデータを消さない設定
更新時の「置換」では、古いリソースの削除によるデータ消失に注意が必要です。公式ドキュメントは更新の挙動を、中断なしの更新・一部中断を伴う更新・置換の3種類に分けています。置換では新しいリソースを先に作り、参照先を付け替えたあとで古いリソースを削除します。データを持つリソースなら、中身は新しいリソースに引き継がれません。
置換を引き起こすプロパティの例:EC2・VPC・RDS・S3
どのプロパティが置換を起こすかは、リソースタイプのスキーマで createOnlyProperties(作成時にしか設定できない値)として定義されています。cfn-lint 1.57.0 に同梱されている東京リージョンのスキーマ(1,591リソースタイプ)から、よく使うリソースの該当プロパティを抜き出しました。
| リソースタイプ | 変更すると置換になる主なプロパティ |
|---|---|
AWS::EC2::Instance |
ImageId/SubnetId/KeyName/AvailabilityZone/PrivateIpAddress |
AWS::EC2::VPC |
CidrBlock |
AWS::EC2::Subnet |
VpcId/CidrBlock/AvailabilityZone |
AWS::RDS::DBInstance |
DBName/MasterUsername/StorageEncrypted/KmsKeyId |
AWS::DynamoDB::Table |
TableName |
AWS::S3::Bucket |
BucketName |
AWS::IAM::Role |
RoleName/Path |
EC2インスタンスの InstanceType はこの一覧に入っていませんが、置換されないとは限りません。EBSをルートボリュームに使うインスタンスでは新旧タイプの互換性によって再起動で済むか置換になるかが決まり、インスタンスストアをルートにするインスタンスは必ず置換されます。一方、KeyName を差し替えるだけでインスタンスは作り直され、既定設定のEBSルートボリュームは旧インスタンスと一緒に削除されます。既存インスタンスとルートボリュームを保持したい場合に、KeyNameの変更を避けるべき理由がこれです。
名前を固定したリソースには別の制約もあります。BucketName や RoleName を明示したリソースは、同じ名前のまま置換が必要な更新をかけると失敗します。置換するには、名前も別の値に変える必要があります。名前を指定しなければCloudFormationがスタック名入りの一意な名前を自動で付けるので、外部から名前で参照される必要がないリソースは名前を書かないほうが更新に強くなります。
SSMパラメータのAMI参照が起こすEC2の置換
前の章のテンプレートで使った AWS::SSM::Parameter::Value 型のパラメータは、スタックの作成時だけでなく更新のたびにParameter Storeの最新値を取り直します。公式ドキュメントには、更新時に「既存の値を使用」を選んでも、それはパラメータのキーを使い続けるという意味であり、CloudFormationは常に最新の値を取得する、と明記されています。
その結果、AWSが新しいAmazon Linux 2023のAMIを公開した後に、たとえばタグを1つ変えるだけの更新をかけると、ImageId の値も変わったと判定され、インスタンスが置換されます。開発環境では最新AMIへの自動追従として便利ですが、状態を持つ本番サーバーでは事故になります。本番ではAMI IDを固定値のパラメータで渡すか、変更セットで AppInstance の Replacement が True になっていないかを毎回確認してください。
DeletionPolicyとUpdateReplacePolicyの違い
リソースを守る属性は2つあり、効く場面が違います。
| 属性 | 効く場面 | 指定できる値 |
|---|---|---|
DeletionPolicy |
スタック削除、テンプレートからの定義削除 | Delete/Retain/RetainExceptOnCreate/Snapshot |
UpdateReplacePolicy |
更新による置換で古いリソースが消えるとき | Delete/Retain/Snapshot |
DeletionPolicyの公式リファレンスは、この属性が更新で物理リソースが置き換わる場合には適用されないと明記しています。DeletionPolicy: Retain だけを書いたS3バケットは、スタック削除からは守られても、置換からは守られません。データを持つリソースには2つを揃えて書くのが原則で、先のテンプレートの LogBucket はそうしています。
既定値にも例外があります。DeletionPolicy を書かなければ削除されるのが原則ですが、AWS::RDS::DBCluster と、DBClusterIdentifier を指定しない AWS::RDS::DBInstance は既定で Snapshot です。S3バケットは中身が空でないと削除できない仕様のため、Retain を付けていないバケットにオブジェクトが残っていると、スタック削除が DELETE_FAILED で止まります。
スタック単位の保護:終了保護・スタックポリシー・ロールバックトリガー
リソースの属性とは別に、スタックそのものに掛ける保護が3つあります。終了保護とスタックポリシーは対象操作を拒否し、ロールバックトリガーはアラーム発報時に進行中の作成・更新を巻き戻します。
| 機能 | 止める操作 | 既定 |
|---|---|---|
| 終了保護 | スタックの削除 | 無効 |
| スタックポリシー | 指定リソースの更新・置換・削除 | 未設定(全更新を許可) |
| ロールバックトリガー | アラーム発報時に作成・更新を巻き戻す | 未設定 |
終了保護は aws cloudformation update-termination-protection --enable-termination-protection --stack-name my-app-prod で有効にします。ネストされたスタックには個別に設定できず、ルートスタックの設定が引き継がれます。ただし、ルートスタックの更新でネストされたスタックの定義を消すと、終了保護があっても削除されます。
スタックポリシーは、一度設定するとスタック内の全リソースが既定で更新不可になるJSONです。全体に Allow を書き、本番DBの論理IDだけに "Effect": "Deny" と "Action": "Update:*"(置換と削除だけなら Update:Replace・Update:Delete)を書く形が基本です。IAMの代わりではなく、更新時の事故防止にだけ効きます。
ロールバックトリガーには、CloudWatchのアラームか複合アラームを最大5個指定します。監視時間は0〜180分で、デプロイ完了後もこの時間内にアラームが ALARM になればスタック操作全体を巻き戻します。更新では監視時間が終わるまで古いリソースの削除が保留されるため、置換を伴う更新を元に戻せる時間を確保できます。
スタックの分け方:ネストされたスタックとスタック間の値の受け渡し
1つのテンプレートに全部を書くと、500リソースの上限より先に、1行の変更でもスタック全体の更新を待つことになります。分け方は「一緒に作って一緒に消すか」で決めます。
| 構成 | 向く場面 | 値の渡し方 |
|---|---|---|
| 1テンプレート・1スタック | 検証環境・小さなシステム | 組み込み関数で直接参照 |
| ネストされたスタック | 共通部品を使い回し、一括で作成・更新・削除する | 親から Parameters、子から Outputs |
| 独立したスタック | ネットワークとアプリなど、更新頻度や担当が違う層 | Fn::ImportValue/Fn::GetStackOutput |
ネストされたスタック(AWS::CloudFormation::Stack)の書き方とpackage
親テンプレートに AWS::CloudFormation::Stack リソースを書くと、別テンプレートを子スタックとして作れます。先のテンプレートを、ネットワーク層の network.yml とEC2の app.yml に分けた場合の親テンプレートは次のとおりです。事前に、network.ymlでEnvNameパラメータとSubnetId出力を、app.ymlでSubnetIdパラメータを定義してください。以下は親側だけの抜粋で、子テンプレートは別途用意する必要があります。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
EnvName:
Type: String
AllowedValues: [dev, prod]
Default: dev
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: network.yml
Parameters:
EnvName: !Ref EnvName
AppStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: app.yml
Parameters:
SubnetId: !GetAtt NetworkStack.Outputs.SubnetId
子スタックの出力は !GetAtt NetworkStack.Outputs.SubnetId の形で受け取り、この参照で AppStack は NetworkStack の完了を待ちます。TemplateURL は本来S3上のURLです。ローカルのファイル名を書いた親テンプレートは、aws cloudformation package --s3-bucket 自分のバケット --template-file root.yml --output-template-file packaged.yml で子テンプレートをS3へアップロードし、URLを書き換えたファイルを作ってから deploy します。
cfn-lint 1.59.0でこの親テンプレートを検査すると、ローカルパスの TemplateURL に対して「package前提の書き方」という警告W3002が出ます。これは意図どおりなので無視してかまいません。一方で、親が渡すパラメータ名と子テンプレートの Parameters が食い違うと、E3043のエラーで「子に無いパラメータを渡している」「子の必須パラメータを渡していない」の両方を指摘します。ローカルパスで書いておくと、デプロイ前にこの照合が効きます。
運用では、更新を必ずルートスタックから始めます。ネストされたスタックのガイドは、更新などの操作を子スタックへ直接行わずルートから行うよう求めており、ルートの更新ではテンプレートが変わった子スタックだけが更新されます。子スタックの1つがロールバック中で止まると、ルートスタックもその完了を待ちます。親子を合わせた1回の操作で扱えるリソースは2,500個までです。
ImportValueとGetStackOutputによるスタック間参照の違い
独立したスタック同士で値を渡す方法は2つあり、参照の強さが違います。
Fn::ImportValue:渡す側がExportで公開した値を、同じアカウント・同じリージョン内で取り込みます。取り込まれている間は値の変更もスタックの削除もできない「強い参照」で、ネットワーク層のVPC IDのように一度作ったらまず変えない値に向きます。Fn::GetStackOutput:2026年5月14日に追加された関数で、StackNameとOutputNameを指定して、Exportの無い出力も読めます。RegionとRoleArnを指定すれば、別リージョンや別アカウントのスタックも参照できます。
Fn::GetStackOutput はリファレンスで「弱い参照」と明記されています。値を読むのはスタックの作成時と更新時だけで、参照先のスタックや出力が後から消えたり変わったりしても、参照する側には何も通知されません。削除を止めてほしい依存には ImportValue、アカウントやリージョンをまたぐ依存には GetStackOutput を使い、後者では参照先スタックに終了保護を付けておきます。なお、短縮形の !GetStackOutput の中に !Ref などの短縮形を入れることはできず、その場合は Fn::GetStackOutput: と完全形で書きます。
コンソールで作った既存リソースをCloudFormation管理へ移す手段
CloudFormationを後から導入する現場では、手作業で作ったリソースがすでに動いています。作り直さずにスタックの管理下へ入れる方法と、入れた後にテンプレートと実物のずれを見張る方法を分けて説明します。
既存リソースの取り込み:IaCジェネレーター・リソースインポート・自動インポート
公式ドキュメントが挙げる取り込み方は次の3つです。テンプレートを一から書く手間が小さい順に並べています。
- IaCジェネレーター(2024年2月5日提供開始):リージョン内のリソースをスキャンし、CloudFormationで管理されていないリソースからテンプレートを生成します。スキャン結果は30日間有効で、1テンプレートに含められるのは500リソースまでです。生成したテンプレートでそのままスタックへ取り込むことも、CDKアプリへ移行することもできます。
- リソースインポート:既存リソースの定義を自分でテンプレートに書き、インポート用の変更セットで取り込みます。取り込むリソースには、値は何でもよいので
DeletionPolicyを必ず付けます。 - 自動インポート:
create-change-setに--import-existing-resourcesを付けると、テンプレートに書いたカスタム名と一致する既存リソースを自動で取り込みます。この場合のDeletionPolicyはRetainかRetainExceptOnCreateに限られます。
どの方法でも、取り込みの時点で DeletionPolicy の指定が必要ですが、指定しただけで削除を防げるわけではありません。手動インポートでは Delete も指定できるため、残す必要があるリソースには Retain を選びます。取り込み後もしばらくは Retain のまま運用し、テンプレートと実物の一致を確かめてから外すのが安全です。
ドリフト検出と2025年11月のドリフト対応変更セット
スタック管理下のリソースをコンソールやCLIで直接変更すると、テンプレートと実物がずれます。これをドリフトと呼び、ドリフト検出はテンプレートやパラメータで明示した期待値と、リソースの実際のプロパティ値を比べます。ただし対応していないリソースは NOT_CHECKED になり、親スタックからの検出ではネストされたスタックの中は調べられません。すべてのリソースの KMSKeyId プロパティも対象外です。
従来の変更セットは「前回のテンプレート」と「新しいテンプレート」しか比べないため、障害対応で手動変更した設定が次のデプロイで上書きされるかどうかを、実行前に知る手段がありませんでした。2025年11月18日に追加されたドリフト対応変更セットは、実際の状態・前回デプロイした状態・新しいテンプレートの3者を比べます。create-change-set に --deployment-mode REVERT_DRIFT を指定して作り、手動変更が上書きされるかどうかを実行前に確認できます。
CloudFormationとTerraform・CDK・SAMの使い分け
AWSのIaCツールとして比較されるのは、Terraform、AWS CDK、AWS SAMの3つです。CDKとSAMは最終的にCloudFormationのテンプレートを生成してCloudFormationでデプロイするので、競合というより上位の書き方です。独立した選択肢として比べるべきはTerraformだけです。
| ツール | 記述 | 状態の保存先 | 対象 |
|---|---|---|---|
| CloudFormation | YAML/JSON | AWS側(スタック) | AWS・拡張経由の外部リソース |
| Terraform | HCL | stateファイル(S3やHCP Terraform) | AWS・Azure・Google Cloud・SaaS |
| AWS CDK | TypeScript・Python等 | CloudFormationのスタック | AWS |
| AWS SAM | YAML(CloudFormationの拡張) | CloudFormationのスタック | AWS(サーバーレス中心) |
実務の分かれ目は状態管理です。CloudFormationはスタックの状態をAWSが保持するので、stateファイルの置き場所やロックを自分で設計する必要がありません。Terraformはstateを自分で管理する代わりに、AWS以外のクラウドやDNS・監視SaaSまで1つのコードで扱えます。
テンプレートが数百行を超えてループや共通化が欲しくなったらAWS CDK、LambdaとAPI Gatewayが中心の構成なら AWS::Serverless::Function などの短縮記法が使えるAWS SAMが向きます。どちらを選んでも、デプロイの実体と置換のルールはCloudFormationのままなので、本記事の変更セットと保護属性の知識はそのまま使えます。
CloudFormationを選ぶべきでないケース
次の条件に当てはまるなら、CloudFormationを主軸にしないほうがよい、というのが筆者の判断です。
- AWSとAzureやGoogle Cloudを併用していて、ネットワークやDNSを1つのコードで管理したい場合。CloudFormationでも拡張を通じてAWS以外を扱えますが、必要なリソースタイプの対応状況や、独自実装の保守負担を確認する必要があります。
- Datadog・PagerDuty・GitHubなど、SaaS側の設定も含めてコード化したい場合。CloudFormationのサードパーティ拡張は対応するSaaSが限られ、ハンドラ操作ごとの課金もかかります。
- チームが既にTerraformのモジュール資産とstate運用を持っている場合。AWSのためだけに2系統のIaCを並走させると、レビューと権限設計が二重になります。
逆に、AWS単独で状態管理の仕組みを自前で持ちたくないチームには、stateファイルの置き場所もロックも設計せずに済むCloudFormation(またはCDK)が合います。既存環境のテンプレート化やスタック分割の設計を外部に任せる場合は、弊社のAWSを中心としたインフラ構築支援でもCloudFormationでの構築を承っています。
複数環境への展開・開発時の高速化・Git連携の関連機能
複数アカウント・複数リージョンへ同じテンプレートを展開するならCloudFormation StackSets、開発環境でのデプロイ待ちを短くしたいなら2026年6月30日に提供が始まったCloudFormation Expressモードが選択肢になります。Expressモードはリソースの安定化を待たずに完了とみなす代わりに、既定でロールバックが無効になる点に注意が必要です。テンプレートの変更をGitへのプッシュから自動デプロイする方法は2つあります。2023年11月26日に提供が始まったGit同期は、GitHub・GitHub Enterprise・GitLab・Bitbucketのリポジトリとスタックを直接つなぎます。承認や複数環境への段階展開を挟みたい場合は、AWS CodePipelineのCloudFormationアクションで変更セットの作成と実行を分ける構成が向きます。
よくある質問
CloudFormationの読み方は?
「クラウドフォーメーション」と読みます。AWSの公式ドキュメントやCLIでは cloudformation、略称として「CFn」も使われます。ファイル名や記事で「CFnテンプレート」とあれば、CloudFormationのテンプレートのことです。なお「cfn-lint」「cfn-signal」のように、関連ツールやヘルパースクリプトの名前にも cfn という接頭辞が使われています。
CloudFormationの利用に料金はかかりますか?
AWS:: で始まるAWSのリソースタイプの管理にはCloudFormation自体の追加料金がかからず、作成したEC2やRDSなどの通常料金を支払います。課金されるのはAWS::*・Alexa::*以外のリソースタイプ(自社作成を含む)とAWS::*以外のカスタムHooksで、ハンドラ操作1回0.0009米ドル、無料枠は月1,000操作です。
テンプレートを自動で作成する方法はありますか?
すでにAWS上にあるリソースからなら、CloudFormationのIaCジェネレーターで生成できます。リージョン内をスキャンし、CloudFormationで管理されていないリソースを選んでテンプレートにします(1テンプレート500リソースまで)。これから作る構成を自動生成したいなら、TypeScriptやPythonで書いてテンプレートを出力するAWS CDKを使う方法もあります。生成されたテンプレートは、デプロイ前にcfn-lintで検査しておくと安心です。
スタックを削除するとリソースはどうなりますか?
スタックが作ったリソースはまとめて削除されます。残したいリソースには DeletionPolicy: Retain を付けます。RDSのDBクラスターと、クラスターに属さないDBインスタンスは既定でスナップショットを取ってから削除されます。中身の残ったS3バケットは削除できず、スタック削除が DELETE_FAILED で止まります。
本番環境のスタックを安全に更新するには?
deploy に --no-execute-changeset を付けて変更セットだけを作り、describe-change-set で各リソースの Replacement を確認してから実行します。DBやバケットには DeletionPolicy と UpdateReplacePolicy の両方に Retain を指定すれば、削除時や置換時に古いリソースを残せます。ただし、置換や新リソースへのデータ移行を制御する設定ではありません。