
The Script: Get-ResourceTypeActions.ps1
This started out as a fairly simple exercise.
I wanted to list all possible Azure RBAC permissions within a given scope, based on what was actually deployed.
So I asked the question:
“Given the resources in this subscription / resource group, what are all the actions available to them?”
On the surface, this sounds straightforward. Azure already gives us the building blocks:
- Get-AzResource to discover deployed resources
- Get-AzProviderOperation to list available operations
But once I started comparing the output with real-world deployments, it became obvious that something was missing.
The Initial Observation
Running a simple discovery flow gave me a good baseline:
- Virtual machines
- Storage accounts
- Key Vaults
- Log Analytics workspaces
So far, so good.
But when I tried to line this up with environments running things like Microsoft Sentinel or other “add-on” services, the permissions did not fully match reality. Certain actions were clearly required at runtime, yet never appeared in the list.
Nothing was broken.
Nothing was misconfigured.
This is where the nuance comes in.
The Nuance: Not All Resource Types Are Equal
The gap was not a bug or an API limitation — it was a resource modelling detail that is easy to miss if you are thinking in terms of “top-level resources”.
Some Azure services do not exist as standalone resources in a resource group. Instead, they are implemented as extension providers that sit under another resource.
A common example is Microsoft Sentinel:
- The parent resource is a Log Analytics workspace
- Sentinel resources live under the Microsoft.SecurityInsights provider
- These resources are scoped beneath the workspace, not alongside it
From an ARM perspective, the resource path looks something like:
Microsoft.OperationalInsights/workspaces/{workspace}
└── providers/Microsoft.SecurityInsights/…
This has an important side effect:
Standard resource discovery only sees the parent, not the extension.
If you enumerate deployed resources using Get-AzResource, you will see the workspace — but not the Sentinel resources attached to it.
And if you do not know the extension provider is there, you will never ask for its operations.
You could argue the existence of the workspace solution, with the name starting SecurityInsights is the give away that Sentinel exists within this resource group, but what about every other extension provider?
Why This Matters for Permission Enumeration
When your goal is complete RBAC visibility, this distinction matters.
If you only enumerate:
- Top-level deployed resource types
- Their directly associated provider operations
…you will miss actions that belong to:
- Extension providers
- Child resource types that do not surface independently
This does not usually show up when assigning broad built-in roles.
It does show up when you are:
- Designing least-privilege custom roles
- Validating managed identity permissions
- Auditing what a workload actually needs
This is the gap I ran into.
What the Script Does Differently
The purpose of Get-ResourceTypeActions.ps1 is not to “fix” Azure — it is to adjust the discovery approach to match how some services are modelled.
At a high level, the script does four things:
1. Discover deployed resources
It uses Get-AzResource to identify all resource types currently deployed in the target scope. This establishes the baseline.
2. Identify extension-capable providers
It queries the ARM /providers endpoint to find providers that support extension resources. This answers the question: which providers could be attached beneath other resources?
3. Probe for active extensions
For each deployed resource, the script probes applicable extension providers to see whether resources actually exist under them.
This avoids assumptions — an extension is only included if it is confirmed to be present.
4. Enumerate operations for everything discovered
Finally, it enumerates all available control-plane and data-plane operations for:
- Deployed resource types
- Confirmed extension providers
The result is a permission surface that aligns with what is genuinely deployed, not just what is visible at the top level.
How I Actually Use This
In practice, this tool is most useful when:
- Designing custom RBAC roles for complex services
- Verifying permissions for managed identities
- Auditing environments where multiple security or monitoring services are layered onto the same base resources
- Implementing the “Do not allow deletion of resource types” built in Azure Policy.
It is not something you run once and forget — it is a way to sanity-check your understanding of what an environment really contains from an RBAC perspective.
Common Misunderstandings This Clears Up
A few patterns I have seen repeatedly:
- “The resource exists, so I should see its permissions.”
Not always — some resources only exist as extensions. - “If it is not returned by Get-AzResource, it is not relevant.”
It may still be very relevant for RBAC. - “Why can I not enforce compliance with Azure Policy.”
Because Azure Policy cannot discover it.
None of this is wrong — it just requires a slightly different discovery approach once you step beyond basic resource types.
Final Thoughts
This script came originally started as a tool I created over a year ago and never got round to publishing, but today as I unearthed it for another task, I realised that unless the user knew all the resource type explicitly, then they would miss the extension.
I was not trying to prove something was broken — I was trying to understand the full permission surface of a real environment and noticed that a naïve approach left gaps.
Once you account for extension providers and how they attach to parent resources, those gaps make sense.
If you are doing anything beyond assigning built-in roles, it is a nuance worth being aware of.
Alistair