How to get the System Assigned Managed Identity Object Id from within the VM?

2 minute read

While working with Terraform and Azure DevOps self-hosted agent, I faced a challenge getting the Object(Principal) Id of a System Assigned Managed Identity to the VM running the agent. I needed that Object Id to assign access to a Key Vault. There is a way to do so via Azure CLI:

az resource list -n $(curl -s -H Metadata:true --noproxy '*' 'http://169.254.169.254/metadata/instance?api-version=2021-02-01' | jq -r .compute.name) -g $(curl -s -H Metadata:true --noproxy '*' 'http://169.254.169.254/metadata/instance?api-version=2021-02-01' | jq -r .compute.resourceGroupName) --query '{principal_id:[0].identity.principalId,tenant_id:[0].identity.tenantId}' --out json

but that approach requires the installation of Azure CLI and configuring Reader access on the VM itself.

A more elegant approach is to extract the Object Id from the access token received by calling the Azure Instance Metadata Service (IMDS) endpoint:

curl "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" --header "Metadata: true" | jq -r .
access_token | jq -R 'split(".") | .[1] | @base64d | fromjson | .oid' -r

In Terraform, you can use the external provider to execute the script:

data "external" "account_info" {
  program = ["bash", "-c", "curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' --header 'Metadata: true' | jq -r .access_token | jq -R 'split(\".\") | .[1] | @base64d | fromjson | {oid: .oid, appid: .appid,tid: .tid}'"]
}

and then access the Object Id (and the Tenant Id) with:

tomap(data.external.account_info.result).oid

For example:

resource "azurerm_key_vault_access_policy" "current-user" {
  key_vault_id = azurerm_key_vault.kv.id

  tenant_id = tomap(data.external.account_info.result).tid
  object_id = tomap(data.external.account_info.result).oid

  key_permissions = [
    "Create",
    "Delete",
    "Get",
    "Purge",
    "Recover",
    "Update",
    "List",
    "Decrypt",
    "Sign"
  ]
}