0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CloudFormation で ループ機能を実現(Fn::ForEach 組み込み関数)

Last updated at Posted at 2023-08-15

内容

  • 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"
    }
  }
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?