LoginSignup
2
0

More than 5 years have passed since last update.

API Gateway + lambda + SESをrubyで書いてみた。

Last updated at Posted at 2019-03-31

slack等チャットサービスが主流となりつつある今日この頃ですが、何かと使う機会が多いAPI Gateway + lambdaでメール通知が必要な案件がありましたので覚え書き。
ついでに、駆け出しRubyistなものでlambdaもrubyで書いてみました。

AWSでメールといえばSES(Simple Mail Service)ですね。今回はAPIにPOSTされたデータの内容をメール本文に入れて送信してみます。

1. lambdaからメール送信してみる

まずはlambda - sesだけでメールの送信テスト。

SESの設定

2019/3時点で日本リージョンにありませんでした。バージニア北部(us-east1)で使います。

Email Addressの登録

送信元となるメールアドレスを登録します。登録すると確認メールが飛ぶのでリンクをクリック。
ステータスがverifyになればOK。

なお、SESの初期状態はサンドボックスモードで稼働するため、送信先もこちらのEmail Addressに登録されたアドレスのみとなります。
今回の要件では送信先・送信元が同一なので実施しませんでしたが、制限を解除する場合はAWSサポートから申請してください。

lambdaの設定

まずはテストということで、lambdaのtestからメールを送信できるようにします。

Roleの作成

先にlambdaからsesをcallできるようにIAM Roleを作っちゃいます。
紐づけるポリシーは以下2つ。可読性のためにcloudwatch(ログ)周りとSES周りで分離しました。

lambda_ses_pol
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*"
            ]
        }
    ]
}
lambda_ses_pol

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendBounce",
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}

lambda_use_ses_roleという名前でRoleを作成し、上記ポリシーを紐づけ。

関数の作成

関数を作成します。名前は適当にsendmail_ses、ランタイムはいざ、Ruby2.5に。
実行ロールには先ほど作成した lambda_use_ses_roleを選択。DesignerにcloudwatchとSESが追加されればOK。

lambda_function.rb
require 'json'
require 'aws-sdk'

def lambda_handler(event:, context:)

  to = "yourmail@example.com"
  from = "yourmail@example.com"
  subject = "Amazon SES test"
  sesregion = "us-east-1"
  encoding = "UTF-8"

  begin

    body = "test mail"

    ses = Aws::SES::Client.new(region: sesregion)
    resp = ses.send_email({
      destination: {
        to_addresses: [
          to,
        ],
      },
      message: {
        body: {
          text: {
            charset: encoding,
            data: body,
          },
        },
        subject: {
          charset: encoding,
          data: subject,
        },
      },
      source: from,
    })
    return body
  rescue Aws::SES::Errors::ServiceError => error
    return "Email not sent. Error message: #{error}"
  end
end

AWS公式ドキュメントを参考にしています。

テスト

lambdaからテストを作成、実行。(リクエストデータは関係ないので初期値で)
メールが届いていたら幸せ。順調ですね。

2. API化してみる

続いて上記lambda-SES構成をAPI化します。

API Gatewayの設定

APIの作成

プロトコルはREST、エンドポイントは外部からのアクセスを想定しているのでリージョンを選択。

リソースの設定

リソース -> アクションからメソッドの作成を選択して任意のものを作成。
今回はwebアクセスで確認したかったのでGETで。

テスト

早速テストしてみます。GETメソッドからテストをクリックするとリクエストデータを入力できます。
が、今回は関係ないので空でもOK。

メールが送信されたら幸せです。いい感じ。

リソースポリシーの設定

公開時にIP制限をかけたかったのでリソースポリシーに以下を追加

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:ap-northeast-1:AWSアカウントID:APIID/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "xxx.xxx.xxx.xxx"
                }
            }
        }
    ]
}

APIIDは画面上API名の横に()でくくって書いてあります。許可するIPは任意のものを。

デプロイ

作成が完了したらAPIをデプロイ(公開)します。
アクションからAPIのデプロイ、ステージは新しいステージを選択し、任意のものを指定します。

完了するとステージが作成され、エンドポイントURLが表示されます。

テスト

早速ブラウザからエンドポイントにアクセス。(webアクセスではGETが実行されます。)
リソースポリシーで許可したIPから接続することを忘れずに!

テスト時の応答と同じく、
メールが送信されたら幸せです。ハッピー!

3. リクエストデータの値を取得してみる

APIが動作してメール送信まで確認出来たので、ちょっと実用的(というか必須ですよね)なことを。
jsonで来たリクエストをlambdaでごにょごにょしてメール本文に加えます。

API Gatewayの設定

こちら側では「マッピングテンプレート」という機能でリクエストをlambdaに渡す条件、内容の編集を行えます。
どこまでをマッピングテンプレートで処理するか、またはlambda側で処理するかは好みがわかれますが、今回はテストなのでjsonデータをハッシュ化してまるっと渡します。

マッピングテンプレートの設定

リソースからメソッドを選択し、統合リクエスト(日本語だと変な感じですね。Integrationだと思いますが。)を選択。

マッピングテンプレートで、リクエスト本文のパススルーは推奨設定を選択。(任意のものを選択してください。)
マッピングテンプレートの追加から、コンテンツタイプを入力します。jsonなのでapplication/jsonで。
ソースはこんな感じで全文取得。

これでlambda側で値を取得できるようになります。

lambdaの設定

続いてlambda側、とりあえずメール本文にそのまま値を突っ込みます。

lambda_function.rb
require 'json'
require 'aws-sdk'

def lambda_handler(event:, context:)

  to = "yourmail@example.com"
  from = "yourmail@example.com"
  subject = "Amazon SES test"
  sesregion = "us-east-1"
  encoding = "UTF-8"

  begin

    body = event["jsonbody"]["text"]

    ses = Aws::SES::Client.new(region: sesregion)
    resp = ses.send_email({
      destination: {
        to_addresses: [
          to,
        ],
      },
      message: {
        body: {
          text: {
            charset: encoding,
            data: body,
          },
        },
        subject: {
          charset: encoding,
          data: subject,
        },
      },
      source: from,
    })
    return body
  rescue Aws::SES::Errors::ServiceError => error
    return "Email not sent. Error message: #{error}"
  end
end

リクエストデータはlambda_handlerのeventに格納されています。context等使い方は公式ドキュメントまで。
bodyをちょこっと編集して、リクエストデータの'test'というキーの値を取得します。

テスト

API Gateway側のテストで、リクエストデータに'test'キーを入れてあげて実施。
メールが届けばすべて完了です。最高!

カスタムドメイン名を設定する

API Gatewayのカスタムドメイン名を使用して、独自ドメインでエンドポイントを提供します。ついでにSSL化も。

※既にRoute53で独自ドメインを管理している前提

証明書の取得

使用したいドメインの証明書を取得します。Certificate Managerから利用したいFQDNの証明書を取得してください。(ワイルドカード証明書でも可)

この時、APIがreginalの場合は同じリージョンで証明書を取得する必要があります。

カスタムドメイン名の登録

API Gateway カスタムドメイン名からFQDNの入力、先ほど取得した証明書を選択すると設定は完了です。(リージョナルを選択)


(黒塗りばかりですみません…)

ベースパスマッピングにデプロイ済みのステージを登録するのを忘れずに。

レコード追加

最後に、Route53にてAレコードの登録を行います。
AレコードのAliasを選択し、上記ターゲットドメイン名を登録します。

以上で設定は完了。登録したカスタムドメイン名でアクセスができたら幸せ。

まとめ

今回は1から全て作りましたが、lambdaでAPIしてるなかで、ちょっとメールで送りたいなーという時に簡単に実装できるかと思います。

要件にもよりますが外部に公開したり本番運用する場合には、SESの制限にご注意ください。

P.S. AWSの日本語訳版、もうちょっと分かり易いと良いんですけどね。「エッジ最適化」とかなんのこっちゃって感じですし。これからに期待です。(^^)

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0