Problem


An application needs to know if itself is running in Azure VM, so that it can apply special logic and optimization for Azure. 

Solution


You can check if your code is running on an Azure VM by querying the Azure Instance Metadata Service.

The Azure Instance Metadata Service (IMDS) provides information about currently running virtual machine instances. You can use it to manage and configure your virtual machines. This information includes the SKU, storage, network configurations, and upcoming maintenance events. 

The service URL is: http://169.254.169.254/metadata/instance?api-version=2021-02-01

Test

Access from outside Azure VM:

Access within Azure Windows VM:

Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -NoProxy -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | ConvertTo-Json -Depth 64

From a Linux VM:

curl -s -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq

It proves the service endpoint can only be reachable from Azure VM.

Example Code

PowerShell

$metadataServiceUrl = "http://169.254.169.254/metadata/instance?api-version=2021-02-01"

$headers = @{
    "Metadata" = "true"
}

try {
    $response = Invoke-RestMethod -Uri $metadataServiceUrl -Headers $headers -Method Get

    Write-Host "Successfully retrieved metadata: "
    Write-Output $response
}
catch {
    Write-Host "Error retrieving metadata. This may not be an Azure VM or the service may not be available."
}

C#

internal class Program
{
    static readonly HttpClient httpClient = new HttpClient();

    private static async Task Main(string[] args)
    {
        try
        {
            string url = "http://169.254.169.254/metadata/instance?api-version=2021-02-01";

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
            request.Headers.Add("Metadata", "true");

            HttpResponseMessage response = await httpClient.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                string responseData = await response.Content.ReadAsStringAsync();
                Console.WriteLine("This code is running on an Azure VM.");
                Console.WriteLine(responseData);
            }
            else
            {
                Console.WriteLine("This code is not running on an Azure VM.");
            }
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine("An error occurred: " + e.Message);
        }
    }
}

Note


Please note the following points when using the above code:

  1. The Metadata header is necessary to retrieve the information; without it, the request will be denied.
  2. The IP address 169.254.169.254 is a link-local address used by the Azure Instance Metadata Service. This address is only reachable from within the VM.
  3. You should use the latest api-version parameter value that is supported. At the time this blog post is written, the latest version is 2021-02-01, but you should check the Azure documentation for any updates.