CloudFormation ForEach と FindInMap を使ってセキュリティグループを作ってみた

2023.07.30

アノテーション 構築チームのいたくらです。
Fn::ForEach と Fn::FindInMap を使ってセキュリティグループを作ってみました。
Fn::ForEach で出来なかった部分が Fn::FindInMap で補えていい感じです。

Fn::ForEach や Fn::FindInMap について

とてもざっくりお伝えすると Fn::ForEach はループ処理のような機能を持つ関数で、
Fn::FindInMap は Mapping セクションで定義された値を取得する関数です。
詳細は公式ドキュメントを参照ください。
あと今回は shima さんのブログも参考にさせていただきました。

まえがき

shima さんのブログを拝見し、先日書いたこちらのブログのテンプレートを Fn::FindInMap で書き直せばいけそう!と思い、やってみたら成功しました!圧倒的感謝!

セキュリティグループを作成するテンプレート例

テンプレートが長いので閉じてあります。
以下のトグルを押すとテンプレートが開きます。
先日のブログで示したテンプレートから変更した部分をハイライトにしています。

CloudFormation テンプレート
AWSTemplateFormatVersion: "2010-09-09"
Transform: 'AWS::LanguageExtensions'
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
  VpcId:
    Type: String
    Default: "vpc-xxxxxxxxxxxxxxxxx" # あらかじめ作成しておいた VPC ID
  SgList:
    Type: List<String>
    # ForEach で コレクションとして使用する値(Mappings の TopLevelKey と合わせる)
    Default: "SgIngress1,SgIngress2"

# ------------------------------------------------------------#
# Mappings
# ------------------------------------------------------------#
Mappings:
  SgMappings:
    SgIngress1:
      Cidr: "XX.XX.XX.XX/32" //自分の Global IP 等を適宜指定
      FromPort: "80"
      ToPort: "80"
    SgIngress2:
      Cidr: "XX.XX.XX.XX/32"
      FromPort: "443"
      ToPort: "443"

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  SecurityGroupAlb:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security Group for ALB"
      GroupName: "test-sg-alb"
      Tags:
        - Key: "Name"
          Value: "test-sg-alb"
      VpcId: !Ref VpcId

  'Fn::ForEach::LoopIngress': # LoopPort の部分はテンプレート内で一意であること
  - SgIdentifier # ループ変数
  - !Ref SgList # ループ変数で呼び出したい値(公式ドキュメントではコレクションと呼ぶ)
  - '${SgIdentifier}': # ループで作成されるリソースの論理 ID
      Type: AWS::EC2::SecurityGroupIngress
      Properties:
        CidrIp: !FindInMap
          - SgMappings # FindInMap の引数: MapName
          - !Ref SgIdentifier # FindInMap の引数: TopLevelKey
          - Cidr # FindInMap の引数: SecondLevelKey
        FromPort: !FindInMap 
          - SgMappings
          - !Ref SgIdentifier
          - FromPort
        ToPort: !FindInMap 
          - SgMappings
          - !Ref SgIdentifier
          - ToPort
        IpProtocol: "TCP"
        GroupId: !Ref SecurityGroupAlb

このテンプレートで作成できたセキュリティグループがこちら。

先日のブログと違って、HTTP と HTTPS のインバウンドルールが設定できています。

疑問:複数回呼び出しできないんじゃなかったのか?

先日書いたブログのここを見ていただくと分かるように、ループ変数を複数回参照したらエラーが出ました。
でも今回のテンプレートで複数回参照しても問題なく成功したのはなぜなのでしょう。

Fn::FindInMap の引数としてループ変数が参照されているだけで、実際にオブジェクトで参照しているのは Mappings で定義した値。
ループ変数をオブジェクトで直接参照できるのは 1 回のみだけど、引数として参照するなら複数回可能、って感じなんでしょうか。
難しいですね・・・

あとがき

先日のブログで「Fn::ForEach はセキュリティグループに向いてないかもしれません」と書いてましたが、「Fn::ForEach だけだと、」に修正しました(笑)
CloudFormation 歴が浅いため、恥ずかしながら後からこんなこともできるのか!と知ることが多く、とても勉強になりました。

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。
「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。
現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。