しばやん雑記

ASP.NET Core とメイドさんが大好きなフリーランスのプログラマーのブログ

ARM Template を使ってリソースへのアクセス権限を付ける

Template Deployment を使って Azure リソースをデプロイする際に、同時に RBAC 周りの設定を行いたいケースがあったので、世界のぶちぞう RD に聞いたら教えてくれました。

結局は後述する制約によって上手くいかなかったのですが、折角なのでメモとして残します。

ドキュメントはイマイチな感じですが、RBAC は Microsoft.Authorization Provider で管理されているので、そこに対してロール割り当てのリソースを新しく作る形になります。

Azure Portal からはデプロイ先のリソースグループと含まれるリソースに対してのみ操作が行えます。サブスクリプションスコープで RBAC を弄る必要がある場合は Azure CLI を使ってデプロイする必要があります。

あまり意識してなかったですが、コマンドは明確に分離されています。

  • az deployment
    • サブスクリプションスコープに対してデプロイ
  • az group deployment
    • リソースグループに対してデプロイ

お分かりの通り Azure Portal の Template Deployment はリソースグループに対してのデプロイなので、リソースグループとその中のリソース以外を対象に出来ません。これが非常に都合が悪かったです。

従って ARM Template で同時にデプロイするリソースに対して割り当てる場合は上手くいきます。

ロール定義の GUID を探す

リソースを作成するためには定義ロールの GUID が必要です。ロール定義もリソースとして ARM 上に用意されているので、Azure CLI を使えば簡単に引っ張ってこれます。

必要なのは GUID と名前ぐらいなので、項目を絞り込むようにすると見やすいです。

az role definition list --query "[].[roleName,name]" --output table

実行すると定義済みのロールが全て表示されます。組み込みのロールが持つ GUID はサブスクリプションなど関係なくグローバルで共通の値です。

f:id:shiba-yan:20190820192421p:plain

ドキュメントにもありましたが、よく使うロールの GUID は以下のような感じです。

  • Owner = 8e3af657-a8ff-443c-a75c-2fe8c4bcb635
  • Contributor = b24988ac-6180-42a0-ab88-20f7382dd24c
  • Reader = acdd72a7-3385-48ef-bd42-f606fba81ae7

ロールの GUID が分かれば、後はロール割り当てのリソースを作成するだけですが、地味に癖があります。とりあえずリソースグループと各リソースで書き方が違うので、それぞれ分けて紹介します。

リソースグループにロール割り当てを追加

サブスクリプションとリソースグループに対してロール割り当てを追加する場合は、シンプルに typeMicrosoft.Authorization/roleAssignments を指定すれば良いです。

割り当てを追加する最小限の ARM Template は以下のようになります。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "principalId": {
      "type": "string"
    },
    "roleNameGuid": {
      "type": "string",
      "defaultValue": "[newGuid()]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2018-09-01-preview",
      "name": "[parameters('roleNameGuid')]",
      "properties": {
        "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
        "principalId": "[parameters('principalId')]"
      }
    }
  ]
}

この例では入力された PrincipalId に対して Reader ロールを割り当てています。実際にテンプレートでは PrincipalId は Managed Identity の System Assigned で作成したものを使うことが多いはずです。

今回は既に作成済みの PrincipalId を使ってデプロイします。謎の roleNameGuid ですが、ロール割り当てのリソース作成時に絶対必要なものです。

f:id:shiba-yan:20190827180433p:plain

ARM Template の仕様で newGuid 関数はパラメータの defaultValue でしか使えないので、こういう不格好な形になっています。冪等性のためには仕方ない部分ですが、不格好です。

デプロイが完了するとリソースグループに対して、ロール割り当てが追加されたのが確認できます。

f:id:shiba-yan:20190827180458p:plain

App Service の Managed Identity で作成された PrincipalId を使ったので、このような表示となります。

各リソースにロール割り当てを追加

リソースグループ内の各リソースに割り当てる場合もほぼ同じですが、typename の値が割り当てたいリソース毎に異なってくるので複雑です。

以下の例のように type にはリソースプロバイダを指定しつつ、name には対象となるリソース名を含める形する必要があります。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "principalId": {
      "type": "string"
    },
    "webAppName": {
      "type": "string"
    },
    "roleNameGuid": {
      "type": "string",
      "defaultValue": "[newGuid()]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Web/sites/providers/roleAssignments",
      "apiVersion": "2018-09-01-preview",
      "name": "[concat(parameters('webAppName'), '/Microsoft.Authorization/', parameters('roleNameGuid'))]",
      "properties": {
        "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
        "principalId": "[parameters('principalId')]"
      }
    }
  ]
}

微妙に手間な書き方ですが、このテンプレートをデプロイすると App Service に対してロール割り当てを追加できます。実行結果はリソースグループの時と同じなので省略します。

Managed Identity (System Assigned) にアクセス権限を付ける

もはやアプリ向けに手動で Service Principal を作る時代は終わっているので、Managed Identity を空気のように利用していきます。

今回は App Service に対するテンプレートを書きます。Managed Identity を自動生成する場合は identity プロパティを追加するだけなので簡単です。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "functionAppName": {
      "type": "string"
    },
    "roleNameGuid": {
      "type": "string",
      "defaultValue": "[newGuid()]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Web/sites",
      "name": "[parameters('functionAppName')]",
      "apiVersion": "2018-11-01",
      "location": "[resourceGroup().location]",
      "kind": "functionapp",
      "identity": {
        "type": "SystemAssigned"
      },
      "properties": {
        /* 省略 */
      }
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2018-09-01-preview",
      "name": "[parameters('roleNameGuid')]",
      "dependsOn": [
        "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
      ],
      "properties": {
        "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions','acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
        "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2018-11-01', 'Full').identity.principalId]"
      }
    }
  ]
}

作成した App Service に紐づいた PrincipalIdreference 関数を使えば参照出来るので、後はその値を使ってロール割り当てを追加するだけです。

reference 関数では Full を指定しないと値が取れなかった気がします。とても簡単でした。

User Assigned Managed Identity にアクセス権限を付ける(問題あり)

つい先日 App Service 向けの User Assigned Managed Identity が GA したので、今後は利用することが増えていく気がしています。

System Assigned は 1 つの App Service に必ず 1 つ生成されますが、User Assigned Managed Identity は個別に作成して、自由に App Service へ割り当て出来るので管理しやすいです。

User Assigned Managed Identity も単なるリソースの一つなので、ARM Template を使って生成できます。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "identityName": {
      "type": "string"
    },
    "roleNameGuid": {
      "type": "string",
      "defaultValue": "[newGuid()]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
      "apiVersion": "2018-11-30",
      "name": "[parameters('identityName')]",
      "location": "[resourceGroup().location]"
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2018-09-01-preview",
      "name": "[parameters('roleNameGuid')]",
      "dependsOn": [
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]"
      ],
      "properties": {
        "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
        "principalId": "[reference(parameters('identityName')).principalId]"
      }
    }
  ]
}

なので組み合わせると、ロール周りの設定も同時に出来て非常に便利!と思っていたのですが、どうも Managed Identity の作成完了が AAD への反映より早いらしくロール割り当てを追加時にエラーとなります。

数秒遅延させると上手くいくという残念な状態なので、App Service を作る前に User Assigned Managed Identity を作成しておいて、最後にロール割り当てを追加すると上手くいきます。*1

*1:直してほしいのでフィードバックする予定