CloudFormationのForEachのループ処理を利用し、パラメータに記載した複数のメールアドレスを1つのSNSトピックに一括登録してみた

2023.09.07

はじめに

AWS CloudFormationを使って、Amazon SNSの一つのトピックへ複数のメールアドレスをサブスクリプションとして登録したいケースがありました。

具体的には、新規アカウント発行後に、アカウントへの初期設定として、複数の担当者へ通知を送るためのSNSトピックを作成するCloudFormationテンプレートが必要でした。

このテンプレートは、各アカウントによって担当者や人数は変わることから、サブスクリプションするメールアドレスを動的に変更できる設定が求められました。

例えば、担当者が5名の場合、作成するSNSトピックは、以下のイメージです。

これを実現するためのテンプレートについて解説します。

ForEach関数を利用したテンプレート

複数のメールアドレスをSNSトピックに登録するためのテンプレートは以下のようになります

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::LanguageExtensions
Description: SNS Sample Stack

Parameters:
  SnsTopicName:
    Description: Topic name
    Type: String
    Default: notify-mail-sns
  MailAddressDomain:
    Description: Domain name of mail address
    Type: String
    Default: example.com
  AddressUserList:
    Description: User name of mail address
    Type: List<String>
    Default: test1,test2,test3,test4,test5

Resources:
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Ref SnsTopicName
  Fn::ForEach::UserName:
    - MailAddressUserName
    - !Ref AddressUserList
    - Topic${MailAddressUserName}:
        Type: AWS::SNS::Subscription
        Properties:
          Endpoint: !Sub ${MailAddressUserName}@${MailAddressDomain}
          Protocol: email
          TopicArn: !Ref SnsTopic

例として、以下のメールアドレスを1つのサブスクリプションに登録するSNSトピックを作成します。

  • test1@example.com
  • test2@example.com
  • test3@example.com
  • test4@example.com
  • test5@example.com

メールアドレスというのは、ユーザー名と、@、ドメイン名で構成されています。(ユーザー名@ドメイン名

それぞれに対応するパラメータは以下のとおりです

  • AddressUserList:メールアドレスのユーザー名をカンマ区切りで入力します。
  • MailAddressDomain:ドメイン名
  • SnsTopicName:作成するSNSトピックの名前

この設定でスタックを作成すると、5つのメールアドレスに対して通知するSNSのトピックが作成されます。

パラメータのAddressUserListに担当者のメールアドレスのユーザー名を入力するだけで、テンプレートのForEach関数がそれぞれのアドレスを1つのSNSトピックにサブスクライブします。

ドメインが異なるメールアドレス

ただし、上記テンプレートの例では、ドメインが異なるメールアドレスを登録することができません。

原因は、CloudFormationの論理IDには、英数字のみが有効であるためです。

論理IDとは、CloudFormationテンプレート内でリソースを一意に識別するための名前です。特殊文字(「@」「:」「-」など)を含めることはできません。

そのため、下記のように、メールアドレスをパラメータに入れてForEach関数を利用すると、23行目のOutputKeyは、論理IDになりますので、Topictest1@example.comTopictest2@example.comが論理IDとなり、エラーになります。

したがって、メールアドレス全体をパラメータとしてForEach関数を利用すると、23行目のOutputKeyが、論理IDになりますので、Topictest1@example.comTopictest2@example.comが許可されない形式となり、エラーが発生します。

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::LanguageExtensions
Description: SNS Sample Stack

Parameters:
  SnsTopicName:
    Description: Topic name
    Type: String
    Default: notify-mail-sns
  MailAddressList:
    Description: User name of mail address
    Type: List<String>
    Default: test1@example.com,test2@example.org,test3@example.net

Resources:
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Ref SnsTopicName
  Fn::ForEach::UserName:
    - MailAddress<img src="https://d1tlzifd8jdoy4.cloudfront.net/wp-content/uploads/2023/08/b89a5dca19b12db09ba8e0bde4ee33ef-640x239.png" alt="" width="640" height="239" class="alignnone size-medium wp-image-1130213" />
    - !Ref MailAddressList
    - Topic${MailAddress}:
        Type: AWS::SNS::Subscription
        Properties:
          Endpoint: !Ref MailAddress
          Protocol: email
          TopicArn: !Ref SnsTopic

以下が実際のエラー内容です。OutputKeyは、英数字のみにするよう怒られています。

Transform AWS::LanguageExtensions failed with: OutputKey 'Topictest1@example.com' should be alphanumeric. 
Rollback requested by user.

回避方法があれば教えていただきたいです。。

Conditionsを利用したテンプレート

不格好ですが、Conditionsセクションを利用してドメインが異なるメールアドレスを登録することが可能です

AWSTemplateFormatVersion: 2010-09-09
Description: SNS Sample Stack

Parameters:
  SnsTopicName:
    Description: Topic name
    Type: String
    Default: notify-mail-sns
  MailAddress1:
    Description: Email address to add to SNS
    Type: String
    Default: test1@example.org
  MailAddress2:
    Description: Email address to add to SNS
    Type: String
    Default: test2@example.com
  MailAddress3:
    Description: Email address to add to SNS
    Type: String

Conditions:
  IsMail1Set: !Not [!Equals [!Ref MailAddress1, '']]
  IsMail2Set: !Not [!Equals [!Ref MailAddress2, '']]
  IsMail3Set: !Not [!Equals [!Ref MailAddress3, '']]

Resources:
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Ref SnsTopicName
  Mail1Subscription:
    Type: AWS::SNS::Subscription
    Condition: IsMail1Set
    Properties:
      Endpoint: !Ref MailAddress1
      Protocol: email
      TopicArn: !Ref SnsTopic
  Mail2Subscription:
    Type: AWS::SNS::Subscription
    Condition: IsMail2Set
    Properties:
      Endpoint: !Ref MailAddress2
      Protocol: email
      TopicArn: !Ref SnsTopic
  Mail3Subscription:
    Type: AWS::SNS::Subscription
    Condition: IsMail3Set
    Properties:
      Endpoint: !Ref MailAddress3
      Protocol: email
      TopicArn: !Ref SnsTopic

例として、以下のメールアドレスを1つのサブスクリプションに登録するSNSトピックを作成します。

  • test1@example.org
  • test2@example.com
  • test3@example.net

各メールアドレスに対応するパラメータは以下の通りです

  • MailAddress1MailAddress2MailAddres3:登録するメールアドレス
  • SnsTopicName: 作成するSNSトピックの名前

この設定でスタックを作成すると、3つのメールアドレスが通知対象とするSNSのトピックが作成されます。

パラメータMailAddress1, MailAddress2, MailAddress3には通知を受け取りたいメールアドレスを記載します。

もし、2つだけが必要な場合には、MailAddress3を空白にしておけばOKです。

4人以上のメールアドレスが必要な場合は、テンプレートのConditionsセクションとResourcesセクションを適宜増やすだけです。

必要になるメールアドレス上限数分をテンプレートに作成しておくとよいでしょう。

参考