Azure DevOpsの変数グループに新規サービスプリンシパルをコードで登録! #Azure リレー

Azure DevOpsのVariable Groupは、API経由でも作成できます。
Azureリソース作成のために用意したサービスプリンシパルの情報の発行とVariable Groupsへの登録したいときはありませんか?
PowerShellを使う場合の手順をまとめてみました。

APIのドキュメント

下記がVariable Groupsの追加のAPIです。

https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/variablegroups/add?view=azure-devops-rest-6.0

POSTで必要な情報を送り付ければ良さそうですね。
POSTするデータの説明は一応ドキュメントに書かれているものの、これを読み解くのは至難の業です・・・

よって、Azure DevOpsでVariable Groupsを登録するときにF12ツールで裏で流れている通信を見てみました。
そうすると具体的なリクエスト内容を見ることができました。
こちらを後述の手順で紹介したいと思います。

あと、ドキュメント上だとAPIのパスにOrganization名しか入らない形をとっていますが、F12ツールの通信だとProjectのIDも含んでいたので、そこも修正して動かそうと思います。

Azure DevOpsのAPIの認証情報の用意

Azure DevOpsのAPIを実行するには、Azure DevOpsのPersonal Access Tokenを用意する必要があります。https://dev.azure.com/<Organization名>/_usersSettings/tokens でトークンを作成してください。

項目設定値備考
Name(任意)
Expiration(任意)必要最低限の期間にするべき
ScopesVariable groups の Read &execute を選択初期表示だと隠れているので「Show all scopes」を選んで表示して設定

作成したトークンは安全な場所に保管してください。

サービスプリンシパルの作成

下記の流れでサービスプリンシパルを作成します。AADのグローバル管理者権限とかを持ったユーザーでログインが必要ですね。

az login -t <テナントID>
az account set -s <サブスクリプションID>
$SP_INFO = az ad sp create-for-rbac -n "my_sp" --role Contributor --scopes "/subscriptions/<サブスクリプションID>" --years 1 | ConvertFrom-Json

これで変数 $SpInfo にサービスプリンシパル情報が格納されます。
ConvertFrom-Json しているので、オブジェクトのように情報が操作できますね。

認証情報の作成

Azure DevOpsのAPIはBasic認証です。
ユーザー名は任意となります。

よって、下記のような流れで、 Invoke-RestMethod を実行する際の認証情報を用意できます。

$AZURE_DEVOPS_PAT_SECURE_STR = ConvertTo-SecureString <Azure DevOpsのPersonal Access Tokenの値> -AsPlainText -Force
$AZURE_DEVOPS_CRED = New-Object System.Management.Automation.PSCredential("dummy", $AZURE_DEVOPS_PAT_SECURE_STR)

ユーザー名に何か書かないとエラーになるので、”dummy”を入れています。

APIのリクエストボディの作成

リクエストボディはJson形式です。ボリューミーなので、別ファイルでテンプレートを用意し、その値を置換するのが良いと思います。
例えばこんなテンプレートです。

{
  "description": "説明文",
  "name": "VARIABLE_GROUP_NAME",
  "providerData": null,
  "type": "Vsts",
  "variableGroupProjectReferences": [
    {
      "description": "説明文",
      "name": "VARIABLE_GROUP_NAME",
      "projectReference": {
        "id": "(Azure DevOpsのProjectのID)",
        "name": ""
      }
    }
  ],
  "variables": {
    "CLIENT_ID": {
      "isSecret": false,
      "value": "<<CLIENT_ID>>"
    },
    "CLIENT_SECRET": {
      "isSecret": true,
      "value": "<<CLIENT_SECRET>>"
    },
    "SUBSCRIPTION_ID": {
      "isSecret": false,
      "value": "<<SUBSCRIPTION_ID>>"
    },
    "TENANT_ID": {
      "isSecret": false,
      "value": "<<TENANT_ID>>"
    }
  }
}

VARIABLE_GROUP_NAME のところは Variable Groupの名前です。任意に設定してください。
(Azure DevOpsのProjectのID)のところは、Variable Groupsを追加する先のProjectのIDです。調べ方がサッと見つからないですが、F12ツールで通信を眺めると、リクエストURLでOrganization名の後ろにGUIDが出ているリクエストを見かけることがあると思います。それです。(ごめんなさい)

isSecretがtrueの変数は、秘密の値にできます。クライアントシークレットは秘密にしておきました。

<<>> で囲んだ値をPowerShellで置換してあげる流れになります。
下記の処理ですね。

$VG_CONTAINS = Get-Content <テンプレートファイルパス> -Encoding utf8
$VG_CONTAINS = $VG_CONTAINS.Replace("<<CLIENT_ID>>", $SP_INFO.appId)
$VG_CONTAINS = $VG_CONTAINS.Replace("<<CLIENT_SECRET>>", $SP_INFO.password)
$VG_CONTAINS = $VG_CONTAINS.Replace("<<SUBSCRIPTION_ID>>", <サブスクリプションID>)
$VG_CONTAINS = $VG_CONTAINS.Replace("<<TENANT_ID>>", <テナントID>)

もうちょっと加工します。

$VG_CONTAINS = $VG_CONTAINS | ConvertFrom-Json -Depth 100 | ConvertTo-Json -Compress -Depth 100
$VG_CONTAINS = [System.Text.Encoding]::UTF8.GetBytes($VG_CONTAINS)

テンプレートファイルは改行文字やインデントを含むのでそれを詰め詰めにして、最後にバイト列に変換します。
最後の行は実施しなくても動くようですが、日本語文字等を含む場合は文字化けするので必要な対応です。

APIを実行

準備ができましたのでAPIを実行してみましょう。

$VG_CREATE_URL = "https://dev.azure.com/<Organization名>/<ProjectのID>/_apis/distributedtask/variablegroups?api-version=6.0-preview.2"
Invoke-RestMethod -Authentication Basic -Credential $AZURE_DEVOPS_CRED -Method Post -ContentType "application/json" -Body $VG_CONTAINS -Uri $VG_CREATE_URL

どうでしょうか、登録できましたか?

まとめ

上記のように、コードだけでAzure DevOpsのVariableGroupの追加ができることが分かりました。
ちなみにこのままだと、 Allow access to all pipelinesのオプションが無効な状態になります。必要に応じて下記のAPIを叩いて有効化しましょう。

https://docs.microsoft.com/en-us/rest/api/azure/devops/build/authorizedresources/authorize%20project%20resources?view=azure-devops-rest-6.0

自動化の夢が広がりますね!

%d人のブロガーが「いいね」をつけました。