Reporting on Resource Group Tags in Azure


You might have seen either of Mike's blog posts on resource groups or resource tagging or just be looking to generate a report on resource group tags in Azure, if so, you're in the right place. Yesterday we were taking a look at our subscription and looking to clean up some resources.  We needed a report to review Azure resource groups and their tags.  While this relatively easy to do with PowerShell, getting a report that you can share easily was a little harder. I thought I would take some time and write a PowerShell script to generate a report utilizing ReportHTML powershell module.

Resource Group Tag Report Generated with ReportHTML

Just like most things in IT there were a few bumps in the road.  Mainly that tag names are in a hashtable and that they are case sensitive.  I wrote some code to auto-discover key names and it will prefix the key name with a number so you can find all case versions of a tag and correct them if needed. This report also includes a hyperlink to take you directly to the resource in Azure.

Once you know the tag names you want to report on you can specify them as an array and pass that in as a parameter. If you specify the Tag Names array the first two tag names will be used to generate some pie charts as shown above. EG -KeyNames=@("Owner","Solution").  By default, the report is generated in your temp directory. You can use the -ReportOutputPath param to specify an output path.  There is also a parameter for your logo URL.  It must be a small image -YouLogoHereURLString

You can view and install this report from the PowerShell Gallery here using the following Install-Script -Name run-ReportAzureResourceGroupTags

Or here is the code.


Param ( [parameter(Mandatory=$false,ValueFromPipeline = $true)] [Array]$KeyNames, [parameter(Mandatory=$false)] [String]$ReportOutputPath, [parameter(Mandatory=$false)] [String]$YouLogoHereURLString )

[switch]$AutoKeyName =$false $m = Get-Module -List ReportHTML if(!$m) {"Can't locate module ReportHTML. Use Install-module ReportHTML";break} else {import-module reporthtml}

if ([string]::IsNullOrEmpty($(Get-AzureRmContext).Account)) {Login-AzureRmAccount}

$RGs = Get-AzureRmResourceGroup if ($KeyNames.count -eq 0) { [switch]$AutoKeyName =$true $KeyNames = (($rgs.Tags.keys) | select -Unique) }

$SubscriptionRGs = @() foreach ($RG in $RGs) {

$myRG = [PSCustomObject]@{ ResourceGroupName = $RG.ResourceGroupName Location = $RG.Location Link = ("URL01" + "https" + "://" + "portal.azure.com/#resource" + $RG.ResourceId + "URL02" + ($RG.ResourceId.Split('/') | select -last 1) + "URL03" ) }

$i=0 foreach ($KeyName in $KeyNames) { if ($AutoKeyName) { $myRG | Add-Member -MemberType NoteProperty -Name ([string]$i + "_" + $keyname) -Value $rg.Tags.($KeyName) $i++ } else { $myRG | Add-Member -MemberType NoteProperty -Name ($keyname) -Value $rg.Tags.($KeyName) } } $SubscriptionRGs += $myRG }

$rpt = @() if ($YouLogoHereURLString -ne $null) { $rpt += Get-HTMLOpenPage -TitleText "Azure Resource Groups" -LeftLogoString $YouLogoHereURLString -RightLogoString ("https" + "://" + "azurefieldnotesblog.blob.core.windows.net/wp-ontent/2017/02/ReportHTML.png") } else { $rpt += Get-HTMLOpenPage -TitleText "Azure Resource Groups" }

if (!$AutoKeyName) { $Pie1 = $SubscriptionRGs| group $KeyNames[0] $Pie2 = $SubscriptionRGs| group $KeyNames[1]

$Pie1Object = Get-HTMLPieChartObject -ColorScheme Random $Pie2Object = Get-HTMLPieChartObject -ColorScheme Generated

$rpt += Get-HTMLContentOpen -HeaderText "Pie Charts" $rpt += Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 2 $rpt += Get-HTMLPieChart -ChartObject $Pie1Object -DataSet $Pie1 $rpt += Get-HTMLColumnClose $rpt += Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 2 $rpt += Get-HTMLPieChart -ChartObject $Pie2Object -DataSet $Pie2 $rpt += Get-HTMLColumnClose $rpt += Get-HTMLContentclose }

$rpt += Get-HTMLContentOpen -HeaderText "Complete List" $rpt += Get-HTMLContentdatatable -ArrayOfObjects ( $SubscriptionRGs) $rpt += Get-HTMLContentClose

$rpt += Get-HTMLClosePage

if ($ReportOutputPath -ne $null) { Save-HTMLReport -ShowReport -ReportContent $rpt -ReportName ResourceGroupTags } else { Save-HTMLReport -ShowReport -ReportContent $rpt -ReportName ResourceGroupTags -ReportPath $ReportOutputPath } [/powershell]

There is a lot more that can be done with this code so please feel free to share your ideas and code below for others. If you want to add your own logos or edit the style of the report, check out the help file here or run Get-htmlReportHelp with the module installed.  I hope you find this helpful


More, HTML Reporting in PowerShell – Part 5


More?  Okay, Part 5.I just created some new functions for creating two columns in a sections and added functionality for optional alternating row colors for tables. If you have worked through the other 4 parts you will be up to speed. If this is all new to you, I'd suggest to start here @ Part 1, otherwise, let's jump straight in.

Pre-requisites We are going to use the same code to create the Azure VMs recordset and other session variables we built up in the other parts. If you have previously installed this module, you will need to update it to from the PowerShell Gallery. You can run update-module -name ReportHTML or if this is new to you you can use install-module -name ReportHTML.  Additionally you can contribute via github ReportHTML which also has all the Examples scripts for download Example 15 without comments and Example 15 with comments.

Section with Columns (Example 15) First we will create a section with two columns and split the array into each column.

[powershell] ####### Example 15 ######## $rpt = @() $rpt += Get-HtmlOpen -TitleText ($ReportName + "Example 15") $rpt += Get-HtmlContentOpen -BackgroundShade 2 -HeaderText "VM States"

# We will start the first column with a get-htmlColumn1of2 $rpt += get-HtmlColumn1of2

# Now add the content you want in the first column $rpt += Get-HtmlContentOpen -BackgroundShade 1 -HeaderText Deallocated $rpt += Get-HtmlContentTable ($RMVMArray | where {$_.PowerState -eq "Deallocated"} | select ResourceGroup, Name, Size, DataDiskCount, ImageSKU) $rpt += Get-HtmlContentClose

# Next we need to close this first section $rpt += get-htmlColumnClose

# Then we can start the second column with a get-htmlColumn2of2 $rpt += get-htmlColumn2of2 $rpt += Get-HtmlContentOpen -BackgroundShade 1 -HeaderText Running $rpt += Get-HtmlContentTable ($RMVMArray | where {$_.PowerState -eq "Running"} | select ResourceGroup, Name, Size, DataDiskCount, ImageSKU ) $rpt += Get-HtmlContentClose

# Now we need to close this second section $rpt += get-htmlColumnClose $rpt += Get-HtmlContentClose [/powershell]

Alternating Row Color (Example 15) This next code section show the Set-TableRowColor with the -Alternating switch. Using this switch is an either or with the expression colouring.

[powershell] $rpt += Get-HtmlContentOpen -HeaderText 'Alternating Row Color'

# For Alternating row color you can use the -Alternating switch which will add the row color definition for Odd and Even and also adds a header color. $rpt += Get-HtmlContentTable (Set-TableRowColor ($RMVMArray | select ResourceGroup, Name, Location, Size, ImageSKU ) -Alternating) $rpt += Get-HtmlContentclose [/powershell]


Report Suggestions? Next, I plan to turn my attention to creating some practical reports for use. Even possibly recreating some existing reports for distribution like this computer report.  If you have a report you can share or perhaps one you would like to see created, with special attention given to Azure content please share links, requests or ideas in the comments below.

Addendum Here is a Sample Systems Report and the script that is a recreation of the computer report linked above it could use some more work but shows you what is possible. SystemReportScreenShot It looks like I have a few system errors I need to go look at :)

Part 1 | Part 2 | Part 3 | Part 4 | Part 5

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  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.

Part 1 | Part 2 | Part 3 | Part 4 | More

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.

Part 1 | Part 2 | Part 3 | Part 4 | More

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



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.

Part 1 | Part 2 | Part 3 | Part 4 | More