CSPのAzure明細をPowerShellで整形する

新年度になりましたね。私も業務の所掌範囲とか変わって色々と新しいことに取り組み始めようと思います。

早速、MicrosoftのCSP事業者向けに毎月発行される請求書の明細のCSVファイルをExcelで加工するということを業務マニュアル見ながらしたのですが、面倒くさい上にピボットで集計したところ何かおかしい。

主に以下の点がおかしいという結論に。

  1. 調整ファイルのCSVの中に2種類の情報が入っていて、途中でカラムが変わる
  2. 調整ファイルの合計額が請求書の額と微妙に一致しない

前者に関しては単に分割すれば良いだけですが、後者に関しては過去の請求書も含めて調べた結果、以下のロジックであることが分かりました。

  • 税抜利用額において、小数点以下が存在するレコードが有るが、実際それは利用されない。
  • 2019年の秋くらいまでは多くの小数点以下の値があったが、以降は1円未満の利用(つまり0円請求)の場合にのみ小数点以下が存在
  • 税抜利用額の各項目に対して消費税が計算され、それが切り捨て
  • 切り捨てされた税抜合計額+消費税合計額がCSP事業者への請求

明細書のCSVが請求書と合わないのは問題なので、補正した値を経理処理上作成しないといけないのですが、数十万レコードとか有るとExcelでやるのも一苦労なので、PowerShellでやっちゃうことにしました。

PowerShellのImportExcelモジュールを使うと、PowerShellからxlsxファイルをそのまま作れます。アウトプットが複数シート必要なExcelなので今回はこれを利用します。

自分用のスクリプトなので汚いですが…。

$inputcsv = ".\AzureBillingUsage.csv"
$outxlsx = ".\CSPAzureBill.xlsx"
$tempfile = $env:TMP + "\_" + (Get-Date -Format yyyyMMddHHmmss) + ".csv"

### CSP請求書を上部のSummaryと下部のDaily Usageの2つのCSVに分ける ###
$bills = Get-Content $inputcsv -Encoding UTF8
$lines = $bills.Count

# Daily Usageの前の行まで出力
for ($i=1;$i -lt $lines;$i++){
    if($bills[$i] -eq "Daily Usage"){
        break
    }
    if($bills[$i] -ne ""){
        $bills[$i] | Out-File $tempfile -append
    }
}
$usages = Import-csv $tempfile
Remove-Item $tempfile

# Daily Usageの次の行から出力
for ($i++;$i -lt $lines;$i++){
    if($bills[$i] -ne ""){
        $bills[$i] | Out-File $tempfile -append
    }
}

### [Summary]シートの作成 ###
# PretaxChargesに小数点以下が存在するが、請求書上は切り捨てた額の総額で計算されているので、
# それを考慮したPreTax、PostTaxを計算
foreach ($usage in $usages){
    $pre  = [Math]::Truncate($usage.PretaxCharges)
    $post = $pre + $usage.TaxAmount
    $usage | Add-Member PreTax  $pre -force
    $usage | Add-Member PostTax $post -force
}

$usages | Export-Excel -Path $outxlsx -WorksheetName Summary

### [Daily_Usage]シートの作成 ###
# Subscription ID毎にマージして、PostTaxとPreTaxの値を合計する
Import-csv $tempfile | Export-Excel -Path $outxlsx -WorksheetName Daily_Usage
$subs = $usages | group "SubscriptionId" | select `
  @{Name="DomainName";Expression={($_.group | select -first 1).DomainName}}, `
  @{Name="CustomerCompanyName";Expression={($_.group | select -first 1).CustomerCompanyName}}, `
  @{Name="CustomerId";Expression={($_.group | select -first 1).CustomerId}}, `
  @{Name="SubscriptionId";Expression={($_.group | select -first 1).SubscriptionId}}, `
  @{Name="TotalPostTax";Expression={($_.group | Measure-Object -sum "PostTax").sum}}, `
  @{Name="TotalPreTax";Expression={($_.group | Measure-Object -sum "PreTax").sum}} `

# 新規開通分に関して DomainName が空欄なケースがあり、その場合の対応。
# (事前に Install-Module -Name PartnerCenter -AllowClobber が必要)
if((($subs | ? {$_.DomainName -eq ""}) | measure).count -gt 0){
	Connect-PartnerCenter
	foreach ($sub in $subs){
		if($sub.DomainName -eq ""){
			$sub.DomainName = (Get-PartnerCustomer -CustomerId $sub.CustomerId).Domain
		}
	}
}

### [Subscriptions]シートの作成 ###
$subs | sort DomainName | Export-Excel -Path $outxlsx -WorksheetName Subscriptions -AutoSize -Show

### テンポラリファイル削除 ###
Remove-Item $tempfile
2020/05/07修正(2020年04月利用分の明細ファイルに対応)
・既定のファイル名を変更
・細かいフォーマット変更に対応
・文字コード変更

最初はPivotで作ろうかと思ったのですが、[小計の非表示]や[表として表示]が上手くいかなかったので、そのままPowerShellで整形させちゃいました。

PowerShellを利用してレポート用のCSVを作ってExcelで整形という業務って結構あるかと思いますが、Import-Excelモジュールを使うとその部分までコードで書けるようになるのでとても便利です。

皆さんも是非試してみて下さい。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です