Cleaning Up Azure Role Assignments with Deployment Stacks
By Anatoly Mironov
When managing Role Assignments in Infrastructure-as-Code, Deployment Stacks offer a cleaner, more predictable solution.
Introduction
Recently I discussed Deployment Stacks with my colleague Andreas.
One shortcoming that we had been struggling with in the classic az deployment... is its inability to cleanup the role assignments. So we decided to see if deployment stacks would do the job better. On the one hand it’s reasonable that it should since, all the parts of bicep templates are connected to a stack. I was sceptical in the beginning and we decided to try it out.
Last time I did my tests in Azure Pipelines. You can of course try it out directly from your local machine, which Andreas did actually - to prove the point.
I decided to share this in a blog post - this post. To make things easier to follow along I chose to try this out in Github Actions, also because by coincidence I wanted to learn more about IaC in Github compared to the ones in Azure DevOps.
The problem with Classic Deployments
The issue is that role assignments remain even when they’re excluded from the deployment or when the associated service principal is removed.

Testing with Github Actions
I have created a repo on Github and set up Github Actions, feel free to look at it and its history, if you are curious about the steps I have done to try this out.
The setup
I created a simple deployment which consisted of:
- main.bicep file with target scope subscription
- a new resource group
- an User Assigned Managed Identity (uami)
- a Key Vault
- a Role Assignment for the uami to the Key Vault

Removing the role assignment
I commented out the role assignment, nothing happened.

In order to tell the az deployment... to cleanup is to switch to --mode Complete.

The problem was that az deployment does not support the targetScope=subcription.

So I added a new bicep file with the targetScope=resourceGroup: rg-scope.main.bicep and excluded the role assignment.

I also excluded the uami itself.

It resulted in the scenario what we have seen many times:

When I tried the original, subscription scoped template, with the uami and the assignment, it failed.

So without a manual intervention or an extra step in a pipeline, it would not work.

Deployment Stacks in Action
After I confirmed that the classic deployment was not able to cleanup orphaned role assignments, I did the same test with az stack sub create...
az stack sub create \
--template-file infra/main.bicep \
--name stack-role-assignment \
--location westeurope \
--action-on-unmanage deleteResources \
--deny-settings-mode None
Note: The magic key is
--action-on-unmanage deleteResources. This holds all the pieces together.

It created my resources as expected.

When I then excluded the role assignment from my stack, it disappeared nicely.

Conclusion
Deployment Stacks offer a cleaner way to manage Role Assignments in Azure, which contributes to a cleaner and easier Infrastructure-as-Code compared to classic deployment.
I’m deliberately not listing all the pros, cons, and trade-offs of Deployment Stacks versus other alternatives — my goal is to keep this post focused and concise. For this use case, it’s a clear win for Deployment Stacks: 1–0.
What’s your scorecard? Have you come across other scenarios where Deployment Stacks shine — or fall short?
- GitHub
- DevOps
- Pipelines
- GitHub Actions
- IaC
- Infrastructure-as-Code
- Deployment
- Deployment Stacks
- Stack
- Azure
- Az
- Azure CLI