ブログ

ryuzeeによるブログ記事。不定期更新

Azureでコマンドラインから利用料金を取得する

みなさんこんにちは。@ryuzeeです。

クラウドを使っていると利用料金を常時監視したり通知するのは当然の行動の1つです。 そこで、Azureの利用料金をいちいちポータルにログインしなくても見られるようにスクリプト化してみたので、やり方を紹介します。 今回は、コマンドラインツール(AzureCLI)とスクリプトの開発にはRubyを使っています。

AzureCLIを用意する

AzureCLIはNode.jsを使うので、環境設定が済んでいない場合は、nvmでもなんでもいいので用意しておきます。

それが終わったらAzureCLIをグローバルにインストールします。いろいろなパッケージをインストールするので3分ぐらいかかります。

npm install -g azure-cli

インストールが完了したら設定に入ります。

Azureにはリソースマネージャー(ARM)デプロイモデルとクラシック(ASM)デプロイモデルがありますが、現在はリソースマネージャー推奨なので、そちらに切り替えます。

azure config mode arm

データを収集して送ってよいか聞かれますので、適当にyかnを選択してください。以下のように表示されれば成功です。

info:    Executing command config mode
info:    New mode is arm
info:    config mode command OK

次にAzureにログインします。

azure login

すると以下のように表示されるので、リンクをブラウザで開きます。

info:    Executing command login
/info:    To sign in, use a web browser to open the page https://aka.ms/devicelogin. Enter the code AB12CDE34 to authenticate.

以下のようにコードを入力する欄があるので、上記の出力のコードを入力して先に進みます。認証画面を経て、最後の画面で「デバイスの Microsoft Azure Cross-platform Command Line Interface アプリケーションにサインインしました。このウィンドウは閉じてかまいません。」と表示されたらブラウザを閉じて構いません。 その時点でコマンドラインでのログインが完了しています。

Azure AD アプリケーションの作成

自前のスクリプトからの認証や権限を管理するために、Azure ADアプリケーションを作成します。 ここでパスワードを設定しますが、これは後ほど自前スクリプトで使いますので控えておいてください。

azure ad app create --name "usage_collection" \
 --home-page "https://www.ryuzee.com/" \
 --identifier-uris "https://www.ryuzee.com" \
 --password sushikuitai

実行すると以下のようなログが表示されます(値はダミーになっています。念のため)。 この中で重要なのは、AppIdの項目で、このデータも後ほど使います。

info:    Executing command ad app create
+ Creating application usage_collection
data:    AppId:                   b7b17624-5536-3888-3328-1231ba716c02
data:    ObjectId:                12ba3f8a-1222-3e5d-a77e-0699b3f2d503
data:    DisplayName:             usage_collection
data:    IdentifierUris:          0=https://www.ryuzee.com
data:    ReplyUrls:
data:    AvailableToOtherTenants: False
data:    HomePage:                https://www.ryuzee.com/
info:    ad app create command OK

次にサービスプリンシパルを作成します。引数は先ほど作成したアプリケーションのAppIdを指定します。

azure ad sp create --applicationId b7b17624-5536-3888-3328-1231ba716c02

以下のように作成が完了しました。ここで表示されたObject Idはあとで使いますので控えておいてください。

info:    Executing command ad sp create
+ Creating service principal for application b6b07346-3719-4177-8618-3661ba716c02
data:    Object Id:               aa0f5bc5-e5d8-4e30-a9cb-999a49343bb1
data:    Display Name:            usage_collection
data:    Service Principal Names:
data:                             b6b07346-3719-4177-8618-3661ba716c02
data:                             https://www.ryuzee.com
info:    ad sp create command OK

ここで対象とするサブスクリプションを確認します。

azure account list

以下のように表示されます。ここで表示されるIdはサブスクリプションIDで後で使います。

data:    Name                    Id                                    Current  State  
data:    ----------------------  ------------------------------------  -------  -------
data:    従量課金                1abc8ad2-2cc5-9876-a0ee-0f76be829d3a  true    Enabled
(以下略。複数のサブスクリプションがある場合は複数表示されます)

サービスプリンシパルに権限を割り当てます。最初のobjectIdには先ほどサービスプリンシパルの作成で表示された値を指定します。 第2引数では読み取り権限を示すReaderを設定し、最後の引数に、サブスクリプションのIDを指定します。 複数のサブスクリプションから利用データを取得する場合は、以下の作業を繰り返し実施してください。

azure role assignment create \
  --objectId aa0f5bc5-e5d8-4e30-a9cb-999a49343bb1 \
  -o Reader \
 -c /subscriptions/1abc8ad2-2cc5-9876-a0ee-0f76be829d3a/

以下のように結果が表示されます。

info:    Executing command role assignment create
+ Finding role with specified name                                             
/data:    RoleAssignmentId     : /subscriptions/1abc8ad2-2cc5-9876-a0ee-0f76be829d3a/providers/Microsoft.Authorization/roleAssignments/69738901-2cb7-46a5-8179-c81431ca6046
data:    RoleDefinitionName   : Reader
data:    RoleDefinitionId     : acdd72a7-9876-55ef-bd42-f606fba81ae7
data:    Scope                : /subscriptions/1abc8ad2-2cc5-9876-a0ee-0f76be829d3a
data:    Display Name         : usage_collection
data:    SignInName           : undefined
data:    ObjectId             : aa0f5bc3-e5d8-4e30-a7cb-977a55555bb1
data:    ObjectType           : ServicePrincipal
data:    
+
info:    role assignment create command OK

次にテナント情報を取得します。以下を実行してください。

azure accout show

画面に以下が出力されます。ここで出力されるTenant IDを控えておいてください。

info:    Executing command account show
data:    Name                        : 従量課金
data:    ID                          : 7bdf8ad2-2cc5-3986-a0ee-0f76be829d3a
data:    State                       : Enabled
data:    Tenant ID                   : 80ed711a-71af-9123-82fa-57d0464c288e
data:    Is Default                  : false
data:    Environment                 : AzureCloud
data:    Has Certificate             : No
data:    Has Access Token            : Yes
data:    User name                   : hoge@example.com

コードによるデータ取得

この時点で揃っているデータは以下の通りです。

  • Application ID
  • パスワード
  • Tenant ID
  • Subscription ID

これらを使って使用量と料金表を取得します。処理の流れは次のようになります。

  1. Azureにログインして、APIのヘッダーに埋め込むTokenを取得する
  2. 使用量をAzure Resource Usage APIを使って取得する
  3. 料金表をAzure RateCard APIを使って取得する
  4. 2で取得した使用量と3で取得した料金表をマージする

1. Azureにログインして、APIのヘッダーに埋め込むTokenを取得

2や3でAPIを投げるためにはリクエストヘッダーにTokenを埋める必要があるので、まずそれを取得します。 取得のためには、https://login.microsoftonline.com/テナントID/oauth2/token?api-version=1.0に対してデータをPOSTします(POSTの内容は下記のpayloadの箇所を参照)。 ここでPOSTの値に、上記で取得したApplication IDをセットします。 正常に動作すれば、JSON形式でデータが返ってくるので、access_tokenの値を取り出します。この値を以降で使います。

def self.api_token(application_id, client_secret, tenant_id)
  url = "https://login.microsoftonline.com/#{tenant_id}/oauth2/token?api-version=1.0"
  payload = {
    'grant_type' => 'client_credentials',
    'client_id' => application_id,
    'client_secret' => client_secret,
    'resource' => "https://management.azure.com/"
  }
  headers = {
    "Content-Type" => "application/x-www-form-urlencoded"
  }
  RestClient.post(url, payload, headers){ |response, request, result, &block|
    case response.code
    when 200
      json = JSON.parse(response)
      token = json["access_token"]
      token
    else
      false
    end
  }
end

2. 使用量をAzure Resource Usage APIを使って取得

ここで使う値は、先ほど取得したTokenとSubscription IDです。下記の例では取得範囲を当月1日から前日までにしていますが適宜変更してください。 ここではヘッダーに先ほど取得したTokenを埋めつつ、https://management.azure.com/subscriptions/サブスクリプションID/providers/Microsoft.Commerce/UsageAggregates?api-version=2015-06-01-preview&reportedStartTime=取得開始日時&reportedEndTime=取得終了日時&aggreagationGranularity=データ粒度&showDetails=falseというURLにGETリクエストを投げて指定期間の指定したサブスクリプションのJSON形式の使用量データを取得します。

def self.usages(token, subscription_id)
  now = DateTime.now.new_offset(0)
  start_time =  DateTime.new(now.year, now.month, 1, 0, 0, 0)
  end_time =  DateTime.new(now.year, now.month, now.day, 0, 0, 0)

  granularity = "Monthly"

  url = "https://management.azure.com/subscriptions/#{subscription_id}/providers/Microsoft.Commerce/UsageAggregates?api-version=2015-06-01-preview&reportedStartTime=#{url_encode(start_time.to_s)}&reportedEndTime=#{url_encode(end_time.to_s)}&aggreagationGranularity=#{granularity}&showDetails=false"
  headers = {
    "Content-type" => "application/json",
    "Authorization" => "Bearer #{token}"
  }

  results = []
  RestClient.get(url, headers){ |response, request, result, &block|
    case response.code
    when 200
      json = JSON.parse(response)
      return json["value"]
    else
      false
    end
  }
end

3. 料金表をAzure RateCard APIを使って取得

そして料金表をJSON形式で取得します。ここでいままで取得したデータの他にOfferDurableIdという値が必要になります。この値は通常の従量課金形式のライセンスの場合は、MS-AZR-0003Pとなります。詳細はAzureのポータルから自分のサブスクリプションを表示して確認します。 送信先は、https://management.azure.com/subscriptions/サブスクリプションID/providers/Microsoft.Commerce/RateCard?api-version=2015-06-01-preview&$filter=OfferDurableId eq 'OfferDurableIdの値' and Currency eq 'JPY' and Locale eq 'ja-JP' and RegionInfo eq 'JP'となります。最後の方のfilterというパラメータでどんなライセンス形式の価格を、どんな通貨、どんな言語で取得するかを指定できます。ここでは通貨と言語は日本に固定しています。

def self.rate_meters(token, subscription_id, offer_durable_id)
  url = "https://management.azure.com/subscriptions/#{subscription_id}/providers/Microsoft.Commerce/RateCard?api-version=2015-06-01-preview&$filter=OfferDurableId eq '#{offer_durable_id}' and Currency eq 'JPY' and Locale eq 'ja-JP' and RegionInfo eq 'JP'"
  headers = {
    "Content-type" => "application/json",
    "Authorization" => "Bearer #{token}"
  }
  RestClient.get(url, headers){ |response, request, result, &block|
    case response.code
    when 200
      json = JSON.parse(response)
      json["Meters"]
    else
      false
    end
  }
end

4. 値をマージする

あとは2と3で取得したデータをマージすればOKです。2で取得した使用量データには、適用する価格を示すMeterIdという項目があるので、これをキーにしてRateCard APIの結果から単価を取得します。また、使用量データにはquantityという数量を示すデータが入っているので、単価と数量を掛け合わせることで金額が算出できます。

あとは適当にテーブル形式で出力したり、もっとデータをまとめたりと好きにすれば良いでしょう。

ここで解説したコードの完全版は、https://github.com/ryuzee/azure_charge_example においてありますので適宜ご覧ください。サンプルを実行すると以下のような出力を取得できます。

アジャイルコーチングやトレーニングを提供しています

株式会社アトラクタでは、アジャイル開発に取り組むチーム向けのコーチングや、認定スクラムマスター研修などの各種トレーニングを提供しています。ぜひお気軽にご相談ください。

詳細はこちら
  • スクラム実践者が知るべき97のこと
  • 著者/訳者:Gunther Verheyen / 吉羽龍太郎 原田騎郎 永瀬美穂
  • 出版社:オライリージャパン(2021-03-23)
  • 定価:¥ 2,640
  • スクラムはアジャイル開発のフレームワークですが、その実装は組織やチームのレベルに応じてさまざまです。本書はスクラムの実践において、さまざまな課題に対処してきた実践者が自らの経験や考え方を語るエッセイ集です。日本語書き下ろしコラムを追加で10本収録
  • プロダクトマネジメント ―ビルドトラップを避け顧客に価値を届ける
  • 著者/訳者:Melissa Perri / 吉羽龍太郎
  • 出版社:オライリージャパン(2020-10-26)
  • 定価:¥ 2,640
  • プロダクト開発を作った機能の数やベロシティなどのアウトプットで計測すると、ビルドトラップと呼ばれる失敗に繋がります。本書ではいかにしてビルドトラップを避けて顧客に価値を届けるかを解説しています。
  • SCRUM BOOT CAMP THE BOOK 【増補改訂版】
  • 著者/訳者:西村直人 永瀬美穂 吉羽龍太郎
  • 出版社:翔泳社(2020-05-20)
  • 定価:¥ 2,640
  • スクラム初心者に向けて基本的な考え方の解説から始まり、プロジェクトでの実際の進め方やよく起こる問題への対応法まで幅広く解説。マンガと文章のセットでスクラムを短期間で理解できます。スクラムの概要を正しく理解したい人、もう一度おさらいしたい人にオススメ。
  • みんなでアジャイル ―変化に対応できる顧客中心組織のつくりかた
  • 著者/訳者:Matt LeMay / 吉羽龍太郎、永瀬美穂、原田騎郎、有野雅士
  • 出版社:オライリージャパン(2020-3-19)
  • 定価:¥ 2,640
  • アジャイルで本当の意味での成果を出すには、開発チームだけでアジャイルに取り組むのではなく、組織全体がアジャイルになる必要があります。本書にはどうやってそれを実現するかのヒントが満載です
  • レガシーコードからの脱却 ―ソフトウェアの寿命を延ばし価値を高める9つのプラクティス
  • 著者/訳者:David Scott Bernstein / 吉羽龍太郎、永瀬美穂、原田騎郎、有野雅士
  • 出版社:オライリージャパン( 2019-9-18 )
  • 定価:¥ 3,132
  • レガシーコードになってから慌てるのではなく、日々レガシーコードを作らないようにするにはどうするか。その観点で、主にエクストリームプログラミングに由来する9つのプラクティスとその背後にある原則をわかりやすく説明しています。
  • Effective DevOps ―4本柱による持続可能な組織文化の育て方
  • 著者/訳者:Jennifer Davis、Ryn Daniels / 吉羽 龍太郎、長尾高弘
  • 出版社:オライリージャパン( 2018-3-24 )
  • 定価:¥ 3,888
  • 主にDevOpsの文化的な事柄に着目し、異なるゴールを持つチームが親和性を高め、矛盾する目標のバランスを取りながら最大限の力を発揮する方法を解説します
  • ジョイ・インク 役職も部署もない全員主役のマネジメント
  • 著者/訳者:リチャード・シェリダン / 原田騎郎, 安井力, 吉羽龍太郎, 永瀬美穂, 川口恭伸
  • 出版社:翔泳社( 2016-12-20 )
  • 定価:¥ 1,944
  • 米国で何度も働きやすい職場として表彰を受けているメンローの創業者かつCEOであるリチャード・シェリダン氏が、職場に喜びをもたらす知恵や経営手法、より良い製品の作り方などを惜しみなく紹介しています
  • アジャイルコーチの道具箱 – 見える化の実例集
  • 著者/訳者:Jimmy Janlén / 原田騎郎, 吉羽龍太郎, 川口恭伸, 高江洲睦, 佐藤竜也
  • 出版社:Leanpub( 2016-04-12 )
  • 定価:$14.99
  • この本は、チームの協調とコミュニケーションを改善したり、行動を変えるための見える化の実例を集めたものです。96個(+2)の見える化の方法をそれぞれ1ページでイラストとともに解説しています。アジャイル開発かどうかに関係なくすぐに使えるカタログ集です
  • カンバン仕事術 ―チームではじめる見える化と改善
  • 著者/訳者:原田騎郎 安井力 吉羽龍太郎 角征典 高木正弘
  • 出版社:オライリージャパン( 2016-03-25 )
  • 定価:¥ 2,138
  • チームの仕事や課題を見える化する手法「カンバン」について、その導入から実践までを図とともにわかりやすく解説した書籍。カンバンの原則などの入門的な事柄から、サービスクラス、プロセスの改善など、一歩進んだ応用的な話題までを網羅的に解説します。
  • Software in 30 Days スクラムによるアジャイルな組織変革“成功"ガイド
  • 著者/訳者:Ken Schwaber、Jeff Sutherland著、角征典、吉羽龍太郎、原田騎郎、川口恭伸訳
  • 出版社:アスキー・メディアワークス( 2013-03-08 )
  • 定価:¥ 1,680
  • スクラムの父であるジェフ・サザーランドとケン・シュエイバーによる著者の日本語版。ビジネス層、マネジメント層向けにソフトウェア開発プロセス変革の必要性やアジャイル型開発プロセスの優位性について説明
  • How to Change the World 〜チェンジ・マネジメント3.0〜
  • 著者/訳者:Jurgen Appelo, 前川哲次(翻訳), 川口恭伸(翻訳), 吉羽龍太郎(翻訳)
  • 出版社:達人出版会
  • 定価:500円
  • どうすれば自分たちの組織を変えられるだろう?それには、組織に変革を起こすチェンジ・マネジメントを学習することだ。アジャイルな組織でのマネージャーの役割を説いた『Management 3.0』の著者がコンパクトにまとめた変化のためのガイドブック