【Google Cloud】Google Cloudの課金データを日次でSlackで通知する

# はじめに

Google Cloudの課金データを日次でSlackに通知する仕組みを作成してみたため、まとめてみました。

下記のサイトを参考にしています。
[BigQuery 破産で泣かないために。。GCP 利用料金を bot で Slack に通知してみた](https://dev.classmethod.jp/articles/gcp-cost-slack-bot-notify/)
[GASで手軽に SlackへのKPI通知Botを作る](https://qiita.com/s_yaginuma/items/26715f776fe6e8c6c969)
[BigQuery課金情報をslackへの通知機能を作る](https://qiita.com/GDP-HKY/items/3feb6ab20404c3d2fb03)

# 1. 使用したもの

+ Cloud Billing
+ BigQuery
+ Google Apps Script
+ Slack


# 2. 仕組み

## 2-1. 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/05bd7823-e526-5180-1314-c9497026eb68.png)

## 2-2. 処理の流れ
①　Google Cloudの課金データをBigQueryのデータセットにエクスポートする
②　BigQueryに対しGoogle Apps Scriptを使用してクエリを実行する
③　クエリの結果をSlackに通知する

# 3. 設定内容

サービスごとの設定内容を説明します。
※Google CloudのCloud BillingやBigQueryのアクセス権限がある前提で説明します。

## 3-1. Cloud Billing、BigQuery
GCPコンソールのナビゲーションメニューの「お支払い」から「課金データのエクスポート」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/0d0c3d44-4445-825a-b47e-6843d2f88747.png)

BigQueryにエクスポートする課金データは下記の3タイプがあります。
・標準の使用料金データ
・詳細な使用料金データ
・料金データ
それぞれの課金データの説明は[Cloud Billingのドキュメント](https://cloud.google.com/billing/docs/how-to/export-data-bigquery?hl=ja)を参照ください。

今回は標準の使用料金データをBigQueryのデータセットにエクスポートします。
「標準の使用料金」の「設定を編集」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/8cd730ac-e276-6014-7c34-07b9027adbec.png)

課金データをエクスポートするプロジェクトと、エクスポート先のBigQueryのデータセットを指定します。
既に課金データをエクスポートする際に使用するデータセットを作成済みの場合は、そちらを選択してください。

今回は課金データのエクスポート設定の中でデータセットを作成します。
「データセット」から「新しいデータセットの作成」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/3d44e11e-ff6f-e67b-1299-a7cde2ffbd68.png)

「データセットID」と「データのロケーション」を選択します。
今回はテーブルの有効期限や詳細オプションの暗号化は設定しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/140775f9-f022-481e-6da2-1a92bbe30a5d.png)

データセットの作成後、作成したデータセットが課金データのエクスポートに設定されていることを確認し、保存します。

すると、課金データのエクスポート設定が有効になることを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/4b48c536-cab3-5357-1d4f-7d0315f00dff.png)

GCPコンソールのナビゲーションメニューの「BigQuery」から「SQLワークスペース」を選択し、前述した手順で作成したデータセットが存在することを確認できます。
課金データのエクスポートの設定後、しばらく放置してから確認すると、課金データがエクスポートされているはずです。
※今回は3時間ほどかかりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/bd4c991e-0ec7-54ba-8fa0-45e80cb934cb.png)

データセットに対しクエリを実行し、サービスごとの課金データを参照してみます。

▼クエリ
```sql
SELECT
	DATE(TIMESTAMP_ADD(usage_start_time, INTERVAL 9 HOUR)) AS query_date,
	service.description,
	SUM(cost) AS cost,
	currency
FROM
	`プロジェクトID.データセット名.テーブル名`
GROUP BY 
	query_date,
	service.description,
	currency
HAVING
	query_date =  DATE_ADD(DATE(TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 9 HOUR)), INTERVAL -1 DAY)
ORDER BY
	query_date;
```

▼実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/52f18c0e-74e8-cc2e-0325-fe516f59a3c4.png)


## 3-2. Slack

先に、課金データを通知するためのSlackチャンネルを作成します。
今回は下記のように作成してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/8f551d74-27eb-51de-0900-f7a912c8211d.png)

チャンネルを作成したら、[slack api](https://api.slack.com/)にてSlackに通知するためのアプリを作成します。

「From scratch」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/7d56ceb1-268e-954b-576d-cab0c445f315.png)

作成するアプリの名前を入力し、連携先のSlackワークスペースを選択した後、「Create App」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f7835d6f-386f-0f4e-f0c3-364180694996.png)

作成したSlackチャンネルにアプリを連携していきます。

「Incoming Webhooks」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/443da6f4-f175-277e-1594-fff01d61b8b0.png)

Incoming Webhooksを有効にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/ca27fb69-8a96-aaf4-5beb-9dc44b9c94e6.png)

通知先のワークスペースを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/45091ba4-01e9-fb94-285a-e4ce32c4c82f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/45104171-dab0-a2d1-16d1-e0b8feff6f13.png)

アプリとSlackチャンネルが連携されると、Slackチャンネルに下記のメッセージが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/ab4cc917-d71c-0cb9-1c6c-537043298a39.png)


## 3-3. Google Apps Script

前述した方法で、Google Cloudの課金データをBigQueryのデータセットにエクスポートできるようにしました。
Google Apps ScriptからBigQueryに対しクエリを実行して、Google Cloudの課金データを取得し、結果をSlackに通知します。

[Google Apps Scriptのサイト](https://www.google.com/script/start/)で「Start Scripting」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/84973418-1b39-8ff1-e228-2e040598aacc.png)

Google Apps Scriptのコンソール画面が開いたら、「新しいプロジェクト」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/99543357-36c0-9c11-3c91-b87225bc435d.png)


下記の処理を実施するプログラムをJavaScriptで記載します。
・Google Cloudの課金データを取得する
・Slackメッセージを組み立て、ポストする

▼プログラム
```js
// BigQueryに対しクエリ実行
function runQuery() {
    var projectId = "プロジェクトID";
    var request = {
      "query" : "SELECT DATE(TIMESTAMP_ADD(usage_start_time, INTERVAL 9 HOUR)) AS query_date, service.description, SUM(cost) AS cost, currency FROM `プロジェクトID.データセット名.テーブル名` GROUP BY   query_date, service.description, currency HAVING query_date =  DATE_ADD(DATE(TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 9 HOUR)), INTERVAL -1 DAY) ORDER BY query_date;", 
      "useLegacySql": false,
    };

    var queryResults = BigQuery.Jobs.query(request, projectId);
    var jobId = queryResults.jobReference.jobId;
    var sleepTimeMs = 500;
    while (!queryResults.jobComplete) {
      Utilities.sleep(sleepTimeMs);
      sleepTimeMs *= 2;
      queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId);
    }

    var rows = queryResults.rows;
    while (queryResults.pageToken) {
      queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId, {
        pageToken: queryResults.pageToken
      });
      rows = rows.concat(queryResults.rows);
    }

    return rows
  }

// Slackメッセージの組み立て、ポスト
function postSlack(date, description, cost, currency) {
    // メッセージを組み立てる
    var body = {
      "attachments" : [
        {
          "color": "00FF00",
          "fields": [
            {
              "title": "日付",
              "value": date,
              "short": true
            },
            {
              "title": "サービス名",
              "value": description,
              "short": true
            },
            {
                "title": "金額",
                "value": cost + " [" + currency + "]",
                "short": true
            }
          ],
        }
      ]
    };

    // ポスト
    var webhookURL = 'Webhook URL';
    var options = {
        'method' : 'POST',
        'contentType' : 'application/json',
        'payload' : JSON.stringify(body),
    };
    UrlFetchApp.fetch(webhookURL, options);
}

// この関数を最初に実行する
function main(){
    var rows = runQuery();
    var date = "";
    var description= "";
    var cost= "";
    var currency= "";

    for (let i=0; i<rows.length; i++) {
      date = rows[i].f[0].v;
      description = rows[i].f[1].v;
      cost = rows[i].f[2].v;
      currency = rows[i].f[3].v;
      postSlack(date, description, cost, currency);
    }
}
```

### 注意点（つまづいた箇所）

１点目：プロジェクトを実行する際に、最初に実行する関数を選択してください。
ここで最初に実行する関数を指定し忘れていて、うまく動かなかったことがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/37f0d104-9f66-934b-a367-0dc9f8f5cc51.png)

２点目：サービス「BigQuery API」を有効にしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/83804231-294e-a59d-ab3b-4fc95979b46f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f105016e-8fed-be52-f6bc-8c47b4906e1c.png)

こちらを有効にしておかないと、BigQueryでクエリを実行する際に下記のようなエラーが発生します。
▼実行ログ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/8c045a87-d665-2f4b-44c3-6968d4991aca.png)

## 3-4. 動作確認
それでは、正常に動作するか確認してみます。
画面上部の「実行」ボタンをクリックして実行します。
ソースの気になる箇所にブレークポイントを貼り、デバッグ実行することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/8c3d9d85-c295-46cc-25e5-11540b6707fd.png)

▼実行ログ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/b7a1332a-ab07-c896-c15d-444c42bbbb36.png)
▼Slackの通知用チャンネル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/44ecf891-f0f3-3696-274e-cc4e098711c3.png)
動きました。
実行したのが2022-06-01で、前日(2022-05-31)の課金データがサービスごとに通知されました。

プログラムが動作することは確認できたため、これを定期的に実行するように設定します。

作成したプロジェクトの「トリガー」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/0aa71a94-8a80-a2f5-809f-8f3803c3c76d.png)

右下の「+トリガーを追加」ボタンを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/278e104a-b999-27cf-9777-5ffdcbde1ceb.png)

今回はGoogle Cloudの課金データを日次で通知したいので、下記のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/d113954c-98ff-8061-d6d0-b0047fe6e27a.png)
設定が完了したら、「保存」を選択します。
指定した日時でSlackに通知されるのを待ちます。

翌日、午前9時～10時の間に実行するように設定していましたが、無事Slackチャンネルに通知されていることを確認できました。

▼実行数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/73ca665d-1006-f51b-6580-8602380d70e3.png)
▼Slackの通知用チャンネル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/6faac59d-29a4-ffa5-2f11-0f74ecc556fe.png)

# おわりに

今回はGoogle Cloudの課金データを通知するといったことをやってみました。
Google Cloudは最近触り始めて、今回使用したサービス以外の物はまだ使ったことがないので、
どんどん活用しながら学んでいきたいと思います。