kyohei's blog

profile picture
Written by Kyohei Tsukuda who lives and works in Tokyo 🇯🇵 , building useful things 🔧.
email / facebook / X / GitHub

AWS Amplify を Slack からデプロイする

July 08, 2019 - API Gateway AWS Lambda Slack amplify
⚠️ 2019/07 の段階でOutgoing WebhooksはLegacy扱いとなり、
   SlackAppへの移行が推奨されています。

AWS Amplify は通常コミット時に自動的にデプロイのスクリプトが走りますが、 Git で管理していないデータを取り込んだりする際には、 以下のように amplify-cli のコマンドをローカルから叩いて実行する必要があります。

$ export AWS_PROFILE=deploy-profile
$ amplify push

シンプルにはこれでよいのですが、

上記のような問題から以下のフローのように Slack から deploy を実行できるようにしたいと思います。

net_flow.png

AWS Amplify の Incoming webhooks の設定

AWS Amplify には Zapier や Jenkins など外部のサービス・ツールからでもビルドをトリガするための webhook が用意されています。 document はこちらIncoming Webhooks

AWS Amplify のコンソールからビルドの設定の下部の Incoming webhooks を新規に作成します。 生成された Command をを実行するとビルドを実行できることがわかります。

$ curl -X POST -d {} \\
  "<https://webhooks.amplify.ap-northeast-1.amazonaws.com/prod/webhooks?id=xxxx&token=xxxxxx&operation=startbuild>"\\
  -H "Content-Type:application/json"

> {"SendMessageResponse":{"ResponseMetadata":{"RequestId":"...

AWS Lambda からビルドを実行する

Slack の Outgoing WebHooks でこの URL を呼び出せるといいのですが、 Outgoing WebHooks から POST するとコンテンツの形式がJSONでなくform-urlencodedで送られてしまうので、うまく送信できないようです 😓

なので、上記のコマンドを Lambda で起動できるようにスクリプトを作成します。

AWS Lambda を開き、以下のように node.js の関数を作成します。(POST のリクエストができればいいので、正直言語は何でもいいです)

以下のような、シンプルな POST リクエストの関数を作成します。

const https = require('https');
// AWS Amplifyから生成されたもの
const amplifyId = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
const token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

const options = {
  hostname: 'webhooks.amplify.ap-northeast-1.amazonaws.com',
  port: 443,
  path: `/prod/webhooks?id=${amplifyId}&token=${token}&operation=startbuild`,
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  }
};

exports.handler = (event) => {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      console.log(`statusCode: ${res.statusCode}`);

      res.on('data', (d) => {
        // レスポンスに`text`を返すことでそれをslackに表示できる
        const response = {
          text: '[AWS Amplify] build starting...'
        };
        resolve(response);
      });
    });

    req.on('error', (error) => {
      console.error(error);
      reject(error);
    });

    req.end();
  });
};

AWS Lambda 上で「テスト」を実行し、成功したら OK です。

lambda_logs.jpg

Slack へのメッセージのカスタマイズは Formatting messages | Slack を参照ください。

AWS API Gateway の設定

AWS API Gateway から Lambda を起動するようにします。

まずは API Gateway で新しい REST の API を作成します。

api_gateway1.jpg

POST アクションを作成し、上記で作成した Lambda 関数を指定します。

api_gateway2.jpg

クライアントの「テスト」を実行し、正常に動作していることを確認します。(現状引数はなくてもよいので指定する必要はないです)

api_gateway3.jpg

Slack の Outgoing WebHooks から送られてくる POST リクエストは本文のコンテンツの形式(Content-Type)がapplication/x-www-form-urlencodedになっているので、それを JSON として扱えるように変換する必要があります。

リクエストのマッピングテンプレートに

application/x-www-form-urlencoded

を追加し、 マッピングテンプレートは以下のようにします。

api_gateway3_5.jpg

## POSTで送られてきたデータ変数に入れる
#if ($context.httpMethod == "POST")
 #set($rawAPIData = $input.path('$'))
#else
 #set($rawAPIData = "")
#end

## &で区切られたKey-Valueをセットにする
#set($tokenisedAmpersand = $rawAPIData.split("&"))
#set($tokenisedEquals = [])

#foreach( $kvPair in $tokenisedAmpersand )
 #set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
 #if ($countEquals == 1)
  #set($kvTokenised = $kvPair.split("="))
  #if ($kvTokenised[0].length() > 0)
   ## we found a valid key value pair. add it to the list.
   #set($devNull = $tokenisedEquals.add($kvPair))
  #end
 #end
#end

## "{" と "}"で括り、出力する
{
#foreach( $kvPair in $tokenisedEquals )
  #set($kvTokenised = $kvPair.split("="))
 "$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}

次に外側から呼び出すために「ステージ」が必要なので、 アクションから API のデプロイ を選択し、新しいステージを作成します。

api_gateway4.jpg

ステージにデプロイされた POST リクエストを確認します。

api_gateway5.jpg

$ curl -X POST \\
  <https://xxxx.execute-api.ap-northeast-1.amazonaws.com/prod/> \\
  -H "Content-Type:application/x-www-form-urlencoded"

> {"text": "..."}

Lambda 関数で定義した戻り値が表示されていることを確認できます。

slack から API を呼び出す

slack の apps 設定ページ(https://[YOUR_ORG].slack.com/apps)から outgoing webhook を追加し、 "Add Configuration"から新規の webhook を追加する。

slack_outgoing.jpg

例として、以下のように設定する。

Descriptive Label、Customize Name、Icon はなんでも OK です。

slack でamplify-buildと発言すると Lambda が動作し、amplify のビルドが実行されていることが確認できました。 引数等も調整すればいろんなブランチへのデプロイも指示できそうですね。

slack.jpg