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:
- authentication - proving who you are (Entra ID: "you are learner").
- authorisation - deciding what you may do (Azure RBAC: "learner may read rg-oncall-lab").
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
- User - a person's account. Its login name is a UPN (user principal name,
name@domain, like an email address). - Group - a named set of users (or other principals). Grant a role to the group and every member has it.
The pair people mix up forever: app registration vs service principal.
- An app registration (
az ad app) is the global definition of an application: its name, its credentials, the permissions it asks for. It has an appId (also called client ID) and its own object ID. - A service principal (SP,
az ad sp) is that app's local identity in one tenant - the account the app actually logs in as. It has its own object ID. Role assignments point at this object ID.
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.
| kind | how it works | risk |
|---|---|---|
| client secret | a password, sent to Entra ID | leaks from variables, logs, laptops; expires (default 1 year) |
| certificate | proves possession of a private key (Ch 9) | the key file still has to live somewhere |
| federated credential | trusts 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:
- It creates the app registration and the service principal.
--roleneeds--scopes. Since CLI 2.37 it no longer assigns Contributor on the whole subscription by default - old blog posts that run it without flags describe behaviour that is gone.passwordis the client secret, and it is shown once. Capture it straight into a variable or a secret store (Key Vault, lesson 22.23); do not let it scroll through a terminal that is being recorded, or a pipeline log.- The warnings go to stderr, so
SP=$(az ad sp create-for-rbac ...)captures only the JSON.
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
- Tell an app registration from a service principal, and appId from object ID.
- Create a service principal with a scoped role and sign in as it.
- Recognise an AADSTS sign-in error and a secret that was copied wrong.