年中アイス

いろいろつらつら

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

前回、Slack Incoming Webhookの作成と、それを使うLambdaを作成したので、続いてAWS API GatewayとLambdaを繋いでいきます。書いていたら思ったより長くなったので、API Gatewayのテスト実行までを中編にして、後編で、API keyの設定とデプロイを行います。

Lambda自体は、サーバレス(正確にはインスタンス管理レス)で動きますが、呼び出しは他のAWSイベントをフックするものが多いです。それはそれでいいんですが、どうしてもAWSよりのコードが入るので、そこをWebAPIという形で、汎用的に扱えるようにします。

しかし、普通にWebAPIを用意しようとすると、サーバを準備して、nginx/apacheやプログラムを作って、Lambdaを呼び出すようにしなければならないため、API Gatewayを使って、サーバの運用なしで使えるWebAPIを作ります。

今回は1機能だけなので、直接/にJSON{"message":"hello world"}をPOSTしたら、Slackに投稿されるようにします。

AWS API GatewayとLambdaを繋ぐ

まずはAPI Gatewayで、WebAPIを作っていきます。AWS Consoleから、API Gatewayを開きます。

f:id:reiki4040:20170202221643p:plain:w400

[Create API]を選択するとフォームが出てくるので、名称を入れます。ここでは、post-slack(好きな名称で良いです)としておきます。

f:id:reiki4040:20170202221709p:plain

[Create API]で作成すると、post-slack APIのメニューが出てきます。Resourceを選び、そこで/を選択し、[Action] -> [create method]を選択し、

f:id:reiki4040:20170202221726p:plain

POSTを選択します。

f:id:reiki4040:20170202221732p:plain:w300

そうすると、Integrationを何にするか選択する画面が出てくるので、Lambdaを選択し、リージョンとLambda function名を入れます。入力を始めるとサジェストしてくれます。ap-northeast-1と前回作ったpost-slack-generalを選びます。 f:id:reiki4040:20170202221752p:plain

[Save]を押すと、API GatewayにLambdaの呼び出しを許可するかと求められるので[OK]にします。

f:id:reiki4040:20170202221933p:plain:w400

Client->Request->Integration->Responseのフローを表す、左右に縦長長方形、間に2x2 4つのマス目が表示されます。HTTPのリクエストをLambdaに渡す時の設定を行います。[Integration Request]を選択します。 f:id:reiki4040:20170202222019p:plain

下部のBody Mapping Templatesを開きます。Request body passthroughは、Neverにしておきます。これは定義したContent-Type以外は、HTTP status code 415でfailさせる扱いです。*1 f:id:reiki4040:20170202222101p:plain

今回は、JSONをPOSTしてくるので、+Add mapping templateを選択し、 f:id:reiki4040:20170202222130p:plain:w300

application/jsonを入力してチェックマークを選択します。(例として表示されてますが入力が必要です)

f:id:reiki4040:20170202222136p:plain:w300

application/jsonを選択すると、下部に入力欄が出てでくるので、以下を入力して[save]します。

{ "text": $input.json('$.message') }

f:id:reiki4040:20170202222155p:plain:w400

POSTされてきたJSONmessageというkeyの値を、textというkeyの値として置き換えるということをやっています。{"message":"hello"}{"text":"hello"}にしています。

Mappingが何に役立つかというと、Lambdaはtextで受け付けるように作ったけど、key名がいけてないなーと思っても、Lambdaには手を入れたくない(入れられない)という時に、WebAPIではmessageで受け取って、Lambdaに渡す時に変換できるということです。*2

とりあえずこれで動くので、API GatewayのTest機能を使って動かしてみます。 上部の<- Method Executionで、マス目の並ぶ画面に戻ります。

テスト実行する

左の[Test]雷マークみたいなのを選ぶと、テスト実行の画面になります。

f:id:reiki4040:20170202222349p:plain:w400

Request Bodyに{"message":"POST from API Gateway + Lambda"}を入れ*3、 [Test]を実行すると、横にレスポンス *4 やログが出てきます。 f:id:reiki4040:20170202222424p:plain

特に問題なければ、Slackにメッセージが出てきます。動かない時はログ見てください。 f:id:reiki4040:20170202224832p:plain

これでとりあえずWebAPIとしての動作はOKです。あとはデプロイすれば、実際に世界中から使えるようになります。

次回、API keyを使ってアクセスを制限して、デプロイして使えるようにします。

参考

*1:AWSのドキュメントみると、Request body passthroughのWhen no templateとNever何が違うんだろうという。

*2:リクエスト情報を一通り受けたい場合は、Lambda Proxy Integrationを使うと楽そうです。

*3:コメントで指摘いただき修正しました

*4:レスポンスが雑なメッセージですが、多分設定追加すれば{“response”:“ok”}とかぐらいにはできるはず・・・

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を追加して、送る前に組み立ててください。

iPhone7に変えました

こっちにも書きましたが、2016年末に、iPhone5sからiPhone7に変えました。決め手はやっぱりApplePayでのSuica(後述しますが、停滞中)で、副産物としてQuicPay(ポストペイ型の電子マネー)が便利という気づきがありました。

値段

AppleStoreOnline SIMフリー 128GBモデルで、消費税込みで約9万円でした。32GBだと72,800円+税で、以前の5s 16GBと同じ値段(当時)。ちなみにiPhoneSEは16GB 44,800円, 64GB 49,800円(いずれも税抜き)という価格で、最後まで迷いました。SEは格安SIMと合わせれば、相当コストパフォーマンスは高いです。

セットアップ

MVNOのSIMを継続して使用してます。初期セットアップ時、認証のためにSIMが必要になります。ないと進められないです。また、WiFiで経由でセットアップしてましたが、MVNOキャリアから、LTE接続用のプロファイルを入れないとモバイル回線は繋がらないので注意。プロファイル入れなくても4Gと表記されて、使えるかと思いきや繋がりませんでした。

良かった点

ApplePay(Suica, QuicPay)便利

ガラケーモバイルsuicaを使ってましたが、iPhoneの方は、使ってすぐに使途が通知されるのと残高が確認しやすくて良いです。加えて、ApplePayにクレジットカードを登録すると、iDかQuicPayが使えるようになるんですが、これらはポストペイ(後から支払い)型なので、事前にチャージする必要がないものです。近所のスーパーがQuicPayに対応していたので、Suica払いをやめて、QuicPayに変えました。週一はチャージしてて地味に面倒なので、無くなると案外楽です。

Live photoが子供には結構いい

子供の写真を撮るときに、LivePhotoにしておくと前後の動きが見れて、可愛さが倍増するケースがあって良いです。使うまでは、どうなんだろうなーと思ってましたが、いい感じ。ただiPhone以外からは再生できるのかな。

容量が大きくなって、写真と動画を気にしなくて良くなった。

iPhone7とは直接関係ないですが、子供の写真や動画を撮るようになってから、16GBでは全く足りなくて、よく「空き容量がありません」という通知で写真が撮れないという状態になってました。子供相手だとすぐ写真撮れないのは致命的だったので、そのストレスからも解放されました。

ちょっと気になる点

ややでかい

5sから変更で、大きさは気になります。手は小さくはないので持てるんですが、やっぱり少し大きい。ノーケースは、滑って落とすので、ややグリップの効く薄型ケースを付けてます。

カメラの出っ張り

5sはフラットだったんですが、6からずっとカメラレンズが出っ張っていて、ケースが必須。丈夫だとは思うんですが、そこから硬いところに置くと、パキッとかならないか心配。前述の滑りもあってケースでカバーしてます。

ApplePayのオンライン決済でVISAが使えない

Suica入れて、まずチャージができないというのでハマったのが、VISAカードは登録できるけど、オンライン決済で使えない点。店舗での決済はVISAも使える様子(まだ試せてないです)Suicaアプリから会員登録して、そっち経由であればVISAでもいけるようですが、私は後述の状態で、MasterCard or JCBを作ってそっちでやる予定。日本でApplePay始まってまだ四半期程度ですが、VISAが進みそうな話も特になく。

Suica移行未完(これ直接iPhone7の話ではない)

ApplePayに登録したカードが、オンライン決済に対応していたら、それを使ってチャージ可能なようです。VISAはダメなので、モバイルsuicaに登録して、そこにカードを登録しようとしましたが、「そのカードは登録できません」とだけ出て、理由がわからず。Webを探すと、本人名義と違ったらとかのエラーもある様子ですが、その文言は出ず。ガラケーモバイルsuicaに登録してあるカードだからダメとか裏ルールでもあるのかしら。

注意点として、機種変でiPhoneに移す場合は、Suicaアプリインストールして最初に移行手続きしないと、他のカード読み込んだり新規発行したら、移行先として使えなくなります。私は、とりあえず早く使いたい一心で、余ってたSuica物理カードを読み込んでしまったので、移行不可に。ガラケーモバイルsuicaは、とりあえずそのまま維持予定です。

感想

まだ1か月も使ってないですが、iPhoneSEに変えないとやってられん、という感じにはならず、変えてよかったと思ってます。3年程度5s使ってましたが、単純性能では特に困ってなかったので、7も次の革新か、廃れすぎるかまでは使う予定です。あとは店舗でApplePay(クレジットカード払い)を試したいところ。財布からだすの面倒で電子マネーに逃げてたので、対応店舗が増えて欲しいですね。電子マネー or ApplePayで、財布を不要にしたい。