Azure利用料金を毎日Slackに通知する

AWSではCloud Watchのメトリックから利用料金を取得することができます。
Azureでも同じように利用料金を取得してSlackに毎日通知したかったんですが、だいぶ苦労しました。

# 前提

## 課金プラン
従量課金プランを対象としています。

## 利用料金を取得する流れ
まずAzure課金APIの種別を以下の記事にまとめました。
[Azureの課金系REST APIをcurlで実行する方法](https://qiita.com/shingo_kawahara/items/d7e9c4e395624259c05a)

Azure Resource Usage APIで取得したJSONと、Azure Resource RateCard APIで取得したJSONの、meterIdを突合して利用料金を算出します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/7f3d710b-3f64-3459-cbdf-0c8ff280da9b.png)

## 構成イメージ図
FunctionsをWeb APIとして利用します。
RESTでGETする契機はAzure Automationを使っています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/5dc319dc-72ce-acde-dcc6-c2d9ea5fed70.png)

## ローカルPC上でFunctionsの実行環境が整っていること
まとめた記事を書いたので参考にしてください。事前インストールの章ですね。
[Azure FunctionsをJava + VS Codeで試してみた（Maven for Java編）](https://qiita.com/shingo_kawahara/items/435941731cade567218f#%E4%BA%8B%E5%89%8D%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)

## 課金確認専用ユーザ作成
以下記事を参考に課金用サービスプリンシパルを作成します。
[Azureの課金系REST APIをcurlで実行する方法](https://qiita.com/shingo_kawahara/items/d7e9c4e395624259c05a#%E8%AA%B2%E9%87%91%E7%94%A8%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%82%B7%E3%83%91%E3%83%AB%E4%BD%9C%E6%88%90)

# Azure Functions実装

それでは早速Functionsから実装していきます。
コードは以下に配置しています。

[Azure-Billing-Api-Sample](https://github.com/shingo-kawahara/Azure-Billing-Api-Sample)

## git clone

* git cloneでリポジトリを持ってくる。

```
git clone https://github.com/shingo-kawahara/Azure-Billing-Api-Sample.git
```

## pom.xml修正

* pom.xmlを開いて、2箇所修正します。
    * functionAppName： これはFunction Appの名前になります。
    * functionResourceGroup： これはFunction Appが配置されるリソースグループ名です。

```java
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <azure.functions.maven.plugin.version>1.0.0-beta-7</azure.functions.maven.plugin.version>
        <azure.functions.java.library.version>1.0.0-beta-5</azure.functions.java.library.version>
        <functionAppName>azure-billing-api-sample</functionAppName>
        <functionAppRegion>japaneast</functionAppRegion>
        <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
        <functionResourceGroup>azure-billing-api-sample-grp</functionResourceGroup>
    </properties>
```

## local.settings.json配置

* 新規ファイル作成する。
* ローカルPCでテストするとき、もしネットワーク上にProxyサーバがいる場合は、このファイルにIDとパスワードを設定します。

```json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "AzureWebJobsDashboard": "",
    "FUNCTIONS_WORKER_RUNTIME": "java",
    "proxyHost": "Your Proxy HostName",
    "proxyPort": "Your Proxy Port",
    "proxyUser": "Your Proxy UserName",
    "ProxyPass": "Your Proxy Password"
  }
}
```

## ローカルテスト

* 以下コマンドを実行して、ビルドとローカル実行をします。

```
mvn clean package
mvn azure-functions:run
```

* Webブラウザを開いて、以下のURLを入力します。

```
http://localhost:7071/api/billing?subscriptionId=[Azure Subscription ID]&clientId=[Clinet ID(Application ID)]&clientSecret=[Client Secret ID(token)]&tenantId=[AAD Tenant ID(Directory ID)]
```

* 利用料金が表示されたらOKです。

## デプロイ

* 以下コマンドを実行して、Azure上にデプロイします。

```
mvn azure-functions:deploy
```

## Functions Key取得
* デプロイしたFunctionsを選択し、[管理]を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/dc4b6435-c97d-5070-a1d5-8d97a556ada3.png)

* [関数キー]をコピーしておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/f6432eac-4740-88a6-4a47-345ad2f09006.png)

## テスト

* Webブラウザを開いて、以下のURLを入力します。ローカル実行と比べて、Functions Keyを追加で付与しています。

```
http://azure-billing-api-sample.azurewebsites.net/api/billing?subscriptionId=[Azure Subscription ID]&clientId=[Clinet ID(Application ID)]&clientSecret=[Client Secret ID(token)]&tenantId=[AAD Tenant ID(Directory ID)]&code=[Functions Key]
```

* 利用料金が表示されたらOKです。

# Azure Automation実装

Functions APIの実行は、Automationにさせることにします。
ここはなんでもよくて、オンプレからcronとかで定期実行してもよいです。
Automationアカウントの作成手順は割愛します。

* Runbookの種類は「PowerShell」で、以下のように作成します。

```powershell
Param(
    [parameter(Mandatory=$True)]
    [string]$functionsKey,

    [parameter(Mandatory=$True)]
    [string]$subscriptionId,

    [parameter(Mandatory=$True)]
    [string]$clientId,

    [parameter(Mandatory=$True)]
    [String] $clientSecret,

    [parameter(Mandatory=$True)]
    [string] $tenantId
)

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$msg = Invoke-RestMethod -Uri "https://azure-billing-api-sample.azurewebsites.net/api/billing" -Method GET -Body @{code="$functionsKey";subscriptionId="$subscriptionId";clientId="$clientId";clientSecret="$clientSecret";tenantId="$tenantId"}

$enc = [System.Text.Encoding]::GetEncoding('ISO-8859-1')
$utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($msg)
$payload = @{
        text = $enc.GetString($utf8Bytes);
        username = "Azure Automation";
        icon_emoji = ":azure:"
        channel = "#channel"
}

Invoke-RestMethod -Uri "https://hooks.slack.com/services/[Slack Incoming Webhook ID]" -Method Post -Body (ConvertTo-Json $payload)
```

* スケジュール設定をして、定期的にSlack通知されればOKです。  
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/2a0a3c4d-6afd-8136-7e05-f523ed4c834c.png)

* Slackじゃなくてメール通知させたい場合は、Automation側で制御すればOKです。

# 未対応というか注意点というか

* 請求書とピッタリは合わないので、ざっくりの目安としてもらえればと思います。
* 以下のような使用量に応じてレートが変わる系のサービスは対応するのがつらすぎたので、一番高いレートで計算してます。

```json
{
  "EffectiveDate": "2018-01-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Data Lake Store",
  "MeterId": "c7929966-0a48-4f73-a86b-30f4e39d1995",
  "MeterName": "従量課金制保存データ",
  "MeterRates": {
    "0": 4.368,
    "102400": 4.256,
    "1024000": 4.144,
    "5120000": 4.032
  },
 }
```