この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんにちは、中山です。
米国時間3/28にCloudFormationのアップデートが発表されました。複数のアップデートがあったのですが、本エントリではその中からAWS::Includeについて、AWS Serverless Application Model(以下 AWS SAM)と交えながらご紹介したいと思います。
AWS::Includeとは何か
一言で説明すると、 CloudFromationにおけるプログラミング言語のモジュールに相当する機能 です。S3にアップロードしたCloudFormationのテンプレートを、別のテンプレートからIncludeすることができます。今までも、ネストスタックやクロススタック参照を利用することで、テンプレートを機能毎に分離して管理することは可能でした。ただし、それぞれ AWS:Include
とは似て非なるものなので、以下のような機能はありませんでした。
- 複数のテンプレートから1つのスタックを作成する
AWS::Include
をトップレベルで利用することにより可能となった- ネストスタックの場合は複数のスタックを作成してしまうので、スタック最大作成数の上限に達してしまう問題があった
- あるリソースのプロパティの一部を別テンプレートから呼び出す
AWS::Include
をリソースのプロパティに指定することにより可能となった
AWS::Include
を使うことでこういったことが可能になりました!CloudFormationがより「コード化」したと言えますね。
ありがちなユースケースを考えてみます。例えば、社内で決められた規定に沿ったVPC/サブネット用のテンプレートを作成したとします。サブネットをパブリック/プライベート/データストアの3階層に分けるようなものを想定してください。こういったテンプレートを作成しておき、S3にアップロードしておくことで、別のテンプレートからそれを再利用できるという訳です。または、プロパティが長くなりがちなリソースをテンプレートで利用する場合、 AWS::Include
で別テンプレートからIncludeすることにより、面倒な作業から開放してくれます。DRY原則の導入ですね!
執筆時点(3/30)では、S3へのアップロードは事前に実施しておく必要があります。ただし、こちらのイシューでAWSの中の人達がコメントしていますが、AWS CLIの aws cloudformation package
コマンドに対応しそうです。これが対応してくれるとより便利に使えると思います。
AWS SAMとの連携
以前AWS SAMとSwaggerを連携させる方法を以下のエントリにまとめました。
上記エントリでは、Swaggerファイルをテンプレート内にインラインで定義する場合、テンプレートを分離して扱うのが現状難しいため、行数が長くなってしまうと記述しました。が、 AWS::Include
の登場により、インラインSwaggerかつテンプレートの分離が簡単に作成可能になりました。
例えば、インラインSwaggerのファイルを以下のように定義したとします。インラインなのでCloudFormationの組み込み関数が利用可能です。ただし、 AWS::Include
でIncludeする場合、現状組み込み関数の短縮表記はサポートされてない点は注意してください。
swagger: 2.0
info:
title:
Fn::Sub: swagger-transform-1-${Stage}
description:
Fn::Sub: swagger-transform-1-${Stage}
version: 1.0.0
schemes:
- https
basePath:
Fn::Sub: /${Stage}
paths:
/:
get:
summary: Root
description: |
Root Method.
consumes:
- application/json
produces:
- application/json
parameters:
- name: number
in: query
description: Some number
required: true
type: number
format: integer
responses:
"200":
description: 200 response
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
responses:
default:
statusCode: 200
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Func1.Arn}/invocations
passthroughBehavior: when_no_templates
httpMethod: POST
type: aws
requestTemplates:
application/json: |
{
#foreach($key in $input.params().querystring.keySet())
"$key": "$util.escapeJavaScript($input.params().querystring.get($key))" #if($foreach.hasNext),#end
#end
}
definitions:
Empty:
type: object
title: Empty Schema
このテンプレートをIncludeするAWS SAMを以下のように記述します。
---
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: swagger-transform-1
Parameters:
Stage:
Type: String
Default: dev
ArtifactBucket:
Type: String
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: General Configuration
Parameters:
- Stage
- ArtifactBucket
ParameterLabels:
Stage:
default: Stage
ArtifactBucket:
default: Artifact Bucket
Resources:
Api:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: !Sub s3://${ArtifactBucket}/swagger.${Stage}.yml
Func1:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/handlers/func1
Handler: index.handler
Runtime: python2.7
Events:
PostApi:
Type: Api
Properties:
Path: /
Method: GET
RestApiId: !Ref Api
Outputs:
ApiUrl:
Value: !Sub https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${Stage}
- 33 - 36行:
AWS::Include
を利用して、DefinitionBody
プロパティを別テンプレートから呼び出しています- 今回は、Swaggerを検証/開発用途で分ける方式(
swagger.dev.yml
/swagger.prod.yml
)にしてみました
最終的なディレクトリ構成は以下の通りです。
$ tree .
.
├── params
│ ├── param.dev.json
│ └── param.prod.json
├── sam.yml
└── src
├── api
│ ├── swagger.dev.yml
│ └── swagger.prod.yml
└── handlers
└── func1
└── index.py
5 directories, 6 files
この状態でスタックを作成してみましょう。 stage
及び s3_bucket
変数はご自身の環境に合うよう、適宜読み替えてください。
$ aws cloudformation package \
--template-file sam.yml \
--s3-bucket $s3_bucket \
--output-template-file .sam/packaged.yml
Uploading to 0906c54356fd357815c158c45a3ff3e5 306 / 306.0 (100.00%)
Successfully packaged artifacts and wrote output template to file .sam/packaged.yml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /path/to/.sam/packaged.yml --stack-name <YOUR STACK NAME>
$ aws cloudformation deploy \
--template-file .sam/packaged.yml \
--stack-name test-$stage \
--capabilities CAPABILITY_IAM \
--parameter-overrides $(cat params/param.$stage.json | jq -r '.Parameters | to_entries | map("\(.key)=\(.value|tostring)") | .[]' | tr '\n' ' ' | awk '{print}')
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - test-dev
スタックの作成後、アウトプットに出力されるAPI GatewayのURLにアクセスするとLambda関数が実行されます(今回はJSONを返すだけ)。やりましたね。
$ curl https://stkgjfohn8.execute-api.ap-northeast-1.amazonaws.com/dev -w '\n'
{"body": "{\"message\": \"Hello World!\"}", "headers": {"x-custom-header": "My Header Value"}, "statusCode": 200}
まとめ
いかがだったでしょうか。
AWS::Include
の機能と、AWS SAMとの連携方法をご紹介しました。CloudFormationは当初「コード」ではなく「テンプレート」という印象が強かったですが、さまざまなアップデートによりとても使いやすいサービスへと進化しています。今後のアップデートにも期待したいですね。
本エントリがみなさんの参考になれば幸いに思います。