Skip to main content
Back to the Azure Footguns Database
SecurityGovernanceHighCommonAZF-0095

A service principal with Owner at subscription scope is one leaked secret from full compromise

An automation identity holding Owner or Contributor across the whole subscription is a standing, non-interactive credential with a subscription-sized blast radius, and PIM can't help it.

Footgun ID
AZF-0095 (Azure Footgun No. 95)
Azure service
Azure RBAC / Entra ID
Resource type
Microsoft.Authorization/roleAssignments
Updated
July 9, 2026

What it is

A service principal granted Owner, Contributor, or User Access Administrator directly at subscription scope. It's usually the identity behind a Terraform pipeline, a DevOps service connection, or a vendor integration, and it holds the entire subscription around the clock. Unlike a human admin, a service principal can't do MFA, gets far narrower conditional-access coverage than an interactive user, and can't activate through PIM: its power is standing by design. Whoever holds its client secret holds the subscription.

Impact (security)
Whoever holds the SP's secret holds the whole subscription: create, modify, delete anything, and for Owner, grant further access
Basis: Azure behavior (authored assessment). Figures are estimates, not measurements.

Why it happens

Subscription-scope Owner or Contributor is the path of least resistance when wiring up automation. The pipeline needs to create resource groups, so someone grants Contributor "on the subscription for now", and the deployment works, so nothing ever prompts a revisit. Vendor onboarding guides and quickstart docs routinely say "grant the app Contributor on your subscription" because it's the one instruction that works for every customer.

The identity also doesn't age the way a person does. A service principal never leaves the company, never changes teams, never triggers an offboarding checklist. Its secret, meanwhile, lives wherever automation secrets live: pipeline variables, config files, a developer's shell history. Access reviews tend to focus on people; the non-human identities holding the biggest grants are exactly the ones nobody re-examines.

Note the distinction from a role that's merely over-privileged for what it does (see AZF-0050, which is about activity not matching the role, for any principal). This footgun is static and specific: a service principal, a privileged built-in role, subscription scope. Even a busy, legitimate pipeline with this grant is carrying more standing power than it should.

What it costs / blast radius

There's no direct cost; the blast radius is the subscription. A leaked secret for a Contributor-holding SP lets an attacker create, modify, or delete every resource in the subscription: exfiltrate storage, spin up cryptomining fleets, delete backups. Owner and User Access Administrator are worse: they can grant further access, so the attacker mints their own persistence before you rotate the secret. (Azure behavior; authored assessment.) And because service-principal sign-ins are non-interactive, there's no MFA prompt, no unusual-login challenge, and often no human who'd notice: the abuse looks like automation doing automation things.

See it

Find them: privileged service-principal assignments at subscription scope, across every subscription
authorizationresources
| where type =~ 'microsoft.authorization/roleassignments'
| extend principalType = tostring(properties.principalType),
         scope = tostring(properties.scope),
         roleId = tolower(tostring(properties.roleDefinitionId))
| where principalType == 'ServicePrincipal'
| where scope matches regex @'^/subscriptions/[^/]+$'   // subscription scope exactly
| where roleId endswith '8e3af657-a8ff-443c-a75c-2fe8c4bcb635'   // Owner
     or roleId endswith 'b24988ac-6180-42a0-ab88-20f7382dd24c'   // Contributor
     or roleId endswith '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'   // User Access Administrator
| project principalId = tostring(properties.principalId), scope, roleId
Fix: scope the grant down to what the workload touches, and prefer identities without secrets
# Drop the subscription-wide grant, re-grant at the narrowest scope that works
az role assignment delete --assignee <appId> \
  --role Owner --scope /subscriptions/<subscriptionId>

az role assignment create --assignee <appId> \
  --role Contributor \
  --scope /subscriptions/<subscriptionId>/resourceGroups/rg-workload

# Better still: remove the secret class entirely. Use a managed identity for
# Azure-hosted workloads, or federated (workload identity) credentials for
# CI/CD, so there is no client secret to leak in the first place.

How StratoLens helps

StratoLens flags every privileged service-principal assignment at subscription scope, continuously, across all your subscriptions, from inside your own tenant. The Terraform SP that got Owner during setup two years ago shows up as a finding with the role, principal, and scope attached, instead of waiting to show up in an incident report.

Start Your 28-Day Free Trial

Every feature unlocked. Deploys to your Azure tenant. No data leaves your tenant.

Available now on the Azure Marketplace.

Not ready to install anything? Browse the Azure Footguns Database: 55+ documented ways Azure quietly costs money or creates risk.

Request a demo

StratoLens catches the cost waste, access risk, and config drift across your whole Azure estate, from inside your own tenant, so your data never leaves it.