AWS 再入門ブログリレー AWS CloudFormation 編

Developers.IO で弊社コンサルティング部による『AWS 再入門ブログリレー 2019』の 8 日目のエントリです。
2019.07.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、岩城です。

当エントリは弊社コンサルティング部による『AWS 再入門ブログリレー 2019』の 8 日目のエントリです。

このブログリレーの企画は、普段 AWS サービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、 今一度初心に返って、基本的な部分を見つめ直してみよう、解説してみようというコンセプトが含まれています。

AWS をこれから学ぼう!という方にとっては文字通りの入門記事として、またすでに AWS を活用されている方にとっても AWS サービスの再発見や 2019 年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。

では、さっそくいってみましょう。8 日目のテーマは『AWS CloudFormation』です。

目次

CloudFormation とは?

AWSの特徴の一つに、マネジメントコンソールで行う操作が API として用意され、CLI やプログラムから AWS リソースをプロビジョニングできる点があります。AWS は API 以外にも AWS リソースを操作する方法を複数提供しており、本エントリで紹介する CloudFormation もその一つです。CloudFormation は、AWS が提供しているインフラの構成管理サービスであり、AWS クラウド内のリソースを JSON や YAML 形式のコードで宣言し、プロビジョニングします。インフラをコード化できるため、「Infrastructure as Code」を実装する取り組みをサポートしてくれます。

CloudFormation の概要

CloudFormation は、テンプレートに基づきスタック単位で各リソースを起動します。以下の概要図を例にすると、VPC を始めとする各リソースをテンプレートに宣言し、CloudFormation によってスタックにまとめられてプロビジョニングされるということになります。テンプレートとスタックについて説明していきます。

テンプレート

テンプレートは、JSON または YAML 形式のテキストファイルであり、AWS リソースをプロビジョニングする際の設計図として利用されます。上記概要図の YAML 形式のテンプレート例を以下に記載します。テンプレートの書き方については後述しますので、ここではテンプレートのニュアンスを掴んでください。

---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation Template Sample'
Resources:
# VPC
  SampleVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: '10.100.0.0/16'
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      InstanceTenancy: 'default'
      Tags:
      - Key: Name
        Value: 'sample-vpc'
# Subnet
  SampleSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: 'ap-northeast-1a'
      CidrBlock: '10.100.1.0/24'
      Tags:
      - Key: Name
        Value: 'sample-subnet-a'
      VpcId: !Ref SampleVPC
# InternetGateway
  SampleInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: 'sample-igw'
  SampleAttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref SampleInternetGateway
      VpcId: !Ref SampleVPC
# RouteTable
  SampleRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: SampleAttachGateway
    Properties:
      Tags:
      - Key: Name
        Value: 'sample-rtb'
      VpcId: !Ref SampleVPC
  SampleRoute:
    Type: AWS::EC2::Route
    DependsOn: SampleAttachGateway
    Properties:
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref SampleInternetGateway
      RouteTableId: !Ref SampleRouteTable

スタック

テンプレートに宣言されたリソースをスタックと呼ばれる単一のユニットとして管理します。スタックの作成や変更の際、どれか一つのリソースで作成や変更が失敗すれば、他の変更も含めて全てロールバックされます。また、スタックを削除すれば、スタックで管理されたリソースは全て削除されます。

CloudFormation のユースケース

CloudFormation のユースケースが AWS Black Belt の資料に記載されています。

上記以外にも、開発、検証、本番環境といった複数の環境が必要となるケースで、テンプレートを流用することで簡単に展開できることもあると思います。また、私の場合は仕事柄よく検証環境を立てることが多いのですが、マネジメントコンソール上で作成してしまうと、リソースの削除漏れによるゴミ設定が溜まったり、最悪余計な費用が発生したりするため、極力 CloudFormation を利用して一括作成と一括削除を行っています。

AWS における CloudFormation の位置づけ

AWS から提供されているデプロイ関連サービス

AWS には様々なデプロイ関連サービスが提供されており、CloudFormation はプロビジョンに属します。

デプロイ、プロビジョニングサービスのカバー範囲

CloudFormation は、AWS リソースをサポートするだけでなく、たとえば EC2 の ユーザーデータ機能を用いて OS や M/W の設定であったり、アプリケーションをデプロイすることも可能です。

利用料金

CloudFormation 自体の利用に追加料金は掛かりません。ただし、作成されたリソースに対して料金が掛かりますので注意してください。

Getting Started

CloudFormation の概要的な話しは終わりました。先に紹介したテンプレートの内容を確認した後、実際に CloudFormation を操作してみましょう。

テンプレートを読み解いてみる

テンプレートの一部を抜粋しました。読み解いていきましょう。

AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation Template Sample'
Resources:
# VPC
  SampleVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: '10.100.0.0/16'
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      InstanceTenancy: 'default'
      Tags:
      - Key: Name
        Value: 'sample-vpc'
# Subnet
  SampleSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: 'ap-northeast-1a'
      CidrBlock: '10.100.1.0/24'
      Tags:
      - Key: Name
        Value: 'sample-subnet-a'
      VpcId: !Ref SampleVPC
# RouteTable
  SampleRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: SampleAttachGateway
    Properties:
      Tags:
      - Key: Name
        Value: 'sample-rtb'
      VpcId: !Ref SampleVPC

AWSTemplateFormatVersion

テンプレートの形式バージョンを指定します。現在2010-09-09以外に有効な値はありません。

Description

テンプレートの説明を記載します。

Resources

スタックに含めるリソースを宣言します。 SampleVPCSampleSubnet は論理 ID と呼ばれ、テンプレート内で一意である必要があります。テンプレート内の他のリソースを参照するために使用します。例えば、SampleSubnetセクションではサブネットに関連付ける VPCID を!Ref SampleVPCで参照しています。Typeでは宣言しているリソースタイプを識別します。ここでは、Type: AWS::EC2::VPCから VPC リソースであることを宣言しています。Propertiesにはリソース固有のオプションを宣言します。SampleVPCCidrBlockを 10.100.0.0/16 にするといった感じです。

DependsOn

特定のリソースが他のリソースに続けて作成されるように指定できます。たとえば、SampleRouteTableでターゲットに InternetGateway を指定する場合、InternetGateway が先に作成されている必要があるため、DependsOnを使用したりします。

組み込み関数

これまで度々登場していた!Refは、組み込み関数と呼ばれ、指定したパラメータを返します。先程の!Ref SampleVPCでは、VPCID を返すといった感じです。本エントリでは紹介しませんが!Ref以外にも組み込み関数が用意されていますので、興味のある方は以下のドキュメントを参照してみてください。

CloudFormation の実行

それでは、いよいよ CloudFormation を実行しスタックおよび各リソースを作成していきます。

マネジメントコンソールから実行

スタックの作成から対象のテンプレートをアップロードして次へをクリックします。

スタックの名前に任意の名前を入力します。ここでは SampleStack としました。次へをクリックしてスタックを作成します。

テンプレートで宣言したリソースが作成されることをイベントから確認することができます。SampleStack のステータスがCREATE_COMPLETEとなれば完了です。

ちなみに、リソースの作成に失敗した場合、スタック内で作成されたリソースは全て削除されロールバックされます。

CLI から実行

CLI でも CloudFormation を実行できます。私の場合、マネジメントコンソールでは GUI 操作やフォーム入力により時間が掛かるため、CLI で構築することが殆どです。

# スタックの作成
$ aws cloudformation create-stack --stack-name SampleStack --template-body file://CloudFormationSample.yaml
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/SampleStack/c7e1c960-a18b-11e9-a0cd-065ce9d23304"
}

# 作成したスタックのイベントを確認
$ aws cloudformation describe-stack-events --stack-name SampleStack
(省略)
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/SampleStack/c7e1c960-a18b-11e9-a0cd-065ce9d23304",
            "EventId": "c7e3ec40-a18b-11e9-a0cd-065ce9d23304",
            "StackName": "SampleStack",
            "LogicalResourceId": "SampleStack",
            "PhysicalResourceId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/SampleStack/c7e1c960-a18b-11e9-a0cd-065ce9d23304",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-07-08T14:22:13.773Z",
            "ResourceStatus": "CREATE_IN_PROGRESS",
            "ResourceStatusReason": "User Initiated"
        }
    ]
}

CloudFormation の知っておきたい便利機能

さいごに、CloudFormation で知っておきたい便利な機能を紹介したいと思います。

テンプレートの検証

テンプレートの検証は、CLI でのみ提供されています。以下のようなコマンドを実行することで、テンプレートの構文エラーを確認することができます。ただし、あくまで構文のチェックであるため、リソースに指定したプロパティの値が有効であるかどうかまでは確認できず、検証は成功したけど実際にスタックを作成すると失敗するということはよくあります。

# 構文に問題ない場合
$ aws cloudformation validate-template --template-body file://CloudFormationSample.yaml
{
    "Parameters": [],
    "Description": "CloudFormation Template Sample"
}

# 構文に問題ある場合
$ aws cloudformation validate-template --template-body file://CloudFormationSample.yaml
An error occurred (ValidationError) when calling the ValidateTemplate operation: Template format error: YAML not well-formed. (line 10, column 26)

もう一歩踏み込んだチェックをしたい場合は、Linter を使用します。Linter では、リソースに指定したプロパティの値が有効であるかであったり、プロパティのスペルミスをチェックしてくれます。興味のある方は、以下のエントリを参照してみてください。インストールから利用方法が紹介されています。

変更セット

変更セットとは、スタックを更新する前に、その更新内容が実行中のリソースに与える影響を確認することができます。たとえば、更新内容によってリソースが削除されたり置き換えられたりしないかを確認します。ただし、更新したいテンプレートとスタック作成時に利用したテンプレートとの差分比較であるため、手動変更した場合の差分は検出されません。スタックに影響するような手動変更を検出するには次に説明するドリフト検出を利用します。

マネジメントコンソールから実行

スタックから既存スタックの変更セットを作成をクリックします。

既存テンプレートを置き換えるを選択し、新しいテンプレートをアップロードします。

変更セット名を入力し、変更セットの作成をクリックします。

作成した変更セットを確認します。SampleVPC に対してModifyアクションが発生ししていることが分かります。JSONの変更から具体的な変更内容を確認できます。

この例では、SampleVPC にてタグの変更が行われることが分かります。

CLI から実行

# 変更セットの作成
$ aws cloudformation create-change-set --change-set-name SampleChangeSet --stack-name SampleStack --template-body file://CloudFormationSample.yaml
{
    "Id": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:changeSet/SampleChangeSet/597379cc-4311-4812-82ec-35be4f2f7fea",
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/SampleStack/c7e1c960-a18b-11e9-a0cd-065ce9d23304"
}

# 変更セットの詳細
$ aws cloudformation describe-change-set --change-set-name arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:changeSet/SampleChangeSet/597379cc-4311-4812-82ec-35be4f2f7fea
{
    "Changes": [
        {
            "Type": "Resource",
            "ResourceChange": {
                "Action": "Modify",
                "LogicalResourceId": "SampleVPC",
                "PhysicalResourceId": "vpc-06289ae38c5efaefa",
                "ResourceType": "AWS::EC2::VPC",
                "Replacement": "False",
                "Scope": [
                    "Tags"
                ],
                "Details": [
                    {
                        "Target": {
                            "Attribute": "Tags",
                            "RequiresRecreation": "Never"
                        },
                        "Evaluation": "Static",
                        "ChangeSource": "DirectModification"
                    }
                ]
            }
        }
    ],
(省略)

# 変更セットの実行
$ aws cloudformation execute-change-set --change-set-name arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxxx:changeSet/SampleChangeSet/597379cc-4311-4812-82ec-35be4f2f7fea

ドリフトの検出

ドリフトの検出とは、スタック外でマネジメントコンソールや CLI からリソースに対して行った変更を検出します。テンプレートの更新に必要な情報や、予期せぬ変更から適切な状態に戻すための情報を得ることができます。

マネジメントコンソールから実行

スタックからドリフトの検出をクリックします。すると、ドリフトステータスDRIFTEDになっていることが分かります。

ドリフトの詳細の表示をクリックして検出した変更の詳細を確認できます。

この例では、タグがsample-vpcからsample-vpc-modifyに変更されていることが分かります。

CLI から実行

# ドリフトの検出
$ aws cloudformation detect-stack-drift --stack-name SampleStack
{
    "StackDriftDetectionId": "c8947090-a1d8-11e9-a212-0a29b069fc22"
}

# ドリフトのステータスを確認
$ aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id c8947090-a1d8-11e9-a212-0a29b069fc22
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/SampleStack/c7e1c960-a18b-11e9-a0cd-065ce9d23304",
    "StackDriftDetectionId": "c8947090-a1d8-11e9-a212-0a29b069fc22",
    "StackDriftStatus": "DRIFTED",
    "DetectionStatus": "DETECTION_COMPLETE",
    "DriftedStackResourceCount": 1,
    "Timestamp": "2019-07-08T23:33:26.169Z"
}

# 検出したドリフトの内容を確認
$ aws cloudformation describe-stack-resource-drifts --stack-name SampleStack
(省略)
            "PropertyDifferences": [
                {
                    "PropertyPath": "/Tags/0/Value",
                    "ExpectedValue": "sample-vpc",
                    "ActualValue": "sample-vpc-modify",
                    "DifferenceType": "NOT_EQUAL"
                }
            ],
            "StackResourceDriftStatus": "MODIFIED",
            "Timestamp": "2019-07-08T23:33:27.922Z"
        }
    ]
}

スタックセット

スタックセットとは、複数の AWS アカウントやリージョンに対し一括でスタックを作成できる機能です。たとえば、全アカウントの CloudTrail の有効化や、全リージョンでの GuardDuty の有効化などに利用するケースが考えられます。本エントリでは詳細に説明しませんが、気になる方は以下のエントリを参照してみてください。

CloudFormation デザイナー

CloudFormation デザイナーとは、CloudFormation テンプレートを作成、参照、および変更するためのグラフィックツールです。デザイナー を使用すると、ドラッグアンドドロップインターフェイスを使用してテンプレートリソースを図示し、統合された JSON および YAML エディタ を使用して詳細を編集できます。私が CloudFormation を独学で学び始めた頃は、デザイナーで AWS リソースを図示し作成されたテンプレートを元に、構文や必要なパラメータを確認して理解を深めました。CloudFormation 初学者にオススメかと思います。

おわりに

実は私、弊社に JOIN してから初めて CloudFormation を扱う案件を担当しており、現在進行系で CloudFormation によって日々汗をかいています。慣れない間は、躓くことも多いですが悩みに悩んだ後に作成できた時の達成感は堪らず、大好きなサービスになりました!

以上、『AWS 再入門ブログリレー 2019』の 8 日目のエントリ『AWS CloudFormation』編でした。

明日 (7/11) は大前諒祐の「Amazon CloudFront」の予定です。お楽しみに!!

Reference

AWS 資料

弊社記事

Tips

CloudFormation の特集記事