All skills
lukemurraynz avatar

/identity-managed-identity

@2cc2455

Azure service-to-service (workload) identity: managed identity, user-assigned identity, Azure RBAC, passwordless Azure SDK connections, AKS workload identity, Azure DevOps Workload Identity Federation, GitHub Actions OIDC to Azure, and federated credential troubleshooting. Use when the user says "use managed identity", "passwordless Azure auth", "remove connection strings or keys", "federated credential", "workload identity federation", "GitHub Actions OIDC to Azure", "AKS workload identity", or "DefaultAzureCredential". Do NOT use for human sign-in, MFA, Conditional Access, or B2C / External ID consumer login ; use a human-identity (Entra) skill instead.

Use this Skill: https://skilld.dev/gh/lukemurraynz/hve-agent-skills/identity-managed-identity

This session only. Nothing lands on disk.

SKILL.md

≈173 tokens always: the name and description. ≈8.9k when used: this file. ≈20k more on demand in 17 files.

Identity & Managed Identity Skill

Use When

Condition Detail
Should trigger User needs Azure service-to-service authentication with managed identity, workload identity federation, Azure RBAC, or passwordless Azure SDK access.
Should NOT trigger User is designing human sign-in, MFA, Conditional Access, delegated Graph permissions, or app-layer authorization without an Azure workload identity problem.
Nearby skill collision Use entra-id-cac, entra-external-id, or api-security-review when the main task is human identity, client auth, or API auth design rather than Azure workload identity.

Anti-Hallucination Rule (MANDATORY before writing managed identity or federated credential config)

Azure managed identity spans system-assigned vs user-assigned MI, AKS workload identity (federated credential with OIDC issuer), GitHub Actions OIDC, Azure DevOps Workload Identity Federation, Azure.Identity credential types per SDK language (.NET DefaultAzureCredential vs WorkloadIdentityCredential vs ManagedIdentityCredential; Python DefaultAzureCredential vs WorkloadIdentityCredential), and federated credential subject string formats (one format for GitHub Actions repo:org/repo:ref:refs/heads/main vs AKS system:serviceaccount:<ns>:<sa> vs Azure DevOps sc://<org>/<project>/<service-connection>). Wrong federated credential subject silently rejects tokens with confusing errors.

Common LLM hallucination traps (community-verified)

These are patterns that LLMs frequently get wrong when generating identity code.

LLM trap: Custom token caching

# WRONG - LLMs create manual token caching
import time
token_cache = {}
def get_token():
    if "token" in token_cache and token_cache["expires"] > time.time():
        return token_cache["token"]
    token = fetch_token_from_api()
    token_cache["token"] = token
    token_cache["expires"] = time.time() + 3600
    return token

# CORRECT - use DefaultAzureCredential (handles caching automatically)
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
token = credential.get_token("https://management.azure.com/.default")

LLM trap: Connection string auth

# WRONG - using connection strings instead of managed identity
connection_string = "DefaultEndpointsProtocol=https;AccountName=..."

# CORRECT - use managed identity
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

client = BlobServiceClient(
    account_url=f"https://{account_name}.blob.core.windows.net",
    credential=DefaultAzureCredential()
)

LLM trap: Custom credential chains

# WRONG - building custom auth chains
def get_credential():
    if os.getenv("AZURE_CLIENT_ID"):
        return ServicePrincipalCredential(...)
    elif os.getenv("MSI_ENDPOINT"):
        return ManagedIdentityCredential(...)
    else:
        return AzureCliCredential()

# CORRECT - use DefaultAzureCredential (handles all cases)
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()

LLM trap: Federated credential subject format

# WRONG - using wrong subject format for GitHub Actions
subject = "github-actions"  # Too vague

# CORRECT - use the exact subject format
subject = f"repo:{org}/{repo}:ref:refs/heads/{branch}"
# For AKS: subject = f"system:serviceaccount:{namespace}:{serviceAccount}"
# For Azure DevOps: subject = f"sc://{org}/{project}/{serviceConnection}"

Before writing any managed identity assignment, federated credential subject, Azure.Identity credential type, or workload identity service account annotation, ground each identifier using one of these methods:

  1. Federated credential subject format: GitHub Actions repo:<org>/<repo>:ref:refs/heads/<branch> or repo:<org>/<repo>:pull_request or repo:<org>/<repo>:environment:<env>. AKS workload identity: system:serviceaccount:<namespace>:<service-account-name>. Azure DevOps: sc://<org>/<project>/<service-connection-name>. Verify each format against the issuer's docs.
  2. OIDC issuer: GitHub https://token.actions.githubusercontent.com; Azure DevOps legacy issuer https://vstoken.dev.azure.com/<organization-id> (the path segment is the organization ID, not the tenant ID), newer service connections instead use the Entra issuer https://login.microsoftonline.com/<tenant-id>/v2.0 with subject <entra-prefix>/sc/<org-id>/<service-connection-id>; AKS cluster az aks show --query oidcIssuerProfile.issuerUrl. Issuer URL must match exactly. Read the actual value from the service connection / federated credential rather than assuming the shape.
  3. Azure.Identity credential type: DefaultAzureCredential is convenient but probes many sources; use specific credential types in production (WorkloadIdentityCredential, ManagedIdentityCredential with explicit clientId).
  4. RBAC role assignment: az role assignment create --assignee <principal-id> --role "<role-name>" --scope <resource-id> against the live target; verify role display name resolves to a definition.
  5. Propagation delay (two separate clocks): a newly created managed identity (its service principal becoming visible in Microsoft Entra) and a newly created role assignment becoming effective on the resource data plane are distinct delays. Role assignments are usually effective within ~5 minutes and can take up to ~30 minutes in the worst case. Do not run smoke tests immediately after deploy; gate first access behind bounded retry (see references/troubleshooting.md).

Forbidden shortcuts:

  • Do not write federated credential subject from memory; format is issuer-specific and exact-match.
  • Do not assume DefaultAzureCredential chain order or composition; the chain has expanded over time. The .NET chain now contains ten credentials including a Broker credential (the VS Code credential requires the Azure.Identity.Broker package) and AzureDeveloperCliCredential; Python azure-identity >= 1.14.0 no longer halts the chain when a developer credential fails to get a token. Pin Azure.Identity / azure-identity version before reasoning about chain order, and use WorkloadIdentityCredential explicitly in pod-identity scenarios.
  • Do not assume the same federated credential works across GitHub branches; subject must match exactly (or use environments / pull_request patterns deliberately).
  • Do not write AKS workload identity without enabling the AKS feature (--enable-workload-identity --enable-oidc-issuer). It is enabled at cluster creation, or in place on an existing cluster with az aks update --enable-oidc-issuer --enable-workload-identity - recreation is not required. Existing pods must be restarted to pick up the injected token after enablement.
  • Do not write client_id of a user-assigned MI from memory; resolve at deploy time.

Known asymmetry traps (illustrative, verify each):

  • Federated credential audience is api://AzureADTokenExchange (default), but some scenarios require a specific audience.
  • AKS workload identity requires service account annotation azure.workload.identity/client-id: <client-id> AND pod label azure.workload.identity/use: "true" - missing either silently falls back to AKS kubelet identity.
  • GitHub Actions subject for branch main is repo:org/repo:ref:refs/heads/main but for tag is repo:org/repo:ref:refs/tags/v1 - different ref types.
  • Azure.Identity DefaultAzureCredential in production may fall back to Azure CLI credential if managed identity probe fails, silently authenticating as the deployer.
  • User-assigned MI vs system-assigned MI: system-assigned dies with the resource; user-assigned outlives it. Cannot convert one to the other.

Safe degraded output when verification is blocked

If you cannot read AKS OIDC issuer URL, check Azure.Identity installed version, or run az role assignment create, do not produce production-shaped managed identity / federated credential config from memory. Federated credential subject format mismatch is the most common silent failure.

Instead, return:

  1. A labelled skeleton with [VERIFY] markers on federated credential subject formats, OIDC issuer URLs, Azure.Identity credential type, role display names, AKS workload identity annotations/labels.
  2. Verification commands: GitHub OIDC subject docs URL, az aks show --query oidcIssuerProfile.issuerUrl, Azure DevOps subject format docs URL, Azure.Identity installed package version.
  3. Identifier guess list grouped by failure mode (federated credential subject mismatch / wrong OIDC issuer / DefaultAzureCredential falls back to deployer identity / AKS workload identity not enabled / system vs user-assigned MI confusion).
  4. A design-level artifact offer: identity inventory (which workload needs which credential type), federated credential subject inventory per workflow/environment, role assignment matrix, OIDC issuer plan, propagation/test plan.

Do not produce speculative managed identity config under blocked verification. Subject-format mismatches and DefaultAzureCredential silent fallback to wrong identity are dominant pains.

Non-negotiable rule

For new Azure projects, use managed identity or workload identity federation for service-to-service authentication. Do not introduce application secrets, shared keys, SAS tokens, publish profiles, certificate passwords, or connection strings with embedded credentials unless the target service has no supported identity-based alternative and the exception is explicitly documented with an owner, expiry date, and migration path.

Use this skill when

  • Replacing secrets, storage keys, SAS tokens, connection strings, or service principal secrets with Azure managed identity or federated identity.
  • Configuring Azure compute to access Azure resources using ManagedIdentityCredential, WorkloadIdentityCredential, or a constrained credential chain.
  • Assigning Azure RBAC or service-native data-plane permissions for application identities.
  • Configuring AKS workload identity, Azure DevOps Workload Identity Federation, or GitHub Actions OIDC to Azure.
  • Troubleshooting identity, role assignment, token exchange, or federated credential failures.
  • Granting the deploying user or service principal access to provisioned resources (AKS cluster admin, PostgreSQL admin, Key Vault access policy) before those resources are needed.
  • Capturing the deploying identity in pre-provision hooks for use in Bicep/Terraform role assignments.

When NOT to Use

  • Designing human sign-in, Microsoft Entra External ID/B2C, MFA, Conditional Access, consent, delegated Graph permissions, or UI login flows. For human identity, use entra-id-cac or entra-external-id instead.
  • Designing business/application authorization models such as personas, entitlements, row-level security, or app roles, unless the implementation also requires Azure workload identity. For API authorization design, use api-security-review instead.
  • Solving non-Azure identity federation unless Azure is the relying party or target resource.

First response behaviour

Before producing code, role assignments, or remediation steps, determine and state:

  1. Runtime: App Service, Function App, Container Apps, VM, AKS, Azure DevOps, GitHub Actions, local dev, or other.
  2. Effective identity: system-assigned managed identity, user-assigned managed identity, workload identity, service connection identity, or app registration.
  3. Target resource and operation: for example, read secret, send message, receive event, read blob, write blob, connect to SQL, deploy infrastructure.
  4. Permission model: Azure RBAC, service-native RBAC, SQL contained database user, or a combination.
  5. Local auth posture: whether shared-key/SAS/local auth can be disabled at creation time.
  6. Verification plan: commands, tests, logs, and expected success/failure signals.

If any item is unknown, make a safe assumption, label it, and continue with a reversible pattern.

Default decisions for new projects

Decision area Default
Workload on one Azure resource System-assigned managed identity
Multiple resources share the same access or blue/green slots need stable identity User-assigned managed identity
AKS pod access to Azure resources Microsoft Entra Workload ID with a user-assigned managed identity
Azure Bot Service for a Teams / M365 agent (incl. published Foundry agents) User-assigned managed identity (single-tenant) instead of an App Registration where supported - avoids creating an app registration when org policy blocks it
CI/CD to Azure Workload Identity Federation/OIDC; no client secrets
Local development Developer tool credential chain only; never production secrets
Production Azure SDK credential Specific credential: ManagedIdentityCredential or WorkloadIdentityCredential
DefaultAzureCredential in production Only when intentionally constrained and documented
Permissions Narrowest data-plane permission at the narrowest practical scope
Local authentication Disabled at resource creation where supported
IaC Bicep/Terraform first; CLI for verification or one-off remediation only

Credential selection

Use deterministic credentials in production. Unrestricted DefaultAzureCredential is acceptable for local development because it improves developer experience, but it can hide which credential is actually used in production and may fall through to unexpected credential sources.

Environment Preferred credential
Local development ChainedTokenCredential containing only approved developer credentials, or DefaultAzureCredential with excludes if needed
App Service, Functions, Container Apps, VM, VMSS ManagedIdentityCredential
User-assigned managed identity ManagedIdentityCredential(ManagedIdentityId.FromUserAssignedClientId(clientId)) or equivalent language option
AKS workload identity WorkloadIdentityCredential, or a constrained chain that only allows workload identity
Azure DevOps pipeline code using Azure SDK Service connection + AzurePipelinesCredential or task-native Azure login, depending on SDK/task support
GitHub Actions to Azure GitHub OIDC + federated credential + azure/login; no AZURE_CLIENT_SECRET
Multi-language app implementation Use references/language-patterns.md for SDK-specific examples

Choosing between DefaultAzureCredential, ManagedIdentityCredential, and WorkloadIdentityCredential

The three differ by how the token is obtained, not just by environment. Pick by mechanism:

Credential How it gets a token Use when Do not use when
ManagedIdentityCredential Calls the Azure Instance Metadata Service (IMDS) token endpoint of the Azure-hosted compute Workload runs on App Service, Functions, Container Apps, VM/VMSS, or other compute with a directly assigned system- or user-assigned managed identity The token comes from a projected federated OIDC file (AKS), or the workload is not Azure-hosted compute
WorkloadIdentityCredential Exchanges a projected OIDC token (AZURE_FEDERATED_TOKEN_FILE) for a Microsoft Entra token via a federated credential AKS pods with Microsoft Entra Workload ID, or any federated / projected-token scenario Plain Azure-hosted compute with a directly assigned managed identity (use ManagedIdentityCredential instead)
DefaultAzureCredential Probes an ordered chain of credentials until one returns a token (env, managed identity, workload identity, developer tools, …) Local development convenience, where the same code should authenticate via whatever the developer is signed in with Production - it can silently fall through to the deployer's developer identity (see the Quality Gate). Use a specific credential, or a constrained, documented chain

Rule of thumb: in production, use the specific credential for the runtime (ManagedIdentityCredential for managed-identity compute, WorkloadIdentityCredential for federated/AKS). Reserve DefaultAzureCredential for local development or an intentionally narrowed, documented chain. For a self-selecting chain that works in both AKS and local dev without an environment check, see references/language-patterns.md.

Runtime binding rules

  • If AZURE_CLIENT_ID is set, treat the workload as intentionally bound to a user-assigned managed identity or federated workload identity.
  • If no user-assigned identity is configured, treat the workload as system-assigned.
  • If two or more user-assigned identities (or a user-assigned plus a system-assigned identity) are attached and AZURE_CLIENT_ID is not set, the identity is ambiguous - IMDS cannot disambiguate and may fail or return a token for the wrong identity. Treat this as a configuration error: bind explicitly with the client ID, do not assume it falls back to system-assigned.
  • Assign roles to the effective runtime principal, not to a similarly named app registration or another identity.
  • Reuse credential instances. Register one credential instance in dependency injection or as a long-lived singleton; do not instantiate credentials per request.
  • For direct token use outside Azure SDK clients, handle token lifetime and refresh. Prefer SDK clients that refresh tokens automatically.
using Azure.Core;
using Azure.Identity;
using Azure.Storage.Blobs;

// App Service / Container Apps / VM — system- or user-assigned managed identity.
TokenCredential credential = builder.Environment.IsDevelopment()
    ? new ChainedTokenCredential(
        new AzureDeveloperCliCredential(),  // azd auth login
        new VisualStudioCredential(),
        new AzureCliCredential(),
        new AzurePowerShellCredential())
    : new ManagedIdentityCredential();

builder.Services.AddSingleton(credential);
builder.Services.AddSingleton(_ => new BlobServiceClient(
    new Uri($"https://{storageAccountName}.blob.core.windows.net"),
    credential));

For AKS workload identity, use a self-selecting chain that does not require an environment check - see references/language-patterns.md for the full pattern.

Permission model selection

Target Permission model Reference
Storage Blob Azure RBAC data-plane roles standards/rbac-patterns.md
Key Vault Azure RBAC permission model + data-plane roles standards/rbac-patterns.md
Service Bus Azure RBAC data-plane roles + local auth disabled references/messaging.md
Event Hubs Azure RBAC data-plane roles + local auth disabled references/messaging.md
Cosmos DB for NoSQL Cosmos DB native data-plane RBAC; local auth disabled references/cosmos-db.md
Azure SQL Microsoft Entra authentication + contained database user + database roles references/azure-sql.md
AKS pods Microsoft Entra Workload ID references/aks-workload-identity.md
Azure DevOps pipelines Azure Resource Manager service connection with Workload Identity Federation references/azure-devops-wif.md
GitHub Actions GitHub OIDC federated credential + azure/login references/github-actions-oidc.md
Federated credential errors Issuer, subject, audience, tenant, and identity checks references/troubleshooting.md
Terraform implementation Identities, RBAC, and federated credentials references/terraform-patterns.md
Landing-zone guardrails Azure Policy and Resource Graph checks references/policy-guardrails.md

IaC-first patterns

System-assigned identity plus RBAC

param location string = resourceGroup().location
param app_name string
param key_vault_name string

var key_vault_secrets_user = '4633458b-17de-408a-b874-0445c86b69e6'

resource app 'Microsoft.Web/sites@2024-04-01' = {
  name: app_name
  location: location
  identity: {
    type: 'SystemAssigned'
  }
}

resource key_vault 'Microsoft.KeyVault/vaults@2026-02-01' existing = {
  name: key_vault_name
}

resource key_vault_role_assignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(key_vault.id, app.identity.principalId, key_vault_secrets_user)
  scope: key_vault
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', key_vault_secrets_user)
    principalId: app.identity.principalId
    principalType: 'ServicePrincipal'
  }
}

User-assigned identity plus RBAC

param location string = resourceGroup().location
param service_name string
param storage_account_name string

var storage_blob_data_contributor = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'

resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = {
  name: 'id-${service_name}'
  location: location
}

resource storage 'Microsoft.Storage/storageAccounts@2024-01-01' existing = {
  name: storage_account_name
}

resource blob_role_assignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(storage.id, identity.properties.principalId, storage_blob_data_contributor)
  scope: storage
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storage_blob_data_contributor)
    principalId: identity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

Deploying identity pattern

When infrastructure-as-code needs to grant the person or service principal running azd up (or terraform apply) access to a provisioned resource, capture the deploying identity before infrastructure creates the resource.

Pre-provision hook (powershell)

$principalId = az ad signed-in-user show --query id --output tsv
$accountType = az account show --query user.type --output tsv
$principalType = if ($accountType -eq 'servicePrincipal') { 'ServicePrincipal' } else { 'User' }
azd env set DEPLOYING_PRINCIPAL_ID $principalId
azd env set DEPLOYING_PRINCIPAL_TYPE $principalType

Bicep consumption

The captured values flow through main.parameters.json using ${AZURE_DEPLOYING_PRINCIPAL_ID} and ${AZURE_DEPLOYING_PRINCIPAL_TYPE} environment-var substitution, then into a role assignment:

param deployingPrincipalId string
param deployingPrincipalType string

var aks_cluster_admin_role = 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b'

resource aks_admin_role_assignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(aksCluster.id, deployingPrincipalId, aks_cluster_admin_role)
  scope: aksCluster
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', aks_cluster_admin_role)
    principalId: deployingPrincipalId
    principalType: deployingPrincipalType
  }
}

Common resources that need this pattern

Resource Typical role Notes
AKS cluster Azure Kubernetes Service RBAC Cluster Admin Required when disableLocalAccounts: true
PostgreSQL Flexible Server Azure Database for PostgreSQL Flexible Server Administrator Used for Entra admin bootstrap
Key Vault Key Vault Secrets User + Key Vault Certificate User For initial secret/cert management

Output requirements

When this skill is used, produce outputs in this order:

  1. Decision summary: chosen identity type, credential, permission model, and scope.
  2. Implementation: Bicep/Terraform first, then app code, then CLI verification commands.
  3. Validation: how to prove the identity is used, role assignments exist, local auth is disabled, and secret paths fail.
  4. Rollback: safe rollback that does not reintroduce secrets unless approved as a temporary exception.
  5. Risks and assumptions: RBAC propagation delay, tenant restrictions, unsupported SDKs, or local-auth limitations.

Guardrails

  • Never recommend Owner, Contributor, or management-plane roles for application data access.
  • Never assign data-plane roles at subscription scope for application workloads.
  • Never grant User Access Administrator, Role Based Access Control Administrator, or role-assignment permissions to application identities unless that is the application’s explicit purpose and a security review exists.
  • Never use listKeys, regenerateKey, connection-string outputs, SAS generation, or publish profiles in app paths for new projects.
  • Prefer separate identities per workload and environment. Do not share one deployment identity across unrelated apps or tenants.
  • Pin federated credential subjects tightly to the expected Azure DevOps service connection, GitHub repository/environment/branch, or Kubernetes service account.
  • For GitHub Actions, use environments and environment protection rules for production deployments.
  • For Azure DevOps, do not grant service connections to all pipelines by default; authorize only the pipelines that need the connection.
  • Confirm before disabling local authentication on an existing resource: verify no active consumer still depends on the key/SAS path, because the change takes effect immediately.

Ownership, approval, and blast radius

  • Name an owner for every role assignment and scope. Granting roles is itself a privileged action (User Access Administrator / Role Based Access Control Administrator) and needs an approver, least privilege must not quietly become "whoever ran the pipeline".
  • Document every temporary secret exception with owner, named approver, expiry, compensating controls, a review cadence at expiry, and a migration issue. A "temporary" secret with no approver and no review becomes permanent.
  • Treat blast radius explicitly: a shared, or subscription/resource-group-scoped, data-plane identity means every workload using it is compromised together. Rollback must be able to revoke one workload without breaking others.
  • Revoking a role does not invalidate already-issued access tokens. Expect up to ~24 hours (or until the app restarts and re-authenticates) before access actually stops, plan incident response and rollback around that lag.
  • For greenfield platforms, add Azure Policy or equivalent guardrails so insecure local-auth defaults cannot be recreated silently.

Validation helpers

Use these scripts when reviewing repositories or generated IaC:

  • scripts/scan-secrets.sh - scans files for obvious Azure secret and connection string patterns.
  • scripts/validate-identity-config.sh - checks common Azure resources for local-auth and managed-identity posture.
  • scripts/list-identity-rbac.sh - lists Azure RBAC assignments for a principal.

Common failure diagnostics

Symptom First checks
403 Forbidden from target resource Correct principal, correct scope, correct data-plane role, RBAC propagation delay
Azure SQL Login failed for user '<token-identified principal>' Missing contained database user or database role
Cosmos DB Forbidden with Entra auth Missing Cosmos native data-plane role assignment; Azure RBAC alone is insufficient for data access
Federated credential not found Issuer, subject, audience, tenant, identity client ID, renamed repo/project/service connection
Wrong identity used AZURE_CLIENT_ID, app settings, pod annotations, service connection, or credential chain fallback
Works locally but not in Azure Developer identity has permissions that workload identity lacks
Secret path still works Local auth/shared key not disabled or legacy secret still present

Resource Directories

Load these directories only when the selected workflow needs deeper examples, prompts, or reference material:

  • references/ - service- and platform-specific implementation patterns (SQL, Cosmos, messaging, AKS, GitHub Actions, Azure DevOps, Terraform, language SDKs, policy, troubleshooting).
  • standards/ - identity selection, RBAC patterns, and the review checklist.
  • actions/ - the step-by-step "configure identity" runbook.

Final Output Contract

Every engagement produces:

  • A clear identity decision summary covering runtime, identity type, credential choice, and permission model.
  • Concrete implementation guidance in the right order: IaC, app/runtime configuration, and verification commands.
  • A permission and scope plan showing which principal gets which access and why.
  • Validation, rollback, and risk notes that avoid reintroducing secrets as the default escape hatch.

Quality Gate

Do not mark this engagement complete until:

  • The chosen identity, target resource, and permission model are all explicit.
  • The guidance includes both implementation steps and verification steps.
  • Any issuer URLs, subject formats, role names, or SDK credential assumptions are verified or marked [VERIFY].
  • Production code uses a deterministic credential (ManagedIdentityCredential / WorkloadIdentityCredential), not an unconstrained DefaultAzureCredential. A startup check logs the resolved credential type and fails fast if it is not the expected managed identity (prevents silent fallback to the deployer's developer identity).

Review checklist

Use standards/checklist.md before considering the change complete.

References

Hidden API Surface Properties

The Microsoft.ManagedIdentity/userAssignedIdentities resource schema exposes properties absent from overview docs:

Resource Schema Property Impact
userAssignedIdentities/federatedIdentityCredentials properties.claimsMatchingExpression Flexible federated credential with wildcard subject matching (languageVersion + value) — available from 2025-01-31-preview — alternatives to fixed subject
userAssignedIdentities/federatedIdentityCredentials properties.audiences Must include api://AzureADTokenExchange — often forgotten
userAssignedIdentities/federatedIdentityCredentials Name constraint: 3-120 chars, ^[a-zA-Z0-9][a-zA-Z0-9-_]{2,119}$ Naming rule for federated credential resources
userAssignedIdentities properties.principalId (read-only) The AAD object ID — needed for RBAC role assignments referencing the identity
userAssignedIdentities properties.clientId (read-only) The AAD application ID — needed for SDK ManagedIdentityCredential
userAssignedIdentities properties.tenantId (read-only) The home tenant ID — important for cross-tenant scenarios
System-assigned identity identity.type: 'SystemAssigned' | 'UserAssigned' | 'SystemAssigned,UserAssigned' The Bicep identity block shape for enabling system-assigned managed identity on Azure resources
Federated credential subject format subject: 'system:serviceaccount:<ns>:<sa>' (AKS) or repo:<owner>:<repo>:ref:refs/heads/<branch> (GitHub) Required subject format varies by issuer type

Flexible federated credentials (Preview) — when a fixed subject is too rigid (for example, trusting all branches of one repo), use a claimsMatchingExpression instead. Constraints (per Entra docs, checked 2026-08-25): Preview; supported only for federated credentials on app registrations (not managed identities); supported issuers are GitHub, GitLab, and Terraform Cloud; operators are matches (wildcards */?), eq, and; GitHub expressions must match sub and at least one immutable claim (repository_id / repository_owner_id). Azure CLI/PowerShell/Terraform do not yet support creating them — use Microsoft Graph via az rest:

az rest --method post \
  --url "https://graph.microsoft.com/beta/applications/<app-object-id>/federatedIdentityCredentials" \
  --body "{'name': 'fic-flexible', 'issuer': 'https://token.actions.githubusercontent.com', 'audiences': ['api://AzureADTokenExchange'], 'claimsMatchingExpression': {'value': \"claims['sub'] matches 'repo:contoso/contoso-repo:ref:refs/heads/*' and claims['repository_id'] eq '456789'\", 'languageVersion': 1}}"

Currency and verification

  • Date checked: 2026-08-25.
  • Verify Azure API versions before final IaC: az provider show --namespace <namespace> --query "resourceTypes[?resourceType=='<type>'].apiVersions" -o tsv.
  • Verify Azure.Identity package behaviour against the version used by the repo, especially when constraining DefaultAzureCredential.
  • Prefer Microsoft Learn, GitHub Docs, SDK docs, and provider schemas over blog posts.

Azure DevOps Workload Identity Federation: creation order changed (verified live 2026-08-25)

Since ADO sprint 253, newly created WorkloadIdentityFederation service connections default to the Microsoft Entra issuer (https://login.microsoftonline.com/{tenant}/v2.0 with subject /eid1/c/pub/t/{b64(tenant)}/a/{b64(appId)}/sc/{orgGuid}/{endpointGuid}), NOT the classic https://vstoken.dev.azure.com/{orgGuid} + sc://{org}/{project}/{connectionName} pair. The Entra-flavour subject embeds the server-assigned endpoint id, so it is uncomputable before creation.

  1. Create the service connection FIRST, read back authorization.parameters.workloadIdentityFederationIssuer / ...Subject, then create the federated credential from those exact values (docs: "automate-service-connections").
  2. A brand-new SC/environment is a protected resource: pipeline runs park at Checkpoint.Authorization until you PATCH {org}/{project}/_apis/pipelines/pipelinepermissions/endpoint/{id} and /environment/{id} (api-version 7.2-preview.1, method PATCH, body {"pipelines":[{"id":N,"authorized":true}]})
    • PUT returns 405.
  3. Symptom of flavour mismatch: pipeline az login --service-principal --federated-token ... fails AADSTS700211: No matching federated identity record found.
  4. The well-known Azure DevOps resource id 499b84ac-1321-427f-aa17-42cd2cad8a26 may not exist in every tenant; use the resource id your tenant actually registered (some CSP/CDX tenants expose a different GUID) - probe before hardcoding.
  5. Multi-tenant apps: the vstoken issuer is deprecated (retirement 2027) but multi-tenant app registrations are explicitly excluded; single-tenant/MI targets should migrate to Entra issuer.

Source: SKILL.md on GitHub

No alerts8d3 checks · Risk SAFE
  • Gen Agent Trust Hub8d

    The skill is a professional toolset for managing Azure identities and promotes security best practices such as passwordless authentication. It includes utility scripts for diagnostic purposes. A low-risk surface for indirect prompt injection exists due to the processing of user-supplied identifiers into shell and cloud management commands.

  • Socket8d

    No alerts

  • Snyk8d

    Risk: LOW · No issues

Signed by skilld at 2cc2455. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated last month
cowork
{
  "category": "automation"
}
metadata
{
  "last_verified": "2026-08-25"
}
Other metadata
compatibility
Azure CLI, Bicep or Terraform, Azure Identity SDK, Microsoft Entra workload identity federation

README badge

README badge for lukemurraynz/hve-agent-skills/identity-managed-identity