API Gateway + Lambda で SNS にメッセージ投稿する


以前からかなり欲しかった機能だった API Gateway が us-east-1 等いくつかの region で利用可能になったので、早速使ってみました。

Amazon SNS にメッセージ投稿を行うと、

- email での通知（標準機能）
- [Twilio 経由で電話での通知](http://qiita.com/u-minor/items/d72e77e79203aab6ca70)
- [Slack での通知](http://qiita.com/u-minor/items/596329da79abd64af658)

までは作ってあるので、後は NewRelic 等の監視サービスや独自監視ツールから Amazon SNS にメッセージを投げられれば、Amazon SNS をハブにしていろいろ連携できるようになります。

これまでは、

- 自前で SNS の API を叩く
  - Webhook しか対応していないサービスは連携不可
- Zapier 等のハブサービスを利用
  - 品質や応答速度がやや心配（見えない）
- サーバを立てて HTTP ラッパーを実装する
  - サーバの維持、メンテナンスコストが問題

ぐらいしか選択肢がなかったのですが、API Gateway の登場で、自前でサーバを作らずに HTTP から Lambda を呼び出せるようになったので、AWS のサービスだけで完結できます。

今回は、試しに NewRelic の channel 設定で Webhook を追加し、Alert を Amazon SNS に投げてみます。

## Lambda から SNS へ投稿するための Role を作成する

Lambda から Amazon SNS へ Publish するため、Role を新たに作成しておきます。

> `lambda_basic_execution` を作成しているのであれば、既存のものに追加しても OK ですが、分けておいたほうがいいと思います。

自分は `lambda_sns_execution` として作成しました。
通常の権限に加えて、`sns:Publish` を有効にしておきます。
resource も特定の Topic を指定してしまったほうが安全でしょう。

![IAM_Management_Console.png](https://qiita-image-store.s3.amazonaws.com/0/15611/260a716d-46f1-aa5a-accd-991812f7cdf7.png "IAM_Management_Console.png")


## Lambda function を作成する

まずは Lambda function の作成です。
package.json や gulpfile.coffee は、これまでのものとほぼ同じなので省略します。

> 必要な package が `aws-sdk` になるだけです。

```coffeescript:src/index.coffee
topicArn = 'YOUR_TOPIC_ARN'
snsRegion = 'ap-northeast-1'

AWS = require 'aws-sdk'
AWS.config.update region: snsRegion
sns = new AWS.SNS()

exports.handler = (event, context) ->
  params =
    Message: JSON.stringify(default: JSON.stringify(event))
    MessageStructure: 'json'
    Subject: event.condition_name
    TopicArn: topicArn

  sns.publish params, (err, data) ->
    if err
      context.done null, err
      return

    context.done null, 'Post succeeded.'
  return
```

`gulp build` でさくっと zip を作成、以下のコマンドで function を作成します。

```console
aws --region us-east-1 lambda create-function \
  --function-name lambda-newrelic-sns \
  --runtime nodejs \
  --role arn:aws:iam::xxxxxxxx:role/lambda_sns_execution \
  --handler index.handler \
  --description 'Send message to Amazon SNS' \
  --timeout 3 \
  --memory-size 128 \
  --zip-file fileb://dist/lambda.zip
```

> Amazon API Gateway から Lambda を呼び出す際、まだ東京リージョンの function は呼び出せませんので、`us-east-1` で作成しておきます。

## API Gateway を作成する

まずは、API Gateway に行き、必要であればリージョンを N.Virginia に変更しておきます。

### API の作成

新規に API を作成します。

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/b628d1e6-87b8-9401-4af6-89dde8de39f6.png "API_Gateway.png")

API が作成されたので、「Create Resource」を押してリソースを作成します。

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/a0c4ed04-ba55-7002-f17c-0b19d504e999.png "API_Gateway.png")

`/newrelic` という endpoint が作成されたので、「Create Method」を押して、POST を選択。

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/7e4c2208-d59b-a839-9ea7-141b61af565e.png "API_Gateway.png")

すると、POST メソッドの設定画面になります。
Lambda Function を選択し、region を us-east-1 に、作成した Lambda function 名を入力します。 

> 自動的に function の候補が表示されます。

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/2e84cbb2-d564-2abb-8f51-e0473a905a72.png "API_Gateway.png")

最後に、以下の様な画面が表示されれば作成完了です。

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/0b11b1e8-9ee8-cab1-02eb-402297f474dc.png "API_Gateway.png")

### API のデプロイ

作成した API はデプロイしないと有効になりません。「Deploy API」をクリックし、

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/97809c8d-1e73-9ba6-0790-0bd5a280b1c5.png "API_Gateway.png")

適当に stage を作成します。
ここで作成した stage は、API のパスに含まれることになります。

> 「prod」と入力すると、ttps://xxxxxxxx.us-east-1.execute-api.com/prod/newrelic のようなパスになります。

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/09d53d44-940a-2daf-aab2-b1391567833a.png "API_Gateway.png")

これで API の利用準備が整いました。
Stages をたどると、API の URL が確認できます。

> Lambda function 側の管理画面でも、「API endpoints」を選択すれば確認できます。 

![API_Gateway.png](https://qiita-image-store.s3.amazonaws.com/0/15611/85ceabbd-2e0c-48c8-461d-c5387565aa3b.png "API_Gateway.png")

## NewRelic の設定を行う

NewRelic にログインし、Alerts(beta) から、Notification Channels に行きます。
「New notification channel」から新規の通知チャネルを作成し、Webhook を選択、チャネルの名称及び、API の URL を入力します。

![Alerts_by_New_Relic.png](https://qiita-image-store.s3.amazonaws.com/0/15611/33e0fe83-7aec-446e-793c-ea8ae92f31db.png "Alerts_by_New_Relic.png")


これで、NewRelic からの Alert 情報が POST で API に送信され、それが Amazon SNS に流れるようになります。

## 補足

現状のままだと、SNS の Message 形式を JSON データにしているため、メールや Slack への投稿時にそのまま JSON データがベタで表示されます。
また、きちんと運用するには Basic 認証等の機構を追加すべきですね。API Gatewayにもその設定がありますので。

NewRelic から投稿されるデータのフォーマットに合わせて、メッセージ内容をうまくカスタマイズすることで、より柔軟な通知が実現できます。

NewRelic から投稿されるデータのフォーマットは、こちらになります。
[Customize your webhook payload](https://docs.newrelic.com/docs/alerts/new-relic-alerts-beta/managing-notification-channels/customize-your-webhook-payload)