Azure FunctionでWebAPIを定期的に監視し異常をSlackに通知


Azure Functionにある指定時間に処理を実行する機能「Timer Triger」を利用して、WebAPIに繋がるか/正常な値を返すか一定時間間隔でチェックを行い、異常があればSLACKに通知を行う。

Timer Trigerは現段階ではC#、F#、JavaScriptしか対応していないようなため、今回はJavaScriptで行う。

## 通知先のSLACKのWeb Hook URL取得

1. slack画面左下の「App」の＋ボタンをクリック

![image.png](https://qiita-image-store.s3.amazonaws.com/0/38382/a536bc7d-7fc5-ac3b-96b2-464548ad0a38.png)

2. 「incomming-webhook」で検索し「インストール」をクリック

![image.png](https://qiita-image-store.s3.amazonaws.com/0/38382/7f2cd713-046a-9dd4-7ac9-04e7d2747766.png)


3. Webhook URLをコピーしておく

![webhook.png](https://qiita-image-store.s3.amazonaws.com/0/38382/47491437-bff6-deb6-6887-faa29855b8c7.png)

## Azure Functionの設定

1. ポータルサイトの左メニューの「App Service」→「＋追加」→「Function App」→「作成」

![image.png](https://qiita-image-store.s3.amazonaws.com/0/38382/e17876cc-54d7-13f8-ebef-fe45fe170e84.png)


2. ポータルサイトの左メニューの「Function App」→「関数」→「新しい機能」

![azure_function2.png](https://qiita-image-store.s3.amazonaws.com/0/38382/417584bb-4441-3faf-5401-54a840491356.png)

3. 「Timer Triger」を選択 → 言語（JavaScript)指定 → 実行する時間指定 → 作成

![azure_function4.png](https://qiita-image-store.s3.amazonaws.com/0/38382/63e2938e-54de-b450-6076-3276dbf85f5d.png)

Scheduleに実行日時を入力。
6時間毎に0分0秒に実行の場合、「0 0 */6 * * *」と入力。

## requestモジュールのインストール

初期状態ではrequestモジュールが使用できないのでインストールする。

https://[function-name].scm.azurewebsites.net/DebugConsole
からデバッグコンソールを開きます。([function-name]に自分のファンクションApp名を入力)

cdでD:\home\site\wwwroot\[関数名]へ移る。

```bash
D:\home>cd site\wwwroot\TimerTriggerJS1
D:\home\site\wwwroot\TimerTriggerJS1> 
```

requestモジュールのインストール

```bash
D:\home\site\wwwroot\TimerTriggerJS1>npm init
D:\home\site\wwwroot\TimerTriggerJS1>npm install --save request
```

## テスト実行と保存

以下を入力して「保存および実行」を押すとテスト実行される。
動作に問題なければ「保存」を押すことでScheduleで指定した時間で実行される。

```js
module.exports = function (context, myTimer) {
    var SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/〇/△/□"; // ↑でコピーした通知先SLACKのWebhook URL
    var API_URL = "http://〇.〇.〇.〇/api/hoge"; // 死活チェックするWebAPIのURL

    var data = "";
    var channel = "channel_name"; // 通知先のSLACKチャンネル名

    var request = require('request');
    var http = require('http');

    
    var options = {
        url: API_URL
        ,method: 'GET'
        ,json: true
    };

    var payload = {};
    payload.username = "hoge_bot"; //slackに表示される名前
    payload.icon_emoji = ":anger:" // アイコン
    payload.channel = channel;


    http.get(API_URL, (res) => {
        const { statusCode } = res;
        const contentType = res.headers['content-type'];

        context.log(statusCode);
        let error;
        if (statusCode !== 200) {
            data = "Failure accessing " + API_URL + ", Status Code : " + statusCode;
        } else if (!/^application\/json/.test(contentType)) {
            error = new Error('Invalid content-type.\n' +
                            `Expected application/json but received ${contentType}`);
        }
        if (error) {
            data = "API_URLにアクセスに失敗した。" + ", " + String(error);
        }

        res.setEncoding('utf8');
        let rawData = '';
        res.on('data', (chunk) => { rawData += chunk; });
        res.on('end', () => {
            try {
                const parsedData = JSON.parse(rawData);
                if ("<返されるJSONデータに含まれるキー>" in parsedData) {
                    articles = parsedData.articles;
                } else {
                    data = "パースした結果にキーが含まれていなかった。" + String(rawData);
                }
                
            } catch (e) {
                context.error(e.message);
                data = "パース自体に失敗した" + String(rawData);
            }

            payload.text = data;

            var options = {
                url: SLACK_WEBHOOK_URL
                ,method: 'POST'
                ,headers: 'Content-type: application/json'
                ,json: payload
            };
            request.post(options, function (error, response, body) {
                context.log("Response Code : "+response.statusCode);
            })
            context.done();
        });

    }).on('error', (e) => {
        data = API_URL + "へアクセスできませんでした。ERROR : " + + String(e);

        payload.text = data;

        var options = {
            url: SLACK_WEBHOOK_URL
            ,method: 'POST'
            ,headers: 'Content-type: application/json'
            ,json: payload
        };
        request.post(options, function (error, response, body) {
            context.log("Response Code : "+response.statusCode);
        })
        context.done();
    });

};
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/38382/70d872d6-b1a8-089a-f16f-28f103403c94.png)


## 動作

![image.png](https://qiita-image-store.s3.amazonaws.com/0/38382/c1fda7ab-627f-013d-89b6-c53188af3519.png)

## 料金

6時間に1回実行で0.32円/1日≒10円/30日程度

![azure_function3.png](https://qiita-image-store.s3.amazonaws.com/0/38382/fbcc3515-0e55-122f-865d-79010990ebde.png)