PowerShell

Capturing and using API queries from Azure in PowerShell with Fiddler

Capturing and using API queries from Azure in PowerShell with Fiddler

This is a walkthrough for using Fiddler to capture traffic to Azure from a browser and writing and running that query in PowerShell.  I wrote this because I don't like posts that skip over a key step and explain the entire thing with a wave of the hand.  Although this article stands on it own, it is a key step in another series.

Creating a custom Windows image for MaaS

Creating a custom Windows image for MaaS

Continuing on the Maas theme from yesterday, I thought I'd put up a post about my experience with Windows imaging for MaaS. The Windows Openstack Imaging Tools are great for creating Windows images for Maas. They provide support for creating gold images, custom drivers, and pretty much anything else you'd want for custom image creation.

Reporting on Resource Group Tags in Azure

show-pinned-tag.png

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.

[powershell]

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

Enjoy

Part 4 – Coding the PowerShell Module

This is part four of a multi-part post. Here are links to all the posts in this series.

Developing an Azure Service Bus PowerShell Module – Part 1/

Part 2 – Developing the First Test Using Pester

Part 3 – More Tests, this time with Pester's Mock

Part 4 – Coding the PowerShell Module

Introduction

In my last post, I developed the Pester unit tests for the New-QueueMessage and Read-QueueMessage cmdlets I am developing for an Azure Service Bus PowerShell module. In this post, I will illustrate developing each of the functions.

New-SasToken

Figuring out how to generate the SAS token took some digging. Fortunately, there are code samples on MSDN (linked below) that illustrate how to correctly compute the SHA hash required for the token. The token is made of four parts:

  • the name of the Azure Service Bus namespace
  • the SHA 256 hash,
  • the expiration time (which is the number of seconds since the beginning of the epoch starting at midnight January 1, 1970 UTC)
  • the SAS Policy name

The SHA 256 hash is the hash of another string, which includes:

  • the name of the Azure Service Bus namespace
  • a newline character
  • the expiration time

First, we need to compute the token expiration time. The code for that looks like this.

[powershell] $origin = [DateTime]"1/1/1970 00:00" $diff = New-TimeSpan -Start $origin -End $Expiry $tokenExpirationTime = [Convert]::ToInt32($diff.TotalSeconds) [/powershell]

Then I create the string that will be hashed.

[powershell] $stringToSign = [Web.HttpUtility]::UrlEncode($Namespace) + "`n" + $tokenExpirationTime [/powershell]

The next step is to new up an instance of an HMACSHA256 class, which will do the work of computing the hash. The Key property of the HMACSHA256 is set to a byte array that contains the SAS Policy key from the Azure portal.

Here's the code to new-up the HMACSHA256 class. Again, note that you don't set the key property to the key from the Azure portal, it's set to a byte array created from the key.

[powershell] $hmacsha = New-Object -TypeName System.Security.Cryptography.HMACSHA256 $hmacsha.Key = [Text.Encoding]::UTF8.GetBytes($Key) [/powershell]

Next the hash is computed with the HMACSHA256 class instance. The hash is converted to a base 64 string. That is what is used in the token.

[powershell] $hash = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = [Convert]::ToBase64String($hash) [/powershell]

The last step is to create the token. The token is a formatted string that looks like this.

Here's the code to create the token. Note the grave character at the end of each line.

[powershell] $token = [string]::Format([Globalization.CultureInfo]::InvariantCulture, ` "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", ` [Web.HttpUtility]::UrlEncode($Namespace), ` [Web.HttpUtility]::UrlEncode($signature), ` $tokenExpirationTime, ` $PolicyName) [/powershell]

Now, when I run the Pester unit test, I get the following.

Note that the last test no longer throws the NotImplementedException. Now that test passes. This is a good time to commit my changes to source control.

New-QueueMessage

One of the parameters of New-QueueMessage is a PSCustomObject that should contain a property named Body. Any other properties on that object will be assigned to the BrokerProperties header parameter of the web request. The first thing the function needs to do is break that object apart.

[powershell] $body = $Message.Body $Message.psobject.properties.Remove("Body") [/powershell]

Next is to set up the parameters for the Invoke-WebRequest cmdlet.

[powershell] $uri = "https://$Namespace.servicebus.windows.net/$QueueName/messages" $token = New-SasToken -Namespace $Namespace -Policy $PolicyName -Key $Key $headers = @{ "Authorization"="$token"; "Content-Type"="application/atom+xml;type=entry;charset=utf-8" } $headers.Add("BrokerProperties", $(ConvertTo-Json -InputObject $Message -Compress)) [/powershell]

Finally, the Invoke-WebRequest call. The normal output of the command is redirected to the $null automatic variable. If an error occurs, Invoke-WebRequest will output the error to the error stream stderr.

[powershell] Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body > $null [/powershell]

When I run my Pester tests, I get the following.

Two of my unit tests are passing now. This is a good time to commit my changes to source control.

Read-QueueMessage

Read-QueueMessage is a lot like New-QueueMessage. Make the call to the Service Bus REST API endpoint and use the results to construct a PSCustomObject that contains the BrokerProperties and the body of the response. I'll start by constructing the parameters required for the Invoke-WebRequest cmdlet.

[powershell] $uri = "https://$Namespace.servicebus.windows.net/$QueueName/messages/head" $token = New-SasToken -Namespace $Namespace -Policy $PolicyName -Key $Key $headers = @{ "Authorization"="$token" } [/powershell]

Next make the call to the Service Bus REST API.

[powershell] $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Delete [/powershell]

Finally, construct the PSCustomObject that contains the brokered message properties and the body of the message.

[powershell] $brokeredMessage = ConvertFrom-Json -InputObject $response.Headers.BrokerProperties Add-Member -InputObject $brokeredMessage ` -MemberType NoteProperty ` -Name "Body" ` -Value $response.Content [/powershell]

When I run my Pester tests, I get the following output.

All tests are passing. This is a good time to commit my changes.

In this post, I walked through the process of developing the three functions that make up my module. The module isn't done yet, it doesn't support all the envisioned scenarios yet. For example, I want to support the Service Bus Peek operation and retrieving messages from the dead letter queue in the Read-MessageQueue cmdlet. Comment based help is also needed so users can use the Get-Help cmdlet to see information about the cmdlets in the module. However, describing that process would be redundant and this series of posts has become long enough.

These posts have described using Visual Studio 2017 and the PowerShell Tools for Visual Studio 2017 plug-in to develop a simple but useful PowerShell module. The module can send and receive messages to Azure Service Bus. It has also described using Git for source control and the Pester unit test framework to support the development best-practice Test-Driven Development. My goal has been to show these technologies working together to create something useful.

Resources:

Service Bus authentication with Shared Access Signatures

Service Bus HTTP Client

Shared Access Signature authentication with Service Bus

Receive and Delete Message (Destructive Read)

Send Message (to an Azure Service Bus Queue)