Python で Azure の サブスクリプションを列に日を行とする使用料金一覧表を作成してみました

## 概要

Azure SDK for Python を利用して、サブスクリプションを列に、日を行とする使用料金表を作成し、この表の最終行にその日までの合計の使用料金を追加した一覧表を作成するための Python プログラムです。また、サブスクリプション毎の合計を利用料金の降順で一覧表示する Python プログラムも追加しています。

### ローカル環境

macOS Monterey 12.1
python 3.8.12

### 実行プログラム

```python:GetSubscriptionCostManagement_22.py
import time
import argparse
from azure.identity import AzureCliCredential, DefaultAzureCredential
from azure.mgmt.resource import SubscriptionClient
from azure.mgmt.costmanagement import CostManagementClient
import pandas as pd


# 接続しているテナントのサブスクリプションを操作するオブジェクトを取得
def GetSubscriptionObject():
    subscription_client = SubscriptionClient(
        # credential=AzureCliCredential()
        credential=DefaultAzureCredential()
    )
    return subscription_client


# CostManagement情報 を操作するオブジェクトを取得
def GetCostManagementObject():
    costmgmt_client = CostManagementClient(
        # credential=AzureCliCredential()
        credential=DefaultAzureCredential()
    )
    return costmgmt_client


# 指定した Subscription について CostManagement からコストを取得
def GetCostManagement(costmgmt_client, subs_id):

    # Query costmanagement
    SCOPE = '/subscriptions/{}'.format(subs_id)
    costmanagement = costmgmt_client.query.usage(
        SCOPE,
        {
            "type": "Usage",
            "timeframe": "MonthToDate",
            "dataset": {
                "granularity": "Daily",
                "aggregation": {
                    "totalCost": {
                        "name": "PreTaxCost",
                        "function": "Sum"
                    }
                },
                "grouping": [
                    {
                        "type": "Dimension",
                        "name": "ResourceGroup"
                    }
                ]
            }
        }
    )
    return costmanagement


# 合計行の追加
def append_sum_row_label(df):
    df.loc['Total'] = df.sum(numeric_only=True)
    return df


# 日単位のサブスクリプション毎の一覧表
def Report_Daily(df):
    print("\n------------------------------------------------\n")
    # reset_indexする
    df.reset_index(inplace=True)
    # 合計行の追加
    df = append_sum_row_label(df)
    # 表示
    post_str1 = df.to_string()
    print(post_str1)


# サブスクリプションの合計一覧表（利用料金での降順）
def Report_Summury(tdf):
    # resampleメソッドで、月単位で集計
    totalcost = tdf['UsageCost'].sum()
    print("\n-------- コスト合計: ¥{:,}".format(round(totalcost)) + " --------\n")
    # 利用料金で降順のソート
    tdf = tdf.sort_values(by=['UsageCost'], ascending=False)
    # カラムの順番を変更
    tdf = tdf.loc[:,['Subscription', 'UsageCost']]
    # reset_indexする
    tdf.reset_index(inplace=True)
    # 不要カラムの削除
    tdf = tdf.drop(['Date'], axis=1)
    # 表示
    post_str2 = tdf.to_string()
    print(post_str2)


# サブスクリプションIDを指定しリソースグループ毎に CostManagement情報を取得
def GetSubscriptionCsotManagement():
    
    # サブスクリプションを操作するオブジェクトの取得
    subscription_list = GetSubscriptionObject()    

    # CostManagementを操作するオブジェクトの取得
    costmgmt_client = GetCostManagementObject()

    # 取得項目の定義
    row_list = ['UsageCost', 'Date', 'ResourceGroup', 'Currency']

    # 小数点以下の桁数：2（デフォルト：6）＋コンマ区切りの挿入 を定義
    pd.options.display.float_format = '{:,.2f}'.format
    
    # サブスクリプション毎に CostManagement からコストを取得
    for n, subs in enumerate(subscription_list.subscriptions.list()):
        # print("\nサブスクリプション ： {}  {}".format(subs.subscription_id, subs.display_name))
        costmanagement = GetCostManagement(costmgmt_client, subs.subscription_id)

        # 表示させるデータのDataFrame化
        if len(costmanagement.rows) > 0 :
            # DataFrame型でデータの取得
            rowdf = pd.DataFrame(costmanagement.rows, columns = row_list)
            # 不必要項目の削除
            rowdf = rowdf.drop(['Currency', 'ResourceGroup'], axis=1)
            # Date項目の型をdatetime型に変換
            rowdf['Date'] = pd.to_datetime(rowdf['Date'].astype(str), format='%Y-%m-%d')

            # インデックス化
            rowdf.set_index('Date', inplace=True)
            # resampleメソッドで、日単位で集計
            dsumdf = rowdf.resample('D').sum()
            # resampleメソッドで、月単位で集計
            msumdf = rowdf.resample('M').sum()
            msumdf['Subscription'] = subs.display_name

            print("\tサブスクリプション : {} ".format(subs.display_name))
                
            if n == 0 :
                df = dsumdf
                df.rename(columns={'UsageCost': subs.display_name}, inplace=True)
                tdf = msumdf
            else :
                df[subs.display_name] = dsumdf['UsageCost']
                tdf = tdf.append(msumdf)
    
    # オブジェクトのクローズ処理
    costmgmt_client.close()
    subscription_list.close()

    # 日単位のサブスクリプション毎の一覧表
    Report_Daily(df)

    ### サブスクリプションの合計一覧表（利用料金での降順）
    Report_Summury(tdf)
    

if __name__ == '__main__':
    
    parser = argparse.ArgumentParser(description='Subscription の 日々のコスト取得')
    args = parser.parse_args()

    start = time.time()
    GetSubscriptionCsotManagement()
    generate_time = time.time() - start

    print("\n 取得時間:{0}".format(generate_time) + " [sec] \n")
```

### プログラムの実行
 
```
$ python GetSubscriptionCostManagement_22.py
	サブスクリプション : iapp
	サブスクリプション : japp 
	サブスクリプション : aaa 
	サブスクリプション : bbb 
	サブスクリプション : ccc 
	サブスクリプション : ddd 
	サブスクリプション : eee 
	サブスクリプション : fff 
	サブスクリプション : ggg 
	サブスクリプション : hhh 
	サブスクリプション : iii 
	サブスクリプション : jjj 
	サブスクリプション : kkk 
	サブスクリプション : MixixTeam 

------------------------------------------------

            Date      iapp       japp     aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk  MixedTeam
0     2022-03-01    595.86     311.44       :   :   :   :   :   :   :   :   :   :   :       1.12
1     2022-03-02    596.53     311.54       :   :   :   :   :   :   :   :   :   :   :       1.12
2     2022-03-03    597.20     311.47       :   :   :   :   :   :   :   :   :   :   :       1.12
3     2022-03-04    597.89     302.30       :   :   :   :   :   :   :   :   :   :   :       1.12
4     2022-03-05    598.57     296.25       :   :   :   :   :   :   :   :   :   :   :       1.12
5     2022-03-06  1,269.99     297.43       :   :   :   :   :   :   :   :   :   :   :       1.12
6     2022-03-07    740.55     309.26       :   :   :   :   :   :   :   :   :   :   :       1.12
7     2022-03-08    709.95     308.61       :   :   :   :   :   :   :   :   :   :   :   2,077.66
8     2022-03-09    710.62     309.39       :   :   :   :   :   :   :   :   :   :   :   1,188.42
9     2022-03-10    716.45     309.13       :   :   :   :   :   :   :   :   :   :   :       1.12
10    2022-03-11    711.99     300.71       :   :   :   :   :   :   :   :   :   :   :       1.12
11    2022-03-12    712.67     295.94       :   :   :   :   :   :   :   :   :   :   :       1.12
12    2022-03-13    713.34     296.27       :   :   :   :   :   :   :   :   :   :   :       1.12
13    2022-03-14    710.47     308.32       :   :   :   :   :   :   :   :   :   :   :       1.12
14    2022-03-15    709.96     309.71       :   :   :   :   :   :   :   :   :   :   :       1.12
15    2022-03-16    710.63     310.50       :   :   :   :   :   :   :   :   :   :   :       1.12
16    2022-03-17    711.30     309.85       :   :   :   :   :   :   :   :   :   :   :       1.12
17    2022-03-18  1,011.15     301.98       :   :   :   :   :   :   :   :   :   :   :       1.12
18    2022-03-19  1,081.67     296.66       :   :   :   :   :   :   :   :   :   :   :       1.12
19    2022-03-20  1,082.34     296.59       :   :   :   :   :   :   :   :   :   :   :       1.12
20    2022-03-21  1,079.47     297.18       :   :   :   :   :   :   :   :   :   :   :       1.12
21    2022-03-22  1,078.95     309.95       :   :   :   :   :   :   :   :   :   :   :       1.12
22    2022-03-23  1,079.63     309.81       :   :   :   :   :   :   :   :   :   :   :       1.12
23    2022-03-24  1,080.30     309.43       :   :   :   :   :   :   :   :   :   :   :       1.12
24    2022-03-25  1,081.18     302.11       :   :   :   :   :   :   :   :   :   :   :       1.12
25    2022-03-26  1,081.68     297.19       :   :   :   :   :   :   :   :   :   :   :       1.12
26    2022-03-27    541.09     161.25       :   :   :   :   :   :   :   :   :   :   :       0.61
Total        NaT 22,311.42   8,080.27       :   :   :   :   :   :   :   :   :   :   :   3,293.57

-------- コスト合計: ¥358,977 --------

     Subscription  UsageCost
0             bbb  62,745.77
1             ddd  57,682.29
2             ccc  42,954.43
3             aaa  40,840.67
4             ggg  35,993.58
5             kkk  34,442.49
6            iapp  22,311.42
7             hhh  18,064.07
8             fff  18,015.47
9            japp   8,080.27
10            jjj   6,602.73
11            eee   5,093.90
12      MixixTeam   3,293.57
13            iii   2,856.59

 取得時間:87.51971793174744 [sec] 
```

---

### まとめ

Azure portal で確認する場合、いちいち画面を切り替えなければいけませんが、プログラムでゴリゴリするとサクッと一覧表示で取得できますね。

### 参考記事

以下の記事を参考にさせていただきました。感謝申し上げます。
[データ分析で頻出のPandas基本操作](https://qiita.com/ysdyt/items/9ccca82fc5b504e7913a)
[PandasのDataFrameに合計行を追加する](https://qiita.com/shinno1993/items/b8d983c199c690e05609)