🚀

【初心者向け】AWS CloudFormation 入門!完全ガイド

2024/03/23に公開

AWS CloudFormation

☘️ はじめに

本ページは、AWS に関する個人の勉強および勉強会で使用することを目的に、AWS ドキュメントなどを参照し作成しておりますが、記載の誤り等が含まれる場合がございます。

最新の情報については、AWS 公式ドキュメントをご参照ください。

👀 Contents

CloudFormation とは

リソースを簡単にモデル化、プロビジョニング、管理することができる Infrastructure as Code (IaC) サービスです。
リソースをテンプレート(JSON または YAML 形式)で管理することができます。
CloudFormation 自体は基本的には無料で利用できますが、プロビジョニングしたリソースにはコストが発生します。(料金については下記を参照してください。)

【AWS Black Belt Online Seminar】AWS CloudFormation(YouTube)(0:56:03)

cfn_1

【AWS Black Belt Online Seminar】AWS CloudFormation deep dive(YouTube)(1:06:10)

cfn_2

【AWS Black Belt Online Seminar】AWS CloudFormation#1 基礎編(YouTube)(0:15:15)

cfn_3

【AWS Black Belt Online Seminar】AWS CloudFormation CloudFormation レジストリ編(YouTube)(0:23:43)

cfn_4

【AWS Black Belt Online Seminar】AWS CloudFormation よくあるユースケースと質問編(YouTube)(0:29:05)

cfn_5

AWS CloudFormation サービス概要

AWS CloudFormation ドキュメント

AWS CloudFormation よくある質問

AWS CloudFormation の料金

コンソールでの表示

テンプレート管理してリリースしたものはこのような画面で確認することができます。

cfn-console

エラーがあった場合はこのようにエラー内容が表示されます。「根本原因を検出」ボタンを押すと、原因となったエラーメッセージ部分にフォーカスされます。

cfn-console-error

基本用語

  • テンプレート
    • リソースの設定などを記述した YAML(xxx.yaml) か JSON(xxx.json)のテキストファイルのことです。
    • ※ 本ドキュメントでは、基本的に YAML で表記します。
  • スタック
    • テンプレートファイルと1:1となるリソースのコレクションです。
    • 後述するテンプレートのネストを使うことで複数テンプレートをまとめて管理することもできます。
    • スタックの単位でリソースを管理します。
  • テンプレートのアップロード
    • スタックを新規作成する、または更新するときにテンプレートファイルをローカルから送信することです。
    • S3 バケットに保存したものを指定または、コンソールから直接アップロードすることができます。
    • Git と同期
      • Nov 26, 2023 に使えるようになった機能で、従来のアップロードではなく Git リポジトリと同期して、Push したら自動的にスタックが更新されるようにできます。

テンプレートの構成

テンプレートファイルは、以下の構成になっています。

AWSTemplateFormatVersion:
Description:
Metadata:
Parameters:
Conditions:
Mappings:
Resources:
Outputs:

【<font color="red">必須</font>】AWSTemplateFormatVersion

ここは固定です。2024 年 3 月時点で有効な値は 2010-09-09 のみです。
詳しくは下記を参照してください。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/format-version-structure.html

AWSTemplateFormatVersion: "2010-09-09"

【任意】Description

テンプレートの説明を記述します。スタックの画面で説明を見ることができます。スタック名で判断できるのであれば無くてもよいです。

Description: >
  Here are some
  details about
  the template.

【任意】Metadata

テンプレートに対する追加情報を記述します。
よく使うのは、AWS::CloudFormation::Interface で、パラメータの量が多い場合、グループ化し変数のソート順を指定して画面表示することができるので、作業が分かりやすくなります。

グループ化されていないパラメータ入力画面です。

image.png

グループ化されたパラメータ入力画面です。グループ化単位でラベルが付与でき、強調表示されます。
image.png

【任意】Parameters

実行時に変更したい値をパラメータとして定義することで、1つのテンプレートファイルで環境に合わせた構築ができます。

  パラメータ名:
    Type: パラメータの型を指定します。指定できる型は、String,Number,List<Number>,CommaDelimitedList,AWS 固有のパラメーター型
    Default: パラメータのデフォルト値を設定しておくことができます。
    Description: パラメータの説明文を設定できます。
    :

指定できるプロパティは以下を参照してください。String 型の場合、AllowedPattern で正規表現による入力制限を行ったり、Number 型は、最小~最大値を指定できるなど様々なオプションがあります。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties

【任意】Conditions

入力されたパラメータに、分岐するフラグを作成することができます。例えば、EnvName に test が設定された場合に、 IsTest というフラグを True にし、Resources セクションでは、このパラメータでリソース作成を制御したり、名称を変更したりすることができます。

パターン ①:パラメータによって条件に利用する

Parameters:
  EnvName:
    Type: String
    Default: true
    AllowedValues:
      - dev
      - prod
      - stage
      - test
Conditions:
  IsTest: # 環境名が test の場合、true
    !Equals ["test", !Ref EnvName]

パターン ②:パラメータの入力有無による条件に利用する

Parameters:
  ManagementAccountId:
    Type: String
Conditions:
  EnableManagementAccount: # 管理アカウントID が指定されていたら true
    !Not [!Equals [!Ref ManagementAccountId, ""]]

パターン ③:いくつかの条件を組み合わせる

Conditions:
  IsSupportedRegion:
    !Or [
      !Equals [!Ref "AWS::Region", us-east-1],
      !Equals [!Ref "AWS::Region", us-east-2],
      !Equals [!Ref "AWS::Region", us-west-1],
      !Equals [!Ref "AWS::Region", us-west-2],
      !Equals [!Ref "AWS::Region", ap-northeast-1],
      !Equals [!Ref "AWS::Region", ap-northeast-2],
      !Equals [!Ref "AWS::Region", ap-southeast-2],
      !Equals [!Ref "AWS::Region", eu-west-1],
      !Equals [!Ref "AWS::Region", eu-west-2],
      !Equals [!Ref "AWS::Region", eu-north-1],
    ]

【任意】Mappings

パラメータでは対応できないようなキーと値の組み合わせを記述するときに利用します。
リージョンが ap-northeast-1 の場合、ELBAccountID が xxxx、ap-northeast-3 の場合 ELBAccountID が zzzz といった変換ができるようになります。

ロードバランサの AWS アカウント ID はリージョンで異なりますので、[ "arn:aws:iam::${AccountId}:root", { AccountId: !FindInMap [ RegionMap, !Ref AWS::Region , LBAWSAccount]}] とすることでリージョンが変更されてもテンプレートを修正する必要がありません。

RegionMap:
  us-east-1: # 米国東部(バージニア北部)
    LBAWSAccount: 127311923021
  us-east-2: # 米国東部 (オハイオ)
    LBAWSAccount: 33677994240
  us-west-1: # 米国西部 (北カリフォルニア)
    LBAWSAccount: 27434742980
  us-west-2: # 米国西部 (オレゴン)
    LBAWSAccount: 797873946194

また、dev と test で VPC やサブネットの CIDR が異なる場合、!Sub [ "${VPCCIDRBASE}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref EnvName, Vpc ]}] と指定することで環境ごとに変更でき、一か所に記述してあるため確認がしやすくなります。

VpcConfig:
  dev:
    Vpc: .0.0/16
    BastionSubnet1: .0.0/24
    DbSubnet1: .1.0/24
    DbSubnet2: .2.0/24
  test:
    Vpc: .0.0/16
    BastionSubnet1: .10.0/24
    DbSubnet1: .11.0/24
    DbSubnet2: .12.0/24

【<font color="red">必須</font>】Resources

AWS の各種リソースを作成する部分です。リソースに合わせて必要なパラメータが異なりますので、ドキュメントを参照して記述していきます。記述が間違っている場合のエラーメッセージはあまり親切とはいえないので、何がエラーか分からないで苦労する場合もあります。

テンプレート内で識別するID:
  Type: AWS リソースのタイプ
  Properties: リソース毎のパラメータ

S3 を作成する場合は以下のようにします。DeletionPolicy: Retain というパラメータを指定すると、テンプレートを削除してもリソースを残すことができるオプションです。(スタックを削除したあと同じテンプレートを再実行する場合は、リソースが残っているので手動で削除する必要があります。)

S3Bucket:
  Type: AWS::S3::Bucket
  DeletionPolicy: Retain
  Properties:
    BucketName: "example-bucket"
    AccessControl: Private
    PublicAccessBlockConfiguration:
      BlockPublicAcls: True
      BlockPublicPolicy: True
      IgnorePublicAcls: True
      RestrictPublicBuckets: True

【任意】Outputs

スタック内で作成したリソースの名やリソース ARN を出力することができます。出力したパラメータは、同一リージョンにある別のスタックで !ImportValue 変数名 として利用することができます。

出力するパラメータID(リソースで指定したIDと同じでもよい):
  Value: 出力するパラメータの値
  Export:
    Name: 出力するパラメータに名前を付けたい場合に指定します。指定しなかった場合は、パラメータIDとなります。組み込み関数を利用して柔軟な名前を作成することができます。

出力するパラメータの値は、!Ref S3Bucket と Resources 内の ID を指定するとリソース毎のデフォルトの値が設定されます。リソースによって、リソースの名前や ARN が設定されます。他の属性は、!GetAtt S3Bucket.Arn と指定すると取得できます。リソースによってサポートしている属性が異なるので、詳しくは AWS ドキュメントを参照してください。

組み込み関数

条件関数

条件関数に、Fn::If Fn::Equals Fn::Not などを利用してリソース名やリソースの削除を制御できます。詳しくは、以下のドキュメントを参照してください。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-if

その他

条件関数以外には、SubImportValueJoin といったものを使うことができます。これらを使えば、他のテンプレートの変数を利用したり、文字列を結合したりといった操作ができるようになり、柔軟なリソース作成ができます。

詳しくは、以下のドキュメントを参照してください。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

Sub と ImportValue を組み合わせる例

個人的には、次の使い方が便利なので紹介します。

Sub の基本的な使い方としては、以下のように動的に名称をつけたり、Arn を指定したりします。

!Sub "arn:aws:iam::${AWS::AccountId}:root"

CloudFormation を使っていると外部テンプレートで Output した変数を使いたい場合があります。外部テンプレートの変数は ImportValue を使う必要があるので、${変数名} では参照できません。このときは、Sub の変数マップを使います。これを使うことで、ImportValueGetAtt の値を利用することが可能になります。

!Sub
- !Sub "arn:aws:iam::${AWS::AccountId}:role/{PjName}-{EnvName}-{XXXXArn}"
- {
    PjName: !ImportValue PjName,
    EnvName: !ImportValue EnvName,
    XXXXArn: !GetAtt XXXX.Outputs.XXXXArn,
  }

テンプレートで利用できる共通変数

CloudFormation では予め利用できる変数が存在します。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html

変数をテンプレート内で利用するには、!Ref 'AWS::AccountId' や、!Sub '${AWS::AccountId}' と記述します。

# 使用例
Policy:
  - Version: 2012-10-17
    Statement:
      - Effect: Allow
        Principal: "*"
        Action: execute-api:Invoke
        Resource:
          !Join [
            ":",
            [
              "arn:aws:execute-api",
              !Ref "AWS::Region",
              !Ref "AWS::AccountId",
              "*",
            ],
          ]
変数名 利用頻度 説明
AWS::AccountId AWS のアカウント ID である 12 桁の数値 (123456789012)が取得できます。
AWS::Region ap-northeast-1 といったリージョン名が取得できます。
AWS::NoValue Conditions で指定した条件と組み合わせて、パラメータの有無を制御したい場合に利用します。
AWS::StackName スタック名が取得できます。リソース名などに利用したいといった用途で使うことができます。
AWS::StackId スタックの ID が取得できます。リソース名などに利用したいといった用途で使うことができます。
AWS::Partition AWS リソースを一意にする ARN arn:aws:iam::aws:policy/AdministratorAccessaws というパーティションを取得することができます。 中国や一部のリージョンを除いて、基本的に aws となっているため、あまり利用することはありません。
AWS::NotificationARNs スタックに設定した通知用の SNS トピック ARN のリストを取得することができます。複数設定した場合は、組込関数である Fn::Select で絞り込みます
AWS::URLSuffix ドメインのサフィックスを取得します。通常は、amazonaws.com なので、一部のリージョンを除いてはあまり利用することはありません。

Rollbacks

更新のロールバックを続ける

スタック更新中にエラーが発生した場合は、スタック更新前の状態に自動的に戻ります。その場合、スタックのステータスは UPDATE_ROLLBACK_COMPLETE になります。
自動的に戻らない場合は、スタックのステータスがUPDATE_ROLLBACK_FAILEDになりますので対処が必要になります。

ChangeSets(変更セット)

変更セット

現在適用されているテンプレートと、これから適用するテンプレートの差分をチェックして表示してくれるものです。
スタックを更新する途中でも同じようにチェックしてくれるものと同じです。

changeset

スタック更新とは別に作成することもできます。作成した変更セットは、その後、実行(スタック更新)することができます。
スタック更新途中でも確認できますが、誤ってスタック更新してしまわないので、安全に確認するのであれば事前に変更セットを作成するのがよいでしょう。

cfn-changeset-create

Drift

CloudFormation スタック全体のドリフトを検出する

テンプレートと実際のリソースの状態をチェックして差分を検出してくれる機能です。
テンプレートによるスタック管理を行っていたが、何らかの理由で手動による修正を行ったものが検出できます。

drift

Nested Stacks

ネストされたスタックの操作

利用する AWS リソースが多くなると、テンプレートの記述量が増えてきて可読性を低下させてしまったり、同じリソースを作る場合には記述が冗長になってしまったりします。

巨大なテンプレートになってしまった場合、サービスや機能ごとにテンプレートを分割して複数のスタックにしてもよいのですが、そうすると依存関係による実行順序を管理しにくくなります。
そんなときに、スタックのネストを行うことで親スタックから呼び出すことができます。

次の例では、それぞれの機能ごとに分割したテンプレートを特定の S3 バケットに格納し、親スタックから呼び出しています。

他のスタックの出力値を使用したい場合は、!GetAtt スタック名.Outputs.出力パラメータ名 のように指定することで使用できます。

  IamGroup:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Join ["", [!Ref CfnTemplateBucketUrl, "IAMGroups.yaml"]]
      Parameters:NetworkSecurity:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Join ["", [!Ref CfnTemplateBucketUrl, "NetworkSecurity.yaml"]]
      Parameters:
        paramxxx: !GetAtt IamGroup.Outputs.xxxxx
        :
  ApiGatewayExternal:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Join ["", [!Ref CfnTemplateBucketUrl, "ApiGateway.yaml"]]
        IsPrivate: falseApiGatewayInternal:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Join ["", [!Ref CfnTemplateBucketUrl, "ApiGateway.yaml"]]
      Parameters:
        IsPrivate: true

StackSet

AWS CloudFormation StackSets の操作

CloudFormation StackSets を使うことで、1 つのテンプレートから複数の AWS アカウント、リージョンに対し Stack を作成することが可能になります。

Secrets Manager

CloudFormation で Secrets Manager の値を使用する方法です。

Secrets Manager のシークレット

次のようにすることで、テンプレートにシークレットの情報を残さずに MasterUsername や MasterUserPassword を取得できます。

MyRDSInstance:
  Type: "AWS::RDS::DBInstance"
  Properties:
    DBName: MyRDSInstance
    AllocatedStorage: "20"
    DBInstanceClass: db.t2.micro
    Engine: mysql
    MasterUsername: "{{resolve:secretsmanager:MyRDSSecret:SecretString:username}}"
    MasterUserPassword: "{{resolve:secretsmanager:MyRDSSecret:SecretString:password}}"

その他、SSM パラメータストアなどの使用は次のドキュメントを参照してください。
スタックテンプレートでの動的な参照の指定

CloudFormation デザイナー

GUI と、YAML または JSON で記述することができます。

実際に操作してみたい場合は、チュートリアルの デザイナー の開始方法 をやってみるとよいです。

cfn-designer

IaC ジェネレータ

既存のリソースのテンプレートを生成

既存のリソースをスキャンして、テンプレート化できる機能です。
テンプレート化できるのは、スタック管理されていないものだけのようです。

同じような機能に、既存のリソースからのスタックの作成というのがありますが、こちらはテンプレートにリソース用のコードを追加した状態で、既存リソースとテンプレートを紐づけてスタック管理できるようにするものです。

IaC ジェネレータで既存リソースからテンプレートを生成してみた記事はこちら

このように生成することができます。
iac_generator_9_finish_5

IaC ジェネレータで生成したものを CloudFormation デザイナーに貼り付けても表示できました。行数が多いと応答がなくなるので注意が必要かもしれません。
cfn-designer

CLI で実行する場合はこちら。テンプレートを生成 (AWS CLI)

create-generated-template — AWS CLI 1.32.32 Command Reference
describe-generated-template — AWS CLI 1.32.33 Command Reference
get-generated-template — AWS CLI 1.32.33 Command Reference

GitHubで編集を提案

Discussion