AWS-CloudFormation

CloudFormation でループ処理が使えるようになったらしいので早速使ってみた – Fn::ForEach

ご挨拶

お疲れさまです、寺井です。

先月のAWSアップデートをチェックしてたところ、目を引くアップデートを見つけました。

  • Accelerate your CloudFormation authoring experience with looping function
    AWS CloudFormationがFn::ForEach関数によるループ機能に対応しました。この機能により同じコード行を複製する簡単かつ迅速になり、見落としや更新漏れ等を防ぐのを可能にします。
    週刊AWS – 2023/7/24週

効率化が好きな人にとっては待望のアップデートだと思いますので、具体的にどのように使えるのか試してみようと思います。

※本記事では「Fn::ForEach」組み込み関数に焦点を置いてお話します。
CloudFormationの具体的な使い方や、IAMなどに関する部分については触れておりません。

「Fn::ForEach」を活用したCloudFormationテンプレート

まずは今回作成したテンプレートを紹介します。
そのままコピペで使えるようになっておりますので、ぜひご活用ください!

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::LanguageExtensions # 「Fn::ForEach」を使えるようにする宣言(※必須)
Parameters:
  DeveloperNames: # 配列を定義(ここで定義した値の数だけリソースが作成される)
    Type: List<String>
    Default: dev1, dev2, dev3, dev4, dev5
  Ec2ImageId: # SSM Parameter StoreからAmazon Linux2のAMIを取得
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  Ec2InstanceType: # EC2インスタンスタイプを指定
    Type: String
    Default: t3.micro

Resources:
  'Fn::ForEach::DeveloperLoop': # ループ処理
    - DeveloperName
    - !Ref DeveloperNames # Parametersセクションで定義した配列を使用
    - '${DeveloperName}VPC': # VPCの作成
        Type: 'AWS::EC2::VPC'
        Properties:
          CidrBlock: '10.0.0.0/16'
          Tags:
            - Key: Name
              Value: !Sub '${DeveloperName}-vpc'
      '${DeveloperName}InternetGateway': # インターネットゲートウェイの作成
        Type: 'AWS::EC2::InternetGateway'
      '${DeveloperName}VPCGatewayAttachment': # VPCとインターネットゲートウェイの関連付け
        Type: 'AWS::EC2::VPCGatewayAttachment'
        Properties:
          VpcId: !Ref
            'Fn::Sub': '${DeveloperName}VPC'
          InternetGatewayId: !Ref
            'Fn::Sub': '${DeveloperName}InternetGateway'
      '${DeveloperName}RouteTable': # ルートテーブルの作成
        Type: 'AWS::EC2::RouteTable'
        Properties:
          VpcId: !Ref
            'Fn::Sub': '${DeveloperName}VPC'
      '${DeveloperName}Route': # インターネットへのルートの作成
        Type: 'AWS::EC2::Route'
        Properties:
          RouteTableId: !Ref
            'Fn::Sub': '${DeveloperName}RouteTable'
          DestinationCidrBlock: '0.0.0.0/0'
          GatewayId: !Ref
            'Fn::Sub': '${DeveloperName}InternetGateway'
      '${DeveloperName}Subnet': # サブネットの作成
        Type: 'AWS::EC2::Subnet'
        Properties:
          VpcId: !Ref
            'Fn::Sub': '${DeveloperName}VPC'
          CidrBlock: '10.0.0.0/24'
          MapPublicIpOnLaunch: true
          AvailabilityZone: !Select [ 0, !GetAZs '' ]
          Tags:
            - Key: Name
              Value: !Sub '${DeveloperName}-subnet'
      '${DeveloperName}SubnetRouteTableAssociation': # サブネットとルートテーブルの関連付け
        Type: 'AWS::EC2::SubnetRouteTableAssociation'
        Properties:
          SubnetId: !Ref
            'Fn::Sub': '${DeveloperName}Subnet'
          RouteTableId: !Ref
            'Fn::Sub': '${DeveloperName}RouteTable'
      '${DeveloperName}EC2': # EC2インスタンスの作成
        Type: 'AWS::EC2::Instance'
        Properties:
          InstanceType: !Ref Ec2InstanceType
          SubnetId: !Ref
            'Fn::Sub': '${DeveloperName}Subnet'
          ImageId: !Ref Ec2ImageId
          Tags:
            - Key: Name
              Value: !Sub '${DeveloperName}-ec2'

完成するリソース

  • ネットワーク
    • VPC
    • インターネットゲートウェイ
    • サブネット
    • ルートテーブル
  • コンピューティング
    • EC2

というごくシンプルな構成ですが、
Parametersセクションで渡す{DeveloperNames}配列の要素の数分、まとめて作成されます。

今回だと{DeveloperNames}には、

dev1, dev2, dev3, dev4, dev5

という5つの値を設定しておりますので、1つのスタックで5つの環境がまとめて作成できるという寸法です。

これがあれば100個でも200個でも同じ環境の複製ができますね。
効率厨歓喜。

(あ、でもサービスクォータにはご注意ください。)

AWS CloudFormation のクォータ

Fn::ForEach 組み込み関数について

概要

組み込みFn::ForEach関数はコレクションとフラグメントを受け取り、コレクション内の項目を指定されたフラグメント内の識別子に適用します。Fn::ForEachそれ自体を含む他の組み込み関数を含めることができ、 Conditions、Outputs、Resources (リソース プロパティを含む) セクションFn::ForEach内で使用できます。これは、 Format version、description、Metadata、Transform、Parameters、Mappings、Rules、またはセクションでは使用できません。
Fn::ForEach - AWS CloudFormation

使い方まとめ

LanguageExtensions の追加

Fn::ForEach関数はデフォルトでは使用できない関数となっておりますので、まずは使用できるようにテンプレート内での宣言が必要です。
(Pythonでいうライブラリをimportする的な作業)

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::LanguageExtensions #←これ
︙

AWS CloudFormation にデフォルトで含まれていない組み込み関数やその他の機能を使用できるようにします。
AWS::LanguageExtensions transform - AWS CloudFormation

構文

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

公式の構文と、先ほど紹介したテンプレートを照らし合わせながら、それぞれの項目ごとに見ていきます。

UniqueLoopName: ループの一意な名前

どんな名前でも大丈夫ですが、テンプレート内で一意である必要があります。
スタック内で同じループ名があると、後の方のループ処理で上書きされてしまいますので、重複しないように注意が必要です。

︙
Resources:
  'Fn::ForEach::DeveloperLoop': # UniqueLoopName
    - DeveloperName
︙

Identifier: ループ内で呼び出せる識別子

  • {任意の変数名}の形式で使用できる

今回は「Parameters」セクションで定義した[DeveloperNames]という配列を「!Ref」で参照しています。

︙
  'Fn::ForEach::DeveloperLoop':
    - DeveloperName # Identifier
    - !Ref DeveloperNames # Value(Collection)
︙

OutputKey: 各リソースの論理IDになる

先ほど宣言した識別子を使用して${DeveloperName}VPCと指定することで、${配列の値}VPCという形になり、一意の論理IDとして利用できます。

OutputValue: 繰り返される処理内容を記述

「OutputValue」の中では、「!Sub」と組み合わせたりして配列の値を使用することも可能です。

︙
    - '${DeveloperName}VPC': # OutputKey
        Type: 'AWS::EC2::VPC' # OutputValue ~
        Properties:
          CidrBlock: '10.0.0.0/16'
          Tags:
            - Key: Name
              Value: !Sub '${DeveloperName}-vpc'
      '${DeveloperName}InternetGateway': # OutputKey
        Type: 'AWS::EC2::InternetGateway'
︙

参考画像:実際に作成されるリソース(論理 ID)

まとめ

めちゃめちゃ良いなと思ったんですけど、「ImportValue」との組み合わせが上手く実現できなかったりで、慣れるまで時間がかかりそうです。
(これやるならCDKでも良いような…。ボソッ)

サクッと簡単なリソースを検証用に複数立てるときに便利なので、皆さんもぜひご利用ください。

ありがとうございました!

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA