AWS CloudFormation ハンズオン手順書

v1.0 / 作成日: 2026-06-07 / IaC入門 / VPC + パブリックサブネット × 2 + EC2 × 2(Nginx)/ AWSマネジメントコンソール操作

目次

  1. CloudFormationとは(概念の整理)
  2. テンプレート構造の解説
  3. 前提条件・事前準備
  4. ハンズオン① スタックの作成
    1. テンプレートファイルの準備
    2. コンソールでスタックを作成
    3. イベントでスタック進捗を確認
    4. 作成されたリソースを確認
    5. OutputsでURLを確認・動作確認
  5. ハンズオン② スタックの更新(変更セット)
    1. テンプレートを修正(v2)
    2. 変更セットの作成・確認・実行
  6. ハンズオン③ スタックの削除
  7. 完了チェックリスト

1. CloudFormationとは(概念の整理)

IaC(Infrastructure as Code)とは

インフラの構成をコード(テキストファイル)として記述し、そのコードを実行することでAWSリソースを自動的に作成・変更・削除する仕組みです。

🔁 再現性

同じテンプレートから何度でも同じ構成を再現できます。開発・ステージング・本番を同一テンプレートで管理できます。

📝 変更管理

テンプレートをGitで管理することで「誰が・いつ・何を変えたか」がコードの差分として追跡できます。

🧹 一括削除

スタックを削除するだけで関連するすべてのリソースを一括削除できます。リソースの削除漏れが防げます。

⚡ 自動化

手動でコンソールをポチポチする代わりに、テンプレートを流すだけで数十個のリソースを一括作成できます。

CloudFormationの主要概念

用語説明例え
テンプレート作成するリソースを定義したYAML/JSONファイル設計図・レシピ
スタックテンプレートから作成されたリソースのグループ設計図から建てた建物
リソーススタックに含まれるAWSのリソース(VPC・EC2など)建物の各部屋・設備
パラメータースタック作成時に外から渡せる変数建物の広さを注文時に指定
アウトプットスタック作成後に参照できる値(IPなど)完成後の住所・電話番号
変更セットスタック更新前に「何が変わるか」をプレビューリフォーム前の見積もり
ドリフトコンソールで手動変更されテンプレートとズレた状態設計図と実際の建物が違う状態

今回のハンズオンで学ぶこと

学習内容

① テンプレート(YAML)の構造を理解する

② コンソールからスタックを作成しリソースが一括作成されることを体験する

③ 変更セットを使ってスタックを安全に更新する方法を学ぶ

④ スタック削除で全リソースが一括削除されることを体験する

作成するインフラ構成

リソース論理ID(テンプレート内の名前)設定値
VPCVPC10.0.0.0/16 / DNS有効
Internet GatewayInternetGatewayVPCにアタッチ
パブリックサブネット(1a)PublicSubnet1a10.0.1.0/24 / パブリックIP自動割り当て
パブリックサブネット(1c)PublicSubnet1c10.0.2.0/24 / パブリックIP自動割り当て
ルートテーブルPublicRouteTable0.0.0.0/0 → IGW
セキュリティグループWebServerSGHTTP:80 from 0.0.0.0/0
IAMロールEC2SSMRoleAmazonSSMManagedInstanceCore(Session Manager用)
EC2(WebServer1)WebServer1t2.micro / 1a / Nginx自動インストール
EC2(WebServer2)WebServer2t2.micro / 1c / Nginx自動インストール

合計 12個 のリソースをテンプレート1つで一括作成します。

2. テンプレート構造の解説

テンプレートの骨格(YAML)

AWSTemplateFormatVersion: '2010-09-09' # 固定値。必ず記載 Description: 'テンプレートの説明' Parameters: # ★ スタック作成時に入力できる変数(任意) InstanceType: Type: String Default: t2.micro Resources: # ★ 作成するリソース(唯一の必須セクション) MyVPC: # 論理ID(テンプレート内での名前) Type: AWS::EC2::VPC # リソースタイプ Properties: CidrBlock: 10.0.0.0/16 Outputs: # ★ 作成後に表示・参照できる値(任意) VPCId: Value: !Ref MyVPC

主要な組み込み関数(Intrinsic Functions)

関数記述例説明
!Ref !Ref PublicSubnet1a リソースのIDまたはパラメーターの値を返す。EC2の !Ref はインスタンスID、サブネットの !Ref はサブネットIDを返す
!Sub !Sub ${EnvironmentName}-vpc 文字列内の変数(${変数名})を展開する。命名規則に便利
!GetAtt !GetAtt WebServer1.PublicIp リソースの属性値を取得する。EC2の PublicIp や DNSName など
Fn::Base64 Fn::Base64: !Sub |
  #!/bin/bash...
文字列をBase64エンコード。EC2のUserDataに使用
DependsOn DependsOn: IGWAttachment 明示的な依存関係を宣言。CFnが自動で依存解決できない場合に使用

リソースタイプの命名規則

リソースタイプは AWS::サービス名::リソース種別 の形式です。

リソースタイプ作成されるリソース
AWS::EC2::VPCVPC
AWS::EC2::Subnetサブネット
AWS::EC2::SecurityGroupセキュリティグループ
AWS::EC2::InstanceEC2インスタンス
AWS::IAM::RoleIAMロール
AWS::RDS::DBInstanceRDSインスタンス
AWS::ElasticLoadBalancingV2::LoadBalancerALB

今回使用するテンプレートのポイント解説

AMI IDをSSMパラメーターで自動取得
# AMI IDをハードコードせず、SSM Parameter StoreからAmazon Linux 2023の最新AMIを取得
ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}'
メリットAMI IDはリージョンやAMIの更新で変わりますが、このSSMパラメーターを使うと常に最新のAmazon Linux 2023 AMIが自動的に使われます。
UserDataでEC2起動時にNginxを自動インストール
UserData:
  Fn::Base64: !Sub |
    #!/bin/bash
    dnf update -y
    dnf install -y nginx
    systemctl enable nginx
    systemctl start nginx
    echo "<h1>${EnvironmentName} - WebServer 1</h1>" \
      > /usr/share/nginx/html/index.html
UserDataとはEC2インスタンスの初回起動時に自動実行されるシェルスクリプトです。Fn::Base64 でBase64エンコードして渡します。!Sub でパラメーターの値(${EnvironmentName})を埋め込めます。
DependsOnで依存関係を明示
PublicRoute:
  Type: AWS::EC2::Route
  DependsOn: IGWAttachment  # IGWがVPCにアタッチされてからルート作成
  Properties:
    RouteTableId: !Ref PublicRouteTable
    DestinationCidrBlock: 0.0.0.0/0
    GatewayId: !Ref InternetGateway
なぜ必要かIGWがVPCにアタッチされる前にルートを作成しようとするとエラーになります。CFnは通常 !Ref!GetAtt で自動依存解決しますが、この場合はアタッチメントが間接的な依存のため DependsOn で明示します。

3. 前提条件・事前準備

項目内容
AWSアカウントAdministratorAccess権限を持つIAMユーザー
リージョン東京(ap-northeast-1)― コンソール右上で必ず確認
ブラウザChrome / Edge / Firefox(最新版)
テンプレートファイルcfn-handson.yaml(本手順書と同梱)と cfn-handson-v2.yaml(更新用)
コスト目安EC2 t2.micro × 2台(無料枠対象)。数時間で削除すれば費用はほぼ0円です。

4. ハンズオン① スタックの作成

CFn

4-1. テンプレートファイルの準備

本手順書と同梱の cfn-handson.yaml をローカルPC上に保存します。テンプレートの全内容は以下のとおりです(コピー&貼り付けも可能です):

AWSTemplateFormatVersion: '2010-09-09'
Description: 'CFn Handson v1 - VPC + Public Subnets + EC2 x2 (Nginx)'

Parameters:
  EnvironmentName:
    Type: String
    Default: handson
    Description: リソース名のプレフィックス

  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t2.small
      - t3.micro

Resources:

  # VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-vpc

  # Internet Gateway
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-igw

  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # Subnets
  PublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-subnet-public-1a

  PublicSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-subnet-public-1c

  # Route Table
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-rt-public

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: IGWAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  Subnet1aRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1a
      RouteTableId: !Ref PublicRouteTable

  Subnet1cRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1c
      RouteTableId: !Ref PublicRouteTable

  # Security Group
  WebServerSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Web server security group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-sg-web

  # IAM Role (Session Manager)
  EC2SSMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${EnvironmentName}-ec2-ssm-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2SSMRole

  # EC2 Instance 1 (1a)
  WebServer1:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}'
      InstanceType: !Ref InstanceType
      SubnetId: !Ref PublicSubnet1a
      SecurityGroupIds:
        - !Ref WebServerSG
      IamInstanceProfile: !Ref EC2InstanceProfile
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-web-1a
        - Key: Version
          Value: v1
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          dnf update -y
          dnf install -y nginx
          systemctl enable nginx
          systemctl start nginx
          echo "<h1>${EnvironmentName} - WebServer 1 (1a) - v1</h1>" \
            > /usr/share/nginx/html/index.html

  # EC2 Instance 2 (1c)
  WebServer2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}'
      InstanceType: !Ref InstanceType
      SubnetId: !Ref PublicSubnet1c
      SecurityGroupIds:
        - !Ref WebServerSG
      IamInstanceProfile: !Ref EC2InstanceProfile
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-web-1c
        - Key: Version
          Value: v1
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          dnf update -y
          dnf install -y nginx
          systemctl enable nginx
          systemctl start nginx
          echo "<h1>${EnvironmentName} - WebServer 2 (1c) - v1</h1>" \
            > /usr/share/nginx/html/index.html

Outputs:
  VPCId:
    Description: 作成されたVPCのID
    Value: !Ref VPC
    Export:
      Name: !Sub ${EnvironmentName}-VpcId

  WebServer1IP:
    Description: WebServer1のパブリックIP
    Value: !GetAtt WebServer1.PublicIp

  WebServer2IP:
    Description: WebServer2のパブリックIP
    Value: !GetAtt WebServer2.PublicIp

  WebServer1URL:
    Description: WebServer1のURL
    Value: !Sub http://${WebServer1.PublicIp}

  WebServer2URL:
    Description: WebServer2のURL
    Value: !Sub http://${WebServer2.PublicIp}

4-2. コンソールでスタックを作成

1
テンプレートの指定
項目
テンプレートソーステンプレートファイルのアップロード
ファイルcfn-handson.yaml を選択してアップロード
次へ
テンプレートデザイナー「デザイナーで表示」をクリックするとテンプレートのリソース関係図が視覚的に確認できます(任意)。
2
スタックの詳細(パラメーターの入力)
項目
スタック名cfn-handson-stack
EnvironmentName(パラメーター)handson(デフォルトのまま)
InstanceType(パラメーター)t2.micro(デフォルトのまま)
パラメーターとはテンプレートの Parameters セクションで定義した変数がここに表示されます。スタック作成時に値を変えることで同じテンプレートから異なる構成を作れます。
次へ
3
スタックオプション

このページはデフォルトのまま変更不要です。

次へ
4
確認と作成(IAM承認)

ページ最下部の 「機能」 セクションにある以下のチェックボックスにチェックを入れます:

必須「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」
にチェックを入れないとスタックの作成ができません。テンプレートに AWS::IAM::Role が含まれているためです。
送信(スタックの作成を開始)

4-3. イベントでスタック進捗を確認

1
イベントタブで作成状況をリアルタイム確認

各リソースの作成状況が時系列で表示されます。全リソースが CREATE_COMPLETE になるまで待ちます(約3〜5分)。

ステータス意味
CREATE_IN_PROGRESSリソース作成中
CREATE_COMPLETEリソース作成完了
CREATE_FAILED作成失敗 → 「理由」列でエラー内容を確認
ROLLBACK_COMPLETE失敗後にロールバック完了
ロールバック機能一部のリソースが作成に失敗すると、CFnは作成済みのリソースも含めてすべてを自動的にロールバック(削除)します。これにより中途半端な状態が残りません。

4-4. 作成されたリソースを確認

1
「リソース」タブで一覧確認

テンプレートに定義した12個のリソースが一覧表示されます。各行の「物理ID」をクリックするとそのリソースのコンソールページに直接ジャンプできます。

論理ID(テンプレートの名前)リソースタイプ確認内容
VPCAWS::EC2::VPChandson-vpc が作成されているか
WebServer1AWS::EC2::Instancehandson-web-1a が「実行中」か
WebServer2AWS::EC2::Instancehandson-web-1c が「実行中」か
EC2SSMRoleAWS::IAM::Rolehandson-ec2-ssm-role が作成されているか
ポイント手動でコンソールを操作した場合は各サービスのコンソールを個別に確認する必要がありますが、CFnの「リソース」タブなら1画面ですべてのリソースを把握できます。

4-5. OutputsでURLを確認・動作確認

1
「出力」タブでEC2のURLを確認

テンプレートの Outputs セクションで定義した値が表示されます:

キー表示される値
WebServer1URLhttp://xx.xx.xx.xx(WebServer1のURL)
WebServer2URLhttp://yy.yy.yy.yy(WebServer2のURL)
WebServer1IPWebServer1のパブリックIPアドレス
WebServer2IPWebServer2のパブリックIPアドレス
VPCId作成されたVPCのID(vpc-xxxxxxxx)
2
ブラウザでNginxの動作確認

WebServer1URL の値(http://xx.xx.xx.xx)をブラウザで開きます。

成功「handson - WebServer 1 (ap-northeast-1a) - v1」と表示されればNginxのインストールと起動が成功しています。UserDataが正常に実行されました。
表示されない場合EC2起動後UserDataの実行に2〜3分かかります。しばらく待ってからリロードしてください。

5. ハンズオン② スタックの更新(変更セット)

CFn

テンプレートを修正してスタックを更新します。変更セット(Change Set) を使うことで、実際に更新を適用する前に「何が変わるか」を安全に確認できます。

5-1. v2テンプレートの変更点を確認

同梱の cfn-handson-v2.yaml では以下の3箇所を変更しています:

変更箇所変更種別内容インスタンス置き換え
WebServerSG のインバウンドルール 変更 HTTPS(TCP:443)のルールを追加 なし(インプレース更新)
WebServer1 / WebServer2 のタグ 変更 Version: v1Version: v2 なし(インプレース更新)
Outputs に StackVersion を追加 追加 新しい出力値 v2 を追加
インプレース更新 vs 置き換え(Replacement)CFnのリソース更新には2種類あります。セキュリティグループのルール変更やタグ変更は「インプレース更新」(既存リソースをそのまま変更)ですが、EC2のInstanceTypeやAMI変更は「Replacement」(旧インスタンスを削除して新インスタンスを作成)になります。変更セットで必ず事前確認しましょう。

5-2. 変更セットの作成・確認・実行

1
変更セットを作成
項目
テンプレートソーステンプレートファイルのアップロード
ファイルcfn-handson-v2.yaml を選択してアップロード
次へ

パラメーターはデフォルトのまま → 次へ → オプションはデフォルトのまま → 次へ

IAM承認のチェックを入れて → 変更セット名 cfn-handson-changeset-v2 を入力 → 変更セットの作成

2
変更内容をプレビューして確認

変更セット一覧 → cfn-handson-changeset-v2 を選択 → 「変更」タブで変更内容を確認します:

アクション論理IDリソースタイプ置き換え
ModifyWebServerSGAWS::EC2::SecurityGroupFalse(置き換えなし)
ModifyWebServer1AWS::EC2::InstanceFalse(置き換えなし)
ModifyWebServer2AWS::EC2::InstanceFalse(置き換えなし)
置き換え(Replacement)がTrueの場合は要注意「置き換え: True」になっているリソースは削除→再作成されます。EC2なら既存のデータが失われます。本番環境では必ずこの確認を行ってください。
3
変更セットを実行

内容を確認したら 変更セットの実行 をクリックします。

スタックのステータスが UPDATE_IN_PROGRESSUPDATE_COMPLETE に変わることを確認します。

「出力」タブに StackVersion: v2 が追加されていれば更新成功です。

変更セットのメリット変更セットは「実行」するまで実際のリソースには何も影響しません。確認後に不要と判断した場合は「削除」することもできます。本番環境での安全な更新フローとして活用してください。

6. ハンズオン③ スタックの削除

CFn

CFnの最大の利点のひとつ:スタックを削除するだけで作成したすべてのリソースが自動的に削除されます。

1
スタックを削除

「スタックの削除」ダイアログで 削除 をクリックします。

ステータスが DELETE_IN_PROGRESSDELETE_COMPLETE(リストから消える)に変わります。

削除される全リソース(12個) EC2 × 2、IAMロール、IAMインスタンスプロフィール、セキュリティグループ、サブネット × 2、ルートテーブル、ルート × 1、サブネットアソシエーション × 2、IGWアタッチメント、Internet Gateway、VPC
注意削除には約3〜5分かかります。ステータスが「DELETE_COMPLETE」になるまで待ちます。
2
リソースが削除されたことを確認

EC2コンソール → 「インスタンス」で handson-web-1ahandson-web-1c が「終了済み」になっていることを確認します。

VPCコンソール → 「お使いのVPC」で handson-vpc が削除されていることを確認します。

体験のポイント手動でリソースを作成した場合、EC2 → ALB → ターゲットグループ → セキュリティグループ → サブネット → ルートテーブル → IGW → VPC と順番に個別削除する必要がありましたが、CFnではスタック削除の1操作ですべて完了します。

ドリフト検出(オプション:削除前に試してみよう)

スタック削除前に、わざとEC2のタグを手動で変更した後にドリフト検出を実行することで、CFnとAWSの実態のズレを検出できます。

1
ドリフトを発生させる

EC2コンソール → handson-web-1a → タグ を編集 → Version タグの値を v2v99 に変更します。

2
ドリフト検出を実行

検出完了後 → スタックアクション → 「ドリフトの表示」で WebServer1「MODIFIED」(修正済み)と表示されることを確認します。

ドリフトとはCFnテンプレートで定義した状態と、実際のAWSリソースの状態がズレていることです。本番環境では定期的にドリフト検出を実行し、手動変更がないか監視することを推奨します。

7. 完了チェックリスト

概念の理解

ハンズオン①:スタック作成

ハンズオン②:スタック更新

ハンズオン③:スタック削除