Azure IMDS
The Azure IMDS method attests agents using the Azure Instance Metadata Service attested document. Unlike Azure MSI, this method does not require the VM to have a Managed Identity assigned, making it usable for a broader set of Azure workloads.
The agent fetches a PKCS#7-signed attested document from the IMDS endpoint; the Trust Domain Server verifies the document's cryptographic signature, validates a challenge nonce, and optionally queries the Azure Compute API for additional VM details.
Attributes available for SVID issuance
| Attribute | Description |
|---|---|
azure_imds.tenant.id | Azure AD tenant ID (from server config) |
azure_imds.subscription.id | Azure subscription ID (from attested document) |
azure_imds.vm.id | Unique VM ID (from attested document) |
azure_imds.vm.name | VM name (validated via Azure Compute API) |
azure_imds.vmss.name | VM Scale Set name, if applicable |
azure_imds.location | Azure region/location (via Compute API) |
azure_imds.resource_group | Resource group name |
azure_imds.network_security_group | Network Security Group name(s) |
azure_imds.virtual_network | Virtual Network name(s) |
azure_imds.virtual_network_subnet | Subnet name(s) |
azure_imds.vm.tag.<tag_key> | VM tag values filtered by attributeVMTagKeys |
All attributes have the origin azure_imds.
Example SPIFFE ID template:
/azure/{{azure_imds.tenant.id}}/{{azure_imds.subscription.id}}/{{azure_imds.vm.name}}
Prerequisites
Certificate pinning: The Trust Domain Server pins the Azure IMDS root certificate when verifying the PKCS#7 attested document. When Azure changes roots for the IMDS signing certificate chain, a new version of the Trust Domain Server must be deployed with the updated roots.
Additionally, the Trust Domain Server makes outbound connections to Microsoft's certificate distribution URL (determined from the leaf certificate) at runtime to retrieve intermediate CAs.
The Trust Domain Server requires:
- Outbound network access to
https://www.microsoft.com(to retrieve intermediate CA certificates) - Outbound network access to the Azure Resource Manager API (to resolve VM details)
- An Azure Managed Identity assigned to the Trust Domain Server with the Azure RBAC Reader role on the relevant VM and VMSS resources
How to Deploy
Step 1 — Update cluster configuration
section: AgentAttestation
schema: v1
spec:
policies:
- name: azure_imds_policy
requiredAttestors:
- type: azure_imds
config:
tenants:
contoso.onmicrosoft.com:
tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
restrictToSubscriptions:
- "ssssssss-ssss-ssss-ssss-ssssssssssss"
attributeVMTagKeys:
- environment
- team
Multiple tenant domains can be configured:
tenants:
contoso.onmicrosoft.com:
tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
restrictToSubscriptions:
- "ssssssss-ssss-ssss-ssss-ssssssssssss"
fabrikam.onmicrosoft.com:
tenantID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
attributeVMTagKeys:
- app
For Azure Government or other sovereign clouds:
cloudEnvironment: AzureUSGovernmentCloud
tenants:
contoso.onmicrosoft.us:
tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
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_imds_policy
requiredAttestors:
- type: azure_imds
config:
tenants:
contoso.onmicrosoft.com:
tenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
restrictToSubscriptions:
- "ssssssss-ssss-ssss-ssss-ssssssssssss"
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 | — | Map of Azure tenant domains (e.g., contoso.onmicrosoft.com) to tenant configuration. |
tenants.<domain>.tenantID | Yes | — | Azure AD tenant ID for this domain. |
tenants.<domain>.restrictToSubscriptions | No | Any subscription in the tenant | List of allowed Azure subscription IDs. |
tenants.<domain>.attributeVMTagKeys | No | — | VM tag keys to include as attributes. |
cloudEnvironment | No | AzurePublicCloud | Azure cloud environment. Valid values: AzurePublicCloud, AzureChinaCloud, AzureUSGovernmentCloud, AzureGermanCloud. |
Step 2 — Configure the Agent
- Helm Installation
- Linux Installation
agent:
auth:
clusterId: c-xxxxxx
attestors:
- type: azure_imds
config:
tenant_domain: "contoso.onmicrosoft.com"
cluster-id: c-xxxxxx
agent-attestors:
- type: azure_imds
config:
tenant_domain: "contoso.onmicrosoft.com"
Agent Configuration Reference
| Field | Required | Description |
|---|---|---|
tenant_domain | Yes | Azure tenant domain. Must match a key in the server's tenants map. |
Step 3 — Verify
Server logs — look for these in order:
"Login started with multi-attestation support"— confirms the agent offeredprovidedMethods: ["azure_imds"]"Authorization received and verified"— includesagentAttestationAttributeswith the VM identity:{
"msg": "Authorization received and verified",
"agentAttestationAttributes": [
"azure_imds:azure_imds.tenant.id=\"contoso.onmicrosoft.com\"",
"azure_imds:azure_imds.vm.name=\"my-vm\"",
"azure_imds:azure_imds.subscription.id=\"ssssssss-ssss-ssss-ssss-ssssssssssss\""
]
}"Connected to agent"— session is fully established
Agent logs — enable debug logging to see "Sending Login" with attestors: ["azure_imds"]. 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_imds",outcome="success"}
spirl_attestation_agent.proof{attestor_type="azure_imds",outcome="success"}
Alert on outcome="failed" to detect PKCS#7 signature or nonce verification failures.
Common errors:
| Error | Likely cause |
|---|---|
no cluster policy authorizes the provided attestors | Agent's azure_imds method doesn't match any policy — verify tenant_domain matches a key in the server's tenants map |
Attestor rejected proof, policy failed | PKCS#7 signature verification failed, subscription not in restrictToSubscriptions, or nonce mismatch |
| VM details not resolving | Trust Domain Server lacks Azure RBAC Reader access on VM or VMSS resources in the relevant subscriptions |
Attestation Flow
- The agent sends its
tenant_domainas an initial proof. - The server validates the tenant domain and responds with a nonce challenge.
- The agent calls the IMDS
/metadata/attested/documentendpoint with the nonce, receiving a signed attested document. It also fetches the VM's resource ID from/metadata/instanceas a hint. - The server verifies the PKCS#7 signature, checks the nonce, enforces the subscription allowlist, and uses the resource ID to resolve additional VM details from the Azure Compute API.
Security Considerations
- Certificate pinning: The Trust Domain Server pins the IMDS root certificate. Plan for server upgrades when Azure rotates these certificates.
- Subscription restriction: Use
restrictToSubscriptionsto limit which Azure subscriptions can attest. Without this, any VM in the tenant can authenticate. - VM tag attributes: Only tags with keys in
attributeVMTagKeysare emitted as attributes, preventing leakage of sensitive or irrelevant tags. - Nonce freshness: Each attestation uses a fresh nonce; replayed attested documents are rejected.
Troubleshooting
IMDS not reachable — The agent cannot reach 169.254.169.254. Verify the workload is running on an Azure VM or VMSS instance.
Tenant domain mismatch — If attestation fails with "tenant domain is not configured", verify that tenant_domain in the agent config exactly matches a key in the server's tenants map.
Subscription not allowed — Check that the VM's subscription ID is included in restrictToSubscriptions (or remove the restriction).
VM details not resolving — Ensure the Trust Domain Server has Azure credentials with read access to VM resources in the relevant subscriptions.
Cloud environment mismatch — If document verification fails with a SAN mismatch, ensure cloudEnvironment in the server config matches the actual Azure cloud where the VMs are running.