Pedro Paulo
Pedro Paulo

Reputation: 103

How to deploy ARM template with user managed identity and assign a subscription level role?

The ARM template below is supposed to create the following resources:

resource group
    - user managed identity
       - subscription level Contributor role assignment

Currently the deployment is failing with the error "error": { "code": "ResourceGroupNotFound", "message": "Resource group 'rg-myproject-deploy' could not be found." } apparently because the role assignment step seem to not be respecting the dependsOn statements that should enforce that it should only happen after the resource group is created. Is there a way to deploy all these resources in a single ARM template?

deployment error

{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "projectName": {
      "type": "string",
      "defaultValue": "myproject",
      "maxLength": 11,
      "metadata": {
        "description": "The name of the project"
      }
    },
    "location": {
      "type": "string",
      "defaultValue": "westus2",
      "metadata": {
        "description": "The region were to deploy assets"
      }
    }
  },
  "variables": {
    "resourceGroupName": "[concat('rg-', parameters('projectName'), '-deploy')]",
    "managedIdentityName": "[concat('msi-', parameters('projectName'), '-deploy')]",
    "bootstrapRoleAssignmentId": "[guid(subscription().id, 'contributor')]",
    "contributorRoleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
    "managedIdentityId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', variables('resourceGroupName'), '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', variables('managedIdentityName'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Resources/resourceGroups",
      "apiVersion": "2019-10-01",
      "name": "[variables('resourceGroupName')]",
      "location": "[parameters('location')]",
      "properties": {}
    },
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2019-10-01",
      "name": "deployment-assets-except-role-assignment",
      "resourceGroup": "[variables('resourceGroupName')]",
      "dependsOn": [
        "[resourceId('Microsoft.Resources/resourceGroups/', variables('resourceGroupName'))]"
      ],
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "parameters": {},
          "variables": {},
          "resources": [
            {
              "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
              "name": "[variables('managedIdentityName')]",
              "apiVersion": "2018-11-30",
              "location": "[parameters('location')]"
            }
          ],
          "outputs": {}
        }
      }
    }
    ,
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2017-09-01",
      "name": "[variables('bootstrapRoleAssignmentId')]",
      "dependsOn": [
        "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('resourceGroupName'))]",
        "deployment-assets-except-role-assignment"
      ],
      "properties": {
        "roleDefinitionId": "[variables('contributorRoleDefinitionId')]",
        "principalId": "[reference(variables('managedIdentityId'), '2018-11-30').principalId]",
        "principalType": "ServicePrincipal",
        "scope": "[subscription().id]"
      }
    }
  ],
  "outputs": {}
}

Upvotes: 2

Views: 4117

Answers (1)

bmoore-msft
bmoore-msft

Reputation: 8717

I think you're running into this:

https://bmoore-msft.blog/2020/07/26/resource-not-found-dependson-is-not-working/

The fix was a little more involved than I thought, but to summarize:

  1. the nested deployment that provisions the MI must be set to inner scope evaluation
  2. output the principalId from that deployment and use that in your reference (i.e. don't directly reference)

Due to #1 I moved some stuff around (params/vars)

{
    "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "projectName": {
        "type": "string",
        "defaultValue": "myproject",
        "maxLength": 11,
        "metadata": {
          "description": "The name of the project"
        }
      },
      "location": {
        "type": "string",
        "defaultValue": "westus2",
        "metadata": {
          "description": "The region were to deploy assets"
        }
      }
    },
    "variables": {
      "identityDeploymentName": "deployment-assets-except-role-assignment",
      "resourceGroupName": "[concat('rg-', parameters('projectName'), '-deploy')]",
      "managedIdentityName": "[concat('msi-', parameters('projectName'), '-deploy')]",
      "managedIdentityId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', variables('resourceGroupName'), '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', variables('managedIdentityName'))]",
      "bootstrapRoleAssignmentId": "[guid(subscription().id, variables('contributorRoleDefinitionId'),variables('managedIdentityId'))]",
      "contributorRoleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
    },
    "resources": [
      {
        "type": "Microsoft.Resources/resourceGroups",
        "apiVersion": "2019-10-01",
        "name": "[variables('resourceGroupName')]",
        "location": "[parameters('location')]",
        "properties": {}
      },
      {
        "type": "Microsoft.Resources/deployments",
        "apiVersion": "2019-10-01",
        "name": "[variables('identityDeploymentName')]",
        "resourceGroup": "[variables('resourceGroupName')]",
        "dependsOn": [
          "[resourceId('Microsoft.Resources/resourceGroups', variables('resourceGroupName'))]"
        ],
        "properties": {
          "mode": "Incremental",
          "expressionEvaluationOptions":{
              "scope": "inner"
          },
          "parameters": {
              "location": {
                  "value": "[parameters('location')]" 
              },
              "managedIdentityName": {
                  "value": "[variables('managedIdentityName')]" 
              }
          },
          "template": {
            "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {
                "location": {
                    "type": "string"
                },
                "managedIdentityName": {
                    "type": "string"
                }
            },
            "variables": {},
            "resources": [
              {
                "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
                "name": "[parameters('managedIdentityName')]",
                "apiVersion": "2018-11-30",
                "location": "[parameters('location')]"
              }
            ],
            "outputs": {
                "principalId": {
                    "type": "string",
                    "value": "[reference(parameters('managedIdentityName')).principalId]"
                }
            }
          }
        }
      }
      ,
      {
        "type": "Microsoft.Authorization/roleAssignments",
        "apiVersion": "2020-04-01-preview",
        "name": "[variables('bootstrapRoleAssignmentId')]",
        "dependsOn": [
          "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('resourceGroupName'))]",
          "[variables('identityDeploymentName')]"
        ],
        "properties": {
          "roleDefinitionId": "[variables('contributorRoleDefinitionId')]",
          "principalId": "[reference(variables('identityDeploymentName')).outputs.principalId.value]",
          "principalType": "ServicePrincipal",
          "scope": "[subscription().id]"
        }
      }
    ]
  }

Upvotes: 4

Related Questions