The identities with no one behind them
At two in the morning a deployment runs. No one typed a password to start it, because no one was awake — and yet it reached into Azure, created resources, and read a database, all of which demanded proof of who was asking. Something authenticated. That something was not a person. Most of what happens in a mature cloud is done by software acting on its own, and every piece of it needs an identity exactly as a human does.
Class Seven and Eight were about people: who they are, what they may touch. This class is about the other population of your tenant — the non-human identities, sometimes called workload identities. A pipeline that deploys, an application that reads storage, a script that runs on a schedule: each is an actor in Entra ID, each holds a role at some scope, and each must prove itself to act.
The whole subject reduces to one uncomfortable question: how does a machine prove who it is when there is no human to type the secret? The answers form a ladder from dangerous to elegant, and where you stand on that ladder is, once again, the part of the job you are actually paid for. Class One warned about a key committed to a public repository at eleven at night. This is the class that teaches you how to never need that key at all.
Service principals and the rotation treadmill
The original answer is the service principal. When you register an application in Entra ID, it gets an identity in your directory — the service principal — that can be granted roles and act on its own, just like a user but without a human.
- Service principal
- The identity of an application or automation inside Entra ID. It can hold RBAC roles at a scope and authenticate on its own, using either a client secret (a password) or a certificate.
The trouble is how it proves itself. A service principal authenticates with a client secret — which is a password, generated once and pasted into wherever the workload runs — or with a certificate, which is stronger but still a file you must hold and protect. Either way, you are now the custodian of a credential, and a credential has three bad properties: it can be copied, it can be leaked, and it expires.
That last one creates the treadmill. Secrets are issued with an expiry, so someone must remember to generate a new one and update every place the old one lived, before the day it dies and the pipeline fails at the worst possible moment — which, by long tradition, is a public holiday. The long-lived secret is worse, because a key that outlives everyone's memory of it will eventually be committed to a repository, pasted into a chat, or copied into a support ticket. Service principals with secrets are not wrong, but they are a standing liability you have to actively manage, and the rest of this class is about climbing above them.
Managed identity: the credential you never hold
Managed identity is the move that takes the credential out of your hands entirely. It is still a service principal underneath3 — an identity in Entra ID that holds roles — but Azure creates it, stores its credential, and rotates it for you, automatically and invisibly. There is no secret for you to paste, leak or renew, because you never see one.
- Managed identity
- A service principal whose credential is created and rotated by Azure itself. The workload asks the platform for a token at runtime; nothing is stored, nothing expires on your watch.
There are two kinds, and the difference is a lifecycle question you should be able to answer on sight.
- System-assigned
- Created on a single resource and bound to its life — enable it on a virtual machine or web app, and it exists only as long as that resource does, deleted automatically with it. One identity, one resource, no leftovers.
- User-assigned
- A standalone identity you create once and attach to many resources. It outlives any single one, which is what you want when a fleet of machines should share one identity, or when the identity must exist before the thing that uses it.
The rule of thumb: reach for system-assigned when one resource needs an identity of its own and should clean up after itself, and user-assigned when several resources share an identity or the identity must be provisioned ahead of them. Either way you have deleted the treadmill: no secret in config, nothing to rotate, nothing to leak. Where a workload runs inside Azure, managed identity is very nearly always the right answer, and "did you consider a managed identity" is the question a reviewer will ask the moment they see a stored secret.
The ladder of credential hygiene
Put the options in order and you get a ladder every cloud engineer carries in their head. Climb as high as the situation allows; know why each rung sits above the next.
A stored secret is a leak with a delay.
The ladder is not academic. Every rung you climb removes an entire category of incident: no stored secret means no leaked secret, no forgotten rotation, no key in a screenshot. When you cannot reach the top — because a workload lives outside Azure and cannot use a managed identity — you climb to the next rung that fits,2 and §5 is that rung.
Workload identity federation — the phrase to know
A managed identity only works for something running inside Azure. But plenty of things that need Azure access run elsewhere — most importantly your build pipeline in GitHub. The old answer was to create a service principal, generate a secret, and paste it into GitHub, which is the bottom of the ladder wearing a tie. The modern answer, and the phrase interviewers listen for, is workload identity federation.
- Federation
- A trust relationship that lets an external workload prove itself to Entra ID with a short-lived token from its own platform — over OpenID Connect — so Azure issues access without any secret ever being stored.
The idea is that a trust is a configuration, not a credential. You tell Entra ID to trust tokens issued by GitHub's identity provider, but only for a specific repository and branch. When the pipeline runs, GitHub hands it a short-lived token proving "I am this workflow, in this repo"; Azure checks that against the trust you configured and, satisfied, issues its own short-lived access token. Nothing long-lived is ever stored on either side, and a token that leaks is worthless in minutes rather than months.1
This is the top of the ladder for anything outside Azure, and it is where Class Twenty-Three takes Campux's pipeline in full. For now, hold the phrase and the shape: federation replaces a stored secret with a scoped, short-lived trust. Say that sentence in an interview and you have signalled, in one breath, that you know where the industry actually is.
Giving Campux's machines their identities
Campux's storefront needs to read product images from storage and pull secrets from a vault; its build pipeline in GitHub needs to deploy to Azure. Two problems, two rungs. Inside Azure, the web app gets a managed identity and is granted the narrow roles it needs — read this storage, read those secrets — so the application holds no connection string and no key, only an identity the platform vouches for at runtime.
For the pipeline, which lives in GitHub and cannot use a managed identity, you choose workload identity federation: Entra ID is configured to trust tokens from Campux's specific repository, and the pipeline deploys with no secret stored anywhere in GitHub. This is the promise from Class One kept — the eleven-at-night key never exists to be committed — and the seed of the full GitOps pipeline you will build in Class Twenty-Three. Six people, real automation, and not one long-lived secret to lose.
That closes Phase One. Campux now has an identity model, a hierarchy, governance, and machines that prove themselves without secrets — a governed subscription ready for the first build.
The official module, and a CAMPUX overview
Implement managed identities
learn.microsoft.com/training/modules/implement-managed-identities/
Federation depth: Workload identity federation
learn.microsoft.com/entra/workload-id/workload-identity-federation
A short walkthrough of managed identity versus a stored secret, and the hygiene ladder, will live here. Video to be added.
Make an identity with no secret to hold
You will create a user-assigned managed identity, grant it a role at a scope, and see for yourself that there is no credential to copy — then contrast it with a service principal, which hands you a password on creation. The whole hygiene ladder in two commands.
Open Cloud Shell — the >_ icon in the top bar — and choose Bash. (Locally: install the Azure CLI and run az login.)
Create a throwaway group and a user-assigned managed identity:
az group create --name rg-campux-lab --location westeurope az identity create --name id-campux-app --resource-group rg-campux-lab
What just happened: an identity now exists in Entra ID — but notice the command returned no password and no secret. There is nothing for you to store, because Azure holds and rotates the credential itself.Grant it the narrow role it needs — read blobs — at the resource-group scope:
PID=$(az identity show --name id-campux-app \ --resource-group rg-campux-lab --query principalId -o tsv) RG=$(az group show --name rg-campux-lab --query id -o tsv) az role assignment create --assignee "$PID" \ --role "Storage Blob Data Reader" --scope "$RG"
On screen: a managed identity is still a principal that holds RBAC roles at a scope — everything from Class Eight applies. Removing the secret changed how it authenticates, not what it is allowed to do.Now see the rung below it. A service principal with a secret does hand you a credential — the treadmill:
az ad sp create-for-rbac --name sp-campux-demo
The lesson: this prints a password to your terminal — a long-lived secret you must now store, protect, and rotate before it expires. The managed identity above printed none. That difference is the whole ladder. (Delete this demo principal: az ad sp delete --id <appId>.)Tear down:
az group delete --name rg-campux-lab --yes --no-wait
Create a managed identity by hand
Build the user-assigned identity in the portal, then grant it a role — and note, as you go, that nowhere are you ever shown a secret to copy. Use a throwaway resource group.
Go to portal.azure.com and sign in.
In the search bar at the top, type Managed Identities, select it, then click + Create.On screen: this creates a user-assigned managed identity — a standalone identity you can attach to many resources. The system-assigned kind is created instead from a resource's own Identity blade, which is step 4.
On Basics, choose your subscription and a throwaway resource group, name it id-campux-app, pick a region, and create.On screen: there is no credentials tab in this flow and never will be. Compare that to registering an app, where you would be creating a client secret to safeguard.
See the other kind: open any App Service or VM you have (or skip if none), select its Identity blade, and look at the System assigned toggle.On screen: flipping this On mints an identity bound to that one resource's lifecycle — deleted automatically when the resource is. That is system-assigned versus the standalone user-assigned one you just made.
Grant the identity a role: open a resource group, Access control (IAM) → Add role assignment → choose a read role → assign it to Managed identity → your id-campux-app.On screen: the member picker has a "Managed identity" category — proof it is a first-class principal, governed by the same RBAC as any human.
Tear down: delete the throwaway resource group.
Portal wording drifts; if a label here does not match your screen, the anchors are the Managed Identities service, a resource's Identity blade, and Access control (IAM).
Read an identity that has nothing to leak
Open the identity you built and read the blades that prove why it sits at the top of the hygiene ladder.
Open your managed identity and read its Overview: a client ID, an object (principal) ID, and a subscription — but no secret, anywhere.On screen: identifiers are not credentials. A client ID is safe to put in config; there is simply no password here to be leaked, which is the entire point.
Open its Azure role assignments blade.On screen: every role this identity holds and where — the same principal-plus-role-plus-scope from Class Eight, now for a non-human actor.
Open its Federated credentials blade.On screen: this is where an outside workload — a GitHub Actions pipeline — is trusted by issuer and subject, with no secret stored on either side. It is the workload identity federation the class named, and the seed of Class Twenty-Three.
Deleting the secret, not rotating it
A scanner flags a database password committed to Git eight months ago. Instead of just rotating it, you delete the whole class of problem: the app gets a managed identity, the connection string loses its password, and there is nothing left in the repo to leak. The incident review asks how this cannot happen again, and your answer is the strongest one there is — there is no secret anymore.
Examination
Four drills, then two situations. The situations have no marking scheme — write your answer before you reveal the reasoning, or the exercise is worthless. Nothing is stored; this is between you and the page.
B. The whole point is that the credential leaves your hands: Azure creates and rotates it, so there is nothing to paste, forget, or commit. A is false and would be alarming. C is the trap — managed identity works only inside Azure; for outside workloads you need federation, the next rung down. D is also wrong: a managed identity still needs roles granted at a scope, exactly as Class Eight taught. It removes the secret, not the authorization.
A. System-assigned is bound one-to-one to its resource and cleaned up with it — no orphan left behind. B describes user-assigned, the standalone identity you attach to many resources and provision ahead of them. C is invented: neither exposes a secret to you, which is the entire family's virtue. Choose system-assigned for a resource that should own and clean up its identity; user-assigned when several must share one or it must exist first.
Both managed identities and federation. All three replace a stored credential with something the platform vouches for at runtime — the top of the ladder. The two wrong answers are stored secrets by another name: a client secret in app settings and an account key in config are the same leak waiting to happen, differing only in what they expose. If you ticked either, re-read the ladder — they are the rung with the red cross through it, or the one just above it.
app configuration — campux storefront
1. AZURE_CLIENT_ID = <managed identity client id>
2. KEY_VAULT_URI = https://campux-kv.vault.azure.net/
3. STORAGE_ACCOUNT = campuxstore
4. STORAGE_KEY = k8Hb9x... # account key, in the config file
Line four. A storage account key is a long-lived secret that grants full control of the storage account, and here it is sitting in plaintext in a config file — the bottom rung of the ladder. Lines one to three are safe to see: a client id, a vault URI and an account name are identifiers, not credentials. The presence of line one is the tell that this is doubly wrong — the app already has a managed identity, so it should reach storage with that identity and RBAC, and the key should not exist in config at all.
Consider the consequence. That file lands in source control, and the key is now in the history forever, readable by anyone who ever clones the repository — the Class One breach, reproduced in four lines. The fix is to delete line four, grant the managed identity the Storage Blob Data role, and let the platform issue tokens at runtime. Catching this in review is exactly the job.
The secret is burned the instant it is pushed, so the order starts there. One: revoke and rotate the secret now — in Entra ID, delete the exposed credential and issue a new one for the places that legitimately need it. Assume it is already copied; a public or widely-cloned repo means the clock started at the push. Everything else is secondary to killing the live credential.
Two: assess the blast radius. What could that secret reach — which roles, which scope? Check sign-in and activity logs for any use you did not expect between the push and the revoke. You are answering "was it used," not just "was it exposed."
Three: remove the secret from the repository and its history — not just a new commit deleting the line, which leaves it in history, but a history rewrite or the platform's secret-removal path. Four: prevent recurrence — turn on push protection and secret scanning, and move the workload off stored secrets entirely to a managed identity or federation. Note the order's logic: rotate first because it is the only step that stops the bleeding; cleanup and prevention matter, but a secret still live while you tidy history is a secret still being exploited.
Concede the small truth, then widen the frame. Yes, the secret works today — "works" was never the question. The question is what it costs over the life of the app: a credential someone must rotate before it expires, protect from leaking, and remember exists. Managed identity is not overkill; it is less work, because it deletes all three of those obligations at once.
Name the specific risk this specific choice creates. A secret in app settings is one screenshot, one support export, one careless commit away from exposure, and it grants whatever the service principal can reach. The app runs inside Azure, so it can use a managed identity — the top of the ladder is available for free, and refusing it keeps a liability for no benefit.
Make the fix sound as easy as it is. Enable a system-assigned managed identity on the web app, grant it the narrow storage role it needs, and delete the secret from config. Same access, less code, nothing to rotate, nothing to leak. "Overkill" is doing more work to be less safe — the change is doing less work to be more safe, which is the easiest argument you will ever make.
Five things worth carrying out of this class
- Machines need identities too. Every pipeline, app and script is a non-human actor in Entra ID that must prove itself to act.
- A service principal authenticates with a secret or certificate — a credential you must hold, protect, and rotate. That is the treadmill.
- A managed identity is a service principal whose credential Azure creates and rotates for you. System-assigned dies with its resource; user-assigned is standalone.
- The hygiene ladder: managed identity, then federation, then certificate, then secret, and never a secret in source code.
- Workload identity federation gives outside workloads a scoped, short-lived trust instead of a stored secret. It is the phrase to know.
- The short-lived tokens in a federated flow are typically valid for a matter of minutes to a few hours, against a stored secret's months. Treat the exact lifetimes as configuration that changes; the durable point is the difference in kind — a leaked token expires on its own, a leaked secret does not. Class Twenty-Three builds the full GitHub-to-Azure federation step by step. ↩
- Not every workload can reach the top rung yet — some services and some third-party systems still require a secret or key, and legacy integrations lag. When you genuinely must use one, store it in a secrets manager such as Key Vault rather than in config, give it the shortest sensible expiry, and treat its rotation as scheduled work, not a someday. The ladder is a target, not a purity test. ↩
- "Managed identity is still a service principal underneath" is worth holding onto: it means everything from Class Eight applies unchanged — a managed identity holds RBAC roles at a scope and is governed by policy exactly like any other principal. Removing the secret changes how it authenticates, not what it is authorized to do. ↩