AWS::Include Transformを使ってCloudFormationのテンプレートをキレイに整理しよう!!

AWS::Includeはあんなところ、こんなところでも使えるんです
2020.03.31

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

CX事業本部@大阪の岩田です。

CloudFormationのAWS:Includeを使うと、CloudFormationのテンプレート内に別テンプレートの中身を読み込むことができます。私も以下の記事を参考にSwaggerの定義を分離するのに愛用しています。

CloudFromationのAWS::Includeを利用してAWS SAMからインラインSwaggerを分離して管理する

このAWS:Include、恥ずかしながらリソースのプロパティを定義する際にしか利用できないと思いこんでいたのですが、実はもっと色々な場所で利用できることに気付きました。ご存知の人も多いかもしれませんが、改めてご紹介します。

AWS:Includeとは??

S3上に置かれたファイルの中身をCloudFormationのテンプレート内に読み込むためのマクロです。公式ドキュメントに記載されているように、プログラミング言語における includecopyimportといった構文をイメージすると分かり良いと思います。前述の中山のブログが執筆された時点ではAWS CLIによるpackageが未対応だったのですが、今は普通に対応しているので、S3にアップロードする手間をあまり意識することなく利用できます。

AWS:Includeの利用例

実際のテンプレートを見て頂くのが早いと思います。以下のように

  • テンプレートの最上位
  • Resourcesの直下
  • リソース定義の直下
  • リソースのProperties指定

色んな場所にAWS:Includeを置くことができます

include_test.ym

AWSTemplateFormatVersion: '2010-09-09'
# 最上位にも置けるし
Fn::Transform:
  Name: AWS::Include
  Parameters:
    Location: description.yml
Resources:
  # Resources直下にも置けるし
  Fn::Transform:
    Name: AWS::Include
    Parameters:
      Location: dynamodb.yml
  TestBucket1:
    Type: AWS::S3::Bucket
    # 各リソース定義の直下もOK
    Fn::Transform:
      Name: AWS::Include
      Parameters:
        Location: del_policy.yml
  TestBucket2:
    Type: AWS::S3::Bucket
    Properties:
      # Propertiesの指定にも使えるし
      Fn::Transform:
        Name: AWS::Include
        Parameters:
          Location: tags.yml
  TestBucket3:
    Type: AWS::S3::Bucket    
    Fn::Transform:
      Name: AWS::Include
      Parameters:
        Location: del_policy.yml    
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
      # 1つのリソースに複数回Includeを使ったり
      # PropertiesをマージしたりしてもOK
      Fn::Transform:
        Name: AWS::Include
        Parameters:
          Location: tags.yml

Includeしている各YAMLファイルは以下の通りです

description.yml

Description: 'Include Test'

dynamodb.yml

TestTable:
  Type: "AWS::DynamoDB::Table"
  Properties:
    AttributeDefinitions:
      -
        AttributeName: "id"
        AttributeType: "N"
    KeySchema:
      -
        AttributeName: "id"
        KeyType: "HASH"
    BillingMode: PAY_PER_REQUEST

del_policy.yml

DeletionPolicy: Delete

tags.yml

Tags: 
  -
    Key: SomeKey
    Value: SomeVal

dynamodb.ymlではリソースの定義を丸々記述しています。このようにDynamoDB用のテンプレート、Lambda用のテンプレート...といった形でサービス単位である程度テンプレートを分けておくと保守性を高く保ちやすくなります(あまり分けすぎでも逆にややこしくなるのでケースバイケースですが)。また、tags.ymlのように各リソースで共通設定するプロパティだけ切り出せば、冗長な記述を削減することも可能です。

Packageしてみる

先程のテンプレートに対してcloudformationのpackageコマンドを実行してみます。これでinclude対象に指定された各ファイルがS3にアップロードされ、デプロイ用のテンプレートファイルが出力されます。

$ aws cloudformation package --template-file include_test.yml --s3-bucket <適当なS3バケット> --output-template-file output.yml

出力されたファイルはこちらです。

output.yml

AWSTemplateFormatVersion: '2010-09-09'
Fn::Transform:
  Name: AWS::Include
  Parameters:
    Location: s3://<package時に指定したS3バケット>/b7a2955ca33cb3908c36b49284fd487a
Resources:
  Fn::Transform:
    Name: AWS::Include
    Parameters:
      Location: s3://<package時に指定したS3バケット>/44f286194fbdc0705011eba5aa2e8c47
  TestBucket1:
    Type: AWS::S3::Bucket
    Fn::Transform:
      Name: AWS::Include
      Parameters:
        Location: s3://<package時に指定したS3バケット>/3b8fa7b7a78df4b066b7b7a3851c4802
  TestBucket2:
    Type: AWS::S3::Bucket
    Properties:
      Fn::Transform:
        Name: AWS::Include
        Parameters:
          Location: s3://<package時に指定したS3バケット>/4c8b60a22160fe0bb6a822088ccddb71
  TestBucket3:
    Type: AWS::S3::Bucket
    Fn::Transform:
      Name: AWS::Include
      Parameters:
        Location: s3://<package時に指定したS3バケット>/3b8fa7b7a78df4b066b7b7a3851c4802
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
      Fn::Transform:
        Name: AWS::Include
        Parameters:
          Location: s3://<package時に指定したS3バケット>/4c8b60a22160fe0bb6a822088ccddb71

デプロイしてみる

packageしたテンプレートを使ってスタックを作成してみましょう

$ aws cloudformation deploy --template-file output.yml --stack-name <適当なスタック名> --capabilities CAPABILITY_IAM

無事にデプロイできました!

AWS:Includeで各テンプレートをIncludeした最終的なテンプレートが「処理されたテンプレートの表示」から確認できます。JSON形式で出力されているので、YAMLに変換すると以下のようになります。

AWSTemplateFormatVersion: '2010-09-09'
Description: Include Test
Resources:
  TestBucket2:
    Type: AWS::S3::Bucket
    Properties:
      Tags:
      - Key: SomeKey
        Value: SomeVal
  TestBucket3:
    Type: AWS::S3::Bucket
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
      Tags:
      - Key: SomeKey
        Value: SomeVal
    DeletionPolicy: Delete
  TestBucket1:
    Type: AWS::S3::Bucket
    DeletionPolicy: Delete
  TestTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
      - AttributeName: id
        AttributeType: N
      KeySchema:
      - AttributeName: id
        KeyType: HASH
      BillingMode: PAY_PER_REQUEST

Includeしたファイルの中身がマージされて、1つのテンプレートに統合されていることが分かります。

まとめ

CloudFormationのAWS:Includeは色んな場所で利用できますというご紹介でした。ある程度インフラの規模が大きくなってくるとCloudFormationのテンプレートも自然と肥大化し、保守性が低くなってしまいます。AWS:Includeをうまく活用することで、読みやすい範囲でテンプレートを分割したり、何度も繰り返す共通プロパティを一元管理するといったことが可能になるので、良ければ利用を検討してみて下さい。