📕

CloudFormationのFn::ForEachを使う

2024/08/29に公開

はじめに

今更だけど初めてForEachを使ったのでメモ
複数のIAMユーザーを作りたいときにForEachが便利

ForEach

昨年の7月にFn::ForEachが追加された。
以前は繰り替えし処理はできなかったので、このアップデートが追加されたときにはタイムラインが少しだけ盛り上がってた気がする。
https://aws.amazon.com/jp/about-aws/whats-new/2023/07/accelerate-cloudformation-authoring-experience-looping-function/

基本的な使い方

以下の資料がわかりやすい。
https://dev.classmethod.jp/articles/cloudformation-foreach/

構文が見慣れないので、こういうものと受け入れて使っていくとよさそう。

cloudformation.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
  'Fn::ForEach::UserLoop':
    - Name
    - - Alice
      - Bob
      - Charlie
    - 'User${Name}':
        Type: 'AWS::IAM::User'
        DeletionPolicy: Delete
        Properties:
          UserName: !Sub '${Name}-${AWS::AccountId}'

デプロイするとちゃんと意図通り3人分のIAMユーザーが作成された。
IAMユーザー

ForEachは以下のような構文で構成されている

'Fn::ForEach::UniqueLoopName':
    - Identifier
    - - Value1 # Collection
      - Value2
    - 'OutputKey':
        OutputValue

意味は以下の通りだが、公式ドキュメントの悪い部分が出いている。(参照)

UniqueLoopName
このループの名前。名前はテンプレート内で一意である必要があり、テンプレートの Resources セクションのどの論理 ID 値とも競合できません。この名前は変換された出力には含まれません。

Identifier
複製されたテンプレートフラグメントを表す、OutputKey と OutputValue パラメーターで置き換える識別子。OutputKey と OutputValue パラメータにある ${Identifier} または &{Identifier} のすべてのインスタンスは、Collection パラメータの値に置き換えられます。

Collection
反復処理の対象となる値のコレクション。このパラメーターの配列または Ref に対する CommaDelimitedList の場合があります。&{Identifier} を使用すると、英数字以外の文字を Collection に渡すことができます。

OutputKey
変換されたテンプレートのキー。${Identifier} または &{Identifier} が OutputKey パラメータに含まれている必要があります。たとえば、テンプレートの Resources セクションで Fn::ForEach が使用されている場合、これは各リソースの論理 ID です。
&{} 構文では、OutputKey パラメータで使用する Collection に英数字以外の文字を使用できます。

OutputValue
Collection パラメータの各項目に変換されたテンプレートで複製される値。たとえば、Fn::ForEach をテンプレートの Resources セクションで使用する場合、これは各リソースを設定するために繰り返されるテンプレートフラグメントです。

これもわかりやすいわけではないけども、自分は以下のように理解している。

  • UniqueLoopName
    • ユニークであれば何でもいい。これ自体を!Refなどで参照することもないと思っている
  • Identifier
    • ForEach内で参照するために使う
      • ${Identifieer}は後述するOutputValue内で参照される
  • Collection
    • 繰り替えしたい要素のリスト
      • 上記のようなフロースタイルでも[Alice, Bob, Charlie]のようなブロックスタイルでもいい
      • よく見るのがCloudFormationのParametersCommaDelimitedListにするやつ
    • 参考:SNSリソースの複製
  • OutputKey
    • CloudFormation上の論理IDになる。したがってユニークである必要がある。
    • ${Identifier}を含む必要がある
  • OutputValue
    • 繰り返し処理を行う場所。${Identifier}を使うことでリストの要素を処理できる。

ユーザーごとにIAMユーザーを2個作る

若干特殊な場合だと思うけども、1ユーザごとに用途が異なるIAMユーザーを作成する。(CLIやデプロイ用のものと、モバイル用のConsoleログイン用。自分の管理下にIICがないのでこういう感じになった)

Fn::ForEachでは、Multiple key-value pairsを記載できるので以下のような形式になる。
最初はそんな書き方できること知らなかったけども、公式ドキュメントのサンプルにもあるFn::ForEachを入れ子してるのと同じことなのかもしれない。
yamlのアンカー・エイリアスでなんとかできるのかと思ってたけども、CloudFormationが対応していないらしい。

AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
  'Fn::ForEach::UserLoop':
    - Name
    - - Alice
      - Bob
      - Charlie
    - 'User${Name}':
        Type: 'AWS::IAM::User'
        DeletionPolicy: Delete
        Properties:
          UserName: !Sub 'User-${Name}-${AWS::AccountId}'
      'Console${Name}':
        Type: 'AWS::IAM::User'
        DeletionPolicy: Delete
        Properties:
          UserName: !Sub 'Console-${Name}-${AWS::AccountId}'

実行すると意図通りのユーザーが作成される
ForEach

以下、参考
https://github.com/aws-cloudformation/cfn-language-discussion/blob/main/RFCs/0009-Fn%3A%3AForEach.md#solution
https://zenn.dev/mjxo/articles/6bae64fa9333cf#まずは感謝したい動画

入れ子にしたFn::ForEach

サンプルの通り、Fn::ForEachは入れ子にできるので、先ほどの例は以下のように書き換えることができる。
この場合はOutputKey${Identifier}を含まなくていいらしい

AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
  'Fn::ForEach::TypeLoop':
    - UseType
    - - User
      - Console
    - 'Fn::ForEach::UserLoop':
      - Name
      - - Alice
        - Bob
        - Charlie
      - '${UseType}${Name}':
          Type: 'AWS::IAM::User'
          DeletionPolicy: Delete
          Properties:
            UserName: !Sub '${UseType}-${Name}-${AWS::AccountId}'

同じものができている。(内容同じなので画像は使いまわし。。。)
ForEach

おわりに

こういう繰り返しはCDKのほうがまぁ楽ではあるけども、CDKをデプロイする用のIAMユーザーとかはCloudFormationでデプロイするようにしているので、Fn::ForEachに多少慣れた気がする。

Discussion