Working with Azure Instance MetaData Services
Tags: azure, iaas, powershell, devops
Published: 2019-07-26 17:23:51

---
title: Working with Azure Instance MetaData Services
published: true
description: How to deal with Azure VM MetaData and what to do with them
tags: Azure, IaaS, PowerShell, DevOps
---

Querying data from Azure VM parameters can be very easy. You can use PowerShell, AZ CLI, Rest API or the Portal to get information about a virtual machine. But what if you need this information from inside the VM?  

There some solutions like installing the AZ PowerShell module and set up a system assigned managed identity to the VM to perform some query on Azure about itself, it works, but it’s inefficient and a bit dangerous. System assigned managed identity should be used only in scenarios where it is needed (access to a database or management tasks).  

There is a better way. Every major cloud provider manages a metadata service you can use to retrieve information from the VM instance where the query was made.   

For Azure VM you can use Metadata services by using a special unroutable IP address, 169.254.169.254. This IP only works in Azure and it works, even if the VM doesn't have Internet access.   
Azure instance Metadata service is based on a restful API. It can be consumed by using an HTTP request there are 3 metadata services:  

* Instance: to retrieve information about the current VM  

* ScheduledEvents : for information about planned maintenance on the VM or availability set  

* Attested : to make sure the VM run on Azure and not somewhere else  

To access these services, you only need to provide the path in the URI.  

* /instance 

* /Scheduledevents 

* /Attested 

You also need to provide an API version, the version changes from time to time and the latest version is 2019-03-11 (it may be not available in all Azure region). 

The webservice URI look like 

**http://169.254.169.254/metadata/instance?api-version=2013-03-11**  

Let’s take a look closer to the Instance service.  

To query the metadata service, I use PowerShell (PS Core), before sending anything I need to send a special header. Without this header, any request fail. 
 

```powershell
$MetaDataHeaders = @{"Metadata"="true"} 
```
Every http request to the service must come with this header.  


```powershell
Invoke-RestMethod -Method GET -uri "http://169.254.169.254/metadata/instance?api-version=2018-10-01" -Headers $MetaDataHeaders 
```

It will return a PSCustomObject with two members, Compute and Network.  

Compute contain information about the instance and network contain an array for all the interface with the private and public IP addresses.  

What should I care about that? 

Getting some information from the current deployment on your VM can ease configuration management and software delivery.  

Take a look at the schema returned 


```json
"compute": 

{ 

"azEnvironment":"AzurePublicCloud", 

"customData":"", 

"location":"northeurope", 

"name":"vmtest", 

"offer":"WindowsServer", 

"osType":"Windows", 

"placementGroupId":"", 

"plan": 

{"name":"","product":"","publisher":""}, 

"platformFaultDomain":"0", 

"platformUpdateDomain":"0", 

"provider":"Microsoft.Compute", 

"publicKeys":[], 

"publisher":"MicrosoftWindowsServer", 

"resourceGroupName":"requirements-rg", 

"resourceId":"/subscriptions/2a21f4a3-c253-47fc-a05d-1c04de8ed626/resourceGroups/requirements-rg/providers/Microsoft.Compute/virtualMachines/vmtest", 

"sku":"2019-Datacenter-smalldisk", 

"subscriptionId":"2a21f4a3-c253-47fc-a05d-1c04de8ed626", 

"tags":"env:lab;labname:requirements", 

"version":"17763.557.20190604", 

"vmId":"32351821-a67d-449d-a00b-ff73cb802781", 

"vmScaleSetName":"", 

"vmSize":"Standard_B2s", 

"zone":"" 

} 
```

kinds of information, Azure internal information like ResourceID, or VMId, current deployment information, like VMName (it may be not the same as the hostname) availibity set or the vm location. And the last one tags.  

Tags are key/value pairs used to identify a resource in Azure. You can use it to identify the owner, the application name. You can have up to 15 tags per resource. So, you can use it also in a configuration management or software delivery process.  

As tags can be updated during the resource life cycle, they can be seen as a way to send information from the Azure side to the Workload side. 

This is the point, during a delivery process, you need to provision immutable virtual machines each time you release a new application version.  You may have different roles (front, back, data processing, …), different environment (Production, Qual, Dev, …) and different packages. 

Instead of using a configuration tool for each situation, you can only have one tool that leverages tags. You can use a version number tag for the release number to download on the server, you can use a role tag to provision services using PowerShell DSC, Requirements, or Chef. 
 

The possibilities are not infinite, you only have 15 tags and you need some of them to identify resources. But you only need to change tags and not the provision and delivery tool.  

Azure Instance metadata return tags as a string  

```json
"tags":"env:lab;labname:requirements", 
```


It will be better to have a hash table instead. We can use the ConvertFrom-StringData cmdlet.  It converts a string to a hash table. To perform this, we need to transform the value of the tag. ConvertFrom-StringData look for key/value pairs in the string, ":" need to be replaced by "=" and as the cmdlet use new line to separate key/value pairs, ";" need to be replaced by "`n" 

```powershell
$MetaDataObject = Invoke-RestMethod -Method GET -uri "http://169.254.169.254/metadata/instance?api-version=2018-10-01" -Headers $MetaDataHeaders 

$AzureInstanceTags =$MetaDataObject.compute.tags 

$AzureInstanceTags = $AzureInstanceTags.Replace(";","`n") 

$AzureInstanceTags =$AzureInstanceTags.Replace(":","=") 

$AzureInstanceTagsHash = ConvertFrom-StringData $AzureInstanceTags 

$AzureInstanceTags =$MetaDataObject.compute.tags 

$AzureInstanceTags = $AzureInstanceTags.Replace(";","`n") 

$AzureInstanceTags =$AzureInstanceTags.Replace(":","=") 

$AzureInstanceTagsHash = ConvertFrom-StringData $AzureInstanceTags 
```

This is one way we can leverage Metadata services and tags, but there are many other solutions. Metadata services include also more services than instance, you can check scheduled maintenance and you can also verify if your instance runs in Azure or not with the attested services (useful if you provide a service or a virtual machine image in the Azure Market Place).