Skip to main content

Azure Managed Identity (MSI)

The Azure MSI method authenticates agents using Azure Managed Identity. The agent fetches a managed identity access token from the Azure Instance Metadata Service (IMDS); the Trust Domain Server verifies the token's authenticity using Azure Entra's OIDC discovery endpoint.

Use this method for Azure VMs, AKS pods, or any Azure workload with a Managed Identity assigned.

Attributes available for SVID issuance

AttributeDescription
azure_msi.tenant.idAzure AD tenant ID (tid claim from the token)
azure_msi.identity.principal_idObject ID (oid claim) of the managed identity

All attributes have the origin azure_msi.

Example SPIFFE ID template:

/azure/{{azure_msi.tenant.id}}/{{azure_msi.identity.principal_id}}

Prerequisites

The workload running the agent must have an Azure Managed Identity assigned — either system-assigned or user-assigned.

Run the following one-time script to create the app registration used as the default token audience (replace <tenantId> with your Azure AD tenant ID):

TENANT_ID=$(az account show --query tenantId -o tsv)
az ad app create \
--display-name "defakto-server" \
--identifier-uris "api://$TENANT_ID/defakto-server" \
--sign-in-audience "AzureADMyOrg"

This creates an app registration with URI api://<tenantId>/defakto-server. Both the agent and server use this URI as the default audience automatically — no further audience configuration is needed in most cases.

How to Deploy

Step 1 — Update cluster configuration

section: AgentAttestation
schema: v1
spec:
policies:
- name: azure_policy
requiredAttestors:
- type: azure_msi
config:
tenants:
- tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
principalID: "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
# audience defaults to "api://<tenantId>/defakto-server"

Multiple tenant entries can be configured to allow agents from different Azure Entra tenants:

tenants:
- tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
principalID: "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
- tenantID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
principalID: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
audience: "https://myotherdefaktoapp.com/"

For V2 token issuers (not the default):

tenants:
- tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
principalID: "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
issuerURL: "https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0"

Apply it using spirlctl:

spirlctl config set cluster --id <cluster-id> attestation-policy.yaml

Or using Terraform:

resource "spirl_cluster_config" "agent_attestation" {
cluster_id = spirl_cluster.my_cluster.id
sections = {
AgentAttestation = <<-YAML
section: AgentAttestation
schema: v1
spec:
policies:
- name: azure_policy
requiredAttestors:
- type: azure_msi
config:
tenants:
- tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
principalID: "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
YAML
}
}

Once a configuration document passes validation and is stored, the Defakto control plane syncs it to your Trust Domain Servers automatically. No server or agent restart is required.

Server Configuration Reference

FieldRequiredDefaultDescription
tenantsYesList of allowed Azure MSI identity configurations. At least one entry is required.
tenants[].tenantIDYesAzure AD tenant ID. Matched against the tid claim.
tenants[].principalIDYesObject ID of the managed identity allowed to connect. Matched against the oid claim.
tenants[].audienceNoapi://<tenantId>/defakto-serverExpected token audience (aud claim).
tenants[].issuerURLNohttps://sts.windows.net/<tenantId>/OIDC issuer URL. Override for V2 tokens (https://login.microsoftonline.com/<tenantId>/v2.0).
note

The default issuer is the V1 endpoint (sts.windows.net). V1 vs V2 is controlled by the accessTokenAcceptedVersion setting in the app registration manifest. Only override issuerURL if you have explicitly configured V2 tokens.

Step 2 — Configure the Agent

agent:
auth:
clusterId: c-xxxxxx
attestors:
- type: azure_msi
config:
# audience defaults to "api://<tenantId>/defakto-server"
# Optional: select a specific user-assigned identity (mutually exclusive)
# clientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# object_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# msi_res_id: "/subscriptions/.../providers/Microsoft.ManagedIdentity/..."

Agent Configuration Reference

FieldDefaultDescription
audienceapi://<tenantId>/defakto-serverResource URI for requesting the MSI token. Must match the server's audience.
clientIdDefault identitySelects a user-assigned identity by client ID.
object_idDefault identitySelects a user-assigned identity by object/principal ID.
msi_res_idDefault identitySelects a user-assigned identity by resource ID.

Only one of clientId, object_id, or msi_res_id may be set. If none is set, the default (system-assigned or sole user-assigned) identity is used.

Step 3 — Verify

Server logs — look for these in order:

  1. "Login started with multi-attestation support" — confirms the agent offered providedMethods: ["azure_msi"]
  2. "Authorization received and verified" — includes agentAttestationAttributes with the managed identity:
    {
    "msg": "Authorization received and verified",
    "agentAttestationAttributes": [
    "azure_msi:azure_msi.tenant.id=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
    "azure_msi:azure_msi.identity.principal_id=\"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\""
    ]
    }
  3. "Connected to agent" — session is fully established

Agent logs — enable debug logging to see "Sending Login" with attestors: ["azure_msi"]. At the default log level, "Connected to server" confirms the session is live.

Metrics — confirm proofs are succeeding:

spirl_attestation_signer.proof{attestor_type="azure_msi",outcome="success"}
spirl_attestation_agent.proof{attestor_type="azure_msi",outcome="success"}

Alert on outcome="failed" to detect token validation failures.

Common errors:

ErrorLikely cause
no cluster policy authorizes the provided attestorsAgent's azure_msi method doesn't match any policy — verify tenantID and principalID in the cluster config match the managed identity
Attestor rejected proof, policy failedAudience or principal ID mismatch — confirm the oid claim in the token matches principalID in the server config
No managed identity token availableThe compute resource has no Managed Identity assigned, or the app registration was not created

Security Considerations

  • The principalID in each tenant entry is the primary access control — only the specific managed identity with that object ID can authenticate.
  • The audience constraint prevents tokens issued for other Azure resources from being used to authenticate to the Trust Domain Server.
  • User-assigned identities are identified by the oid claim, which corresponds to the identity's object ID (not the client ID).

Troubleshooting

No managed identity available — The Azure Identity SDK cannot obtain a token. Verify the VM, AKS pod, or other compute resource has a Managed Identity assigned.

Audience mismatch — If attestation fails with an audience-related error, verify that the audience in the agent config exactly matches the audience under the relevant tenants entry in the server config.

Wrong principal — If the token is valid but attestation is rejected, confirm that the principalID in the cluster config matches the object ID of the managed identity assigned to the agent workload. Find the object ID in the Azure portal under Managed Identities, or via az identity show.