Azure ARM deployment - deleting resource group can't delete role assignments cleanly

Problem statement

Once an ARM template is deployed into a resource group, one way to completely delete those deployed resources is to delete that resource group. But it seems roles assignments can't be cleaned up entirely.

In our case, we have an ARM template to deploy Azure AKS cluster in a specified resource group, and two role assignments. One is to assign "Network Contributor" on VNET to the managed Id of the AKS cluster, and the other is to assign "Contributor" role on an already existing Azure Container Registry (ACR) to the managed Id of the AKS cluster.

First we successfully deployed the ARM template. The AKS was setup, and those two role assignments were deployed. Then we deleted that resource group. All resources under that resource group, including the AKS cluster, VNET,  etc. were deleted successfully. The role assignment on VNET was cleaned up as well, but the role assignment on ACR was still there.

Role assignments before and after resource group deletion


Since role assignments are Azure subscription level resources, they won't be listed under a resource group on Azure portal. You can use below command to list all role assignments under an Azure subscription.
az role assignment --all

The below is the role assignment on the ACR before deletion.
  {
    "canDelegate": null,
    "condition": null,
    "conditionVersion": null,
    "description": null,
    "id": "/subscriptions/<subscription Id>/resourcegroups/<resource group name>/providers/Microsoft.ContainerRegistry/registries/<ACR name>/providers/Microsoft.Authorization/roleAssignments/4b6bbdc6-0051-4b09-ae6b-2287c57aa5b2",
    "name": "4b6bbdc6-0051-4b09-ae6b-2287c57aa5b2",
    "principalId": "08706c32-1e0d-4bf5-8537-cf87e5eaf706",
    "principalName": "6a04bf60-6240-4133-b568-e36a363f458f",
    "principalType": "ServicePrincipal",
    "resourceGroup": "<resource group name>",
    "roleDefinitionId": "/subscriptions/<subscription Id>/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
    "roleDefinitionName": "Contributor",
    "scope": "/subscriptions/<subscription Id>/resourcegroups/<resource group name>/providers/Microsoft.ContainerRegistry/registries/<ACR name>",
    "type": "Microsoft.Authorization/roleAssignments"
  }

After the deletion of the resource group, we can see the role assignment on the ACR is still there, but the principleName field is cleared.
  {
    "canDelegate": null,
    "condition": null,
    "conditionVersion": null,
    "description": null,
    "id": "/subscriptions/<subscription Id>/resourcegroups/<resource group name>/providers/Microsoft.ContainerRegistry/registries/<ACR name>/providers/Microsoft.Authorization/roleAssignments/4b6bbdc6-0051-4b09-ae6b-2287c57aa5b2",
    "name": "4b6bbdc6-0051-4b09-ae6b-2287c57aa5b2",
    "principalId": "08706c32-1e0d-4bf5-8537-cf87e5eaf706",
    "principalName": "",
    "principalType": "ServicePrincipal",
    "resourceGroup": "<resource group name>",
    "roleDefinitionId": "/subscriptions/<subscription Id>/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
    "roleDefinitionName": "Contributor",
    "scope": "/subscriptions/<subscription Id>/resourcegroups/<resource group name>/providers/Microsoft.ContainerRegistry/registries/<ACR name>",
    "type": "Microsoft.Authorization/roleAssignments"
  }

If you are going to re-deploy the ARM template, you must ensure the name of that role assignment on ACR is unique, otherwise you will get an error like below,

{
RoleAssignmentUpdateNotPermitted",
"message": "Tenant ID, application ID, principal ID, and scope are not allowed to be updated."

}

Template to ensure every role assignment name unique

The snippet of the ARM template of creating that role assignment with an unique name is following. 
  "parameters": {
	...
    "guidValue-1": {
      "type": "string",
      "metadata": {
      "description": "The unique id used in the role assignment of the kubernetes service to the container registry service. It is recommended to use the default value."
      },
      "defaultValue": "[newGuid()]"
    },
	...
  }

  "resources": [
    ...  
	{
      "condition": "[or(equals(parameters('newOrExistingACR'), 'new'), equals(parameters('newOrExistingCluster'), 'new'))]",
      "name": "ConnectAKStoACR-RoleAssignment",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2017-05-10",
      "resourceGroup": "[parameters('acrResourceGroup')]",      
      "dependsOn": [
        "[concat('Microsoft.ContainerService/managedClusters/', parameters('clusterName'))]"        
      ],
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              "apiVersion": "2018-09-01-preview",
              "type": "Microsoft.ContainerRegistry/registries/providers/roleAssignments",
              "name": "[concat(parameters('acrName'), '/Microsoft.Authorization/', parameters('guidValue-1'))]",
              "properties": {
                "principalId": "[reference(parameters('clusterName'), '2020-03-01').identityProfile.kubeletidentity.objectId]",
                "principalType": "ServicePrincipal",
                "roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
                "scope": "[resourceId(parameters('acrSubscriptionId'),parameters('acrResourceGroup'),'Microsoft.ContainerRegistry/registries/', parameters('acrName'))]"
              }
            }
          ]
        }
      }
    },
	...
  }

References

Microsoft mentions this kind of 'undeleted role assignments' at Role assignments with identity not found.

Comments

Popular posts from this blog

Deep-archive an aws S3 bucket with versioning enabled once

Good practices of using Python logging

Auto-installing NVIDIA device plug-in on GPU nodes only