【AWS】【CLI版】Amazon SES+Lambdaで各種メールサービスの通知をDiscordへ転送する

AWS

今回はAWSに関する記事です。
掲題にある各種メールサービスの通知をDiscordへ
転送する方法を解説していきます

  • 本記事のターゲット
  • 構成図
  • 本構成の事前確認事項/注意事項
  • Discordの設定
  • Lambdaの設定
  • S3の設定
  • Route53の設定
  • AmazonSESの設定
  • メールクライアントの転送設定
  • 最後に
  • おまけ
  1. 【AWS】Amazon SES+Lambdaで各種メールサービスの通知をDiscordへ転送する
    1. 本記事のターゲット
    2. 構成図
    3. 本構成の事前確認事項/注意事項
    4. Discordの設定
      1. (1)通知用のサーバー作成
      2. (2)どういうサーバーなのか質問される
      3. (3)サーバー名を入力して新規作成
      4. (4)テキストチャンネルを通知用にひとつ作成
      5. (5)チャンネル作成時の設定
      6. (6)メンバーまたはロールの追加
      7. (7)Webhook URLの作成
      8. (8)ウェブフックを作成
      9. (9)Webhook URLを取得
      10. (10)おまけ:アイコンとWebhookの通知名を設定
    5. Lambdaの設定
      1. CloudShellの準備
        1. AWS CLIの確認
        2. AWSアカウントの確認
      2. IAMロールの作成
        1. (1)ロールを作成
        2. (2)許可を追加
        3. (3)作成確認
      3. Lambda関数の作成
        1. (1)コードの記述
        2. (2)コードをZIPにする
        3. (3)関数を作成
        4. (4)関数が利用可能になるまで待つ
        5. (5)環境変数の設定
        6. (6)環境変数を反映
        7. (7)更新完了を待つ
        8. (8)設定確認
    6. S3の設定
        1. (1)バケット名の確認
        2. (2)バケットを作成
        3. (3)ライフサイクルルールの設定
        4. (4)ライフサイクルルールを反映
        5. (5)設定確認
        6. (6)SESからのアクセス許可
        7. (7)バケットポリシーを反映
        8. (8)ポリシー確認
    7. Route53の設定
    8. AmazonSESの設定
      1. ID作成
        1. (1)IDを作成
        2. (2)DKIMのDNS情報を確認
        3. (3)CNAMEレコードを作成
        4. (4)CNAMEを登録
        5. (5)ID作成完了確認
        6. (6)MXレコードを作成
        7. (7)MXレコードを登録
        8. (8)MXレコードの確認
      2. Eメール受信設定
        1. (9)既存のルールセットを確認
        2. (10)ルールセットを作成
        3. (11)ルールセットの有効化
        4. (12)ルールとアクションの設定
        5. LambdaのARNを取得し、FUNCTION_ARN変数へ格納します。
        6. (13)Lambdaへのアクセス許可追加
        7. (14)ルールを作成
        8. (15)ルールの作成確認
    9. メールクライアントの転送設定
      1. Gmailによる動作確認
      2. (補足)Gmailのフィルタ動作について
    10. 最後に
    11. おまけ:作成したAWSリソースを削除する
      1. (1)メールの転送を停止する
      2. (2)CloudShellで変数を設定し直す
      3. (3)SESの受信ルールセットを削除する
      4. (4)Lambda関数を削除する
      5. (5)CloudWatch Logsを削除する
      6. (6)IAMロールを削除する
      7. (7)S3のメール原本とバケットを削除する
      8. (8)SESのドメインIDを削除する
      9. (9)DNS設定を片付ける
      10. (10)DiscordのWebhookを片付ける
    12. 関連記事

【AWS】Amazon SES+Lambdaで各種メールサービスの通知をDiscordへ転送する

本記事のターゲット

Outlookなどに代表される各種メールサービスは、
自前でメールの仕分け機能を備えていることが多いです。

受信トレイ へ格納された新着メールを、送信元や件名に応じて、
フォルダに振り分ける機能。
自分に関係ある、もしくは気をつけなければいけないメールを
固定の場所へ配置することで、対処の優先度が高い連絡を見落とさないようにする。

上記を利用している人は多いと思います。

メールソフトを起動することが始業時の日課になっている方も多いと思いますので
業務だとコレで問題ないのですが、

プライベート用のメールソフトは毎日確認しない場合が多く
なおかつ連絡用のメールアドレスは複数所持しており、
数日経過してから連絡があったことに気づく

という状況になっている方も多いと考えます。(筆者もこのパターン)

そこで本記事では、通知に気づきやすいDiscordへメール受信を知らせることにより
いろいろなメールサービスへ届く「重要な連絡」や「見落としたくないメッセージのみ」を
より早く気づける仕組みを構築していきます。

本記事は、以下のような方を対象としています。

  • 複数のメールアドレスを所持しており、メールの確認が煩雑になっている方
  • 普段あまり確認しないメールアドレスに、重要な連絡が届くことがある方
  • すべての新着メールではなく、特定の送信元や件名に該当するメールだけを通知したい方
  • Discordを日常的に利用しており、通知先を一本化したい方
  • AWSを利用して、メール通知の仕組みを構築してみたい方

なお、本記事では既存のメールサービスが持つ振り分け/転送機能を活用するため、
メールサービスそのものをAWSへ移行する必要はありません。

構成図

今回は以下のようなAWSの構成を作成します。(設定する連携サービスも記述)

補足事項:
本記事ではDiscordを利用しますが、Lambdaの通知処理を変更することで
Slackなどへの連携も可能です

本構成の事前確認事項/注意事項

本構成を利用するにあたり、以下の事項を事前に確認してください。

  • メールの自動転送環境:通知対象のメールをAmazon SES宛てに自動転送できる環境が必要です。
  • AWS環境:Amazon SES、AWS Lambda、Amazon Route 53を利用できるAWSアカウントが必要です。
  • ドメイン:Amazon SESでメールを受信するための独自ドメイン、または利用可能なサブドメインが必要です。
  • Discord環境:通知先となるDiscordサーバーと、Webhookを設定できる権限が必要です。
  • 通知の受信方法:本記事ではDiscordのスマートフォンアプリで通知を確認する想定ですが、PCアプリでも利用できます。

なお、AWSの各サービスには利用料金が発生する場合があります。

本記事では、特に指定がなければすべてap-northeast-1(東京リージョン)
で構築するものとします。
またIAMやRoute53はグローバルで固定されており、リージョンを選択できません。
※悩まず手順通りに設定してください

Discordの設定

Discordアカウントの作成とログインは完了しているものとします
また、PCのデスクトップアプリ版を利用して解説します

(1)通知用のサーバー作成

左ペインのサーバーを作成するボタンを押下すると、中央にポップアップが出現します。
とりあえず「オリジナルの作成」をクリック。
※サンプルが切り抜きだらけになっているあたりご容赦ください。。。

(2)どういうサーバーなのか質問される

あまり気にせずイチバン上のを選択しておきましょう。
スキップしてもいいみたいです。

(3)サーバー名を入力して新規作成

「検証用サーバー」とします。
※あとで変更できるので、適当な名前で問題なし

(4)テキストチャンネルを通知用にひとつ作成

テキストチャンネル横の + をクリックします。
一般はメンバー招待したときの初期スポーン地点だったりするので一応分割しておきます。
※メンバー招待したときの初期スポーン地点は後で変更可能

(5)チャンネル作成時の設定

テキスト、チャンネル名「通知」、プライベートチャンネルをONにして作成します。
※チャンネル名は後で変更可能

(6)メンバーまたはロールの追加

今設定する必要ないので、スキップします。

(7)Webhook URLの作成

まずは「チャンネルの編集」を押下します(チャンネル右の歯車)マークです

(8)ウェブフックを作成

連携サービス→ウェブフックを作成 を押下します。

(9)Webhook URLを取得

「ウェブフックURLをコピー」を押下すると、クリップボードにコピーされるので
メモ帳などに貼り付けて内容確認します。

以下のようなURLが取得できるので控えておきます。(Lambda作成時に利用する)
※筆者のWebhookは公開できないので途中で切ってますが、実際にはもっと長いです。
 合計120文字ぐらいあります。

(10)おまけ:アイコンとWebhookの通知名を設定

キャラ設定的な、システム通知の名前と画像を編集できます。
※特に設定しなくてもいいです。デフォルトだと味気ないと感じるなら設定してみてください

Lambdaの設定

CloudShellの準備

AWSコンソールの東京リージョンでCloudShellを開きます。以降のコマンドはすべてCloudShellで実行します。手元PCへのAWS CLIのインストールは不要です。

AWS CLIの確認

CLIのバージョンを確認します。

aws --version

確認する内容:aws-cli/2で始まるバージョンが表示されること。

AWSアカウントの確認

今回構築するアカウントか確認します。

aws sts get-caller-identity

確認する内容:Accountが今回使用するAWSアカウントIDであること。

作業用フォルダーを作成して移動します。

mkdir -p ~/ses-to-discord-article
cd ~/ses-to-discord-article

以降のコマンドで使う値を設定します。DOMAINは自分の受信用サブドメインに置き換えてから、まとめて貼り付けてください。

# AWSリージョン(東京)
export AWS_REGION=ap-northeast-1

# AWS CLIの出力をページ送りせず表示
export AWS_PAGER=""

# 現在のAWSアカウントIDを取得
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# Lambda関数名
FUNCTION_NAME=Lambda-to-Discord

# Lambda実行ロール名
ROLE_NAME=Lambda-to-Discord

# SES受信ルールセット名
RULE_SET_NAME=Lambda-to-Discord

# SES受信ルール名
RULE_NAME=Lambda-to-Discord

# メール受信用サブドメイン(自分の値へ変更)
DOMAIN='mail.blog.addache.jp'

# メール原本を保存するS3バケット名
BUCKET_NAME="ses-reception-email-${ACCOUNT_ID}-${AWS_REGION}-an"

JSONは用途ごとにファイルを作成します。変数を使う箇所は、後述のsedコマンドで仮JSONの置換箇所を実際の値に変換します。

IAMロールの作成

ロール名をLambda-to-Discordとして作成し、AWSLambdaBasicExecutionRoleを付与します。

Lambdaが利用する実行ロールの信頼ポリシーです。 以下のコマンドでファイルを開き、JSONを貼り付けます。

nano lambda-trust-policy.json

☆lambda-trust-policy.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、
CloudShellで開いたファイルへ貼り付けて保存します。

nanoの操作:Ctrl+O、Enterで保存し、Ctrl+Xで閉じます。

(1)ロールを作成

信頼ポリシーを指定して作成します。

aws iam create-role \
  --role-name "$ROLE_NAME" \
  --assume-role-policy-document file://lambda-trust-policy.json

確認する内容:Role.RoleNameがLambda-to-Discordとなること。

(2)許可を追加

CloudWatch Logsにログを出力する権限を付けます。

aws iam attach-role-policy \
  --role-name "$ROLE_NAME" \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

確認する内容:通常は出力なし。次の確認コマンドで結果を確認します。

(3)作成確認

ロールに付いたポリシーを確認します。

aws iam list-attached-role-policies --role-name "$ROLE_NAME"

確認する内容:AWSLambdaBasicExecutionRoleが表示されること。

Lambda関数の作成

関数名、ランタイム、タイムアウトは、Lambda-to-Discord、Python 3.14、180秒とします。CLIではコードをZIPにしてから関数を作成します。

(1)コードの記述

以下のコマンドでファイルを開き、コードを貼り付けて保存します。

nano lambda_function.py

☆lambda_function.pyのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでコードをコピーし、
CloudShellで開いたlambda_function.pyへ貼り付けて保存します。

(2)コードをZIPにする

CloudShell上でコードをZIPにします。

zip function.zip lambda_function.py

確認する内容:lambda_function.pyが追加されること。

(3)関数を作成

直前に作成した実行ロールとZIPを指定します。

aws lambda create-function \
  --function-name "$FUNCTION_NAME" \
  --runtime python3.14 \
  --role "arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" \
  --handler lambda_function.lambda_handler \
  --zip-file fileb://function.zip \
  --timeout 180 \
  --region "$AWS_REGION" \
  --query '{FunctionName:FunctionName,Runtime:Runtime,State:State}'

確認する内容:関数名とpython3.14が表示されること。作成直後はPendingの場合があります。

ロールを引き受けられない旨のエラーが出た場合は、IAMの反映を待ってから同じ作成コマンドを実行します。

(4)関数が利用可能になるまで待つ

作成処理の完了を待ちます。

aws lambda wait function-active-v2 --function-name "$FUNCTION_NAME" --region "$AWS_REGION"

確認する内容:正常終了時は出力なし。

(5)環境変数の設定

Webhook URLを設定します。このファイルには実際のURLを記入するため、掲載しません。
以下のコマンドでファイルを開き、JSONを貼り付けます。

nano lambda-environment.json

☆lambda-environment.example.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、
CloudShellで開いたファイルへ貼り付けて保存します。

コピーしたJSONのDISCORD_WEBHOOK_URLを、
実際のWebhook URLに置き換えて保存してください

(6)環境変数を反映

関数にWebhook URLを設定します。

aws lambda update-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --environment file://lambda-environment.json \
  --region "$AWS_REGION" \
  --query '{FunctionName:FunctionName,LastUpdateStatus:LastUpdateStatus}'

確認する内容:関数名が表示されること。Webhook URLは表示させません。

(7)更新完了を待つ

設定が反映されるまで待ちます。

aws lambda wait function-updated-v2 --function-name "$FUNCTION_NAME" --region "$AWS_REGION"

確認する内容:正常終了時は出力なし。

(8)設定確認

ランタイムとタイムアウトを確認します。

aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$AWS_REGION" \
  --query '{FunctionName:FunctionName,Runtime:Runtime,Timeout:Timeout,State:State,LastUpdateStatus:LastUpdateStatus}'

確認する内容:python3.14、Timeout=180、State=Active、LastUpdateStatus=Successfulとなること。

S3の設定

転送したメール原本を一時保管するバケットを作成します。
アカウントのリージョナル名前空間を使用します。

(1)バケット名の確認

この後使う実際のバケット名を表示します。

printf '%s\n' "$BUCKET_NAME"

確認する内容:ses-reception-email-アカウントID-ap-northeast-1-anの形式で表示されること。

(2)バケットを作成

東京リージョンに作成します。

aws s3api create-bucket \
  --bucket "$BUCKET_NAME" \
  --bucket-namespace account-regional \
  --region "$AWS_REGION" \
  --create-bucket-configuration "LocationConstraint=$AWS_REGION"

確認する内容:作成したバケットのLocation等が表示されること。

(3)ライフサイクルルールの設定

原本を3日経過で期限切れの対象にする設定です。今回作成したバケット全体に適用します。
以下のコマンドでファイルを開き、JSONを貼り付けます。

nano s3-lifecycle.json

☆s3-lifecycle.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、
CloudShellで開いたファイルへ貼り付けて保存します。

(4)ライフサイクルルールを反映

専用バケットへ適用します。

aws s3api put-bucket-lifecycle-configuration \
  --bucket "$BUCKET_NAME" \
  --lifecycle-configuration file://s3-lifecycle.json \
  --region "$AWS_REGION"

確認する内容:通常は出力なし。

(5)設定確認

反映したルールを確認します。

aws s3api get-bucket-lifecycle-configuration --bucket "$BUCKET_NAME" --region "$AWS_REGION"

確認する内容:Status=Enabled、Expiration.Days=3が表示されること。

3日経過で期限切れの対象となります。実際の削除には時間差があります。

(6)SESからのアクセス許可

まず置換用の仮JSONを作成します。 以下のコマンドでファイルを開き、JSONを貼り付けます。

nano s3-bucket-policy.template.json

☆s3-bucket-policy.template.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、
CloudShellで開いたファイルへ貼り付けて保存します。

設定済みの変数を使い、仮JSONからバケットポリシーを生成します。

sed -e "s|__BUCKET_NAME__|${BUCKET_NAME}|g" -e "s|__ACCOUNT_ID__|${ACCOUNT_ID}|g" s3-bucket-policy.template.json > s3-bucket-policy.json
(7)バケットポリシーを反映

保存したポリシーを適用します。

aws s3api put-bucket-policy \
  --bucket "$BUCKET_NAME" \
  --policy file://s3-bucket-policy.json \
  --region "$AWS_REGION"

確認する内容:通常は出力なし。

(8)ポリシー確認

設定した内容を確認します。

aws s3api get-bucket-policy \
  --bucket "$BUCKET_NAME" \
  --region "$AWS_REGION" \
  --query Policy \
  --output text

確認する内容:バケット名とアカウントIDが指定した値で、ses.amazonaws.comにPutObjectが許可されていること。

Route53の設定

筆者の環境では、既存ドメインからサブドメインを権限委譲して利用しています。
既に独自ドメインを所有している場合は新たにドメインを取得せず、
サブドメインを利用して構築することも可能です。

今回はRoute 53を使用したサブドメインの権限委譲手順を簡単に紹介します。

  1. 委譲元のドメイン名を確認(例:blog.addache.jp)
  2. 構築中のAWSアカウントでパブリックホストゾーンを作成(例:mail.blog.addache.jp)
  3. 作成したホストゾーンに自動生成されたNSレコードの値(ネームサーバー)を取得
  4. サブドメイン権限委譲元のドメインのホストゾーンを開く(例:blog.addache.jp)
  5. NSレコードを作成(例:mail.blog.addache.jp)し、取得したネームサーバーを登録する

ドメインの管理環境によって設定方法は異なります。
既にSESで利用するドメインのDNSレコードを管理できる場合は、サブドメインの権限委譲は必須ではありません。

以下は検証用に作成したパブリックホストゾーン例です。
このホストゾーンは、後続のSES設定時に利用します。

※上記サブドメイン権限委譲手順も実施しています

  • ドメイン名:mail.blog.addache.jp

AmazonSESの設定

ID作成

(1)IDを作成

Easy DKIMのドメインIDを作成します。

aws sesv2 create-email-identity \
  --email-identity "$DOMAIN" \
  --dkim-signing-attributes NextSigningKeyLength=RSA_2048_BIT \
  --region "$AWS_REGION"

確認する内容:DkimAttributes.Tokensが返ること。

(2)DKIMのDNS情報を確認

DNS登録に必要な情報をファイルに取得します。

aws sesv2 get-email-identity \
  --email-identity "$DOMAIN" \
  --region "$AWS_REGION" \
  --query DkimAttributes \
  --output json > dkim-attributes.json

確認する内容:正常終了後、dkim-attributes.jsonに3つのTokensとSigningHostedZoneが保存されること。

取得した値を変数へ格納します。以下はまとめて貼り付けられます。

# DKIMトークン(3件)
TOKEN1=$(jq -r '.Tokens[0]' dkim-attributes.json)
TOKEN2=$(jq -r '.Tokens[1]' dkim-attributes.json)
TOKEN3=$(jq -r '.Tokens[2]' dkim-attributes.json)
# DKIMの参照先DNSゾーン
SIGNING_HOSTED_ZONE=$(jq -r '.SigningHostedZone' dkim-attributes.json)

取得値を確認します。nullや空欄がある場合は、CNAME登録へ進まず取得結果を確認してください。

printf '%s\n' "$TOKEN1" "$TOKEN2" "$TOKEN3" "$SIGNING_HOSTED_ZONE"
(3)CNAMEレコードを作成

画面で行う場合に自動登録されていたCNAMEを、CLIでは明示して登録します。取得した変数をsedで仮JSONへ反映します。

認証用の3件のCNAMEを仮JSONとして保存します。
以下のコマンドでファイルを開き、JSONを貼り付けます。

nano route53-dkim.template.json

☆route53-dkim.template.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、
CloudShellで開いたファイルへ貼り付けて保存します。

仮JSONの置換箇所を変数の値に変換し、登録用JSONを生成します。

sed -e "s|__TOKEN1__|${TOKEN1}|g" -e "s|__TOKEN2__|${TOKEN2}|g" -e "s|__TOKEN3__|${TOKEN3}|g" -e "s|__DOMAIN__|${DOMAIN}|g" -e "s|__SIGNING_HOSTED_ZONE__|${SIGNING_HOSTED_ZONE}|g" route53-dkim.template.json > route53-dkim.json

受信用サブドメインのホストゾーンIDを取得します。委譲元ではなく、このAWSアカウントで作成した受信用ゾーンのIDです。

HOSTED_ZONE_ID=$(aws route53 list-hosted-zones-by-name \
  --dns-name "$DOMAIN" \
  --query "HostedZones[?Name == '${DOMAIN}.' && Config.PrivateZone == \`false\`].Id | [0]" \
  --output text)

表示されたIDが受信用サブドメインのゾーンと一致することをRoute 53画面で確認します。Noneの場合は登録へ進まずゾーン名を確認します。

printf '%s\n' "$HOSTED_ZONE_ID"
(4)CNAMEを登録

受信用サブドメインのゾーンに登録します。

aws route53 change-resource-record-sets \
  --hosted-zone-id "$HOSTED_ZONE_ID" \
  --change-batch file://route53-dkim.json

確認する内容:ChangeInfo.Idが返ること。

(5)ID作成完了確認

DNS反映後に認証状態を確認します。

aws sesv2 get-email-identity \
  --email-identity "$DOMAIN" \
  --region "$AWS_REGION" \
  --query '{VerificationStatus:VerificationStatus,DkimStatus:DkimAttributes.Status}'

確認する内容:VerificationStatusとDkimStatusがSUCCESSとなること。反映待ちの場合は時間をおいて再確認します。

(6)MXレコードを作成

MXレコードの仮JSONを作成します。 以下のコマンドでファイルを開き、JSONを貼り付けます。

nano route53-mx.template.json

☆route53-mx.template.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、CloudShellで開いたファイルへ貼り付けて保存します。

設定済みのDOMAIN変数を使って、登録用JSONを生成します。JSON内の文字列はシェル変数として自動展開されないため、この置換を行ってから次へ進みます。

sed "s|__DOMAIN__|${DOMAIN}|g" route53-mx.template.json > route53-mx.json

登録前にレコード名を確認します。受信用サブドメイン(例:mail.blog.addache.jp)が表示されることを確認してください。DOMAINや__DOMAIN__、空欄の場合は登録へ進まず、変数の設定と上のsedコマンドを確認します。

jq -r '.Changes[0].ResourceRecordSet.Name' route53-mx.json
(7)MXレコードを登録

SESの受信エンドポイントを登録します。

aws route53 change-resource-record-sets \
  --hosted-zone-id "$HOSTED_ZONE_ID" \
  --change-batch file://route53-mx.json

確認する内容:ChangeInfo.Idが返ること。

(8)MXレコードの確認

AWS CLIでRoute 53に登録されたMXレコードを確認します。この操作ではDNSの反映状況までは確認しません。

aws route53 list-resource-record-sets \
  --hosted-zone-id "$HOSTED_ZONE_ID" \
  --query "ResourceRecordSets[?Type == 'MX' && Name == '${DOMAIN}.']" \
  --output json

確認する内容:受信用サブドメインのMXレコードが表示され、ResourceRecordsのValueが10 inbound-smtp.ap-northeast-1.amazonaws.comとなること。空の配列の場合は、ホストゾーンIDとDOMAIN、MXの登録結果を確認します。

Eメール受信設定

(9)既存のルールセットを確認

有効な受信ルールセットを確認します。

aws ses describe-active-receipt-rule-set --region "$AWS_REGION"

確認する内容:既存の有効セットがある場合は内容を確認してから切り替えます。未設定の場合はRuleSetDoesNotExistエラーとなる場合があります。

(10)ルールセットを作成

Lambda-to-Discordという名前で作成します。

aws ses create-receipt-rule-set --rule-set-name "$RULE_SET_NAME" --region "$AWS_REGION"

確認する内容:通常は出力なし。

(11)ルールセットの有効化

作成したルールセットを有効化します。

aws ses set-active-receipt-rule-set --rule-set-name "$RULE_SET_NAME" --region "$AWS_REGION"

確認する内容:通常は出力なし。次にルールを追加します。

(12)ルールとアクションの設定

受信者条件は指定しません。アクションはS3保存→Lambda呼び出しの順にします。
BUCKET_NAMEとFUNCTION_ARNは後述のsedコマンドで反映します。

LambdaのARNを取得し、FUNCTION_ARN変数へ格納します。
FUNCTION_ARN=$(aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$AWS_REGION" \
  --query FunctionArn \
  --output text)

取得したARNを確認します。
arn:aws:lambda:ap-northeast-1:アカウントID:function:Lambda-to-Discord
の形式であることを確認してください。
空欄やNoneの場合は、取得時のエラーと関数名を確認してから進めます。

printf '%s\n' "$FUNCTION_ARN"

受信ルールの仮JSONを保存します。 以下のコマンドでファイルを開き、JSONを貼り付けます。

nano receipt-rule.template.json

☆receipt-rule.template.jsonのソースコード置き場☆

リンク先のコード欄右上にあるコピーボタンでJSONをコピーし、CloudShellで開いたファイルへ貼り付けて保存します。

バケット名と取得したLambdaのARNをsedで反映し、登録用JSONを生成します。

sed -e "s|__BUCKET_NAME__|${BUCKET_NAME}|g" -e "s|__FUNCTION_ARN__|${FUNCTION_ARN}|g" receipt-rule.template.json > receipt-rule.json

登録前にバケット名とLambdaのARNを確認します。作成したバケット名と関数ARNが表示され、置換用の文字列や空欄が残っていないことを確認してから進みます。

jq -r '.Actions[0].S3Action.BucketName, .Actions[1].LambdaAction.FunctionArn' receipt-rule.json
(13)Lambdaへのアクセス許可追加

CLIではSESからLambdaへの呼び出し許可を先に明示的に付けます。

aws lambda add-permission \
  --function-name "$FUNCTION_NAME" \
  --statement-id AllowSESInvoke \
  --action lambda:InvokeFunction \
  --principal ses.amazonaws.com \
  --source-account "$ACCOUNT_ID" \
  --source-arn "arn:aws:ses:${AWS_REGION}:${ACCOUNT_ID}:receipt-rule-set/${RULE_SET_NAME}:receipt-rule/${RULE_NAME}" \
  --region "$AWS_REGION"

確認する内容:Statementにses.amazonaws.comからのInvokeFunction許可が表示されること。

(14)ルールを作成

S3とLambdaの許可設定後、ルールを登録します。

aws ses create-receipt-rule \
  --rule-set-name "$RULE_SET_NAME" \
  --rule file://receipt-rule.json \
  --region "$AWS_REGION"

確認する内容:通常は出力なし。

(15)ルールの作成確認

有効セット内のルールを確認します。

aws ses describe-active-receipt-rule-set --region "$AWS_REGION"

確認する内容:Metadata.NameがLambda-to-Discord、Rules内のEnabledがtrue、ActionsがS3→Lambdaの順であること。

メールクライアントの転送設定

Gmailによる動作確認

今回はGmailを利用した転送設定を紹介していきます。
[左ペイン]-[ラベルの管理]-[タブ:メール転送とPOP/IMAP]-[転送先アドレスを追加]

以下の形式で転送先アドレスを設定します。

  • 構文:[適当な名前]+@+mail.blog.addache.jp
  • サンプル:gmail@mail.blog.addache.jp

適当な名前 は、英数字で構成さえしていれば本当になんでもいいです。
@ より後方は、本記事で作成したドメインを指定します。

※本人確認画面は省略します

以下のように確認リンクを送付した旨のメッセージが表示されます。

Discordへ通知が飛んできていたら成功です。

なお、Lambda経由でのSES転送メールには「本文」が含まれないので
Gmailの確認リンクをDiscordで直接受け取ることはできません。
※そのため、S3バケットへメールの原本を格納する構成を取っています

作成したS3バケットを開くと、以下のようにメール原本が格納されてます。

  1. 一番新しいファイルをダウンロード ※何度も操作するとこのようにゴチャります。。。
  2. DLしたファイルへ拡張子[.eml]を付与
  3. OutLookなどのメールソフトでファイルを開く

以下はThunderBirdで開いた例です。
途中のURLを伏せてますが、この確認リンクをクリックすると先へ進めます。
※本人確認などが発生します

Gmailへ戻りフィルタ設定で転送先を設定します。

注1) 本人確認済みにも関わらず、転送メールが設定できない場合は画面リロードするのが有効
注2) 転送先メールを設定したフィルタは、更新のたびに本人確認を要求されて少しうっとおしい

(補足)Gmailのフィルタ動作について

Gmailのフィルタによる自動転送は、新しく受信したメールが対象です。
既存メールを別のラベルへ移動したり、「一致するスレッドにもフィルタを適用する」を選択したりしても、過去に受信したメールは転送されません。
※上記のサンプル画像にも明記されています

Gmailでの動作確認の際は、フィルタ条件に一致するメールを新たに受信する必要があります。

hotmailなどのサービスを利用しているなら、そちらで転送確認を行ったほうが
実際の挙動に対する手応えがあると思います。

最後に

今回はAmazon SESとLambdaを利用して、
各種メールサービスで受信したメールをDiscordへ通知する仕組みを構築しました。

普段利用しているメールサービスを変更することなく、
必要なメールだけをDiscordへ集約できるのが本構成の特徴です。

Discordは通知専用とし、メール本文は従来どおり各メールサービスで確認します。

また、S3にメール原本を一時保存することで、Gmailの転送先確認メールのように
本文の確認が必要なケースにも対応できるようにしました。

メールの振り分けや転送設定はサービスごとに異なりますが、一度設定してしまえば
あとは普段どおりメールを利用するだけです。

重要なメールを見落とさないための仕組みとして、参考になれば幸いです。

最近だとAIにGmailを連携して重要なメールをキャッチアップする手法もあります。

ただ、筆者のGmailはあまり仕事で利用していないのでAIと連携するほどでもなく
このぐらいの固定通知設定ができれば十分だったりします。
※配達会社のお荷物お届けメールなどが拾えれば大体満足

この仕組みのいいところは、
通知先をDiscordに一本化しながら、メールサービスごとに同じ仕組みを展開できることです。

Gmailに限らず、転送機能を持つメールサービスなら同じSESの受信口を利用できるため、
通知対象を増やす際にもAWS側の構成を大きく変更する必要がありません。

おまけ:作成したAWSリソースを削除する

検証が終わり、この仕組みを使わなくなった場合の後片付けです。

  • この記事で作成したリソースに限定して削除します。
  • AWS CLIで実施できるものだけ記述します。
  • DiscordやGmailなど、CLIで削除できないものについては備忘として文章で説明します。
  • S3のメール原本やCloudWatch Logsも削除するため、必要なものは先に保存してください。
  • 他の用途でも使っているリソースは削除しないでください。

(1)メールの転送を停止する

Gmailなどのメールサービスで、今回設定した転送を停止します。
フィルタで転送している場合は、そのフィルタの転送設定を外します。

(2)CloudShellで変数を設定し直す

AWSコンソールでCloudShellを開きます。

前回の変数は残っていないことがあるため、削除に必要な変数だけ再設定します。

  • 名前を変更して構築した場合は、実際に使用した名前へ変更してください。
  • DOMAINも構築時に使用したドメイン名に合わせてください。
# AWSリージョン(東京)
export AWS_REGION=ap-northeast-1

# 現在のAWSアカウントIDを取得
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# 削除するLambda関数名
FUNCTION_NAME=Lambda-to-Discord

# 削除するLambda実行ロール名
ROLE_NAME=Lambda-to-Discord

# 削除するSES受信ルールセット名
RULE_SET_NAME=Lambda-to-Discord

# 削除するSES IDの受信用サブドメイン
DOMAIN='mail.blog.addache.jp'

# 削除するS3バケット名
BUCKET_NAME="ses-reception-email-${ACCOUNT_ID}-${AWS_REGION}-an"

操作するアカウントを確認します。

aws sts get-caller-identity

削除対象の名前も確認します。意図したアカウントとリソースであることを確認してから進めてください。

printf 'Lambda: %s\nIAM role: %s\nSES rule set: %s\nSES identity: %s\nS3 bucket: %s\n' "$FUNCTION_NAME" "$ROLE_NAME" "$RULE_SET_NAME" "$DOMAIN" "$BUCKET_NAME"

(3)SESの受信ルールセットを削除する

現在有効な受信ルールセットを確認します。

aws ses describe-active-receipt-rule-set --region "$AWS_REGION" --query Metadata.Name --output text

表示された名前が今回のルールセット(Lambda-to-Discord)である場合だけ、次のコマンドで無効化します。
このコマンドは、このアカウント・リージョンのSES受信処理を停止します。

注) 別のルールセットが有効な場合は、直下の無効化コマンドを実行せず、
今回のルールセットの削除だけ実施ください。

aws ses set-active-receipt-rule-set --region "$AWS_REGION"

今回作成したルールセットを削除します。セット内の受信ルールもまとめて削除されます。

aws ses delete-receipt-rule-set --rule-set-name "$RULE_SET_NAME" --region "$AWS_REGION"

(4)Lambda関数を削除する

通知用のLambda関数を削除します。関数に付けたSESからの呼び出し許可も、関数とともに削除されます。

aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$AWS_REGION"

(5)CloudWatch Logsを削除する

Lambda関数を削除してもログは残ります。今回の関数のロググループを削除します。
関数を実行しておらずロググループが存在しない場合、ResourceNotFoundExceptionとなるため、そのまま次へ進みます。

aws logs delete-log-group --log-group-name "/aws/lambda/${FUNCTION_NAME}" --region "$AWS_REGION"

(6)IAMロールを削除する

実行ロールから、構築時に付けたログ出力ポリシーを外します。

aws iam detach-role-policy \
  --role-name "$ROLE_NAME" \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

実行ロールを削除します。

aws iam delete-role --role-name "$ROLE_NAME"

(7)S3のメール原本とバケットを削除する

バケット内のメール原本をすべて削除します。

注) S3バケットそのものを削除する場合、バケットの中身が空になっている必要があります。
中身がある状態でS3バケットを削除しようとすると
エラーになって削除できないためご注意ください。

aws s3 rm "s3://${BUCKET_NAME}" --recursive --region "$AWS_REGION"

空になったバケットを削除します。
バケットポリシーとライフサイクル設定もバケットとともに削除されます。

aws s3api delete-bucket --bucket "$BUCKET_NAME" --region "$AWS_REGION"

注)この記事の手順ではS3バージョニングを有効にしていません。
後から有効化した場合、過去バージョンや削除マーカーが残り、
バケットを削除できないことがあります。
その場合はS3画面でバージョンも含めて空にしてから、バケット削除を実行してください。

(8)SESのドメインIDを削除する

今回の受信用サブドメインのSES IDを削除します。

aws sesv2 delete-email-identity --email-identity "$DOMAIN" --region "$AWS_REGION"

(9)DNS設定を片付ける

ドメイン権限委譲は構築時と同じく、画面から片付けます。

  • 委譲元ドメインの管理画面で、今回の受信用サブドメインへの委譲に使ったNSレコードを削除します。委譲元ドメイン自体のNSレコードは削除しません。
  • 続いてRoute 53で、今回作成した受信用サブドメインのホストゾーンを開きます。
  • 今回登録したMXとDKIM用CNAME(3件)を削除します。
  • 初期のNS・SOAだけになったら、そのホストゾーンを削除します。
    他のゾーンは削除しないでください。
    ※ドメインの登録契約そのものは削除しません。

(10)DiscordのWebhookを片付ける

Discordの通知用チャンネルで
「チャンネルの編集」→「連携サービス」→「ウェブフック」を開き、
今回作成したWebhookを削除します。

関連記事

【AWS】【マネコン操作】Amazon SES+Lambdaで各種メールサービスの通知をDiscordへ転送する
今回はAWSに関する記事です。掲題にある各種メールサービスの通知をDiscordへ転送する方法を解説していきます本記事のターゲット構成図本構成の事前確認事項/注意事項Discordの設定Lambdaの設定S3の設定Route53の設定Ama…

コメント

タイトルとURLをコピーしました