Least-Privilege Entra ID and Azure RBAC With AI as Your Reviewer
Owner on a subscription is a liability, not a convenience. Here's how AI helps you draft scoped Azure RBAC, decode role definitions, and find the over-privileged principals you forgot about.
- #azure
- #ai
- #rbac
- #entra-id
- #security
I inherited a subscription last year where a CI service principal had Owner at the management group root. Nobody could tell me why. It had been granted “to unblock a pipeline” eighteen months earlier and never walked back. That single assignment could have deleted every resource group across four subscriptions. This is the normal state of Azure RBAC in most orgs: a graveyard of broad grants that nobody remembers and nobody dares revoke.
The reason it stays broken is that Azure RBAC is genuinely tedious to reason about. Roles, scopes, inheritance, conditions, ABAC — decoding what a principal can actually do is real work. That tedium is exactly where AI earns its keep. It won’t decide your security policy, but it will read a wall of role assignments faster than you can, and it will draft a tightly scoped custom role from a plain-English description. You verify; it does the grunt work.
Start by seeing what you actually granted
You can’t fix what you can’t see. Pull every role assignment at a scope and look at the broad ones first.
# Every assignment on a subscription, flattened to something readable
az role assignment list \
--subscription "$SUB_ID" \
--include-inherited \
--query "[].{principal:principalName, type:principalType, role:roleDefinitionName, scope:scope}" \
-o table
# Just the dangerous ones
az role assignment list --subscription "$SUB_ID" --include-inherited \
--query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor' || roleDefinitionName=='User Access Administrator'].{p:principalName, role:roleDefinitionName, scope:scope}" \
-o table
That second command is the one that finds your skeletons. User Access Administrator is the sneaky one — it can’t touch resources directly, but it can grant itself anything, so it’s effectively Owner with a delay.
Dump the full JSON and hand it to AI when the list gets long enough that your eyes glaze over:
Prompt: “Here is the JSON output of
az role assignment listfor a production subscription. Group the assignments by principal. Flag any service principal (not a human) holding Owner or Contributor at subscription or management-group scope, since those should almost always be scoped to a resource group. List each flagged principal with its role and scope, and for each one suggest the narrowest built-in role that would likely still work for a typical CI/CD deployment task.”
The AI gives you a ranked worklist. It does not get the final say — a service principal that genuinely deploys across resource groups may legitimately need Contributor at a higher scope. But now you’re reviewing exceptions instead of reading raw JSON.
Use AI to draft custom roles, not to invent permissions
The mistake people make with custom roles is starting from Contributor and removing a few things. Start from nothing and add only what’s needed. The problem is that nobody memorizes Azure’s resource provider operation strings — Microsoft.Compute/virtualMachines/start/action and its thousand siblings. This is a perfect AI task because the answer is verifiable: every operation either exists in the provider catalog or it doesn’t.
Prompt: “I need an Azure custom role for an on-call engineer who can restart VMs and read their diagnostics, but cannot create, delete, or resize VMs, and cannot read or write any networking or storage. Give me the role definition JSON with the exact
Microsoft.Computeoperation strings, and explain why eachActionsentry is included.”
A reasonable draft looks like this:
{
"Name": "VM Operator - Restart Only",
"Description": "Restart and read VMs for on-call; no create/delete/resize.",
"Actions": [
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/instanceView/read",
"Microsoft.Insights/metrics/read"
],
"NotActions": [],
"AssignableScopes": ["/subscriptions/REPLACE_ME"]
}
Now verify before you trust it. AI hallucinates operation strings that look plausible but don’t exist. Check every one against the live catalog:
# List the real operations a provider exposes, then confirm yours are in there
az provider operation show --namespace Microsoft.Compute \
--query "resourceTypes[?name=='virtualMachines'].operations[].name" -o tsv \
| grep -E 'restart|start|read'
If an operation isn’t in that output, the AI made it up. Delete it. This verify step is non-negotiable — a role with a typo’d action silently grants nothing, and you find out at 3 a.m. during the incident the role was supposed to cover.
Decode the blast radius before you ship a grant
Before you create any assignment, ask the same question an attacker would: what does this principal reach? Inheritance makes this non-obvious — a grant at a resource group also covers everything that will ever be created in it.
# What can this principal do, everywhere?
az role assignment list --assignee "$PRINCIPAL_ID" --all \
--query "[].{role:roleDefinitionName, scope:scope}" -o table
# Show the actual operations a role definition permits
az role definition list --name "Storage Blob Data Contributor" \
--query "[0].permissions[0].{actions:actions, dataActions:dataActions}" -o json
Feed the role definition to AI and ask the adversarial question:
Prompt: “Here is a role definition’s actions and dataActions. Assume an attacker compromises a principal holding this role at a storage account scope. What is the worst they can do — exfiltration, deletion, privilege escalation, persistence? Be specific about which dataAction enables each.”
This reframes a dry permissions list as a threat model, and it routinely surfaces things like listKeys being a quiet path to full account access. The AI is the red-teamer; you decide whether the risk is acceptable.
Wire it into PIM and stop standing access
The endgame for least privilege isn’t smaller standing roles — it’s no standing roles for privileged work. Entra ID Privileged Identity Management gives time-bound, approval-gated, just-in-time activation. AI is useful here for the part everyone skips: writing the activation justification policy and the access-review questions in language an approver can actually act on.
Ask it to turn “engineers shouldn’t sit on Contributor” into a concrete PIM configuration: eligible (not active) assignments, 4-hour max activation, MFA on activation, and a quarterly access review with a default of revoke when the reviewer doesn’t respond. The default-deny on no-response is the detail that keeps the system honest over time.
Keep the human in the loop, on purpose
Everything above follows one rule: AI reads, drafts, and explains; you approve and apply. Never pipe an AI-generated role straight into az role definition create in a script with no eyes on it — that’s how you end up with the next Owner-at-root story, just faster. The workflow that actually works is AI proposes the narrow grant, you sanity-check the operations against the live provider catalog, and you commit the role definition to a repo so the next person inherits a reason, not a mystery.
If you want a starting point for the kind of prompts that hold up under that scrutiny, I keep a running set in the prompts library, and there’s more Azure-specific material in the Azure category. RBAC is one of those areas where the tooling has been the same for years but the review burden quietly grew past what a human wants to do by hand. That’s the gap AI fills — not the judgment, just the reading.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.