OnCallReady

Lesson 22.8 · Azure I: CLI, Identity & Data Planes · 16 min read

Entra ID: users, apps, service principals

In plain words

Imagine a toy that you design once, with a drawing, instructions and a list of what it needs. Then each school that wants one builds its own copy, which gets its own name tag and its own place in that school's cupboard. The drawing is shared; the copy in each school is what that school's teachers give permissions to.

In Entra ID the drawing is the app registration (az ad app), with its appId. The copy in a tenant is the service principal (az ad sp), with its own object ID, and that's what you grant roles to. Users, groups and managed identities live in the same directory. Entra ID answers "who are you?" by issuing tokens; Azure RBAC then answers "what may you do?".

A deploy script needs to create resources in Azure at 2am with nobody at the keyboard. It cannot log in with your account (you would be sharing your password with a script, and it leaves with you when you change jobs). It needs its own identity, with its own narrow access. This lesson is about the kinds of identity Azure has, and which ID of theirs goes where.

What you need to know already: 22.1 (tenant, subscription, az login, tokens), 22.4 (--query), 9.15 (TLS certificates and private keys).

Microsoft Entra ID (formerly Azure Active Directory) is the identity provider behind every Azure call: the service that knows who exists and hands out tokens. Before anything can touch a resource, Entra ID has to issue it a token, and Azure RBAC then decides what that token may do. Two separate systems, two words worth keeping apart:

A principal is anything that can be authenticated and granted roles: a user, a group, or an application identity.

What lives in a tenant

users                 [email protected]
groups                platform-admins, shop-developers
app registrations     the DEFINITION of an application (one, in its home tenant)
service principals    the INSTANCE of an app in a tenant - the thing you grant roles to
managed identities    service principals whose credentials Azure manages for you

The pair people mix up forever: app registration vs service principal.

Analogy: the app registration is the job description; the service principal is the employee badge issued in your building. Permissions go on the badge.

In the portal, app registrations are under App registrations and service principals under Enterprise applications. For an app used only inside your own tenant, you get one of each, created together.

Three IDs, three uses

appId / clientId     identifies the application when it LOGS IN   (az login -u <appId>)
SP objectId          identifies the principal in RBAC               (role assignment --assignee-object-id)
tenantId             which directory issued the token

az ad sp list --display-name sp-shop-ci -o table: find service principals by their display name.

$ az ad sp list --display-name sp-shop-ci -o table
DisplayName    Id                                    AppId                                 CreatedDateTime
-------------  ------------------------------------  ------------------------------------  -----------------
sp-shop-ci     4802bf9d-54aa-4df7-b396-7a1eeaae0e8c  9c1f6e0a-...                          ...

Id is the service principal's object ID. AppId is the client ID. When a role assignment "does nothing", the first thing to check is which of the two it was created against. az role assignment create --assignee <appId> looks up the SP object ID for you; --assignee-object-id does not look up anything - give it the object ID or the assignment points at nothing.

Credentials a service principal can have

A credential is what the app shows to prove it is itself.

kindhow it worksrisk
client secreta password, sent to Entra IDleaks from variables, logs, laptops; expires (default 1 year)
certificateproves possession of a private key (Ch 9)the key file still has to live somewhere
federated credentialtrusts a token from another issuer (GitHub, Azure DevOps, a Kubernetes cluster)no secret at all

Federated credentials are the modern answer, and they rest on OIDC (OpenID Connect): a standard where a system issues signed tokens saying "this is workload X". The token carries claims (fields) such as iss (the issuer, who signed it) and sub (the subject, which workload it is).

Example: a GitHub workflow (a script GitHub runs for you on every push; its automation service is covered in Ch 25) gets its own OIDC token for each run (iss: https://token.actions.githubusercontent.com, sub: repo:org/repo:ref:refs/heads/main). Entra ID checks it matches a federated credential on the app, and hands back an Azure token. Nothing to rotate, nothing to leak. Lesson 22.21 builds this.

create-for-rbac

az ad sp create-for-rbac creates an app plus its service principal and gives it a role in one go. --name its display name, --role the role to grant, --scopes where (a resource ID).

$ az ad sp create-for-rbac --name sp-sysop-ci --role Contributor \
    --scopes /subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/rg-oncall-lab
Creating 'Contributor' role assignment under scope '/subscriptions/.../resourceGroups/rg-oncall-lab'
The output includes credentials that you must protect. Be sure that you do not include these
credentials in your code or check the credentials into your source control. For more
information, see https://aka.ms/azadsp-cli
{
  "appId": "6b25465b-bb08-42cd-b802-514455982a97",
  "displayName": "sp-sysop-ci",
  "password": "Ee98Q~Fe78e0C0e096D3007720d02a7779f99257",
  "tenant": "11111111-2222-3333-4444-555555555555"
}

Read it carefully:

Running it again with the same name resets the secret ("Found an existing application instance ... We will patch it"). The old secret stops working - anything that still uses it breaks. That is also how you rotate it (replace a credential with a new one).

Signing in as a service principal

az login --service-principal -u "$APP_ID" -p "$PASSWORD" --tenant "$TENANT_ID"
az account show --query user
{
  "name": "6b25465b-bb08-42cd-b802-514455982a97",
  "type": "servicePrincipal"
}

name is the appId, type says you are now an app, not a user.

From then on the CLI sees only what the SP's role assignments allow. A Contributor scoped to one resource group sees one resource group: az group list returns just that, because listing is a read and reads are authorised too.

A wrong secret fails at sign-in, not later. Entra ID errors start with an AADSTS code (a numbered Entra sign-in error - search the number):

ERROR: AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the
request is the client secret value, not the client secret ID, for a secret added to app '...'.

The classic cause in the message: someone copied the secret's ID column from the portal instead of the value (which is only visible once, at creation).

Groups beat individual assignments

Grant roles to groups, then manage group membership. A leaver removed from the group loses every role at once; nobody has to hunt down twenty individual assignments. For service principals that is less common, but the same logic applies to people. Lesson 22.18 does it.

Graph vs ARM

az ad ... calls Microsoft Graph (graph.microsoft.com), the API for directory objects: users, groups, apps. az role ... calls ARM (lesson 22.1): Azure RBAC. Different APIs, different permissions.

So there are two kinds of role. Entra roles (like Application Administrator) are about the directory. Azure roles (like Owner, Contributor) are about resources. Being Owner of a subscription does not let you create app registrations - that needs an Entra role, or the tenant setting that lets users register apps.

What you can now do

Why it helps

Every pipeline, app and tool that calls Azure needs an identity, and service principals are where most security incidents and broken access come from: a client secret that leaked from a pipeline variable, a secret that expired after one year and broke deployments at night, a role assigned to the appId instead of the object ID. This lesson gives you the vocabulary to fix those and the direction to avoid them: federated credentials instead of secrets.

It also explains two surprises you'll hit early: being subscription Owner doesn't let you create app registrations, because that's a directory permission in Microsoft Graph, not RBAC; and AADSTS7000215 usually means someone pasted the secret's ID instead of its value. Knowing that turns hours into minutes.

Commands in this lesson

az

FAQ

What's the difference between an app registration and a service principal?

The app registration is the global definition of an application: name, credentials, redirect URIs, requested permissions, with an appId and its own object ID, living in its home tenant. The service principal is that app's identity in one specific tenant, with its own object ID, and it's what role assignments point at. For an app used only in your tenant you get one of each; in the portal they're App registrations and Enterprise applications respectively.

Which ID do I use where?

The appId or client ID when the application signs in, for example az login --service-principal -u <appId>, and when a workload selects an identity. The service principal's object ID in RBAC, for example --assignee-object-id. The tenant ID to say which directory issues the token. az role assignment create --assignee <appId> resolves the appId to the object ID for you; --assignee-object-id does not, so give it the real object ID.

Why are client secrets considered risky?

A client secret is a password: it's sent to Entra ID, so it has to live somewhere the app can read it, and that's where it leaks: pipeline variables, logs, laptops, config files. It also expires, one year by default from create-for-rbac, and forgotten expiry dates break deployments. Certificates are better but still need a key file. Federated credentials, trusting an OIDC token from GitHub, Azure DevOps or a Kubernetes cluster, have no secret at all.

Why can't I create an app registration even though I'm Owner?

Because app registrations are directory objects in Entra ID, managed through Microsoft Graph, not Azure resources managed through ARM. Owner is an Azure RBAC role on a subscription and says nothing about the directory. Creating apps needs an Entra role like Application Developer or Application Administrator, or a tenant setting that allows users to register applications. Many enterprises disable that setting, so app creation goes through a request process.

What happens if I run create-for-rbac again with the same name?

It finds the existing application, patches it and resets its secret. The new password is shown once, and the old secret stops working immediately, so anything still using it breaks. That makes it a way to rotate the secret, but an unintended one if you just wanted to check something. Also remember that since CLI 2.37 you must pass --role and --scopes for any role assignment; it no longer grants subscription Contributor by default.

In an interview Mid

What is a service principal, and how is it different from an app registration?

Job description vs employee badge: permissions go on the badge. Three IDs, three uses: appId to log in (az login --service-principal -u <appId>), SP object ID for RBAC, tenantId for the directory. An assignment made with --assignee-object-id <appId> points at nothing.

Credentials, best last: a client secret (leaks, expires, shown once by az ad sp create-for-rbac), a certificate, a federated credential - Entra ID trusts an OIDC token from another issuer (a GitHub workflow, a cluster), so there is no secret at all.

Sign-in errors carry an AADSTS code; AADSTS7000215 usually means someone copied the secret's ID instead of its value.

Also asked: How should a deploy job running outside Azure authenticate to Azure? · What is the difference between authentication and authorisation in Azure? · What is the difference between Entra roles and Azure roles?

Practise this lesson in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.