年中アイス

いろいろつらつら

AWS API Gateway+LambdaでSlackにメッセージをPOSTする(前編)

Slackはincoming webhooksがあり、POSTするだけで、簡単にメッセージを送ることができます。

スクリプトで直接Incoming Webhook使ってもいいんですが、これ自体は別に認証も何もないので、誰かが知ってしまうと、好き放題投稿できます。そんな時に入れ替えたりする場合、いろんなスクリプトの設定に埋め込まれていて、方々に散っているのはちょっと辛いです。また、多言語環境の場合は、一定の処理を行う場合に各言語でライブラリ作るのもしんどいかなーということで、AWSAPI Gateway + Lambdaを使って汎用化してみます。*1

Slackのteam、AWSアカウントはあるものとして、以下を行っていきます。

  • Slack Incoming Webhookの作成
  • AWS Lambdaでincoming Webhookを使う
  • AWS API GatewayとLambdaを繋ぐ(後編で)

Slack Incoming Webhookの作成

Incoming Webhookは、channelを指定するので、今回は#generalを使います。必要に応じてchannelを作成してください。

次に、incoming webhook integration(新規作成ページへのリンク)を開きます。

channelに#generalを選択して作成します。 f:id:reiki4040:20170129234607p:plain

そうすると、settingsのページに行くので、Webhook URLをコピーしておきます。 f:id:reiki4040:20170129234617p:plain

また、下の方に行くと、名称とアイコンを設定できるので、用途に合わせて変更します。 f:id:reiki4040:20170129234624p:plain

以下のcurlで試すと、メッセージが表示されます。(URL部分は置き換えてください)

curl -X POST -H 'Content-type: application/json' \
--data '{"text":"This is a line of text.\nAnd this is another one."}' \
 https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

レスポンスは、成功したらokだけです。以下のように表示されます。(画像は名称の反映が遅延しているのか、incoming webhookのままでした) f:id:reiki4040:20170129234629p:plain

AWS Lambdaでincoming Webhookを使う

次に、Lambda上に実装していきます。

-- 2018/02/18 追記

この後の、Lambda+API GatewayをCloudFormationで自動構築できるようにしたので、面倒な方はそちらもどうぞ。

reiki4040.hatenablog.com

-- 追記ここまで

AWS consoleからLambdaサービスを開き、Functionsから[create a Lambda function]を選んでスタートします。BluePrintには、pythonでのCloudWatch->APIGateway->Lambda->Slackはありますが、今回はnode.jsで行うので、Blank functionから作ります。 f:id:reiki4040:20170129234702p:plain

Triggerは、作らないまま進みます。 f:id:reiki4040:20170129234753p:plain

詳細が出てくるので、Nameにpost-slack-general(好きな名称で構いません)を入力して、RuntimeはデフォルトのNode.js 4.3にします。 f:id:reiki4040:20170129234739p:plain

Lambda function codeは[Edit in inline]で、以下を入れます。

console.log('Loading function');

const https = require('https');
const url = require('url');
const slack_url = process.env.SLACK_WEBHOOK_URL;
const slack_req_opts = url.parse(slack_url);
slack_req_opts.method = 'POST';
slack_req_opts.headers = {'Content-Type': 'application/json'};

exports.handler = function(event, context) {
  if (event.text) {
    var req = https.request(slack_req_opts, function (res) {
      if (res.statusCode === 200) {
        context.succeed('posted to slack');
      } else {
        context.fail('status code: ' + res.statusCode);
      }
    });

    req.on('error', function(e) {
      console.log('problem with request: ' + e.message);
      context.fail(e.message);
    });

    // ここで、メッセージの編集を必要に応じて行う。
    var message = event.text;

    req.write(JSON.stringify({text: message}));

    req.end();
  }
  });
};

このAWS LambdaでSlackのIncoming Webhookでメッセージを送る例は、 CloudWatchのAlertをAWS Lambda経由でSlackに飛ばすを参考にさせていただきました。

上記ページは、SNS用のpayload構造になっているので、その部分などを変更しています。{"text":"message"}というJSONが来て、そのtextのvalueをSlackに送るLambda(node.js)です。*2

また、Webhook URLは環境変数で入れるように変更しています。そのあとのEnvironment variablesで、SLACK_WEBHOOK_URLというkeyでWebhook URLを設定します。 f:id:reiki4040:20170129234730p:plain

Lambdaの実行のIAMRoleは、特に追加の権限は必要ないので、最低限Lambda実行に必要なIAMRoleを作ります。すでにある方は飛ばしてください。[Create a custom role]を選択して、IAMの画面に移動します。 f:id:reiki4040:20170129234653p:plain

デフォルトで表示されているものを[Allow]で許可すれば大丈夫です。 f:id:reiki4040:20170130000047p:plain

作成したIAMRoleが選択されます。 f:id:reiki4040:20170129234711p:plain

Advanced settingsのところは特に変更しません。最下部の[Next]で次のプレビューに進み、[Create function]で作成完了です。

Lambdaの実行確認

Lambdaは、テスト実行ができます。[Action] -> [configure test event]を選びます。 f:id:reiki4040:20170129234807p:plain

デフォルトで[Hello world]が選択されているので、その下のJSONを、以下に変更して、[Save and Test]で実行します。

{"text":"Message from AWS Lambda"}

f:id:reiki4040:20170129234817p:plain

成功したら、Slackにメッセージが表示されます。 f:id:reiki4040:20170129234800p:plain 出てこない場合は、AWS console画面下部に実行ログが出てくるので、それで確認しましょう。

次回、API Gatewayと繋いでいきます。

参考

*1:使ってみたいだけというのもある

*2:お好みで、他のkeyを追加して、送る前に組み立ててください。