Four values unlock programmatic access to Azure: the subscription ID (which subscription to bill and manage), the tenant ID (which Microsoft Entra directory you authenticate against), and — for an app or script acting as itself — a client ID and client secret from an app registration. This guide shows where each one lives in the portal and the one-line CLI command for it, then how the four fit together in code.
Naming note: Azure Active Directory is now Microsoft Entra ID. Older docs and screenshots say "Azure AD" — same service, new name.
Get the subscription ID
The subscription is the billing and management boundary; its ID is a GUID you'll pass to SDKs and az commands.
- Log in to the Azure Portal.
- Search Subscriptions and open the list.
- Copy the Subscription ID of the subscription you need.

CLI:
az account show --query id --output tsv
az account list --output table # all subscriptions you can see
Get the tenant ID
The tenant is your Microsoft Entra directory — where users, groups, and app registrations live.
- In the portal, search Microsoft Entra ID.
- The Overview page shows the Tenant ID directly; with multiple tenants, Manage tenants lists them all.

CLI:
az account show --query tenantId --output tsv
Get a client ID and client secret (app registration)
When a script, CI pipeline, or server app needs to call Azure without a signed-in user, you register an application — a service principal — and authenticate with its client ID + secret.
- Microsoft Entra ID → App registrations → New registration. Name it (e.g.
geekstore-automation) and register. - The app's Overview shows the Application (client) ID — copy it. (The Directory (tenant) ID is repeated here too.)

- Certificates & secrets → New client secret. Set an expiry (6–12 months; shorter is safer) and add.
- Copy the secret Value immediately — it is shown once and never again. Store it in a secret manager, not in code or a repo.

- Give the principal permissions with an RBAC role — nothing works until it has one:
az role assignment create \
--assignee <client-id> \
--role Contributor \
--scope /subscriptions/<subscription-id>/resourceGroups/geekstore-rg
Scope as narrowly as you can — one resource group beats the whole subscription.
The CLI shortcut that does registration + secret + role in one step:
az ad sp create-for-rbac \
--name geekstore-automation \
--role Contributor \
--scopes /subscriptions/<subscription-id>/resourceGroups/geekstore-rg
Its JSON output contains appId (client ID), password (client secret), and tenant — everything below.
Using the four values
Modern Azure SDKs read them from environment variables via DefaultAzureCredential:
export AZURE_TENANT_ID=<tenant-id>
export AZURE_CLIENT_ID=<client-id>
export AZURE_CLIENT_SECRET=<client-secret>
var client = new ArmClient(new DefaultAzureCredential());
var subscription = await client.GetSubscriptionResource(
SubscriptionResource.CreateResourceIdentifier("<subscription-id>")).GetAsync();
The same credential chain also picks up az login on your laptop and managed identity in Azure — so the code with no secrets at all is identical everywhere. See it in a full example in creating an Azure VM from C#.
Secrets, certificates, or federated credentials?
The client secret is only one of three ways an app registration can authenticate — and the weakest:
- Client secret — a password. Simple, works everywhere, leaks like a password, expires and takes production with it if unrotated.
- Certificate — upload a public key to Certificates & secrets; the app signs with the private key. Stronger, and required by some org policies.
DefaultAzureCredentialpicks it up viaAZURE_CLIENT_CERTIFICATE_PATH. - Federated credentials (OIDC) — no stored secret at all. The app registration trusts tokens from an external issuer — this is how GitHub Actions deploys to Azure without any secret in the repo:
# .github/workflows/deploy.yml
permissions:
id-token: write
steps:
- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
Configure the trust under Certificates & secrets → Federated credentials (issuer: GitHub, subject: your repo + branch). Those three IDs are not secrets — the token exchange is the credential. For new CI/CD, start here and skip client secrets entirely.
Auditing and rotating what you have
# every credential on an app registration, with expiry dates
az ad app credential list --id <client-id> -o table
# rotate: create the new secret first, deploy it, then delete the old
az ad app credential reset --id <client-id> --append --years 1
az ad app credential delete --id <client-id> --key-id <old-key-id>
--append is the important flag — without it, reset replaces all secrets and everything using the old one breaks immediately. Store the value in Azure Key Vault or your platform's secret store; the portal will never show it again, and neither should your git history.
The errors you'll actually hit
- AADSTS7000215 invalid client secret — the secret expired, or you pasted the secret's ID instead of its Value (everyone does this once).
- AADSTS700016 application not found in directory — client ID and tenant ID are from different tenants.
- AuthorizationFailed on an API call — authentication worked, but the principal has no RBAC role on the target scope; assign one as shown above.
Good hygiene
- Prefer managed identity over client secrets when the caller runs inside Azure — no secret to rotate or leak.
- Set secret expirations short and calendar the rotation; an expired secret is the classic "everything broke at 2am" cause.
- One app registration per purpose, least-privilege role per registration — never share one god-principal across projects.
Comments (0)
No comments yet — be the first to share your thoughts.