I know this question is old but it doesn't have a complete answer. I use this PowerShell script to gather XML data of the VMs in a single subscription in to a file. In essence, it gets everything and then filters.
$VMs = Get-AzureRmVM
$NICs = Get-AzureRmNetworkInterface
$VNETs = Get-AzureRmVirtualNetwork
$AzureVMInventory = foreach ($VM in $VMs) {
$NIC = $NICs | Where { $_.Id -eq $VM.NetworkProfile.NetworkInterfaces.Id }
$Subnet = $VNETs.Subnets | Where { $_.Id -eq $NIC.IPConfigurations.Subnet.Id }
$VNET = $VNETs | Where {$_.Subnets.Id -eq $NIC.IPConfigurations.Subnet.Id}
$Property = [ordered]@{
Name = $VM.OSProfile.ComputerName
VMSize = $VM.HardwareProfile.VmSize
ResourceGroup = $VM.ResourceGroupName
OSDisk = $VM.StorageProfile.OsDisk.Vhd.Uri
DataDisk = $VM.StorageProfile.DataDisks.Vhd.Uri -join "#"
IPAddresses = $NIC.IpConfigurations.PrivateIPAddress
IPAllocationMethod = $NIC.IpConfigurations.PrivateIPAllocationMethod
nicName = $NIC.Name
vNet = $VNET.Name
Subnet = $Subnet.Name
Location = $NIC.Location
}
New-Object -TypeName PSObject -Property $Property
}
$XML = $AzureVMInventory | ConvertTo-Xml -Depth 99
# Inject XSLT processing into the XML
$ProcessInstruction = $XML.CreateProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="style.xsl"')
$XML.InsertBefore($ProcessInstruction, $XML.DocumentElement)
$XML.Save("$PSScriptRoot\ProductionVMs.xml")
Since reading XML is a royal nightmare, I insert a process instruction and call an XSL Stylesheet so that when the XML file is opened by a browser, it can be formatted as HTML and made readable as a table. This suits the XML generated above but has some specific code in for my purposes.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="ISO-8859-1"/>
<xsl:template name="tokenizeString">
<!--passed template parameter -->
<xsl:param name="list"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<!-- get everything in front of the first delimiter -->
<xsl:value-of select="substring-before($list,$delimiter)"/>
<br />
<xsl:call-template name="tokenizeString">
<!-- store anything left in another variable -->
<xsl:with-param name="list" select="substring-after($list,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$list = ''">
<xsl:text/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$list"/>
<br />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>Azure Virtual Machines</title>
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>IP Addresses</th>
<th>IP Allocation Method</th>
<th>Virtual Network</th>
<th>Subnet</th>
<th>VMSize</th>
<th>Resource Group</th>
<th>Network Interface</th>
<th>Location</th>
<th>OS Disk</th>
<th>Data Disks</th>
</tr>
<xsl:for-each select="//Objects/Object">
<xsl:sort select="Property[@Name='Name']" order="ascending" data-type="text"/>
<xsl:variable name="datadisks" select="Property[@Name='DataDisk']"/>
<tr>
<td><xsl:value-of select="Property[@Name='Name']" /></td>
<td><xsl:value-of select="Property[@Name='IPAddresses']" /></td>
<td><xsl:value-of select="Property[@Name='IPAllocationMethod']" /></td>
<td><xsl:value-of select="Property[@Name='vNet']" /></td>
<td><xsl:value-of select="Property[@Name='Subnet']" /></td>
<td><xsl:value-of select="Property[@Name='VMSize']" /></td>
<td><xsl:value-of select="Property[@Name='ResourceGroup']" /></td>
<td><xsl:value-of select="Property[@Name='nicName']" /></td>
<!-- <td><xsl:value-of select="Property[@Name='Location']" /></td> -->
<xsl:choose>
<xsl:when test="Property[@Name='Location'] = 'northeurope'">
<td class="loc_ne">North Europe</td>
</xsl:when>
<xsl:when test="Property[@Name='Location'] = 'westeurope'">
<td class="loc_we">West Europe</td>
</xsl:when>
<xsl:otherwise>
<td class="loc_err">NOT EUROPE</td>
</xsl:otherwise>
</xsl:choose>
<td><xsl:value-of select="Property[@Name='OSDisk']" /></td>
<td>
<!-- <xsl:value-of select="Property[@Name='DataDisk']" /> -->
<xsl:choose>
<xsl:when test="boolean(Property[@Name='DataDisk'])">
<xsl:call-template name="tokenizeString">
<xsl:with-param name="list" select="Property[@Name='DataDisk']"/>
<xsl:with-param name="delimiter" select="'#'"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Finally a little CSS to make it tidy and presentable.
body * {
font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
font-size: 9pt;
}
table {
width: 100%;
}
table, th, td {
border: 1px solid rgba(0,188,242,0.2);
border-collapse: collapse;
text-align: center;
}
th {
background: rgb(0,32,80);
color: #fff;
font-size: 11pt;
}
td {
width: 10%;
padding: 2px;
}
td.loc_ne {
background: rgba(0,188,242,0.5);
}
td.loc_we {
background: rgba(0,127,0,0.5);
}
td.loc_err {
background: rgba(219,20,61,0.5);
}
tr:nth-child(even) {background: rgba(0,188,242,0.5);}
tr:nth-child(odd) {background: #fff}
tr:hover {background-color: #ddd}
Since the object is a PowerShell object, it doesn't have to be exported to XML, it can be exported to JSON, CSV etc. By tagging a little extra code on to the bottom of the PowerShell script, you can import the XSL directly and then export to pure HTML if you need to share a single file.
# Convert to HTML (if required)
$XSLT = New-Object System.Xml.Xsl.XslCompiledTransform;
$XSLT.Load("style.xsl");
$XSLT.Transform("VMs.xml", "VMs.html");