- AWS
CloudFormationテンプレートの書き方と組み込み関数一覧
CloudFormation テンプレートとは、作りたい AWS リソースを YAML(または JSON)で宣言したファイルです。 必須のセクションは Resources だけで、そこに Parameters(入力値)、Mappings(対応表)、Conditions(条件)、Outputs(出力値)を足していくと、1つのテンプレートを開発用・本番用の両方に使い回せます。
この記事では、5つのセクションを全部使った完成形の YAML を1本載せ、スタックを作って describe-stacks の Outputs が返るところまでを出力つきで示します。後半は、!Ref や !Sub などの組み込み関数を「短縮形・完全形・返る値」の一覧にし、リソース別に Ref と GetAtt が何を返すかの早見表、テンプレート検査で出るエラー文と直し方を並べます。
確認環境:Ubuntu 24.04 / AWS CLI 1.46.1 / cfn-lint 1.57.1。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しました。掲載するコマンドは実際の AWS でそのまま使える形にしてあります。
CloudFormation テンプレートの早見表
| セクション | 役割 | 必須 | 参照のしかた |
|---|---|---|---|
AWSTemplateFormatVersion |
テンプレート形式の版('2010-09-09') |
任意 | − |
Description |
テンプレートの説明文 | 任意 | − |
Parameters |
スタック作成時に渡す入力値 | 任意 | !Ref パラメータ名 |
Mappings |
キーから値を引く対応表 | 任意 | !FindInMap [表, キー1, キー2] |
Conditions |
リソースを作るか、値をどちらにするかの条件 | 任意 | Condition: 条件名、!If [条件名, 真, 偽] |
Resources |
作成するリソース | 必須 | !Ref 論理ID、!GetAtt 論理ID.属性 |
Outputs |
スタックが返す値(他スタックへの公開も可) | 任意 | describe-stacks、!ImportValue 公開名 |
| よく使う関数 | 短縮形の例 | 返る値の例 |
|---|---|---|
| Ref | !Ref EnvName |
dev |
| Sub | !Sub 'app-${EnvName}-${AWS::AccountId}' |
app-dev-123456789012 |
| GetAtt | !GetAtt JobQueue.Arn |
arn:aws:sqs:ap-northeast-1:123456789012:taskboard-dev-jobs |
| Join | !Join ['-', [app, !Ref EnvName, queue]] |
app-dev-queue |
| If | !If [IsProd, 'production', 'not production'] |
not production |
| FindInMap | !FindInMap [EnvMap, !Ref EnvName, Size] |
small |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CloudFormationテンプレートの書き方と組み込み関数一覧」
テンプレートのセクション構成
公式ドキュメント(CloudFormation ユーザーガイドのテンプレートの構造のページ)に載っているセクションは次の10個で、必須は Resources だけです。
| セクション | 内容 |
|---|---|
| Format version | テンプレート形式の版 |
| Description | 説明文 |
| Metadata | テンプレートについての追加情報 |
| Parameters | スタックの作成・更新時に渡す値 |
| Mappings | 条件に応じた値を引く対応表 |
| Conditions | リソースを作るか、プロパティに値を入れるかの条件 |
| Transform | テンプレート処理時に適用するマクロ |
| Resources | スタックのリソースとそのプロパティ(必須) |
| Outputs | スタックのプロパティとして返す値 |
| Rules | 作成・更新時のパラメータの検証 |
この記事では、利用頻度の高い Parameters・Mappings・Conditions・Resources・Outputs の5つを扱います。
5つのセクションを全部使った完成 YAML
タスク管理アプリ用の保存先(S3 バケット、SQS キュー、SNS トピック)を作るテンプレートです。EnvName を prod にすると、デッドレターキューが追加され、タイムアウトなどの値が切り替わります。template.yaml として保存してください。
AWSTemplateFormatVersion: '2010-09-09'
Description: Task board storage (S3 bucket, SQS queue, SNS topic)
Parameters:
AppName:
Type: String
Default: taskboard
AllowedPattern: '^[a-z][a-z0-9-]{2,20}$'
Description: Lowercase application name used as a name prefix
EnvName:
Type: String
Default: dev
AllowedValues: [dev, stg, prod]
Description: Environment name
AlertEmail:
Type: String
Default: ''
Description: Email address for alerts (empty = no subscription)
Mappings:
EnvMap:
dev:
VisibilityTimeout: 30
RetentionSeconds: 86400
stg:
VisibilityTimeout: 60
RetentionSeconds: 345600
prod:
VisibilityTimeout: 120
RetentionSeconds: 1209600
Conditions:
IsProd: !Equals [!Ref EnvName, prod]
HasAlertEmail: !Not [!Equals [!Ref AlertEmail, '']]
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${AppName}-${EnvName}-${AWS::AccountId}'
VersioningConfiguration:
Status: !If [IsProd, Enabled, Suspended]
Tags:
- Key: Env
Value: !Ref EnvName
DeadLetterQueue:
Type: AWS::SQS::Queue
Condition: IsProd
Properties:
QueueName: !Sub '${AppName}-${EnvName}-jobs-dlq'
JobQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Join ['-', [!Ref AppName, !Ref EnvName, jobs]]
VisibilityTimeout: !FindInMap [EnvMap, !Ref EnvName, VisibilityTimeout]
MessageRetentionPeriod: !FindInMap [EnvMap, !Ref EnvName, RetentionSeconds]
RedrivePolicy: !If
- IsProd
- deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
maxReceiveCount: 5
- !Ref AWS::NoValue
AlertTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Sub '${AppName}-${EnvName}-alerts'
AlertSubscription:
Type: AWS::SNS::Subscription
Condition: HasAlertEmail
Properties:
TopicArn: !Ref AlertTopic
Protocol: email
Endpoint: !Ref AlertEmail
Outputs:
BucketName:
Description: Bucket name (Ref)
Value: !Ref DataBucket
BucketArn:
Description: Bucket ARN (GetAtt)
Value: !GetAtt DataBucket.Arn
JobQueueUrl:
Description: Queue URL (Ref)
Value: !Ref JobQueue
JobQueueArn:
Description: Queue ARN (GetAtt), exported for other stacks
Value: !GetAtt JobQueue.Arn
Export:
Name: !Sub '${AWS::StackName}-JobQueueArn'
AlertTopicArn:
Description: Topic ARN (Ref)
Value: !Ref AlertTopic
DeadLetterQueueArn:
Condition: IsProd
Description: Dead letter queue ARN (prod only)
Value: !GetAtt DeadLetterQueue.Arn
各セクションの読み方
| セクション | このテンプレートでの使い方 |
|---|---|
| Parameters | AppName・EnvName・AlertEmail の3つ。Default があるので指定を省ける。AllowedValues と AllowedPattern で受け付ける値を絞る |
| Mappings | EnvMap に環境ごとの VisibilityTimeout と RetentionSeconds を置き、!FindInMap で引く |
| Conditions | IsProd(本番かどうか)と HasAlertEmail(メールアドレスが空でないか) |
| Resources | DeadLetterQueue と AlertSubscription は Condition: 付きで、条件が真のときだけ作られる |
| Outputs | Ref と GetAtt の結果を返す。JobQueueArn は Export で他スタックに公開。DeadLetterQueueArn は本番のときだけ出力 |
ポイントは !Ref AWS::NoValue です。 !If の「偽」の側に置くと、そのプロパティ自体が無かったことになります。上の RedrivePolicy は、本番のときだけ設定されます。
スタックを作って Outputs を確かめる
開発用(パラメータは既定値のまま)
aws cloudformation create-stack --stack-name taskboard-dev --template-body file://template.yaml
aws cloudformation wait stack-create-complete --stack-name taskboard-dev
{
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/taskboard-dev/84f4cfe5-4bee-4705-8f5e-2b035cbf94bd"
}
wait stack-create-complete は作成完了まで待つコマンドで、完了すると何も出力せずに戻ります。Outputs を見てみます。
aws cloudformation describe-stacks --stack-name taskboard-dev --query 'Stacks[0].Outputs'
[
{
"OutputKey": "BucketName",
"OutputValue": "taskboard-dev-123456789012",
"Description": "Bucket name (Ref)"
},
{
"OutputKey": "BucketArn",
"OutputValue": "arn:aws:s3:::taskboard-dev-123456789012",
"Description": "Bucket ARN (GetAtt)"
},
{
"OutputKey": "JobQueueUrl",
"OutputValue": "https://sqs.ap-northeast-1.amazonaws.com/123456789012/taskboard-dev-jobs",
"Description": "Queue URL (Ref)"
},
{
"OutputKey": "JobQueueArn",
"OutputValue": "arn:aws:sqs:ap-northeast-1:123456789012:taskboard-dev-jobs",
"Description": "Queue ARN (GetAtt), exported for other stacks",
"ExportName": "taskboard-dev-JobQueueArn"
},
{
"OutputKey": "AlertTopicArn",
"OutputValue": "arn:aws:sns:ap-northeast-1:123456789012:taskboard-dev-alerts",
"Description": "Topic ARN (Ref)"
}
]
同じ SQS キューでも、!Ref は URL、!GetAtt ... .Arn は ARN を返していることがわかります。DeadLetterQueueArn は Condition: IsProd が偽なので、出力に含まれていません。
スタックに渡ったパラメータも確認できます。
aws cloudformation describe-stacks --stack-name taskboard-dev --query 'Stacks[0].[StackStatus,Parameters]'
[
"CREATE_COMPLETE",
[
{
"ParameterKey": "AppName",
"ParameterValue": "taskboard"
},
{
"ParameterKey": "EnvName",
"ParameterValue": "dev"
},
{
"ParameterKey": "AlertEmail",
"ParameterValue": ""
}
]
]
本番用(パラメータを渡す)
aws cloudformation create-stack --stack-name taskboard-prod --template-body file://template.yaml \
--parameters ParameterKey=EnvName,ParameterValue=prod ParameterKey=AlertEmail,ParameterValue=ops@example.com
aws cloudformation describe-stacks --stack-name taskboard-prod \
--query 'Stacks[0].Outputs[].[OutputKey,OutputValue]' --output table
-----------------------------------------------------------------------------------------------------
| DescribeStacks |
+--------------------+------------------------------------------------------------------------------+
| BucketName | taskboard-prod-123456789012 |
| BucketArn | arn:aws:s3:::taskboard-prod-123456789012 |
| JobQueueUrl | https://sqs.ap-northeast-1.amazonaws.com/123456789012/taskboard-prod-jobs |
| JobQueueArn | arn:aws:sqs:ap-northeast-1:123456789012:taskboard-prod-jobs |
| AlertTopicArn | arn:aws:sns:ap-northeast-1:123456789012:taskboard-prod-alerts |
| DeadLetterQueueArn| arn:aws:sqs:ap-northeast-1:123456789012:taskboard-prod-jobs-dlq |
+--------------------+------------------------------------------------------------------------------+
本番では DeadLetterQueueArn が増えました。Mappings と !If が効いているかは、作られたキューの属性で確かめられます。
aws sqs get-queue-attributes \
--queue-url "$(aws sqs get-queue-url --queue-name taskboard-prod-jobs --output text)" \
--attribute-names VisibilityTimeout MessageRetentionPeriod RedrivePolicy
{
"Attributes": {
"VisibilityTimeout": "120",
"MessageRetentionPeriod": "1209600",
"RedrivePolicy": "{\"deadLetterTargetArn\": \"arn:aws:sqs:ap-northeast-1:123456789012:taskboard-prod-jobs-dlq\", \"maxReceiveCount\": 5}"
}
}
同じコマンドを taskboard-dev-jobs に対して実行した結果です。RedrivePolicy が無く、値は dev の行のものになっています。
{
"Attributes": {
"VisibilityTimeout": "30",
"MessageRetentionPeriod": "86400"
}
}
| 項目 | dev | prod | 効いている仕組み |
|---|---|---|---|
| VisibilityTimeout | 30 | 120 | !FindInMap [EnvMap, !Ref EnvName, VisibilityTimeout] |
| MessageRetentionPeriod | 86400 | 1209600 | !FindInMap |
| RedrivePolicy | なし | あり | !If [IsProd, {...}, !Ref AWS::NoValue] |
| DeadLetterQueue | 作られない | 作られる | Condition: IsProd |
| Outputs の DeadLetterQueueArn | 出ない | 出る | Outputs 側の Condition: IsProd |
Export した値の一覧
aws cloudformation list-exports --query 'Exports[].[Name,Value]' --output text
taskboard-dev-JobQueueArn arn:aws:sqs:ap-northeast-1:123456789012:taskboard-dev-jobs
taskboard-prod-JobQueueArn arn:aws:sqs:ap-northeast-1:123456789012:taskboard-prod-jobs
公開名は !Sub '${AWS::StackName}-JobQueueArn' で作っているので、スタック名が頭に付きます。別のスタックからは !ImportValue taskboard-dev-JobQueueArn で受け取れます(次の章で実行します)。
作成と更新を1つのコマンドで行う deploy
create-stack は新規作成専用です。AWS CLI のリファレンスによると、aws cloudformation deploy は変更セットを作って実行するコマンドで、スタック名が既存なら更新、新しければ作成になります。
aws cloudformation deploy --template-file template.yaml --stack-name taskboard-prod \
--parameter-overrides EnvName=prod AlertEmail=ops@example.com
| オプション | リファレンスの説明(要約) |
|---|---|
--parameter-overrides |
キー=値 を空白区切りで並べる。更新時に省いたパラメータは、スタックの既存の値が使われる |
--capabilities |
IAM リソースを含むテンプレートで必要。値は CAPABILITY_IAM か CAPABILITY_NAMED_IAM(名前を指定した IAM リソースがある場合) |
--no-execute-changeset |
変更セットを作るだけで実行しない。変更内容を見てから実行したいときに使う |
スタックを削除する
aws cloudformation delete-stack --stack-name taskboard-dev
aws cloudformation describe-stacks --stack-name taskboard-dev --query 'Stacks[0].StackStatus'
An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id taskboard-dev does not exist
削除が終わると、スタック名での describe-stacks はこのメッセージになります。
組み込み関数一覧(短縮形・完全形・返る値)
「返る値」は、次のテンプレートでスタックを作り、Outputs に出た値をそのまま写したものです(EnvName は既定の dev、ImportValue は先ほどの taskboard-dev スタックの公開値)。
AWSTemplateFormatVersion: '2010-09-09'
Description: Intrinsic function samples (values appear in Outputs)
Parameters:
EnvName:
Type: String
Default: dev
Mappings:
EnvMap:
dev:
Size: small
prod:
Size: large
Conditions:
IsProd: !Equals [!Ref EnvName, prod]
Resources:
SampleQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub 'sample-${EnvName}'
Outputs:
RefParameter:
Value: !Ref EnvName
RefResource:
Value: !Ref SampleQueue
RefPseudo:
Value: !Ref AWS::Region
SubSimple:
Value: !Sub 'app-${EnvName}-${AWS::AccountId}'
SubWithMap:
Value: !Sub
- '${Prefix}/${EnvName}/logs'
- Prefix: archive
SubGetAtt:
Value: !Sub 'queue=${SampleQueue.QueueName}'
GetAttArn:
Value: !GetAtt SampleQueue.Arn
JoinList:
Value: !Join ['-', [app, !Ref EnvName, queue]]
SplitThenJoin:
Value: !Join ['|', !Split [',', 'web,api,batch']]
SelectSecond:
Value: !Select [1, [web, api, batch]]
SplitThenSelect:
Value: !Select [2, !Split [':', 'arn:aws:sqs:ap-northeast-1:123456789012:jobs']]
IfValue:
Value: !If [IsProd, 'production', 'not production']
FindInMapValue:
Value: !FindInMap [EnvMap, !Ref EnvName, Size]
ImportValueArn:
Value: !ImportValue taskboard-dev-JobQueueArn
aws cloudformation create-stack --stack-name fn-demo --template-body file://functions.yaml
aws cloudformation describe-stacks --stack-name fn-demo \
--query 'Stacks[0].Outputs[].[OutputKey,OutputValue]' --output text
RefParameter dev
RefResource https://sqs.ap-northeast-1.amazonaws.com/123456789012/sample-dev
RefPseudo ap-northeast-1
SubSimple app-dev-123456789012
SubWithMap archive/dev/logs
SubGetAtt queue=sample-dev
GetAttArn arn:aws:sqs:ap-northeast-1:123456789012:sample-dev
JoinList app-dev-queue
SplitThenJoin web|api|batch
SelectSecond api
SplitThenSelect sqs
IfValue not production
FindInMapValue small
ImportValueArn arn:aws:sqs:ap-northeast-1:123456789012:taskboard-dev-jobs
EnvName=prod で作ると、IfValue は production、FindInMapValue は large になりました。
一覧表
| 関数 | 短縮形(YAML) | 完全形(YAML) | 返る値の例 |
|---|---|---|---|
| Ref(パラメータ) | !Ref EnvName |
Ref: EnvName |
dev |
| Ref(リソース) | !Ref SampleQueue |
Ref: SampleQueue |
https://sqs.ap-northeast-1.amazonaws.com/123456789012/sample-dev |
| Ref(擬似パラメータ) | !Ref AWS::Region |
Ref: AWS::Region |
ap-northeast-1 |
| Sub | !Sub 'app-${EnvName}-${AWS::AccountId}' |
Fn::Sub: 'app-${EnvName}-${AWS::AccountId}' |
app-dev-123456789012 |
| Sub(変数の対応表つき) | !Sub ['${Prefix}/${EnvName}/logs', {Prefix: archive}] |
Fn::Sub: [...] |
archive/dev/logs |
| Sub(属性の埋め込み) | !Sub 'queue=${SampleQueue.QueueName}' |
Fn::Sub: 'queue=${SampleQueue.QueueName}' |
queue=sample-dev |
| GetAtt | !GetAtt SampleQueue.Arn |
Fn::GetAtt: [SampleQueue, Arn] |
arn:aws:sqs:ap-northeast-1:123456789012:sample-dev |
| Join | !Join ['-', [app, !Ref EnvName, queue]] |
Fn::Join: ['-', [app, {Ref: EnvName}, queue]] |
app-dev-queue |
| Select | !Select [1, [web, api, batch]] |
Fn::Select: [1, [web, api, batch]] |
api(番号は0始まり) |
| Split | !Split [',', 'web,api,batch'] |
Fn::Split: [',', 'web,api,batch'] |
リスト [web, api, batch](Join すると web|api|batch) |
| Split + Select | !Select [2, !Split [':', 'arn:aws:sqs:...']] |
Fn::Select: [2, {'Fn::Split': [':', ...]}] |
sqs |
| Equals | !Equals [!Ref EnvName, prod] |
Fn::Equals: [{Ref: EnvName}, prod] |
真偽(Conditions で使う) |
| If | !If [IsProd, 'production', 'not production'] |
Fn::If: [IsProd, 'production', 'not production'] |
not production(dev)/production(prod) |
| FindInMap | !FindInMap [EnvMap, !Ref EnvName, Size] |
Fn::FindInMap: [EnvMap, {Ref: EnvName}, Size] |
small(dev)/large(prod) |
| ImportValue | !ImportValue taskboard-dev-JobQueueArn |
Fn::ImportValue: taskboard-dev-JobQueueArn |
arn:aws:sqs:ap-northeast-1:123456789012:taskboard-dev-jobs |
| GetAZs | !GetAZs '' |
Fn::GetAZs: '' |
アベイラビリティーゾーン名のリスト |
完全形でも同じ値になることは、Ref:・Fn::GetAtt:・Fn::Sub:・Fn::Join:・Fn::Select:・Fn::Split:・Fn::ImportValue: で書いたテンプレートを別に作り、Outputs(a-b、x、a+b+c、region=ap-northeast-1 など)で確かめました。
GetAZs は公式リファレンスの記述にもとづきます。指定したリージョンのアベイラビリティーゾーンをアルファベット順のリストで返し、空文字列 '' は AWS::Region(スタックを作るリージョン)と同じ意味です。リファレンスには us-east-1 での戻り値の例として [ "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1e" ] が載っています。サブネットの配置先を決めるときは、!Select [0, !GetAZs ''] のように Select と組み合わせます。
書き方のコツ
| 場面 | 向いている関数 | 理由 |
|---|---|---|
| 値をそのまま渡す | !Ref |
もっとも短い |
| 文字列の中に値を埋め込む | !Sub |
${名前} で読みやすい。${論理ID.属性} で GetAtt 相当も書ける |
| リストを区切り文字でつなぐ | !Join |
リストをそのまま渡せる |
| 環境ごとに値を変える | !FindInMap |
値を Mappings に集められる |
| 作る・作らないを切り替える | Condition: |
リソース単位で切り替わる |
| プロパティだけ切り替える | !If と !Ref AWS::NoValue |
偽のときプロパティごと省ける |
公式リファレンスの組み込み関数の一覧には、このほか Fn::Base64、Fn::Cidr、Fn::ForEach、Fn::GetStackOutput、Fn::Length、Fn::ToJsonString、Fn::Transform、条件関数、ルール関数が載っています。組み込み関数を使える場所は、リソースのプロパティ、Outputs、メタデータ属性、更新ポリシー属性、そしてリソースを条件つきで作る箇所です。
擬似パラメータ
宣言なしで !Ref や !Sub から使える値です。今回の出力で確かめたものを載せます。
| 擬似パラメータ | 返った値 | 使った場所 |
|---|---|---|
AWS::Region |
ap-northeast-1 |
!Ref AWS::Region |
AWS::AccountId |
123456789012 |
!Sub '...${AWS::AccountId}' |
AWS::StackName |
taskboard-dev |
Export 名 taskboard-dev-JobQueueArn |
AWS::NoValue |
(プロパティを省く) | !If の偽の側 |
リソース別:Ref と GetAtt が返す値の早見表
Ref が返すものはリソースの種類によって違います。 名前のもの、URL のもの、ARN のものがあるので、迷ったらこの表を見てください。値は、各リソースを1つずつ作ったスタックの Outputs に出たものです。
| リソースタイプ | !Ref が返す値 |
例 | よく使う !GetAtt |
例 |
|---|---|---|---|---|
AWS::S3::Bucket |
バケット名 | refdemo-bucket-123456789012 |
.Arn |
arn:aws:s3:::refdemo-bucket-123456789012 |
| 同上 | − | − | .DomainName |
refdemo-bucket-123456789012.s3.amazonaws.com |
| 同上 | − | − | .RegionalDomainName |
refdemo-bucket-123456789012.s3.ap-northeast-1.amazonaws.com |
AWS::SQS::Queue |
キューの URL | https://sqs.ap-northeast-1.amazonaws.com/123456789012/refdemo-queue |
.Arn |
arn:aws:sqs:ap-northeast-1:123456789012:refdemo-queue |
| 同上 | − | − | .QueueName |
refdemo-queue |
AWS::SNS::Topic |
トピックの ARN | arn:aws:sns:ap-northeast-1:123456789012:refdemo-topic |
.TopicName |
refdemo-topic |
AWS::DynamoDB::Table |
テーブル名 | refdemo-table |
.Arn |
arn:aws:dynamodb:ap-northeast-1:123456789012:table/refdemo-table |
AWS::IAM::Role |
ロール名 | refdemo-role |
.Arn |
arn:aws:iam::123456789012:role/refdemo-role |
AWS::Lambda::Function |
関数名 | refdemo-function |
.Arn |
arn:aws:lambda:ap-northeast-1:123456789012:function:refdemo-function |
AWS::Logs::LogGroup |
ロググループ名 | /refdemo/app |
− | − |
AWS::EC2::VPC |
VPC の ID | vpc-ca5544c6cfae5dba2 |
.CidrBlock |
10.0.0.0/16 |
AWS::EC2::Subnet |
サブネットの ID | subnet-18e8c87291542b6e9 |
− | − |
AWS::EC2::SecurityGroup |
セキュリティグループの ID | sg-a049e94be491bf0fa |
.GroupId |
sg-a049e94be491bf0fa |
| ほしい値 | 書き方の目安 |
|---|---|
| ARN がほしい | SNS トピックは !Ref、それ以外の多くは !GetAtt 論理ID.Arn |
| 名前がほしい | S3・DynamoDB・IAM ロール・Lambda は !Ref、SQS は !GetAtt 論理ID.QueueName、SNS は !GetAtt 論理ID.TopicName |
| URL がほしい | SQS は !Ref |
IAM ロールの ARN を Lambda 関数の Role に渡すときは !GetAtt Role.Arn、SNS のサブスクリプションの TopicArn には !Ref Topic、というように、渡す先が求める形(名前か ARN か)に合わせて Ref と GetAtt を選びます。
テンプレート検査で出るエラー文と直し方
デプロイの前に、テンプレートを手元で検査しておくと修正が早く済みます。ここでは CloudFormation のテンプレート検査ツール cfn-lint(pip install cfn-lint)の出力を載せます。AWS 側で構文を確かめる aws cloudformation validate-template --template-body file://template.yaml と併用できます。
cfn-lint template.yaml
問題が無ければ何も出力せず、終了コード 0 で戻ります(上の完成 YAML はこの状態です)。
未定義のパラメータを参照している
AWSTemplateFormatVersion: '2010-09-09'
Resources:
JobQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub '${EnvName}-jobs'
Tags:
- Key: App
Value: !Ref AppName
E1019 'EnvName' is not one of ['JobQueue', 'AWS::AccountId', 'AWS::NoValue', 'AWS::NotificationARNs', 'AWS::Partition', 'AWS::Region', 'AWS::StackId', 'AWS::StackName', 'AWS::URLSuffix']
undefined-ref.yaml:6:7
E1020 'AppName' is not one of ['JobQueue', 'AWS::AccountId', 'AWS::NoValue', 'AWS::NotificationARNs', 'AWS::Partition', 'AWS::Region', 'AWS::StackId', 'AWS::StackName', 'AWS::URLSuffix']
undefined-ref.yaml:9:11
…(省略)…
is not one of [...] の中身が、その場所で参照できる名前の一覧(リソースの論理 ID と擬似パラメータ)です。EnvName と AppName が入っていないので、Parameters に足します。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
EnvName:
Type: String
Default: dev
AppName:
Type: String
Default: taskboard
Resources:
JobQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub '${EnvName}-jobs'
Tags:
- Key: App
Value: !Ref AppName
直したあとの cfn-lint は出力なし(終了コード 0)でした。
循環参照になっている
2つのリソースが互いの属性を参照すると、どちらを先に作ればよいか決まりません。
AWSTemplateFormatVersion: '2010-09-09'
Resources:
QueueA:
Type: AWS::SQS::Queue
Properties:
Tags:
- Key: Pair
Value: !GetAtt QueueB.QueueName
QueueB:
Type: AWS::SQS::Queue
Properties:
Tags:
- Key: Pair
Value: !GetAtt QueueA.QueueName
E3004 Circular Dependencies for resource QueueA. Circular dependency with [QueueB]
circular.yaml:3:3
E3004 Circular Dependencies for resource QueueB. Circular dependency with [QueueA]
circular.yaml:9:3
直し方は、相手の「属性」を参照するのをやめ、パラメータから同じ文字列を組み立てることです。 名前を自分で決めておけば、相手のリソースを待つ必要がなくなります。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
EnvName:
Type: String
Default: dev
Resources:
QueueA:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub '${EnvName}-queue-a'
Tags:
- Key: Pair
Value: !Sub '${EnvName}-queue-b'
QueueB:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub '${EnvName}-queue-b'
Tags:
- Key: Pair
Value: !Sub '${EnvName}-queue-a'
こちらも直したあとは出力なし(終了コード 0)です。
そのほかのエラー文と直し方
それぞれ最小のテンプレートで再現した出力です。
| 原因 | cfn-lint の出力 | 直し方 |
|---|---|---|
| Resources セクションが無い | E1001 'Resources' is a required property |
リソースを1つ以上書く |
| インデントがずれている | E0000 mapping values are not allowed in this context(indent.yaml:7:19) |
表示された行・桁の字下げを揃える |
| 定義していない条件名を使った | E3015 'IsProd' is not one of [] |
Conditions に IsProd を定義する |
| 無い属性を GetAtt した | E6101 'Url' is not one of ['Arn', …, 'QueueName', 'QueueUrl', …] |
一覧にある属性名(QueueUrl など)に直す |
| リソースタイプの綴り | E3006 Resource type 'AWS::SQS::Queu' does not exist in 'us-east-1' |
AWS::SQS::Queue に直す |
| Mappings に無いキーを引いた | E1011 'prod' is not one of ['dev'] for mapping 'EnvMap' |
Mappings に prod の行を足す |
| プロパティ名の綴り | E3002 Additional properties are not allowed ('QueueNames' was unexpected. Did you mean 'QueueName'?) |
提案された名前に直す |
| 値の型が違う | E3012 'thirty' is not of type 'integer' |
数値で書く |
| 使っていないパラメータ(警告) | W2001 Parameter EnvName not used. |
使うか、消す |
| 変数の無い Sub(警告) | W1020 'Fn::Sub' isn't needed because there are no variables |
!Sub を外す |
出力の2行目(ファイル名:行:桁)が該当箇所です。E で始まるものはエラー、W で始まるものは警告で、終了コードはエラーがあると 2、警告だけだと 4 でした。
よくある質問
Q. テンプレートに最低限必要なものは何ですか?
A. Resources セクションと、その中のリソース1つ以上です。AWSTemplateFormatVersion や Description は任意ですが、書いておくと読み手に親切です。
Q. !Ref と !GetAtt はどう使い分けますか?
A. !Ref はリソースの「代表的な識別子」(S3 はバケット名、SQS は URL、SNS は ARN)を返し、!GetAtt は属性を名前で指定して取り出します。ARN がほしい場面の多くは !GetAtt 論理ID.Arn です。
Q. !Sub と !Join はどちらを使えばよいですか?
A. 文字列に値を埋め込むなら !Sub が読みやすく、リストを区切り文字でつなぐなら !Join が向いています。同じ app-dev-queue はどちらでも作れます。
Q. 開発用と本番用でテンプレートを分ける必要はありますか?
A. 1本で済ませられます。違いを Parameters で受け取り、値の違いは Mappings と !FindInMap、作る・作らないの違いは Conditions で表します。この記事の完成 YAML がその形です。
まとめ
- テンプレートで必須なのは Resources だけ。Parameters・Mappings・Conditions・Outputs を足すと使い回せる
!Refが返す値はリソースごとに違う(S3 は名前、SQS は URL、SNS は ARN)。ARN は多くの場合!GetAtt 論理ID.Arn- 文字列の組み立ては
!Sub、環境ごとの値は!FindInMap、切り替えはCondition:と!If - プロパティごと省きたいときは
!Ref AWS::NoValue - スタック間の受け渡しは Outputs の
Exportと!ImportValue - デプロイ後は
describe-stacks --query 'Stacks[0].Outputs'で値を確かめる - デプロイ前に cfn-lint で検査すると、未定義の参照や循環参照を行番号つきで見つけられる
参考・出典
- AWS CloudFormation User Guide「CloudFormation template sections」: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html
- AWS CloudFormation Template Reference「Intrinsic function reference」: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/intrinsic-function-reference.html
- AWS CloudFormation Template Reference「Fn::GetAZs」: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/intrinsic-function-reference-getavailabilityzones.html
- AWS CLI Command Reference「cloudformation deploy」: https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deploy.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CloudFormationテンプレートの書き方と組み込み関数一覧」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。