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
| Attribute | Description |
|---|---|
azure_msi.tenant.id | Azure AD tenant ID (tid claim from the token) |
azure_msi.identity.principal_id | Object 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
| Field | Required | Default | Description |
|---|---|---|---|
tenants | Yes | — | List of allowed Azure MSI identity configurations. At least one entry is required. |
tenants[].tenantID | Yes | — | Azure AD tenant ID. Matched against the tid claim. |
tenants[].principalID | Yes | — | Object ID of the managed identity allowed to connect. Matched against the oid claim. |
tenants[].audience | No | api://<tenantId>/defakto-server | Expected token audience (aud claim). |
tenants[].issuerURL | No | https://sts.windows.net/<tenantId>/ | OIDC issuer URL. Override for V2 tokens (https://login.microsoftonline.com/<tenantId>/v2.0). |
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
- Helm Installation
- Linux Installation
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/..."
cluster-id: c-xxxxxx
agent-attestors:
- type: azure_msi
config:
# audience defaults to "api://<tenantId>/defakto-server"
# clientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# object_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# msi_res_id: "/subscriptions/.../providers/Microsoft.ManagedIdentity/..."
Agent Configuration Reference
| Field | Default | Description |
|---|---|---|
audience | api://<tenantId>/defakto-server | Resource URI for requesting the MSI token. Must match the server's audience. |
clientId | Default identity | Selects a user-assigned identity by client ID. |
object_id | Default identity | Selects a user-assigned identity by object/principal ID. |
msi_res_id | Default identity | Selects 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:
"Login started with multi-attestation support"— confirms the agent offeredprovidedMethods: ["azure_msi"]"Authorization received and verified"— includesagentAttestationAttributeswith 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\""
]
}"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:
| Error | Likely cause |
|---|---|
no cluster policy authorizes the provided attestors | Agent'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 failed | Audience or principal ID mismatch — confirm the oid claim in the token matches principalID in the server config |
| No managed identity token available | The compute resource has no Managed Identity assigned, or the app registration was not created |
Security Considerations
- The
principalIDin each tenant entry is the primary access control — only the specific managed identity with that object ID can authenticate. - The
audienceconstraint prevents tokens issued for other Azure resources from being used to authenticate to the Trust Domain Server. - User-assigned identities are identified by the
oidclaim, 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.