内容
- AWS CloudFormation が Fn::ForEach 組み込み関数によるループ機能を発表
- Fn:: ForEach を使用するには、AWS::LanguageExtensions 変換を宣言する。
というのがざっくりした内容です。
リソースが少ない場合は、1 つ 1 つ書いていけば良いですが、リソースが多数存在する場合は少ないコード数で記載することができます。
AWS CDK を利用すればプログラミング言語内でループ処理の実現は可能ですが、プログラミング言語に熟知していないインフラエンジニアの方などにとっては CloudFormation でループ処理ができるため嬉しいアップデートですね。
やってみた
公式の Examples を元にいくつか動作確認してみました。
Resources
Resources1
success,Failure,Timeout,Unknown の 4 つの SnsTopic を作る例です。
FifoTopic: true では動かなかったためコメントアウトしています。
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
'Fn::ForEach::Topics':
- TopicName
- - Success
- Failure
- Timeout
- Unknown
- 'SnsTopic${TopicName}':
Type: 'AWS::SNS::Topic'
Properties:
TopicName: !Ref TopicName
# FifoTopic: true
Cloudformation の「処理されたテンプレートの表示」から確認すると次の通り処理されました。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"SnsTopicSuccess": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "Success"
}
},
"SnsTopicFailure": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "Failure"
}
},
"SnsTopicTimeout": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "Timeout"
}
},
"SnsTopicUnknown": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "Unknown"
}
}
}
Resources2
Points,Score,Name,Leaderboard の 4 つの DynamoDB Table を作成する例です。
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
'Fn::ForEach::Tables':
- TableName
- [Points, Score, Name, Leaderboard]
- 'DynamoDB${TableName}':
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: !Ref TableName
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: '5'
WriteCapacityUnits: '5'
Cloudformation の「処理されたテンプレートの表示」から確認すると次の通り処理されました。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"DynamoDBPoints": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "Points",
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5"
}
}
},
"DynamoDBScore": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "Score",
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5"
}
}
},
"DynamoDBName": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "Name",
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5"
}
}
},
"DynamoDBLeaderboard": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "Leaderboard",
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5"
}
}
}
}
}
Resources3
次の例では、
インスタンスA は、 t2.micro(default)
インスタンスB は、 t2.small
インスタンスC は、 t3.small
とそれぞれ異なったプロパティを参照する例です。
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Mappings:
Instances:
InstanceType:
B: t2.small
C: t3.small
Resources:
'Fn::ForEach::Instances':
- Identifier
- [A, B, C]
- 'Instance${Identifier}':
Type: 'AWS::EC2::Instance'
Properties:
InstanceType: !FindInMap [Instances, InstanceType, !Ref 'Identifier', {DefaultValue: t2.micro}]
ImageId: ami-04beabd6a4fb6ab6f
Outputs:
SecondInstanceId:
Description: Instance Id for InstanceB
Value: !Ref 'InstanceB'
SecondPrivateIp:
Description: Private IP for InstanceB
Value: !GetAtt [InstanceB, PrivateIp]
Cloudformation の「処理されたテンプレートの表示」から確認すると次の通り処理されました。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Mappings": {
"Instances": {
"InstanceType": {
"B": "t2.small",
"C": "t3.small"
}
}
},
"Outputs": {
"SecondInstanceId": {
"Description": "Instance Id for InstanceB",
"Value": {
"Ref": "InstanceB"
}
},
"SecondPrivateIp": {
"Description": "Private IP for InstanceB",
"Value": {
"Fn::GetAtt": [
"InstanceB",
"PrivateIp"
]
}
}
},
"Resources": {
"InstanceA": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-04beabd6a4fb6ab6f"
}
},
"InstanceB": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.small",
"ImageId": "ami-04beabd6a4fb6ab6f"
}
},
"InstanceC": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t3.small",
"ImageId": "ami-04beabd6a4fb6ab6f"
}
}
}
}
Resources4
次の例では、
インスタンスA は、 t2.micro
インスタンスB は、 t2.small
インスタンスC は、 t3.small
とそれぞれ異なったプロパティを繰り返し処理する例です。
結果は、Resources3 と同じです
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Mappings:
InstanceA:
Properties:
ImageId: ami-04beabd6a4fb6ab6f
InstanceType: t2.micro
InstanceB:
Properties:
ImageId: ami-04beabd6a4fb6ab6f
InstanceType: t2.small
InstanceC:
Properties:
ImageId: ami-04beabd6a4fb6ab6f
InstanceType: t3.small
Resources:
'Fn::ForEach::Instances':
- InstanceLogicalId
- [InstanceA, InstanceB, InstanceC]
- '${InstanceLogicalId}':
Type: 'AWS::EC2::Instance'
Properties:
'Fn::ForEach::Properties':
- PropertyName
- [ImageId, InstanceType]
- '${PropertyName}':
'Fn::FindInMap':
- Ref: 'InstanceLogicalId'
- Properties
- Ref: 'PropertyName'
- {DefaultValue: !Ref 'AWS::NoValue'}
Cloudformation の「処理されたテンプレートの表示」から確認すると次の通り処理されました。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Mappings": {
"InstanceA": {
"Properties": {
"ImageId": "ami-04beabd6a4fb6ab6f",
"InstanceType": "t2.smicro"
}
},
"InstanceB": {
"Properties": {
"ImageId": "ami-04beabd6a4fb6ab6f",
"InstanceType": "t2.small"
}
},
"InstanceC": {
"Properties": {
"ImageId": "ami-04beabd6a4fb6ab6f",
"InstanceType": "t3.small"
}
}
},
"Resources": {
"InstanceA": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-04beabd6a4fb6ab6f",
"InstanceType": "t2.micro"
}
},
"InstanceB": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-04beabd6a4fb6ab6f",
"InstanceType": "t2.small"
}
},
"InstanceC": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-04beabd6a4fb6ab6f",
"InstanceType": "t3.small"
}
}
}
}
Outputs
Outputs1
次の例では、
ForEach_S3Bucket_A
ForEach_S3Bucket_B
という S3バケットを作成し、
S3バケットの Arn と DomainName を 出力しています。
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
'Fn::ForEach::Buckets':
- Identifier
- [A, B]
- 'S3Bucket${Identifier}':
Type: 'AWS::S3::Bucket'
Outputs:
'Fn::ForEach::BucketOutputs':
- Identifier
- [A, B]
- 'Fn::ForEach::GetAttLoop':
- Property
- [Arn, DomainName]
- 'S3Bucket${Identifier}${Property}':
Value: !GetAtt [!Sub 'S3Bucket${Identifier}', !Ref Property]
Cloudformation の「処理されたテンプレートの表示」から確認すると次の通り処理されました。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
"S3BucketAArn": {
"Value": {
"Fn::GetAtt": [
"S3BucketA",
"Arn"
]
}
},
"S3BucketADomainName": {
"Value": {
"Fn::GetAtt": [
"S3BucketA",
"DomainName"
]
}
},
"S3BucketBArn": {
"Value": {
"Fn::GetAtt": [
"S3BucketB",
"Arn"
]
}
},
"S3BucketBDomainName": {
"Value": {
"Fn::GetAtt": [
"S3BucketB",
"DomainName"
]
}
}
},
"Resources": {
"S3BucketA": {
"Type": "AWS::S3::Bucket"
},
"S3BucketB": {
"Type": "AWS::S3::Bucket"
}
}
}