12
votes

Is there a way to apply RBAC rules at the resource level via ARM? I was able to follow this Microsoft guide to add a user/role at the resource group level, but not at the resource. In particular, I am trying to add a new reader role to AppInsights via ARM. However, when I adjust the scope, the template just fails with this error:

"error": {
"code": "InvalidCreateRoleAssignmentRequest",
"message": "The request to create role assignment '{guid}' is not valid. Role assignment scope '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/Microsoft.Insights/components/{resourceGroupName}' must match the scope specified on the URI  '/subscriptions/{resourceGroupName}/resourcegroups/{resourceGroupName}'."
  }

I am left wondering what the scope variable is for if it cannot be changed. Is there some other place I should be modifying the scope to get this working?

Thanks in advance!

5

5 Answers

8
votes

The key is to drop the scope property, and instead nest the role assignment under the desired resource by using Microsoft.FooResource/BarSubType/providers/roleAssignments as the type, and using the following format for the name: {resourceName}/Microsoft.Authorization/{uniqueRoleAssignmentGuid}. Note that the GUID should be stable but unique to this role assignment, one easy option is guid(subscription().subscriptionId, 'some-sub-identifier-if-you-wish').

Here is a template that shows you how to apply RBAC to a single resource, using a user-assigned managed identity defined in the same template:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": { 
      "storageAccountName": { "type": "string" },
      "userAssignedIdentityName": { "type": "string" }
  },
  "variables": {
    "ContributorRoleDefinition": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
  },
  "resources": [
    {
      "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
      "name": "[parameters('userAssignedIdentityName')]",
      "location": "[resourceGroup().location]",
      "apiVersion": "2018-11-30"
    },
    {
      "type": "Microsoft.Storage/storageAccounts",
      "name": "[parameters('storageAccountName')]",
      "location": "[resourceGroup().location]",
      "apiVersion": "2016-12-01",
      "sku": { "name": "Standard_LRS" },
      "kind": "Storage",
      "resources": [
          {
              "type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
              "apiVersion": "2017-05-01",
              "name": "[concat(parameters('storageAccountName'), '/Microsoft.Authorization/', guid(subscription().subscriptionId, 'foo'))]",
              "properties": {
                "roleDefinitionId": "[variables('ContributorRoleDefinition')]",
                "principalId": "[reference(parameters('userAssignedIdentityName'), '2018-11-30').principalId]"
              },
              "dependsOn": [
                  "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
                  "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
              ]
         }
      ]
    }
  ]
}

Source: https://www.henrybeen.nl/creating-an-authorization-rule-using-an-arm-template/

1
votes

You apply RBAC rules at the resource level via an ARM and there is example template that applies RBAC rules at Azure VM here:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "principalId": {
            "type": "string",
            "metadata": {
                "description": "Principal ID associated with the subscription ID"
            }
        },
        "virtualMachineName": {
            "type": "string",
            "metadata": {
                "description": "Name of the virtual machine"
            }
        },
        "builtInRoleType": {
            "type": "string",
            "metadata": {
                "description": "Built In Role Type for the Virtual Machine"
            },
            "allowedValues": [
                "Owner",
                "Contributor",
                "Reader",
                "Virtual Machine Contributor"
            ]
        },
        "guid": {
            "type": "string",
            "metadata": {
                "description": "A new GUID used to identify the role"
            }
        },
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]",
            "metadata": {
                "description": "Location for all resources."
            }
        }
    },
    "variables": {
        "Owner": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
        "Contributor": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
        "Reader": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
        "Virtual Machine Contributor": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]",
        "resourceName": "[concat(parameters('virtualMachineName'), '/Microsoft.Authorization/', parameters('guid'))]"
    },
    "resources": [
        {
            "type": "Microsoft.Compute/virtualMachines/providers/roleAssignments",
            "apiVersion": "2017-05-01",
            "name": "[variables('resourceName')]",
            "properties": {
                "roleDefinitionId": "[variables(parameters('builtInRoleType'))]",
                "principalId": "[parameters('principalId')]"
            }
        }
    ]
}

Hope this will help you.

0
votes

It is possible to apply RBAC on resource level using ARM.

The example what you referred shows how to apply RBAC on a particular resource group, where the scope is the path of the resource group.

Here, you are trying to assign a role to a particular resource. Changing the scope from resource group to resource (AppInsights) will work.

From the exception, I can see that the path of the resource may not be in the expected format.

The path of AppInsights should be in the following format,

/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/components/{insightName}

Hope framing the scope like this helps!

0
votes

Agree that the documentation on this issue is less than useful. I have an array of Role IDs that I wanted to add as Owners at the App Insight resource, without making the users Owners at the Resource Group level. I didn't want to use the nested resource approach as I wanted to iterate over an array of objects to dynamically create the roles, so after tweaking the type, name, and scope attributes, the following resource block is what ended up working for me:

    {
      "comments": "Add the Application Insights resource",
      "apiVersion": "2014-04-01",
      "name": "[variables('appInsightsName')]",
      "type": "Microsoft.Insights/components",
      "location": "[resourceGroup().location]",
      "properties": {
        "ApplicationId": "[variables('appInsightsName')]"
      }
    },
    {
      "comments": "Add the IAM roles to the App Insights resource",
      "condition": "[parameters('isProduction')]",
      "type": "Microsoft.Insights/components/providers/roleAssignments",
      "name": "[concat(variables('appInsightsName'),'/Microsoft.Authorization/',guid(parameters('roleAssignments')[copyIndex()].principalId))]",
      "apiVersion": "2017-05-01",
      "location": "[resourceGroup().location]",
      "properties": {
        "roleDefinitionId": "[concat(subscription().Id, '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", // Owner Role
        "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]",
        "scope": "[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
      },
      "copy": {
        "name": "appInsightsRoleAssignments",
        "count": "[length(parameters('roleAssignments'))]"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
      ]
    }