Azure Role Based Access Control (RBAC) | Removing Orphaned Role Assignments

Problem Space

Deploying solutions into Azure that rely on Role Based Access often involve us creating IaC automation for the assignment of roles, such as:

  • A services access to Key Vault
  • A services access to a Key Vault specific secret
  • A services access to a storage account
  • A services access to a Service Bus Queue or Topic

In many of these instances we may wish to leverage the source resource identity (System Assigned Managed Identity) for the assigned access.

But what happens when we delete the source resource, are the role assignments applied on the target resources removed?

The answer… No they are not.

Your target resources are left with orphaned role assignments. In many cases the use of User Assigned Managed Identity is used to avoid this particular issue as the identity exists regardless of the resource life cycle, at which point there is no orphaning.

But whats the big problem, just redeploy and everything should line up again right?

Unfortunately this is not the case. If you follow through with this sentiment, you will receive the following error from the Azure Resource Manager:

Tenant ID, application ID, principal ID, and scope are not allowed to be updated. (code: RoleAssignmentUpdateNotPermitted)

The reasoning behind is role assignments require a globally unique identifier (GUID) of which out of good practices you have made deterministic for solution deployments. So when you have deleted and recreated the resource the underlying principal ID has now changed, but the role assignment name has not. This infers the problem, you cannot create two role assignments with the same name, even in different Subscriptions, followed with properties of an existing role assignment cannot be changed.

Surly there is a automated process in Azure that will remove these for me? Sadly this is not the case and it has pushed many in resulting to manually searching for and removing these orphaned assignments. This is tedious and time consuming, not to mention requiring the user to have User Access Administrator role access which you may not wish to grant to everyone.

Solution

The solution I came up with uses PowerShell and the az cli to retrieve all role assignments for a given subscription. This is then filtered down to any orphaned assignments scoped to resources in a specific resource group (scope can be changed to what you need). Given that there are orphaned assignments these are then removed through the az cli.

The script looks like this:

$ResourceGroupName = ""

$roleAssignments = az role assignment list --all | ConvertFrom-Json
$orphaned = $roleAssignments | Where-Object { ($_.principalName -eq "") -and ($_.scope -match "resourcegroups/$ResourceGroupName") }
$orphaned.Count
$orphaned | ForEach-Object { az role assignment delete --ids $_.id }

To run this script you will need the following Roles:

  • Directory.Read.All
  • User Access Administrator (At the respective Scope)

To avoid assigning User Access Administrator to anyone who may need to run this script, I placed this script into a CI/CD pipeline of which the Pipeline Identity has the roles applied as shown above. This way, the development team can remove orphaned role assignments with ease without the need for elevation of privilege.

⚠️ Best Practice of Least Privilege.

Hope this helps, and have fun