Skip to content

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

  1. 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
  2. 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>'
  3. Register both secrets in secret-inventory.md
  4. Add the SPN to the registry table below — App ID and metadata only, no secret values
  5. Assign the minimum required role(s)
  6. 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-spn form from AB#21's acceptance criteria, not the full <org>-<env-name>-<purpose>-spn standard. Standards-alignment is a follow-on item.

Display nameApp IDVaultKV secret namesPurposeRole assignmentsExpiryNotes
hcs-platform-automation-spnf6c58fe8-b505-40e0-9131-c47edaa24b0ekv-hcs-vault-01hcs-platform-automation-spn-client-id / hcs-platform-automation-spn-secretPlatform automation (hybridsolutions.cloud)Owner @ root MG, Global Admin2027-05-11—
tl-platform-automation-spna7f4c522-7238-4668-8356-8d74d61ebee0kv-turnerlegacy-prodtl-platform-automation-spn-client-id / tl-platform-automation-spn-secretPlatform automation (turnerlegacy.org)Owner @ root MG, Global Admin2027-05-11—
azl-platform-automation-spn92cba745-be4d-45af-b2ae-f944aa976d13kv-azlmgmt-platformazl-platform-automation-spn-client-id / azl-platform-automation-spn-secretPlatform automation (azurelocal.cloud)Owner @ root MG, Global Admin2027-05-11—
tpdemos-platform-automation-spn4e87f1ef-f114-442a-9ab6-83dd74468cf2kv-demos-platformtpdemos-platform-automation-spn-client-id / tpdemos-platform-automation-spn-secretPlatform automation (tierpointdemos.cloud)Owner @ root MG, Global Admin2027-05-11Vault in sub 5e04c7f2
tppoc-platform-automation-spn1c34b4bf-c86d-4ffc-882c-bfe1ef9396d9kv-tppoc-platformtppoc-platform-automation-spn-client-id / tppoc-platform-automation-spn-secretPlatform automation (tierpointpoc.cloud)Owner @ root MG, Global Admin2027-05-11—
tplabs-platform-automation-spnfc07570b-f97d-41c3-a1d9-1de106503084kv-tplabs-platformtplabs-platform-automation-spn-client-id / tplabs-platform-automation-spn-secretPlatform automation (tierpointproductlabs.cloud)Owner @ root MG, Global Admin2027-05-11Vault in sub 2caa0b8a
phx-platform-automation-spne1953212-8a75-49ee-a031-3f7225560cd7kv-phx-platformphx-platform-automation-spn-client-id / phx-platform-automation-spn-secretPlatform automation (projectphx.cloud)Owner @ root MG, Global Admin2027-05-11Vault in sub 2f075d7e
ptlmgmt-platform-automation-spn31fc0219-15a3-4022-a6f7-f4f82deade95kv-ptlmgmt-platformptlmgmt-platform-automation-spn-client-id / ptlmgmt-platform-automation-spn-secretPlatform automation (prodtechlabmgmt.com)Owner @ root MG, Global Admin2027-05-11—
42-platform-automation-spn3a8f6c9a-be3e-48eb-92c4-e12375424c34kv-hcs-vault-0142-platform-automation-spn-client-id / 42-platform-automation-spn-secretPlatform automation (project-42.dev, tenant 76825617-409d-4777-8986-e464f35bbcd9)Global Admin only2028-07-27Entra-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

  1. Generate a new credential: az ad sp credential reset --id <app-id>
  2. Update the secret in KV (KV versions automatically — same name)
  3. Update the Expiry date in the registry table above
  4. Pipelines pulling from a Variable Group pick up the new value automatically on next run
  5. 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 Reader not Contributor
  • 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

Copyright © Hybrid Cloud Solutions LLC — Kristopher Turner