Disk to LUN Mapping for a Windows VM using Microsoft Storage Spaces in Azure
When multiple data disks are attached to a Windows VM using Microsoft Storage Spaces in Azure it can be difficult to identify the LUN a disk belongs to. This is especially troublesome when there is a need to delete, clone, or migrate a specific disk(s) from on VM to another VM in Azure. A scenario where this may come in handy is if you need to migrate only 1 or 2 disks associated with Microsoft Storage Spaces while leaving other disks in place on the VM.
When a disk is not associated with a storage pool the disk’s LUN can easily be seen in Location property of the general tab on the disk’s properties under the “Disk Management” utility.
When a disk is associated with a storage pool the disk will not show up in the “Disk Management” utility and the LUN is not easily viewable because of this.
The following can help identify the Disk to LUN Mapping on your Windows VM in Azure when Microsoft Storage Spaces are used.
We will collect some information on some of the disks displayed in Device Manager. Use this information to identify the LUNs of the disks you would like to delete, clone, or migrate. You will need to do these steps for each of the “Microsoft Storage Space Device” objects in the “Disks drives” object in the Device Manager utility.
- Physical Device Object Name – Can be used as the unique identifier for the “Microsoft Storage Space Device”.
- Volume and Drive Letter(s) – Will show what volumes are associated with the storage space device.
- Volume Capacity – This is optional but helpful to see.
- Power relations – Identifies uniquely each “Microsoft Virtual Disk” used by the “Microsoft Storage Space Device”.
- LUN ID(s) – This derived by manually converting the last 6 characters of each of the “Power Relations” values (see above) from HEX to Decimal.
1. Open Device Manager
a. Remote Desktop into your Azure VM that has the disks you would like to delete, clone, or migrate.
b. Right-Click Start Menu –> Select “Device Manager”
2. Get properties of the “Disk drives” object.
Note: This will need to be done for each of the “Microsoft Storage Space Device”
a. Right Click on the “Microsoft Storage Space Device” object and select properties.
3. Get the “Microsoft Storage Space Device” object’s “Physical Device Object name” property.
a. Select the “Details” tab of the “Microsoft Storage Space Device” properties window.
b. Select the “Physical Device Object name” property in the “Property” field dropdown menu.
c. Write down the “Physical Device Object Name” value for this “Microsoft Storage Space Device”.
Sample Value: “\Device\Space1”
4. Get the Volumes associated with the “Microsoft Storage Space Device”.
a. Select the “Volumes” tab of the “Microsoft Storage Space Device” properties window.
b. Click the “Populate” button to retrieve all volume information for this “Microsoft Storage Space Device”.
c. For each volume in the “Microsoft Storage Space Device” make note of the “Volume” and “Capacity” fields.
Sample Value “Volume”: “volume-on-pool-a (M:)”
Sample Value “Capacity”: “2016MB”
5. Get the “Power relations” properties for the “Microsoft Storage Space Device”.
a. Select the “Details” tab of the “Microsoft Storage Space Device” properties window.
b. Select the “Power relations” property in the “Property” field dropdown menu.
c. Write down the “Power relations” value(s) for this “Microsoft Storage Space Device”.
Sample “Power relations” Value(s):
SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\000001 SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\000000
6. Identify the LUN information in the “Power relations” Value string(s) and convert from HEX to Decimal.
Note: The LUN information is stored in hexadecimal format as the last 6 characters on the “Power relations” Value string(s). At this time this will need to be manually identified and converted to a decimal format for LUN identification. See this sample data below:
Sample “Power relations” value: “SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\00001c” Sample Hexadecimal value (last six characters from above): “00001c”
Sample LUN ID (converted from HEX): “28”
a. Get the last 6 characters of the “Power relation” string(s).
b. Convert the last 6 characters from hexadecimal to decimal using Calc.exe (in Programmer mode). This is the LUN ID.
c. Note down the LUN ID with the other collected information.
Below are some example tables that were created using the method above. These can be used to aggregate the data when multiple disks are involved.
|
Volumes Table |
|||
|
Microsoft Storage Space Device (Physical Device Object Name) |
Drive Letter |
Volume Name |
Capacity |
|
\Device\Space1 |
M: |
volume-on-pool-a (M:) |
2016 MB |
|
\Device\Space2 |
N: |
volume-on-pool-b (N:) |
2016 MB |
|
Power Relations Table (LUNs) |
|||
|
Microsoft Storage Space Device (Physical Device Object Name) |
Power relations |
HEX (Manual - Last 6 of Power Relations) |
LUN (Manual - Convert Hex to Decimal) |
|
\Device\Space1 |
SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\000001 |
000001 |
1 |
|
\Device\Space1 |
SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\000000 |
000000 |
0 |
|
\Device\Space2 |
SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\000003 |
000003 |
3 |
|
\Device\Space2 |
SCSI\Disk&Ven_Msft&Prod_Virtual_Disk\000002 |
000002 |
2 |
Generating HTML Reports in PowerShell – Part 4
Almost doneI'm sure you would like to have something other than "Your Logo Here" at the top of these reports, well you can. I have modified the module to accept two methods for rendering logos. The Module will now to accept a file path to a logo or alternatively you can code your base64 string into the module which is the default if no files are specified.
Pre-requisites Again, we are going to use the code to create the Azure VMs recordset and other session variables we built up in Part 1. The module is available through the PowerShell Gallery or can be installed from PowerShell using install-module -name ReportHTML. Additionally you can contribute via github ReportHTML which also has and all the Examples scripts for download. You can get the example code for part 4 from these Github links with comments and without comment.
Using images for Logos (Example 12) This example will utilize two jpg files, clientlogo and mainlogo and will encode these into base64 strings in the Get-HTMLClose function.
[powershell] ####### Example 12 ######## # The two logo files are stored in the report path $MainLogoFile = join-path $ReportOutputPath "ACELogo.jpg" $ClientLogoFile = join-path $ReportOutputPath "YourLogo.jpg"
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 12") $rpt += Get-HtmlContentOpen -HeaderText "Size Summary" $rpt += Get-HtmlContentTable ($RMVMArray | group Size | select name, count | sort count -Descending) $rpt += Get-HtmlContentClose
# In this case we are going to swap the logos around using ClientLogoFile and MainLogoFile parameters and switching the files used $rpt += Get-HtmlClose -ClientLogoFile $MainLogoFile -MainLogoFile $ClientLogoFile Test-Report -TestName Example12 [/powershell]
Change logos in the module (Example 13) This example show how the default option works. There are 5 client logo base64 strings encoded into the module. Simply calling the module with -ClientLogoType ClientLogo1 to ClientLogo5 with use a switch statement to select which logo to use. You can use Powershell or a website to create the encoding string. This string can then be put into the module. This obviously breaks receiving updates so I would recommend not using this but it's there at the moment. Below shows the code where the ClientLogo and MainLogo strings are located.
[powershell] ####### Example 13 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 13") $rpt += Get-HtmlContentOpen -HeaderText "Size Summary" $rpt += Get-HtmlContentTable ($RMVMArray | group Size | select name, count | sort count -Descending) $rpt += Get-HtmlContentClose
# We have been using Get-HTMLClose up until now which has a default of ClientLogo1 # In this case we can specify ClientLogo5 $rpt += Get-HtmlClose -ClientLogoType ClientLogo5
Test-Report -TestName Example13 [/powershell]
Using images for Logos (Example 14) This example we can pass in the base 64 string directly making moving a report around without an image file easier. This uses the ClientLogoBase64 and MainLogoBase64 parameters.
Edit Please note I uploaded some bad code relating to get-htmlclose and logos please update to 1.0.0.12. Apologies for any inconvenience.
[powershell] ####### Example 14 ######## # for this we need to get the file and create the string. You could do this once and code the base64 string into the script $MainLogoFilePath = join-path $ReportOutputPath "ACELogo.jpg" $ClientLogoFilePath = join-path $ReportOutputPath "YourLogo.jpg" $MainBase64 = [Convert]::ToBase64String((Get-Content $ClientLogoFilePath -Encoding Byte)) $clientBase64 = [Convert]::ToBase64String((Get-Content $MainLogoFilePath -Encoding Byte))
# if you run the $clientBase64 and copy the content into a here string you can create the logo image without access to the file.
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 14") $rpt += Get-HtmlContentOpen -HeaderText "Size Summary" $rpt += Get-HtmlContentTable ($RMVMArray | group Size | select name, count | sort count -Descending) $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose -ClientLogoBase64 $MainBase64 -MainLogoBase64 $MainBase64
Test-Report -TestName Example14 [/powershell]
Conclusion I really hope you find this useful and hope it can help you generate HTML reports on the fly to make someone's job easier. There is another function ConvertTo-AdvHTML here that requires some HTML knowledge. However this output could be intertwined into ReportHTML module for advanced usage. There is also a module here which has column sort which could be merged. There is a lot of potential here for expansion on what is there and a lot of room to improve as well. Please feel free to post suggestions, contact me or contribute via Github.
Good luck.
Generating HTML Reports in PowerShell – Part 3
Welcome BackYou're still here, hopefully you got the content working from Part 1 and Part 2 in this series about generating HTML reports in Powershell. Now let's have a look at creating a Pie Chart in the script. The module is available through the PowerShell Gallery or can be installed from PowerShell using install-module -name ReportHTML. Additionally you can contribute via github ReportHTML which also has and all the Examples scripts for download.
Pre-requisites Again, we are going to use the code to create the Azure VMs recordset and other session variables we built up in Part 1. You can get the code for part 3 from these Github links with comments and without comment
Module Changes I have added two functions to the module to help create Pie charts. The first function creates an object that contains the properties for creating the chart. There is lot of room for expansion and options to create different chart styles I will discuss at the end of this post. Create-HTMLPieChartObject will create a custom object with default chart properties that we pass to the Create-HTMLPieChart function along with a grouped recordset. Simply using the Powershell group command on an array will create an array with name and count properties that get used as the data points on the chart. You can alternatively create your own array with Name and Count headings or use and expression to change the existing headings to name and count, meaning you dont have to use group by function. Please make sure to update your local module files from Github
Creating a Pie Chart (Example 9) First we will create a very basic chart showing a summary of virtual machine power state.
[powershell] ####### Example 9 ######## # First we create a PieChart Object and load it into a variable $PieChartObject = Create-HTMLPieChartObject
# Have a look at what is in this object. $PieChartObject
# Let's set one property $PieChartObject.Title = "VMs Power State"
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 9") $rpt += Get-HtmlContentOpen -HeaderText "Chart Series" $rpt += Create-HTMLPieChart -PieChartObject $PieChartObject -PieChartData ($RMVMArray | group powerstate) $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example9 [/powershell]
Pie Chart & additional Properties (Example 10) Let's have a look at some of the properties we can set
[powershell] ####### Example 10 ######## $PieChartObject = Create-HTMLPieChartObject $PieChartObject.Title = "VMs Sizes Deployed"
# There is a lot of data so let's make the pie chart a little bigger and explode the largest value $PieChartObject.Size.Height = 600 $PieChartObject.Size.Width = 600 $PieChartObject.ChartStyle.ExplodeMaxValue = $true
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 10") $rpt += Get-HtmlContentOpen -HeaderText "Chart Series"
# To summarize the data I have simply changed the group by property to size $rpt += Create-HTMLPieChart -PieChartObject $PieChartObject -PieChartData ($RMVMArray | group size) $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose Test-Report -TestName Example10 [/powershell]
Pie Chart & Results Table (Example 11) Here is a quick example with a pie chart and results table
[powershell] ####### Example 11 ######## $PieChartObject1 = Create-HTMLPieChartObject $PieChartObject.Title = "VMs Powerstate" $PieChartObject2 = Create-HTMLPieChartObject $PieChartObject.Title = "VMs Sizes" $PieChartObject.Size.Height = 800 $PieChartObject.Size.Width = 800 $PieChartObject.ChartStyle.ExplodeMaxValue = $true
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 10") $rpt += Get-HtmlContentOpen -HeaderText "Power Summary" $rpt += Create-HTMLPieChart -PieChartObject $PieChartObject1 -PieChartData ($RMVMArray | group powerstate) $rpt += Get-HtmlContentTable ($RMVMArray | group powerstate | select name, count) $rpt += Get-HtmlContentClose $rpt += Get-HtmlContentOpen -HeaderText "VM Size Summary" $rpt += Create-HTMLPieChart -PieChartObject $PieChartObject2 -PieChartData ($RMVMArray | group size) $rpt += Get-HtmlContentTable ($RMVMArray | group Size | select name, count | sort count -Descending) $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example11 [/powershell]
More Charting Properties There are a lot of properties that can be added to this object. I even thought about creating a couple of defaults sets of properties, for instance defaults for 'Exploded', 'SLA' or 'DisplayValues'. If you want to help expand this chart object, please contribute and edit the github project. The options for charting can be found at microsoft in the Class Reference
Additionally I would like to create some chart functions for line and bar charts, however I haven't had the time to create those functions recently. If you want to explore this please contribute to github or you can reach out and we can discuss how to go about this.
Summary We have one component left to cover which is changing the logos at the top left and top right that are displayed on the report. Currently these base64 strings are hard coded in the module. I will be working to create some more dynamics options before posting about this in Part 4.
Generating HTML Reports in PowerShell – Part 2
Welcome BackHopefully you found Part 1 in this series about generating HTML reports useful. That was an introduction to the basic functions and how to construct a report using ReportHTML module, available through the PowerShell Gallery or can be installed from PowerShell using install-module -name ReportHTML. Additionally you can contribute via github ReportHTML. Now we are going to build upon those functions and show what else we can do with this module.
Pre-requisites To continue with these examples you will need the code to create the Azure VMs recordset and other session variables from Part 1. Here is a link for the code for part 2 with comments Report-AzureVMsExamples_Part2WithComments and code without comments Report-AzureVMsExamples_Part2
Adding HyperLinks (Example 6) In this example we are going to create a Hyperlink as a string of values. To do this we need to wrap the hyperlink and the link name in three strings URL01, URL02 and URL03. The construction of a link will look like this. ("URL01 "+ HyperLink + "URL02" + LinkName + "URL03"). We will do this in the below example and create the structure of a hyperlink to the Azure Virtual Machine for each row in the recordset.
[powershell] ####### Example 6 ######## # We need to set some variables for building the URLs to the Azure Resource $Base = "https://portal.azure.com/#resource/subscriptions/" $SubID = $AzureRMAccount.Context.Subscription.SubscriptionId $RG = "/resourceGroups/" $vm = "/providers/Microsoft.Compute/virtualMachines/"
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + " Example 6") $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines"
# Using the expression we can create an expression for each record. # We string the base URL with the subscription ID add the resource group and VM name, # This link is used between URL01 and URL02 we reuse the VM name for the link name and add URL03 $rpt += Get-HtmlContentTable ($RMVMArray | select ResourceGroup, ` @{Name="Azure VM";Expression={"URL01" + $Base + $SubID + $RG + $_."ResourceGroup" + $vm + $_."Name" + "URL02" + $_."Name" + "URL03" }}, ` location, Size,PowerState, DataDiskCount, ImageSKU ) -GroupBy ResourceGroup
$rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example6 [/powershell]
Adding dynamic row colors (Example 7 & 8) In this example we are going to use an to add a colour name to a recordset, which when passed passed to the table content function will render in HTML with colour. There are three colors you can set, green, yellow and red. The first step is to create an expressions that can be evaluated. For this example we are going to color row based on the number of hard disks attached to the VM. You must use a single quote for this expression, that is '. In the expression the work $this is used to represent the current record. In this case we create three expressions, one for each color.
[powershell] ####### Example 7 ######## $VMs = ($RMVMArray | select ResourceGroup, Name, Location, Size,PowerState, DataDiskCount, ImageSKU )
# You must use single quotes here for the expression $Red = '$this.DataDiskCount -ge 2' $Yellow = '$this.DataDiskCount -eq 1' $Green = '$this.DataDiskCount -lt 1'
# call the function and pass the array and color expressions $VMsColoured = Set-TableRowColor $VMs -Red $Red -Yellow $Yellow -Green $Green
# let's just see what the function did $VMsColoured | select ResourceGroup ,name, datadiskcount , RowColor -first 30
$rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + "Example 7") $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines" $rpt += Get-HtmlContentTable $VMsColoured $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example7 [/powershell]
[powershell] ####### Example 8 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + "Example 8") $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines"
# here we can sort the rows so the colours are grouped together $rpt += Get-HtmlContentTable ( $VMsColoured | sort DataDiskCount) $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
[/powershell]
Summary I hope this has been a useful and you can see potential for using this module in your own scripting. There is still a few more examples to work through. Stay tuned for Part 3. Additionally I will be loading the project into Github.
Generating HTML Reports in PowerShell - Part 1
The problemPowerShell is an amazing tool for gathering, collecting, slicing, grouping, filtering and collating data. However, trying to show that information or several sets of it on one report is not as easy. A few years we ago built our own solution, we created a set of HTML reporting functions. I have been using these functions for years to help myself, my team and customers to deliver Powershell data to people that just need the details and not a CSV file or a code snippet. We've now decided to make these available to the rest of you.
This solution This is developed as a Powershell module. It is now available through the PowerShell Gallery or can be installed from PowerShell using install-module -name ReportHTML. Additionally it can be accessed from github ReportHTML, download and deploy to an appropriate module directory for example 'C:\Users\User\Documents\WindowsPowerShell\Modules\' It can be deployed and run ad-hoc or with your scheduled report Powershell jobs. There are several ways to build up a report. In this post we will build five example reports based around an Azure VMs array.
The report at a glance
This screenshot of a report that shows information about patching results grouped in two sections, successful and failed patches.

This screenshot of a report that shows multiple collapsible sections with an array of functions from within a file displayed in an open section.

Here is a more complete report I have created at the end of this series in More, Part 5 just to give you an idea of what is possible. I provide the script and this sample in the link above. Please note there is java script to enable hiding of sections which does generate a warning in your browser. I do explain this later in this post.

This Blog Series There are lots of different ways to leverage this module including changing the logos, highlighting rows, creating different sections with code loops and much more. This first article is going to work through 5 examples to get you started. Here are two versions of the same code detailed below or you can create your own script with the code snippets as we work through the examples. You will need to save the file to a local folder or provide a report output path parameter. - A version without all the comments Report-AzureVMsExamples_Part1 - A version with all the comments Report-AzureVMsExamples_Part1WithComments
Reporting functions summary There are a handful of functions that generate HTML code, you string this code together and then save the content as a file. This code was originally borrowed from Alan Renouf for a vSphere healthcheck report by Andrew Storrs and myself for a more dynamic reporting style, being able to create reports on the fly with minimal effort. In addition these reports once built can be scheduled to run, dropped on a file share or emailed. I will outline the main functions and then build a report collecting information about virtual machines from an Azure subscription. will walk through several examples of how to use the functions to generate different types of reports.
- Get-HtmlOpen
- Get-HtmlClose
- Get-HtmlContentOpen
- Get-HtmlContentClose
- Get-HtmlContentTable
- Get-HtmlContentText
- Set-TableRowColor
- New-HTMLPieChartObject
- New-HTMLPieChart
- New-HTMLBarChartObject
- New-HTMLBarChart
- Get-HTMLColumn1of2
- Get-HTMLColumn2of2
- Get-HTMLColumnClose
Let's get started First let's create the header section. This contains some parameters, report file path, reportname, loads the ReportHTML module and checks for an Azure Account and if one isn't present asks displays a login prompt.
[powershell] param ( $ReportOutputPath )
Import-Module ReportHtml Get-Command -Module ReportHtml
$ReportName = "Azure VMs Report"
if (!$ReportOutputPath) { $ReportOutputPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent }
# see if we already have a session. If we don't don't re-authN if (!$AzureRMAccount.Context.Tenant) { $AzureRMAccount = Add-AzureRmAccount } [/powershell]
Building a recordset We will need a record set to work with. I am going to take some code Barry Shilmover shared here and add resource group name as a property to or an array of VMs
[powershell] # Get arrary of VMs from ARM $RMVMs = get-azurermvm
$RMVMArray = @() ; $TotalVMs = $RMVMs.Count; $i =1
# Loop through VMs foreach ($vm in $RMVMs) { # Tracking progress Write-Progress -PercentComplete ($i / $TotalVMs * 100) -Activity "Building VM array" -CurrentOperation ($vm.Name + " in resource group " + $vm.ResourceGroupName)
# Get VM Status (for Power State) $vmStatus = Get-AzurermVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Status
# Generate Array $RMVMArray += New-Object PSObject -Property @{`
# Collect Properties ResourceGroup = $vm.ResourceGroupName Name = $vm.Name; PowerState = (get-culture).TextInfo.ToTitleCase(($vmStatus.statuses)[1].code.split("/")[1]); Location = $vm.Location; Tags = $vm.Tags Size = $vm.HardwareProfile.VmSize; ImageSKU = $vm.StorageProfile.ImageReference.Sku; OSType = $vm.StorageProfile.OsDisk.OsType; OSDiskSizeGB = $vm.StorageProfile.OsDisk.DiskSizeGB; DataDiskCount = $vm.StorageProfile.DataDisks.Count; DataDisks = $vm.StorageProfile.DataDisks; } $I++ } [/powershell]
Testing the report We are just going to write a short function to generate and invoke the report file
[powershell] Function Test-Report { param ($TestName) $rptFile = join-path $ReportOutputPath ($ReportName.replace(" ","") + "-$TestName" + ".mht") $rpt | Set-Content -Path $rptFile -Force Invoke-Item $rptFile sleep 1 } [/powershell]
The process of building the report Let's run this line to see what happens and what output we get.
[powershell] Get-HtmlContentOpen -HeaderText "Virtual Machines" [/powershell]
You should see this HTML as output. Each function generates HTML code, incorporating the parameters you pass it. We are going to collect all this code into an array variable called $rpt.
[html]
<div class="section">
<div class="header"> <a name="Virtual Machines">Virtual Machines</a> </div>
<div class="content" style="background-color:#ffffff;"> [/html]
Building a Basic Report (Example 1) Let's generate a very quick report before we dive into some of the other features and functions. We want to display the VM array in an table.
[powershell] ####### Example 1 ####### # Create an empty array for HTML strings $rpt = @()
# note from here on we always append to the $rpt array variable. # First, let's add the HTML header information including report title $rpt += Get-HtmlOpen -TitleText $ReportName
# This content open function add a section header $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines"
# This creates an HTML table of whatever array you pass into the function $rpt += Get-HtmlContentTable $RMVMArray
# This content close function closes the section $rpt += Get-HtmlContentClose
# This HTML close adds HTML footer $rpt += Get-HtmlClose
# Now let's test what we have Test-Report -TestName Example1 [/powershell]
Allow Blocked Content
Depending on your browser settings you may receive an warning asking if you want to 'Allow blocked content'. This is the Javascript function to optionally hide sections of the report. You can click allow blocked content or change your IE settings.

Order and grouping data (Example 2) Here we will select specific columns from the array, with the column we are grouping by being the first in the select statement
[powershell] ####### Example 2 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText $ReportName $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines"
# here we are going to filter the recordset, reorder the columns and group the results by location. # The value you group by must be first in the select statement $rpt += Get-HtmlContentTable ($RMVMArray | select Location, ResourceGroup, Name, Size,PowerState,DataDiskCount, ImageSKU ) -GroupBy Location $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example2 [/powershell]
Creating more sections and hiding them (Example 3) Let's create a summary section and a section about VM size counts and we will hide two sections
[powershell] ####### Example 3 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText $ReportName
# adding the summary section $rpt += Get-HtmlContentOpen -HeaderText "Summary Information" $rpt += Get-HtmlContenttext -Heading "Total VMs" -Detail ( $RMVMArray.Count) $rpt += Get-HtmlContenttext -Heading "VM Power State" -Detail ("Running " + ($RMVMArray | ? {$_.PowerState -eq 'Running'} | measure ).count + " / Deallocated " + ($RMVMArray | ? {$_.PowerState -eq 'Deallocated'} | measure ).count) $rpt += Get-HtmlContenttext -Heading "Total Data Disks" -Detail $RMVMArray.datadisks.count $rpt += Get-HtmlContentClose
# adding the VM size section. Note the -IsHidden switch $rpt += Get-HtmlContentOpen -HeaderText "VM Size Summary" -IsHidden $rpt += Get-HtmlContenttable ($RMVMArray | group size | select Name, Count | sort count -Descending ) -Fixed $rpt += Get-HtmlContentClose
# Note I have also added the -IsHidden Switch here $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines" -IsHidden $rpt += Get-HtmlContentTable ($RMVMArray | select Location, ResourceGroup, Name, Size,PowerState, DataDiskCount, ImageSKU ) -GroupBy Location $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example3 [/powershell]
Looping with foreach and section background shading (Example 4) We are going group the recordset by location and add a foreach loop.
[powershell] ####### Example 4 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText $ReportName $rpt += Get-HtmlContentOpen -HeaderText "Summary Information" $rpt += Get-HtmlContenttext -Heading "Total VMs" -Detail ( $RMVMArray.Count) $rpt += Get-HtmlContenttext -Heading "VM Power State" -Detail ("Running " + ($RMVMArray | ? {$_.PowerState -eq 'Running'} | measure ).count + " / Deallocated " + ($RMVMArray | ? {$_.PowerState -eq 'Deallocated'} | measure ).count) $rpt += Get-HtmlContenttext -Heading "Total Data Disks" -Detail $RMVMArray.datadisks.count $rpt += Get-HtmlContentClose $rpt += Get-HtmlContentOpen -HeaderText "VM Size Summary" -IsHidden $rpt += Get-HtmlContenttable ($RMVMArray | group size | select Name, Count | sort count -Descending ) -Fixed $rpt += Get-HtmlContentClose
# We are introducing -BackgroundShade 2 so that we can clearly see the sections. # This helps with larger reports and many when there are many levels to the sections $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines by location" -IsHidden
# adding the foreach loop for the group recordset. foreach ($Group in ($RMVMArray | select Location, ResourceGroup, Name, Size,PowerState, DataDiskCount, ImageSKU | group location ) ) {
#for every group that exists for a location we will create an HTML section. I have also specified the -BackgroupShade to 1 $rpt += Get-HtmlContentOpen -HeaderText ("Virtual Machines for location '" + $group.Name +"'") -IsHidden -BackgroundShade 1
# Each recordset may have different data in the columns and therefore create different width in the table columns. # We would like it to look the same. We can use the -Fixed switch to produce evenly space columns for the table $rpt += Get-HtmlContentTable ($Group.Group | select ResourceGroup, Name, Size,PowerState, DataDiskCount, ImageSKU ) -Fixed $rpt += Get-HtmlContentClose } $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example4 [/powershell]
Filtering Sections based on Conditions (Example 5) This will cover adding some IF conditions to the syntax to display a section or not.
[powershell] ####### Example 5 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + "Example 5") $rpt += Get-HtmlContentOpen -HeaderText "Summary Information" -BackgroundShade 1 $rpt += Get-HtmlContenttext -Heading "Total VMs" -Detail ( $RMVMArray.Count) $rpt += Get-HtmlContenttext -Heading "VM Power State" -Detail ("Running " + ($RMVMArray | ? {$_.PowerState -eq 'Running'} | measure ).count + " / Deallocated " + ($RMVMArray | ? {$_.PowerState -eq 'Deallocated'} | measure ).count) $rpt += Get-HtmlContenttext -Heading "Total Data Disks" -Detail $RMVMArray.datadisks.count $rpt += Get-HtmlContentClose $rpt += Get-HtmlContentOpen -HeaderText "VM Size Summary" -IsHidden -BackgroundShade 1 $rpt += Get-HtmlContenttable ($RMVMArray | group size | select Name, Count | sort count -Descending ) -Fixed $rpt += Get-HtmlContentClose $rpt += Get-HtmlContentOpen -HeaderText "Virtual Machines by location" -BackgroundShade 3 foreach ($Group in ($RMVMArray | select Location, ResourceGroup, Name, Size,PowerState, DataDiskCount, ImageSKU | group location ) ) {
# Here we are creating a group to use for the IF condition, so we can create sections for VMs by powerstate, Running or Deallocated $PowerState = $Group.Group | group PowerState $rpt += Get-HtmlContentOpen -HeaderText ("Virtual Machines for location '" + $group.Name +"' - " + $Group.Group.Count + " VMs") -IsHidden -BackgroundShade 2
# If there are VMs in the running group, continue and create a section for them if (($PowerState | ? {$_.name -eq 'running'})) { $rpt += Get-HtmlContentOpen -HeaderText ("Running Virtual Machines") -BackgroundShade 1 $rpt += Get-HtmlContentTable ($Group.Group | where {$_.PowerState -eq "Running"} | select ResourceGroup, Name, Size, DataDiskCount, ImageSKU ) -Fixed $rpt += Get-HtmlContentClose }
# If there are VMs in the running group, continue and create a section for them if (($PowerState | ? {$_.name -eq 'Deallocated'})) { $rpt += Get-HtmlContentOpen -HeaderText ("Deallocated") -BackgroundShade 1 -IsHidden $rpt += Get-HtmlContentTable ($Group.Group | where {$_.PowerState -eq "Deallocated"} | select ResourceGroup, Name, Size, DataDiskCount, ImageSKU)-Fixed $rpt += Get-HtmlContentClose } $rpt += Get-HtmlContentClose } $rpt += Get-HtmlContentClose $rpt += Get-HtmlClose
Test-Report -TestName Example5 [/powershell]
Summary I hope you have had success working through these examples and can find a use for this code. Part 2 in this series will move into some more techniques and reporting functions. Please share any questions or issues you have executing this content.
Topic Search
Posts by Date
- August 2025 1
- March 2025 1
- February 2025 1
- October 2024 1
- August 2024 1
- July 2024 1
- October 2023 1
- September 2023 1
- August 2023 3
- July 2023 1
- June 2023 2
- May 2023 1
- February 2023 3
- January 2023 1
- December 2022 1
- November 2022 3
- October 2022 7
- September 2022 2
- August 2022 4
- July 2022 1
- February 2022 2
- January 2022 1
- October 2021 1
- June 2021 2
- February 2021 1
- December 2020 2
- November 2020 2
- October 2020 1
- September 2020 1
- August 2020 1
- June 2020 1
- May 2020 2
- March 2020 1
- January 2020 2
- December 2019 2
- November 2019 1
- October 2019 7
- June 2019 2
- March 2019 2
- February 2019 1
- December 2018 3
- November 2018 1
- October 2018 4
- September 2018 6
- August 2018 1
- June 2018 1
- April 2018 2
- March 2018 1
- February 2018 3
- January 2018 2
- August 2017 5
- June 2017 2
- May 2017 3
- March 2017 4
- February 2017 4
- December 2016 1
- November 2016 3
- October 2016 3
- September 2016 5
- August 2016 11
- July 2016 13


















