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.

referencesveteran-patterns.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Identity Veteran Patterns

Hard-won managed identity and workload identity patterns.

Workload Identity Federation: the #1 silent failure

The ordering trap

When setting up Workload Identity on AKS or ACA, the sequence matters:

1. Enable OIDC issuer on cluster
2. Create managed identity
3. Create federated credential (subject = system:serviceaccount:<ns>:<sa>)
4. Annotate service account with client-id
5. Label service account with use=true
6. Deploy workload (pod picks up identity)

Common mistakes:

Mistake Symptom Fix
Annotate SA before OIDC enabled Pod gets kubelet identity Enable OIDC issuer first
Wrong SA name in federated credential AADSTS700213: No matching federated identity Verify system:serviceaccount:<ns>:<sa> matches exactly
Missing azure.workload.identity/use=true label Pod doesn't get workload identity env vars Add label to SA
SA doesn't exist when creating FIC NotFound error Create SA first (e.g., deploy a pod that uses it)

The AKS per-resource SA gotcha

Drasi and other operators create per-resource service accounts:

  • source.<name> (not default)
  • reaction.<name> (not default)

The federated credential subject must match the actual SA name, not default.

# Verify SA exists before creating FIC
kubectl get sa -n <namespace> source.<name> -o yaml

# Then create FIC with exact subject
az identity federated-credential create \
  --name <name> \
  --identity-name <mi-name> \
  --resource-group <rg> \
  --issuer <oidc-issuer-url> \
  --subject "system:serviceaccount:<namespace>:source.<name>"

Managed identity assignment order

Wrong order (fails silently)

# Pod starts before MI is assigned → gets kubelet identity
kubectl apply -f pod.yaml
az identity assign --name my-identity --resource-group my-rg --scope <scope>

Right order

# 1. Create identity
az identity create --name my-identity --resource-group my-rg

# 2. Assign roles
az role assignment create --assignee <principal-id> --role "Key Vault Secrets User" --scope <vault>

# 3. Deploy workload (pod now gets correct identity)
kubectl apply -f pod.yaml

Credential rotation patterns

Entra ID secret rotation

# Create new secret
az ad app credential reset --id <app-id> --append --credential-description "new-secret"

# Verify new secret works
az account get-access-token --client-id <client-id> --client-secret <new-secret>

# Delete old secret (after verification window)
az ad app credential delete --id <app-id> --key-id <old-key-id>

Managed identity doesn't rotate (that's the point)

Managed identities eliminate credential rotation entirely:

  • System-assigned: lifecycle tied to resource
  • User-assigned: lifecycle independent, but no secret to rotate
  • Prefer user-assigned when the identity must be shared across resources or survive resource recreation; for single-resource workloads, follow the default decision table in SKILL.md (system-assigned)

SQL connection patterns

Azure SQL + Entra ID

// BAD: SQL auth (connection string with password)
var conn = new SqlConnection("Server=...;Database=...;User Id=...;Password=...");

// GOOD: Managed identity (no password in connection string)
var conn = new SqlConnection("Server=...;Database=...;Authentication=Active Directory Managed Identity;");
conn.AccessToken = await credential.GetTokenAsync(new TokenRequestContext(
    new[] { "https://database.windows.net/.default" }));

Connection pooling with identity

// Connection pool key includes access token
// When token expires, old connections are invalid
// Solution: Refresh token before expiry

private static async Task<string> GetAccessTokenAsync()
{
    var token = await credential.GetTokenAsync(new TokenRequestContext(
        new[] { "https://database.windows.net/.default" }));
    return token.Token;
}

GitHub Actions OIDC

The token audience gotcha

# WRONG: Missing audience — token rejected by Azure
- uses: azure/login@v3
  with:
    client-id: ${{ vars.AZURE_CLIENT_ID }}
    tenant-id: ${{ vars.AZURE_TENANT_ID }}
    subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

# RIGHT: Explicit audience
- uses: azure/login@v3
  with:
    client-id: ${{ vars.AZURE_CLIENT_ID }}
    tenant-id: ${{ vars.AZURE_TENANT_ID }}
    subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
    audience: api://AzureADTokenExchange

Federated credential subject for GitHub Actions

# Repo-level
repo:<owner>/<repo>:ref:refs/heads/<branch>

# PR-level
repo:<owner>/<repo>:pull_request

# Environment-level
repo:<owner>/<repo>:environment:<env-name>

Rule: Scope the subject as narrowly as possible. repo:<owner>/<repo>:ref:refs/heads/main is better than repo:<owner>/<repo>:.

Azure DevOps WIF issuer deprecation (July 2026)

Breaking change: Azure DevOps issuer (https://vstoken.dev.azure.com) is deprecated as of July 1, 2026. Retirement: July 1, 2027. Microsoft Entra issuer (https://login.microsoftonline.com/) is now the standard.

Migration:

  1. New service connections already use Microsoft Entra issuer by default
  2. Existing connections: Azure DevOps shows warning in pipeline runs and config UI
  3. Convert via the "Update" button in service connection settings
  4. No downtime during conversion, existing credential continues working until new one is verified

Scope: Only applies to single-tenant Entra applications in Azure public cloud. Multi-tenant apps and non-public clouds are excluded.

azure-workload-identity v1.6.0 token path change (June 2026)

Breaking change: Projected service account token mount path moved:

Before After
/var/run/secrets/azure/tokens /var/run/secrets/azure/wi/token
Full path: .../tokens/azure-identity-token Full path: .../wi/token/azure-identity-token

Applications using DefaultAzureCredential / WorkloadIdentityCredential work without changes, they read AZURE_FEDERATED_TOKEN_FILE which is updated by the webhook.

Workaround for apps that hardcode the old path:

initContainers:
- name: symlink-token
  image: busybox:latest
  command: ["sh", "-c", "mkdir -p /var/run/secrets/azure/tokens && ln -sf /var/run/secrets/azure/wi/token/azure-identity-token /var/run/secrets/azure/tokens/azure-identity-token"]
  volumeMounts:
  - { name: azure-tokens-compat, mountPath: /var/run/secrets/azure/tokens }

Rule: Never hardcode the token file path. Always read AZURE_FEDERATED_TOKEN_FILE, the webhook owns this path and may change it again.

Key Vault access patterns

RBAC vs access policies

Mode When to use Limitation
RBAC (recommended) New deployments, least-privilege Requires role assignments per vault
Access policies (legacy) Existing deployments Coarse-grained, hard to audit
# RBAC: Grant Key Vault Secrets User role
az role assignment create \
  --assignee <principal-id> \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault>

Secret naming conventions

# Use structured names for discoverability
database/connection-string
database/password
api/stripe-secret-key
certificates/tls-cert

# Use labels for environment separation
database/connection-string#prod
database/connection-string#dev

References

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