Power BI API 経由で RDL ファイルを Power BI にアップロードする

同僚: プログラムで RDL ファイルを Power BI にあげたいけどどうしたらいい？
私: え、Power BI って RDL サポートしてるんだ。。まじか

というやり取りが今朝あり、調査したので結果を共有しておきます。

# RDL on Power BI 

そうなんです。Power BI は RDL を "ページ分割されたレポート" としてサポートしていたのです。とりあえず [チュートリアル:ページ分割されたレポートを作成して Power BI サービスにアップロードする](https://docs.microsoft.com/ja-jp/power-bi/paginated-reports-quickstart-aw) を一通りやって勉強しました。いいチュートリアルなので是非お試しください。

# API 経由でのアップロード

本題の RDL ファイルを API 経由でアップロードする方法ですが、Power BI の API は [Power BI REST APIs](https://docs.microsoft.com/en-us/rest/api/power-bi/) に詳細が公開されています。

ドキュメントを見た結果、以下の手順で行けることが分かりました。

1. [Post Import In Group API](https://docs.microsoft.com/en-us/rest/api/power-bi/imports/postimportingroup) を使って RDL ファイルをインポートする。この過程でレポートが作成される。
1. [Get Import In Group](https://docs.microsoft.com/en-us/rest/api/power-bi/imports/getimportingroup) を使ってインポートの情報と取得する。結果として作成されたレポートの情報も取得できる。

RDL ファイルのアップロードのみであればこれで終わりですが、データソースの資格情報を手動で入れたくないので、そのやり方も調べておきました。

1. [Reports - Get Datasources In Group API](https://docs.microsoft.com/en-us/rest/api/power-bi/reports/getdatasourcesingroup) でレポートに紐づくデータソースの情報を取得する。ここでゲートウェイとデータソースの情報が取得できる。
1. [Gateways - Update Datasource API](https://docs.microsoft.com/en-us/rest/api/power-bi/gateways/updatedatasource) を使って資格情報を設定する。

これだけです。

# PowerShell の例

以前紹介したとおり、オフィシャルの Power BI PowerShell モジュールがあり、今回もそちらを使いました。一部機能がまだサポートされていないため、そこは PowerShell の機能で書いてみました。PR 作らないとなぁ。

```powershell
function Publish-ImportRDLFile 
{
    param
    (
        [string]$RdlFilePath,
        [string]$GroupId,
        [string]$nameConflict = "Abort"
    )

    # RDL ファイルから body を作る
    $fileName = [IO.Path]::GetFileName($RdlFilePath)
    $boundary = [guid]::NewGuid().ToString()
    $fileBody = Get-Content -Path $RdlFilePath -Encoding UTF8
    
    $body = @"
----FormBoundary$boundary
Content-Disposition: form-data; name="$filename"; filename="$filename"
Content-Type: application/rdl

$fileBody 
----FormBoundary$boundary--

"@
    
    # アクセストークンをヘッダーに設定
    $headers = Get-PowerBIAccessToken

    if ($GroupId) {
        $url = "https://api.powerbi.com/v1.0/myorg/groups/$GroupId/imports?datasetDisplayName=$fileName&nameConflict=$nameConflict"
    }
    else {
        $url = "https://api.powerbi.com/v1.0/myorg/imports?datasetDisplayName=$fileName&nameConflict=$nameConflict"
    }

    # インポート作成
    $report = Invoke-RestMethod -Uri $url -Method Post -Headers $headers -Body $body -ContentType "multipart/form-data"   
    $report.id
}

function Set-BasicPassword-To-RDL 
{
    param
    (
        [string]$Id,
        [string]$GroupId,
        [string]$UserName,
        [string]$Password
    )
        
    # アクセストークンをヘッダーに設定
    $headers = Get-PowerBIAccessToken

    # 作成したインポートからレポート情報を取得。時間かかる場合があるので取れるまで繰り返し。
    $reportId = $null
    while($reportId -eq $null)
    {
        if ($GroupId) {
            $url = "https://api.powerbi.com/v1.0/myorg/groups/$GroupId/imports/$Id"
        }
        else {
            $url = "https://api.powerbi.com/v1.0/myorg/imports/$Id"
        }

        $import = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
        $reportId = $import.reports.id
    }

    # Datasource 情報を取得
    if ($GroupId) {
        $url = "https://api.powerbi.com/v1.0/myorg/groups/$GroupId/reports/$reportId/datasources"
    }
    else {
        $url = "https://api.powerbi.com/v1.0/myorg/reports/$reportId/datasources"
    }

    $datasources = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
    $gatewayId = $datasources.value[0].gatewayId
    $datasourceId = $datasources.value[0].datasourceId
     
    # 最後に認証を設定。
    $url = "https://api.powerbi.com/v1.0/myorg/gateways/$gatewayId/datasources/$datasourceId"
        
    # ここは基本認証の例だが、他の例は https://docs.microsoft.com/en-us/rest/api/power-bi/gateways/updatedatasource#examples を参照
    $body = @"
{
  "credentialDetails": {
	"credentialType":"Basic",
	"credentials": "{\"credentialData\":[{\"name\":\"username\", \"value\":\"$UserName\"},{\"name\":\"password\", \"value\":\"$Password\"}]}",
	"encryptedConnection": "Encrypted",
    "encryptionAlgorithm": "None",
    "privacyLevel": "None"
	}
}
"@
    $datasources = Invoke-RestMethod -Uri $url -Method Patch -Headers $headers -ContentType "application/json" -Body $body
}

# Power BI に接続
Connect-PowerBIServiceAccount

$groupId = "<group id>"
# インポート作成
$id = Publish-ImportRDLFile -GroupId $groupId -RdlFilePath "<path to rdl file>"
# 資格情報を設定
Set-BasicPassword-To-RDL -Id $id -GroupId $groupId -UserName "<username>" -Password "<password>"
```

# まとめ

Power BI は充実した API を公開しているので、自動化したい場合は色々試してください。

# Reference

[Power BI Community Get Help: REST import rdl files](https://community.powerbi.com/t5/Developer/REST-import-rdl-files/td-p/708986)