AWS CloudFormationでインフラをコード管理する方法|テンプレートの基本構造とマルチAZ構成パターン入門

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > AWS CloudFormationでインフラをコード管理する方法|テンプレートの基本構造とマルチAZ構成パターン入門
「AWSのリソースをマネジメントコンソールで手作業で作り続けていると、本番と検証で設定が違う、先月誰かが触って壊れた、復旧しようにも手順書がない——そんな問題が積み重なっていませんか?」
AWS CloudFormationはAWSが公式に提供するInfrastructure as Code(IaC)ツールです。YAMLまたはJSON形式のテンプレートファイルにAWSリソースを宣言的に記述することで、VPCからEC2・ALBに至るまで、複雑なマルチAZ構成を再現性高く構築できます。

この記事では、CloudFormationテンプレートの基本構造から、VPC・サブネット・ALB・Auto Scalingを含むマルチAZ構成のテンプレート化、aws cloudformationコマンドによるスタック操作まで、実機確認結果とともに解説します。Amazon Linux 2023 / RHEL 9.4環境で動作確認済みです。

この記事のポイント

・CloudFormationのテンプレートはResources(必須)・Parameters・Outputsの3セクションが基本
・VPC + マルチAZサブネット + ALBの冗長構成は1つのYAMLテンプレートで定義できる
・aws cloudformation deployコマンドで変更セットを確認してから安全に更新できる
・スタック削除時の誤消去はDeletionPolicyとTerminationProtectionで防ぐ


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜCloudFormationでインフラをコード化するのか

AWS環境をマネジメントコンソールから手動で構築し続けていると、時間とともに必ず以下の問題が発生します。

環境の差異:本番と検証で設定値が微妙に違う。誰かが手動で変更を加えた記録がない
復旧の困難さ:インスタンス障害や誤削除が起きたとき、同じ構成を再現するのに時間がかかる
引き継ぎの問題:どのリソースをどの設定で作ったかが口頭や手順書にしかない

CloudFormationはこれらをテンプレートファイル1枚で解決します。Gitで管理すれば変更履歴が残り、CI/CDパイプラインに組み込めば自動デプロイも可能になります。

TerraformとCloudFormationの使い分けについては、「AWS専用で完結するならCloudFormation、マルチクラウドやAWS以外のリソース(GitHub・Datadog等)も一緒に管理したいならTerraform」が判断の軸です。AWSネイティブツールとして、CloudFormationはコンソールとの統合(スタックセット・ドリフト検出等)が強みです。

CloudFormationテンプレートの基本構造

1. 必須セクション(Resources)と任意セクション

CloudFormationテンプレートのセクション構成を以下に示します。Resourcesだけが必須で、他は任意です。

# CloudFormationテンプレートの基本骨格(YAML形式) AWSTemplateFormatVersion: '2010-09-09' # バージョン宣言(固定値) Description: "VPC + マルチAZ サブネット + ALB の基本構成" # 任意の説明文 Parameters: # 実行時に渡す可変値(任意) Environment: Type: String Default: production AllowedValues: [production, staging, development] Resources: # AWSリソースの定義(必須・このセクションのみ必須) VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 Outputs: # スタック出力値(任意) VpcId: Value: !Ref VPC Export: Name: !Sub "${AWS::StackName}-VpcId"

2. Parametersで可変値を外出しする

環境ごとに変わる値(CIDRブロック、インスタンスタイプ等)はParametersに切り出すと、テンプレートを変更せずに本番/検証を使い分けられます。

Parameters: VpcCidr: Type: String Default: 10.0.0.0/16 Description: "VPCのCIDRブロック" InstanceType: Type: String Default: t3.micro AllowedValues: [t3.micro, t3.small, t3.medium] Description: "EC2インスタンスタイプ" LatestAMI: Type: AWS::SSM::Parameter::Value Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 # SSMパラメータストアから最新のAmazon Linux 2023 AMI IDを自動取得

3. Outputsでリソース情報を出力・スタック間で共有する

Outputsに記述した値は、マネジメントコンソールから確認できるほか、!ImportValueを使って別のスタックから参照できます。ネットワーク層(VPC)とアプリ層(EC2/ALB)をスタックで分離するときに活用します。

Outputs: VpcId: Description: "作成されたVPCのID" Value: !Ref VPC Export: Name: !Sub "${AWS::StackName}-VpcId" PublicSubnet1Id: Value: !Ref PublicSubnet1 Export: Name: !Sub "${AWS::StackName}-PublicSubnet1Id" # 別スタックからの参照例(アプリ層スタックのテンプレート内で使用) # SubnetId: !ImportValue "network-stack-PublicSubnet1Id"

4. 覚えておくべき組み込み関数

テンプレート内でリソースIDを参照したり文字列を組み立てたりするために組み込み関数を使います。

!Ref:リソースのIDを参照する(例: !Ref VPC → VPCのID)
!Sub:文字列内に変数を埋め込む(例: !Sub "${AWS::StackName}-vpc"
!GetAtt:リソースの特定属性を取得する(例: !GetAtt ALB.DNSName
!Select:配列から要素を選択する(例: !Select [0, !GetAZs '']
!GetAZs:リージョンのAZ一覧を取得する(!GetAZs ''で現在のリージョンのAZ配列)

VPCとマルチAZサブネット構成をテンプレートで定義する

1. VPC・InternetGateway・サブネットの定義

ap-northeast-1a / ap-northeast-1c の2AZにパブリックサブネット(ALB用)とプライベートサブネット(EC2用)を配置します。!Select [0, !GetAZs '']で現在のリージョンの最初のAZを動的に取得するため、リージョンを変えてもテンプレートを修正する必要がありません。

Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCidr EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub "${Environment}-vpc" InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${Environment}-igw" VPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway # パブリックサブネット(AZ-a) PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: 10.0.1.0/24 AvailabilityZone: !Select [0, !GetAZs ''] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "${Environment}-public-1a" # パブリックサブネット(AZ-c) PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: 10.0.2.0/24 AvailabilityZone: !Select [1, !GetAZs ''] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "${Environment}-public-1c" # プライベートサブネット(AZ-a) PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: 10.0.10.0/24 AvailabilityZone: !Select [0, !GetAZs ''] # プライベートサブネット(AZ-c) PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: 10.0.20.0/24 AvailabilityZone: !Select [1, !GetAZs '']

2. ルートテーブルとNATゲートウェイの設定

プライベートサブネットのEC2が外部通信するためにNATゲートウェイをAZ-aに配置します。

NatEIP: Type: AWS::EC2::EIP DependsOn: VPCGatewayAttachment Properties: Domain: vpc NatGateway: Type: AWS::EC2::NatGateway Properties: SubnetId: !Ref PublicSubnet1 AllocationId: !GetAtt NatEIP.AllocationId # パブリックルートテーブル(IGW経由) PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PublicRoute: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable # プライベートルートテーブル(NAT経由) PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PrivateRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTable PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTable

ALBとAuto ScalingグループでマルチAZ冗長化する

1. セキュリティグループの定義

ALB用(インターネットからHTTP/HTTPSを受け付ける)とEC2用(ALBからのトラフィックのみ許可)の2層のセキュリティグループを定義します。

# ALBセキュリティグループ ALBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "ALB: HTTP/HTTPS inbound from internet" VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 # EC2セキュリティグループ(ALBからのみ許可) EC2SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "EC2: HTTP inbound from ALB only" VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Ref ALBSecurityGroup

2. ALBとターゲットグループのテンプレート定義

ALBをパブリックサブネット2AZに配置し、ヘルスチェックを設定します。HealthCheckPathには実際に200を返すエンドポイントを指定してください。

# ターゲットグループ TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: VpcId: !Ref VPC Protocol: HTTP Port: 80 TargetType: instance HealthCheckPath: /health HealthCheckIntervalSeconds: 30 HealthyThresholdCount: 2 UnhealthyThresholdCount: 3 HealthCheckTimeoutSeconds: 5 # ALB本体(2AZのパブリックサブネットに配置) ApplicationLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Type: application Scheme: internet-facing Subnets: - !Ref PublicSubnet1 # ap-northeast-1a - !Ref PublicSubnet2 # ap-northeast-1c SecurityGroups: - !Ref ALBSecurityGroup # リスナー(HTTP 80番) ALBListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: LoadBalancerArn: !Ref ApplicationLoadBalancer Protocol: HTTP Port: 80 DefaultActions: - Type: forward TargetGroupArn: !Ref TargetGroup

3. Auto ScalingグループのマルチAZ配置

VPCZoneIdentifierに2AZのプライベートサブネットを指定することで、EC2インスタンスが複数AZに自動分散されます。1つのAZが障害になっても、残りのAZのインスタンスでサービスを継続できます。

# 起動テンプレート LaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateData: ImageId: !Ref LatestAMI InstanceType: !Ref InstanceType SecurityGroupIds: - !Ref EC2SecurityGroup UserData: Fn::Base64: | #!/bin/bash dnf update -y dnf install -y httpd systemctl start httpd systemctl enable httpd echo "

$(hostname -f)

" > /var/www/html/health # Auto Scalingグループ(2AZに分散) AutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup Properties: LaunchTemplate: LaunchTemplateId: !Ref LaunchTemplate Version: !GetAtt LaunchTemplate.LatestVersionNumber MinSize: '2' MaxSize: '6' DesiredCapacity: '2' VPCZoneIdentifier: - !Ref PrivateSubnet1 # ap-northeast-1a - !Ref PrivateSubnet2 # ap-northeast-1c TargetGroupARNs: - !Ref TargetGroup HealthCheckType: ELB HealthCheckGracePeriod: 300

AWSのVPC設計・マルチAZ冗長化をより体系的に学びたい方はAWSマルチAZ冗長設計の実践ガイドもあわせてご確認ください。

aws cloudformationコマンドでスタックを操作する

1. テンプレートの検証とスタック作成

デプロイ前にvalidate-templateで構文チェックを行います。構文エラーは検出できますが、IAMパーミッションや実際のリソース作成可否は確認できません。

# テンプレートの構文チェック $ aws cloudformation validate-template --template-body file://vpc-multiaz.yaml { "Parameters": [...], "Description": "VPC + マルチAZ サブネット + ALB の基本構成" } # エラーがなければテンプレート情報が返る # スタックの新規作成 $ aws cloudformation create-stack --stack-name production-vpc --template-body file://vpc-multiaz.yaml --parameters ParameterKey=Environment,ParameterValue=production --capabilities CAPABILITY_IAM { "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/production-vpc/a1b2c3d4-1234-5678-abcd-ef0123456789" } # 作成完了まで待機 $ aws cloudformation wait stack-create-complete --stack-name production-vpc # 状態確認 $ aws cloudformation describe-stacks --stack-name production-vpc --query 'Stacks[0].StackStatus' --output text CREATE_COMPLETE

2. 安全な更新(deployコマンドとChangeSet)

スタックの更新にはdeployコマンドが便利です。内部でChangeSetを作成して差分を確認してから適用するため、意図しない変更を防げます。

# deployコマンドで更新(変更がなければスキップ) $ aws cloudformation deploy --template-file vpc-multiaz.yaml --stack-name production-vpc --parameter-overrides Environment=production --capabilities CAPABILITY_IAM --no-execute-changeset # まずChangeSetだけ作成して確認 # 変更内容を確認 $ aws cloudformation describe-change-set --stack-name production-vpc --change-set-name $(aws cloudformation list-change-sets --stack-name production-vpc --query 'Summaries[0].ChangeSetName' --output text) --query 'Changes[*].[ResourceChange.Action,ResourceChange.LogicalResourceId,ResourceChange.ResourceType]' --output table +----------+------------------+---------------------------------------+ | Action | LogicalResourceId| ResourceType | +----------+------------------+---------------------------------------+ | Modify | TargetGroup | AWS::ElasticLoadBalancingV2::TargetGrp| +----------+------------------+---------------------------------------+ # 内容を確認してからChangeSetを実行 $ aws cloudformation execute-change-set --stack-name production-vpc --change-set-name [ChangeSet名]

3. スタック削除と保護設定(DeletionPolicy・TerminationProtection)

本番スタックの誤削除を防ぐ2つの仕組みを必ず設定してください。設定を忘れると一度のコマンドミスで本番リソースが全消去されるため、【注意】デプロイ直後に必ず確認する習慣をつけてください。

TerminationProtection:スタック自体の削除操作をブロックする(CLIからでも削除できなくなる)
DeletionPolicy: Retain:スタックを削除してもリソースを残す(RDSやS3バケットに設定する)

# TerminationProtectionを有効化 $ aws cloudformation update-termination-protection --stack-name production-vpc --enable-termination-protection # テンプレートでDeletionPolicyを設定する例(RDSの場合) # DBInstance: # Type: AWS::RDS::DBInstance # DeletionPolicy: Retain # スタック削除時もDBを残す # Properties: ... # 保護を解除してからスタックを削除する(本番環境では慎重に) $ aws cloudformation update-termination-protection --stack-name production-vpc --no-enable-termination-protection $ aws cloudformation delete-stack --stack-name production-vpc $ aws cloudformation wait stack-delete-complete --stack-name production-vpc

よくあるエラーと対処法

「ROLLBACK_COMPLETE」でスタックが停止する

最も頻発するエラーです。スタック作成中にリソース作成が失敗するとロールバックが走り、最終的にROLLBACK_COMPLETE状態になります。この状態のスタックは更新できないため、いったん削除して作り直す必要があります。

# 失敗原因をイベントログで確認する $ aws cloudformation describe-stack-events --stack-name production-vpc --query 'StackEvents[?ResourceStatus==`CREATE_FAILED`].[LogicalResourceId,ResourceStatusReason]' --output table +--------------------+------------------------------------------------+ | NatGateway | Elastic IP limit exceeded. | +--------------------+------------------------------------------------+ # → EIPの上限に達している。AWSサポートにクォータ引き上げを申請 # ROLLBACK_COMPLETEのスタックを削除して再作成 $ aws cloudformation delete-stack --stack-name production-vpc $ aws cloudformation wait stack-delete-complete --stack-name production-vpc $ aws cloudformation create-stack ...

「InsufficientCapabilitiesException」が出る

IAMリソース(Role・Policyなど)を含むテンプレートをデプロイする際に発生します。IAMリソースの変更を意図的に承認するための--capabilitiesフラグが必要です。

# エラー例 # An error occurred (InsufficientCapabilitiesException) when calling # the CreateStack operation: Requires capabilities : [CAPABILITY_IAM] # 対処: --capabilities フラグを追加する $ aws cloudformation create-stack --stack-name my-stack --template-body file://template.yaml --capabilities CAPABILITY_IAM # カスタム名でないIAMリソース向け # または # --capabilities CAPABILITY_NAMED_IAM # カスタム名付きIAMリソースを含む場合

「Circular dependency detected」エラー

リソース間の依存関係が循環していると発生します。またDependsOnが不足していてリソースが依存先よりも先に作成されようとする場合も失敗します。DependsOn属性で依存関係を明示することで解決します。

# パブリックルートをIGWアタッチ後に作成する(DependsOn例) PublicRoute: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment # IGWアタッチ後に作成する Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway

デプロイ後にEC2インスタンスのポートが想定通りに開いているか確認する際は、ssコマンドとlsofでポートを確認する方法が役立ちます。ALBのDNS名の名前解決確認にはLinux DNS設定の基本(resolv.conf・nmcli)も参照してください。

本記事のまとめ

やりたいこと コマンド / テンプレート要素
テンプレートの構文チェック aws cloudformation validate-template --template-body file://template.yaml
スタックを新規作成する aws cloudformation create-stack --stack-name NAME --template-body file://template.yaml --capabilities CAPABILITY_IAM
スタックを安全に更新する aws cloudformation deploy --template-file template.yaml --stack-name NAME --capabilities CAPABILITY_IAM
スタックの状態を確認する aws cloudformation describe-stacks --stack-name NAME --query 'Stacks[0].StackStatus'
エラー原因を調査する aws cloudformation describe-stack-events --stack-name NAME
誤削除を防止する aws cloudformation update-termination-protection --stack-name NAME --enable-termination-protection
マルチAZにサブネットを分散配置する AvailabilityZone: !Select [0, !GetAZs ''](テンプレート内)
スタック削除後もリソースを残す DeletionPolicy: Retain(テンプレート内)
別スタックからVPC IDを参照する !ImportValue "${StackName}-VpcId"(テンプレート内)
CloudFormationは最初のテンプレート作成に少し手間がかかりますが、一度テンプレート化してしまえば本番・検証・DR環境を同じ品質で素早く立ち上げられます。「手順書が頭の中にある」「マネジメントコンソールで手作業」という状態から抜け出すのに最短の道です。

AWSのVPC設計・マルチAZ冗長化をより体系的に学びたい方はAWSマルチAZ冗長設計の実践ガイドもご確認ください。

CloudFormationのYAMLは書ける——でも「本番で安全に変更できるか」が問題です

aws cloudformation deployのコマンドは調べれば分かります。でも「ChangeSetを見てどこが変わるか判断できるか」「ROLLBACK_COMPLETEが出たとき冷静に対処できるか」を、自信を持って判断できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。