The 'Azure Instance Metadata Service' (IMDS) is a REST endpoint, available at a well-known non-routable IP address (169.254.169.254
), on a variety of Azure compute resources, such as Virtual Machines or also on Azure Container Instances. You can find various samples or articles how to call that service.
The problem however is that developing 'locally' can be cumbersome, because the service is not available. The simple Go utility here essentially mimics IMDS.
For that to work, you need to bind that special IP address to one of your network interfaces. For example, on Windows, I run an administrative command prompt, and run this:
Windows:
netsh.exe interface ipv4 add address ^
name="vEthernet (Default Switch)" ^
address="169.254.169.254"
Linux (Ubuntu):
ifconfig:
sudo ifconfig eth0:1 169.254.169.254 netmask 255.255.255.0
ip:
sudo ip address add 169.254.169.254/255.255.255.0 dev eth0:1
To remove that binding, you can run this:
Windows:
netsh.exe interface ipv4 delete address ^
name="vEthernet (Default Switch)" ^
address="169.254.169.254"
Linux (Ubuntu):
ifconfig
sudo ifconfig eth0:1 del 169.254.169.254
ip
sudo ip address del 169.254.169.254/255.255.255.0 dev eth0:1
The application looks at a small config file, and exposes the file's information via REST:
{
"tenantID": "sometenant.onmicrosoft.com",
"servicePrincipals": {
"deadbeef-efa9-42ea-9ac3-28a920370be6": "ewewM/OblEL/WOJweC0="
},
"metadata": {
"compute": {
"provider":"Microsoft.Compute",
"tags":"tag1:val2",
"azEnvironment":"AzurePublicCloud",
"location":"westeurope",
"subscriptionId":"deadbeef-bee4-484b-bf13-d6a5505d2b51",
"resourceGroupName":"spring",
"vmId":"c7619932-27e3-4a63-988c-460bd290ca55",
"name":"somevm",
"vmSize":"Standard_D2s_v3",
"customData":"",
"placementGroupId":"", "platformFaultDomain":"0", "platformUpdateDomain":"0",
"osType":"Linux",
"publisher":"Canonical", "offer":"UbuntuServer", "sku":"18.04-LTS", "version":"18.04.201905290",
"vmScaleSetName":"",
"zone":""
}
}
}
Essentially, the .metadata
part is exposed on 'http://169.254.169.254/metadata/instance/compute'...
One of the funky capabilities of IMDS is that it can fetch Azure AD tokens on behalf of the application, without exposing credentials such as service principal keys to the application. For example, this script expects to fetch a token for Azure KeyVault:
#!/bin/bash
apiVersion="2018-02-01"
curl --request GET \
--silent \
-H Metadata:true \
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=${apiVersion}&client_id=${service_principal_application_id}&resource=https%3A%2F%2Fvault.azure.net"
Managed Identities can, by design, not be directly reachable on your developer laptop (unless we would proxy that REST call to a real Azure compute resource). To mimic that token endoint (/metadata/identity/oauth2/token
), you can configure service principal credentials in the config file as well.
To build:
Windows:
go build -o .\bin\server.exe server.go
Linux:
go build -o ./bin/server server.go
Windows:
.\bind_imds_ip.cmd && .\bin\server.exe
Linux:
sudo -- sh -c './bind_imds_ip.sh && ./bin/server'
To validate that the service is running correctly:
Linux:
curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2020-09-01"
To stop, stop the running process, then:
.\release_imds_ip.cmd
Linux:
sudo ./release_imds_ip.sh