Service principals
Registry of all Azure service principals (SPNs) used across HCS projects. App IDs and metadata are documented here. Client secrets are stored in Key Vault only — never here.
For KV secret naming, see Key Vault content standard. SPN secrets follow the pattern <org>-<env-name>-<purpose>-spn-secret and client IDs follow <org>-<env-name>-<purpose>-spn-client-id.
When to use a service principal
Use a service principal when:
- A pipeline or automation tool needs to authenticate to Azure from outside Azure (GitHub Actions, ADO pipelines, local scripts)
- Workload identity federation is not available for the scenario
Prefer managed identities for workloads running on Azure infrastructure — see managed-identities.md.
Entra-ID-only tenants (no Azure subscription): a managed identity is an ARM resource (Microsoft.ManagedIdentity/userAssignedIdentities) and must live inside an Azure subscription + resource group — it cannot exist in a tenant with zero subscriptions. For those tenants the SPN is the only identity object available, since an app registration/service principal is a pure Entra object with no ARM dependency. Invoke-TenantIdentitySetup.ps1 supports this: leave managementSubscriptionId, platformVault, vaultResourceGroup, identityResourceGroup, and miName empty in config/tenant-identities.json and the script skips subscription context, RG/vault creation, and MI creation, provisioning only the SPN. Store its credentials in spnVault pointed at the central kv-hcs-vault-01 rather than a per-tenant vault, since there's no subscription to host one. If a subscription is added to that tenant later, backfill the omitted fields and re-run the script to create the MI retroactively — see the 42 tenant entry in master-registry.db for the pattern.
Naming
Azure AD display name: <org>-<env-name>-<purpose>-spn
Examples: hcs-platform-ado-pipelines-spn, tp-tplabs-arc-registration-spn
KV secret names (stored in the appropriate vault per the KV standard):
- Client ID:
<org>-<env-name>-<purpose>-spn-client-id - Client secret:
<org>-<env-name>-<purpose>-spn-secret
How to register a new SPN
- Create the SPN in Azure:powershell
az ad sp create-for-rbac ` --name <org>-<env-name>-<purpose>-spn ` --role <role> ` --scopes /subscriptions/$env:AZURE_SUBSCRIPTION_ID - Store the client ID and secret in the correct vault immediately — clear terminal history after:powershell
az keyvault secret set --vault-name <vault> --name <org>-<env-name>-<purpose>-spn-client-id --value '<app-id>' az keyvault secret set --vault-name <vault> --name <org>-<env-name>-<purpose>-spn-secret --value '<client-secret>' - Register both secrets in secret-inventory.md
- Add the SPN to the registry table below — App ID and metadata only, no secret values
- Assign the minimum required role(s)
- Commit:
docs(identity): register SPN <display-name>
Registry
All platform SPNs have Owner at root management group and Global Admin (Entra direct assignment) in their respective tenant. Credentials stored in the per-tenant platform vault only. Created 2026-05-12 via Invoke-TenantIdentitySetup.ps1 (AB#21). Expiry set to 1 year from creation.
Exception: 42-platform-automation-spn (2026-07-26) has Global Admin only, no Owner @ root MG — its tenant has no Azure subscription, so no ARM scope exists to assign Owner at. Created via direct Microsoft Graph calls, not the script — Invoke-TenantIdentitySetup.ps1 has an unfixed tenant-context bug for subscription-less tenants (see script .NOTES/history). Credentials stored in the central kv-hcs-vault-01, not a per-tenant vault (none exists for this tenant).
Naming note: These use the shorter
<short>-platform-automation-spnform from AB#21's acceptance criteria, not the full<org>-<env-name>-<purpose>-spnstandard. Standards-alignment is a follow-on item.
| Display name | App ID | Vault | KV secret names | Purpose | Role assignments | Expiry | Notes |
|---|---|---|---|---|---|---|---|
hcs-platform-automation-spn | f6c58fe8-b505-40e0-9131-c47edaa24b0e | kv-hcs-vault-01 | hcs-platform-automation-spn-client-id / hcs-platform-automation-spn-secret | Platform automation (hybridsolutions.cloud) | Owner @ root MG, Global Admin | 2027-05-11 | — |
tl-platform-automation-spn | a7f4c522-7238-4668-8356-8d74d61ebee0 | kv-turnerlegacy-prod | tl-platform-automation-spn-client-id / tl-platform-automation-spn-secret | Platform automation (turnerlegacy.org) | Owner @ root MG, Global Admin | 2027-05-11 | — |
azl-platform-automation-spn | 92cba745-be4d-45af-b2ae-f944aa976d13 | kv-azlmgmt-platform | azl-platform-automation-spn-client-id / azl-platform-automation-spn-secret | Platform automation (azurelocal.cloud) | Owner @ root MG, Global Admin | 2027-05-11 | — |
tpdemos-platform-automation-spn | 4e87f1ef-f114-442a-9ab6-83dd74468cf2 | kv-demos-platform | tpdemos-platform-automation-spn-client-id / tpdemos-platform-automation-spn-secret | Platform automation (tierpointdemos.cloud) | Owner @ root MG, Global Admin | 2027-05-11 | Vault in sub 5e04c7f2 |
tppoc-platform-automation-spn | 1c34b4bf-c86d-4ffc-882c-bfe1ef9396d9 | kv-tppoc-platform | tppoc-platform-automation-spn-client-id / tppoc-platform-automation-spn-secret | Platform automation (tierpointpoc.cloud) | Owner @ root MG, Global Admin | 2027-05-11 | — |
tplabs-platform-automation-spn | fc07570b-f97d-41c3-a1d9-1de106503084 | kv-tplabs-platform | tplabs-platform-automation-spn-client-id / tplabs-platform-automation-spn-secret | Platform automation (tierpointproductlabs.cloud) | Owner @ root MG, Global Admin | 2027-05-11 | Vault in sub 2caa0b8a |
phx-platform-automation-spn | e1953212-8a75-49ee-a031-3f7225560cd7 | kv-phx-platform | phx-platform-automation-spn-client-id / phx-platform-automation-spn-secret | Platform automation (projectphx.cloud) | Owner @ root MG, Global Admin | 2027-05-11 | Vault in sub 2f075d7e |
ptlmgmt-platform-automation-spn | 31fc0219-15a3-4022-a6f7-f4f82deade95 | kv-ptlmgmt-platform | ptlmgmt-platform-automation-spn-client-id / ptlmgmt-platform-automation-spn-secret | Platform automation (prodtechlabmgmt.com) | Owner @ root MG, Global Admin | 2027-05-11 | — |
42-platform-automation-spn | 3a8f6c9a-be3e-48eb-92c4-e12375424c34 | kv-hcs-vault-01 | 42-platform-automation-spn-client-id / 42-platform-automation-spn-secret | Platform automation (project-42.dev, tenant 76825617-409d-4777-8986-e464f35bbcd9) | Global Admin only | 2028-07-27 | Entra-ID-only tenant, no subscription — no Owner @ root MG possible (no ARM scope), credentials stored centrally in kv-hcs-vault-01 (this tenant has no vault of its own) |
Client secret rotation
- Generate a new credential:
az ad sp credential reset --id <app-id> - Update the secret in KV (KV versions automatically — same name)
- Update the Expiry date in the registry table above
- Pipelines pulling from a Variable Group pick up the new value automatically on next run
- Commit:
docs(identity): rotate secret for <display-name>
Do not delete the old credential until the new one is confirmed working in all consumers.
Principles
- Least privilege — if a pipeline only reads resources, the SPN gets
ReadernotContributor - SPNs are never used for interactive login
- Workload identity federation is preferred over client secrets for GitHub Actions
- Client secrets expire — set expiry at creation and track it in the registry