CloudWatchアラームとSNSで日本語の件名・本文のメールを送るためのCloudFormationテンプレートを作ってみた

2022.02.24

データアナリティクス事業本部の鈴木です。

今回はCloudWatchアラームの状態遷移が起きた際に日本語の件名・本文のメールをSNSから送信する方法を調べました。また、構成をデプロイできるようCloudFormationテンプレートを作成したのでご紹介します。

やりたかったこと

CloudWatchアラームがアラーム状態に遷移した時に、日本語の件名・本文のメールをSNSから送信したいです。

例えば以下のような通知です。

メール例

調べていたところ、以下の二つの記事がとても参考になり、組み合わせてCloudFormationテンプレートにしてみました。

構成

CloudWatchアラームがアラーム状態に遷移するイベントをEventBridgeで検知し、SNSでメール通知します。SNSをEventBridgeのターゲットに直接指定すると件名が指定できないので、Step Functionsを介して呼び出します。[2] 基本の構成

作成したテンプレート

今回は、SQSでデッドレターキューを運用しているとして、キューにメッセージが入った際の通知を想定します。

以下のような構成です。

今回検証したい構成

分かりやすさのため、まずデッドレターキューのテンプレートを記載します。

sample-dlq.yaml

AWSTemplateFormatVersion: 2010-09-09
Description: SQS Standard Queue

Parameters:
  QueueName:
    Description: Name for queue
    Type: String

Resources:
  ################################
  # イベント作成用のサンプルのリソース #
  ################################
  
  ## SQSキュー
  SQSQueue:
    Type: AWS::SQS::Queue
    Properties:
      MessageRetentionPeriod: 1209600
      QueueName: !Sub ${QueueName}
      VisibilityTimeout: 120

続いて、今回作成した通知用のテンプレートです。

email-notification.yaml

AWSTemplateFormatVersion: 2010-09-09
Description: Alarm & SNS notification

Parameters:
  SNSTopicName:
    Description: SNSTopicName for Alarm
    Type: String
  NotificateDestinationEmail:
    Description: Destination Email for SNS
    Type: String
  QueueName:
    Description: Name for queue
    Type: String

Resources:
  #################
  # 通知用のリソース #
  #################

  ## CloudWatch アラーム
  SQSAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub ${QueueName}-alerm
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Threshold: 0
      Namespace: AWS/SQS
      Dimensions:
          - Name: QueueName
            Value: !Sub ${QueueName}
      MetricName: ApproximateNumberOfMessagesVisible
      Period: 60
      Statistic: Maximum
      TreatMissingData: notBreaching

  ## 件名カスタマイズ用のステートマシン
  SNSStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      DefinitionString: !Sub |-
            {
              "StartAt": "PublishSns",
              "States": {
                "PublishSns": {
                  "Type": "Task",
                  "Resource": "arn:aws:states:::sns:publish",
                  "Parameters": {
                    "TopicArn": "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${SNSTopicName}",
                    "Message.$": "$.message",
                    "Subject.$": "$.subject"
                  },
                  "End": true
                }
              }
            }
      RoleArn: !GetAtt [ PublishRole, Arn ]

  ## SNSトピック
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties: 
      TopicName: !Sub ${SNSTopicName}
      Subscription:
        - Endpoint: !Sub ${NotificateDestinationEmail}
          Protocol: email
        
  ## EventBridge
  AlarmEvent:
    Type: AWS::Events::Rule
    Properties: 
      Description: String
      EventPattern: !Sub |
        {
          "source": ["aws.cloudwatch"],
          "detail-type": ["CloudWatch Alarm State Change"],
          "resources": [{"prefix": "arn:aws:cloudwatch:${AWS::Region}:${AWS::AccountId}:alarm:${QueueName}-alerm"}],
          "detail": {"state": {"value": ["ALARM"]}}
        }
      Targets: 
        - Arn: !Ref SNSStateMachine
          Id: step-function
          RoleArn: !GetAtt [ StateMachineExecuteRole, Arn ]
          InputTransformer:
            InputPathsMap:
              "Account": "$.account"
              "AlarmName": "$.detail.alarmName"
              "MetricsName": "$.detail.configuration.metrics[0].metricStat.metric.name"
              "Reason": "$.detail.state.reason"
              "Time": "$.time"
            InputTemplate: |
              {
                "subject": "デッドレターキューにメッセージが入りました",
                "message": "AWSアカウントID: \"<Account>\" \n 発生時間(GMT): \"<Time>\" \n 発生アラーム名: \"<AlarmName>\" \n 発生メトリクス: \"<MetricsName>\" \n\n 理由: \"<Reason>\""
              }
  
  # Role
  PublishRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - states.amazonaws.com
            Action: "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: SNSPublish
          PolicyDocument:
            Statement:
              - Effect: Allow
                Resource:
                  - !Ref SnsTopic
                Action:
                  - sns:Publish

  StateMachineExecuteRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - events.amazonaws.com
            Action: "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: StateMachineExecute
          PolicyDocument:
            Statement:
              - Effect: Allow
                Resource:
                  - !Ref SNSStateMachine
                Action:
                  - states:StartExecution

テンプレート作成時のポイントは以下になります。

  • アラームの設定を修正することで、監視対象のメトリクスや状態に遷移する条件を調整することができます。今回は作成しておいたSQSのキューのApproximateNumberOfMessagesVisibleメトリクスを見るように設定しています。(24〜36行目)
  • 通知先の設定はSNS TopicのSubscriptionにて行います。今回はパラメータで渡した1つのEmailを設定するようにしています。(65〜67行目)
  • EventBridgeのルールのEventPatternで監視対象のリソースとイベント内容を指定しています。(74〜80行目)
  • 日本語の件名およびメッセージはEventBridgeのルールのInputTemplateにて設定しています。Step Functionsを挟んだ場合、本文で改行したい場合はテンプレートの文字列に\nを入れることで改行ができました。(92〜96行目)

やってみる

1. CloudFormationでデプロイする

上記のテンプレートをCloudFormationからデプロイしておきます。

デプロイが完了すると各リソースが作成されます。また、SNSからメール通知されるアドレスに、サブスクリプションの承認依頼メールが届くので、承認しておきます。

例えば、作成したアラームは以下のようになっており、これがアラーム状態になると、EventBridge以降が稼働してメール送信までを行います。

作成したアラーム

EventBridgeのルールの画面からは、ターゲットおよび入力トランスフォーマーの設定が期待通りにできていることが分かります。

ターゲットの設定例

2. アラーム状態に遷移させてメール通知する

アラームをアラーム状態に遷移させて、メールが飛ぶことを確認します。

まず、作成したキューをコンソールから選択します。

メッセージを送受信を開きます。

キューにメッセージを投入する1

適当なメッセージをキューに送信しておきます。

キューにメッセージを投入する2

しばらくして、アラームがアラーム状態になることを確認します。

アラーム状態例

SNSトピックに登録したメールを確認すると、通知が飛んできていることを確認できました。

メール例

最後に

今回はCloudWatchアラームの状態遷移と関連づけて日本語の件名・本文のメールをSNSから送信する方法を調べ、その構成とCloudFormationテンプレート例をご紹介しました。

実際の運用では、通知を日本語で行いたいケースが多々あると思いますが、EventBridgeルールの入力トランスフォーマーに日本語のテンプレートを設定しておき、Step Functionsを介してSNSに渡すことで簡単に日本語のメールが設定できるのはとてもよいですね。

参考になりましたら幸いです。

参考

CloudFormation以外でのデプロイ方法や、各機能の詳細も分かるので、ぜひ併せてご確認ください。

[1] CloudWatch アラームの通知メールを少しでも読みやすくしたい | DevelopersIO

[2] Amazon SNSのEメール通知のデフォルトの件名をAWS Step FunctionsとAmazon EventBridge Input Transformerで変更してみた | DevelopersIO