Posting Messages to MS Teams with PowerShell

We are starting to use Microsoft Teams more and more for (suprisingly) team communication. We have a dedicated channel for product releases that Azure DevOps pipelines post to for build notifications but strangely I had always thought of wiring that together as being really arcane and difficult. Turns out, I tried and it was super easy.

First, in Teams, you need a channel that you can post to. Click on the right-hand side ‘…’ and pick connectors from the menu.

Next, select “Incoming Webhook”, give the web hook a name, an optional icon that will appear in each posting, and copy out the url that is generated.

Write a PowerShell script like this to post to the webhook and you’re done.



[CmdletBinding()]
Param (
    [Parameter(Mandatory=$True, HelpMessage="Web hook endpoint configured on a channel in Teams")]
    [string]$WebHook,

    [Parameter(Mandatory=$True, HelpMessage="Message to post to the channel")]
    [string]$Message
)

$Post = @{ text = $Message }

$JsonifiedObject = $Post | ConvertTo-Json

Invoke-RestMethod -Uri $WebHook -Method post -Body $JsonifiedObject -ContentType 'Application/Json' 


So, the moral of the story is, try a project you know nothing about how to do, it may not be as bad as you thought.

DDD North 2019

ddd logo

Once again I am lucky enough to have been selected to speak at DDD North 2019 on the 2nd of March at Hull University. I proposed 3 different sessions, two of them technical (Learning Elixir and Refactoring PowerShell for Reliability) and one on developer practices.

The session that got voted for is called “Married to the Mob (programming)” and is a remaster/repeat of the talk I did at DDD 2017 (with some improvements) on adopting the practice of mob programming for software development.

Robocopy instead of Copy-Item

Robocopy is an often forgotten about tool that is absolutely awesome and runs wonderfully when called from a PowerShell script.

Get-ChildItem and Copy-Item always seem to have too much flexibility built into them so that I see lots of scripts that work correctly accidentally, most often failing when trying to extend beyond copying a single folder’s worth of files. Where should the wildcard go, in the path? in the filter? in the include?

Robocopy is installed on all modern windows OSes and has retry, recursive copy etc. built in. So rather than fiddle with intricacies of Copy-Item, you can be done with the script and have it fun faster by using Robocopy.


& Robocopy.exe $SourceFolder $DestinationFolder *.* /s | Out-Null 

Docs on msdn.

PSake Turtles All The Way Down

I have been trying for a while to come up with a way to redesign a long PowerShell script into discrete PSake Tasks. Lots of the code is repeated throughout the script as small components are required in different configurations for different scenarios.

My original approach was to move chunks of repeated code to separate .psake.ps1 files and name tasks according to the component and the action. For example, Component1-CopyLocal and Component1-RunInstaller, while other components had similar long-winded names to avoid namespace clashes when each file was included the larger scenario/parent script.



Task Component1-CopyFiles -Description 'Copy component 1 files from network to local folder' {

    Write-Host "Copying component1.msi from network to local folder"
}


Task Component1-Install -Depends Component1-CopyFiles -Description 'Installs component 1 to local machine' {

    Write-Host "Installing component1.msi"
}




Task Component2-CopyFiles -Description 'Copy component 2 files from network to local folder' {

    Write-Host "Copying component2.msi from network to local folder"
}


Task Component2-Install -Depends Component2-CopyFiles -Description 'Installs component 2 to local machine' {

    Write-Host "Installing component2.msi"
}



# Install scenario: Install dependent components before running main script.

# Include other tasks in this script
Include '.\Component1-PSake.ps1'
Include '.\Component2-PSake.ps1'


Task ? -Description 'List tasks' -alias 'Help' { WriteDocumentation }
Task Default -Depends RunTool 


Task RunTool -Depends DeploymentDependencies -Description 'Runs the newly installed version of the tool' {

    Write-Host 'Running tool after dependencies installed'
}

Task DeploymentDependencies -Depends Component1-Install, Component2-Install, ...


This arrangement had three particular disadvantages, task names were particularly long but not really descriptive or helpful, the calling script had to know the names of each of the child tasks (or at least the names of all of the initiating tasks) making refactoring and renaming difficult and, finally, none of the child components were able to be standalone due to the namespace clash for the Default task.

That all changed when I discovered PSake has support for nested builds. We can redesign each component to be standalone, reduce the names of each task so they make sense at the component level, hide all of the task names from the calling scripts while still preserving the reuse of scripts and the mix-and-match nature that I wanted to begin with.


# Install Component 1

Task Default -Depends CopyFiles, RunInstaller


Task CopyFiles -Description 'Copy component 1 files from network to local folder' {

    Write-Host "Copying component1.msi from network to local folder"
}


Task RunInstaller -Depends CopyFiles -Description 'Installs component 1 to local machine' {

    Write-Host "Installing component1.msi"
}



# Install Component 2

Task Default -Depends CopyFiles, RunInstaller


Task CopyFiles -Description 'Copy component 2 files from network to local folder' {

    Write-Host "Copying component2.msi from network to local folder"
}


Task RunInstaller -Depends CopyFiles -Description 'Installs component 2 to local machine' {

    Write-Host "Installing component2.msi"
}



# Install scenario: Install dependent components before running main script.

Task ? -Description 'List tasks' -alias 'Help' { WriteDocumentation }
Task Default -Depends RunTool 


Task RunTool -Depends InstallComponent1, InstallComponent2 -Description 'Runs the newly installed version of the tool' {

    Write-Host 'Running tool after dependencies installed'
}


Task InstallComponent1 {

    Invoke-Psake .\Component1-PSake.ps1 -NoLogo
}


Task InstallComponent2 {

    Invoke-Psake .\Component2-PSake.ps1 -NoLogo 
}


And of course, we can repeat this nesting as needed with each component.

Error Handling

One thing to watch out for is error handling and exceptions when calling nested psake scripts. If a sub-task throws an exception, psake won’t report that problem in the calling script and will just terminate all succeeding tasks. You will need to wrap the Invoke-Psake call in a Try block and use Write-Error in the Catch. To continue with succeeding tasks in a psake script, use the ContinueOnError switch in a Task that may throw.

GUI Automation in PowerShell using AutoIt

AutoIt is an automation program I used to use a long time ago to do GUI automation and always found the programming model to make sense. The whole package seemed to be generally reliable and I’m not sure why it fell out of favour. One thing that didn’t exist at that time was PowerShell so it’s no surprise that I never thought to check for updates to it and if it now had a binding for PowerShell.

Part of the download from AutoIt now is a folder called AutoItX which you can use to do automation from PowerShell. It doesn’t need an install so you can copy the folder to a machine where you need it and just import the .psd1 file.


$here = Split-Path -Path $MyInvocation.MyCommand.Path

Get-Module AutoItX | Remove-Module -Force
Import-Module $here\AutoItX.psd1 -Force

Initialize-AU3

Invoke-AU3Run -Program notepad.exe

$NotepadTitle = "Untitled - Notepad"
Wait-AU3Win -Title $NotepadTitle

$hWnd = Get-AU3WinHandle -Title $notepadTitle

Show-AU3WinActivate -WinHandle $hWnd
$edit = Get-AU3ControlHandle -WinHandle $hWnd -Control "Edit1"

Set-AU3ControlText -ControlHandle $edit -WinHandle $hWnd -NewText "Hello World!" 

Sleep -Seconds 10

# Close even if prompted.
Close-AU3Win($hWnd)

The core AutoIt assemblies are loaded via the PowerShell cmdlet assembly AutoItX3.PowerShell.dll. I’m not a huge fan of the naming used for the cmdlets, they seem very clunky to me but probably as a result of trying to avoid namespace clashes with other modules and PowerShell’s verb conventions.

Cory Doctorow at Google

Cory Doctorow in 2017 talking about his most recent book at the time, Walkaway, at the google.

So "Walkaway" is an optimistic disaster novel...and the thesis underlying it is that you are not a pessimistic person if you assume something will go wrong with a system. Engineers who design systems on the assumption that nothing will ever go critically wrong with them don't make good systems. They build the Titanic. And so optimism is not the belief that nothing will go wrong. Optimism is the belief that we can design systems that coast to a gentle stop when they go wrong, instead of exploding and showering all the people around them with white hot shrapnel. And it's also the belief that once they've coasted to a stop, we can get them started again.

Build Monitor on Raspberry Pi

I’m starting a small but slightly ambitious project, around integrating several bits of technology that I’ve never had fully working together so in the spirit of divide-and-conquer here’s a quick snippet to create a build monitor on a raspberry pi using some python code, the GPIO library and some basic electronics.

Electronics

You will need a couple of electronics bits and bobs for this project:

  • Raspberry Pi (running Raspbian or similar)
  • Breadboard
  • Red LED
  • 330 Ohm Resistor
  • Jumper Wires

Wiring

The Raspberry Pi has specific pins on the connector set aside for power, ground and GPIO. Refer to the GPIO documentation to work out which pins you want to pick for power and IO (and change the code below if you decide to pick different output pins). The IO pins are subtly different on each version of the PI so if you aren’t sure which Pi you have you can find the right version here.

led circuit

Code


# Circuit Diagram
# Pin7 (GPIIO4) 
#     --¬
#       |
#       |
#       _
#      \ /  Red Led
#      ---
#       |
#       -
#      | | 300 Ohm 
#      | | Resistor
#       -
#       |
#     --
# Pin 9 (GND) 


from gpiozero import LED
import time

red_led = LED(17)
blink_time = 0.5

while True:
   red_led.on()
   time.sleep(blink_time)
   red_led.off()
   time.sleep(blink_time)
   

Running the code, you should see the LED blinking on and off.

Build Monitor

Now a build monitor is supposed to keep an eye on a build process and report on good builds by showing a green light and failed builds by showing a red light.

We can add a second coloured LED to the circuit and attach it to a different output pin on the pi. So a good build (controlled by a variable for now) will show one LED and a bad build will turn that off and turn on the other LED.


from gpiozero import LED
import time

red_led = LED(17)
green_led = LED(18)

blink_time = 0.5

build_good = True

while True:

  unused_led = red_led if build_good else green_led  
  status_led = green_led if build_good else red_led

  unused_led.off()
  
  status_led.on()
  time.sleep(blink_time)
  status_led.off()
  time.sleep(blink_time)
  

Tidying Pester API Code

PowerShell’s Invoke-RestMethod is a really nice cmdlet to use if you write Rest API test code using the Pester framework. One of the biggest advantages is that if an non-success code is returned from any request you don’t have to handle the exception, it gets reported by Pester as an error, stopping the test. The other advantage I would say is that successful calls return a nicely created object rather than a Json document so that tests can use the returned object directly rather than searching in text or having to hydrate a custom object from the Json.

Tests against an (imagined) rest API could look something like this



Describe 'API Get Method' {

	$ApiObject = Invoke-RestMethod -Method Get -Uri $Resource 

	It 'Contains correct number of fields' {
	
		
	}
}


Given that you now have a nice object handed to you by Invoke-RestMethod, it’s easy to see that specific fields are present with the correct values but it’s difficult to work out if you have the right size of object - that a field hasn’t been added or removed by accident.

Luckily, thanks to the introspective nature of PowerShell, you can use Get-Member to work out how many properties have been returned in the Json and converted to properties on the custom object. These properties seem to be a special type of NoteProperty rather than the plain Property type I was expecting.



Get-Member -InputObject $ApiObject -MemberType NoteProperty

Putting that into the body of our test works but it’s a bit complex to read and would probably not fair very well if someone was to copy this test as the basis for another.



Describe 'API Get Method' {

	$ApiObject = Invoke-RestMethod -Method Get -Uri $Resource 

	It 'Contains correct number of fields' {
	
		(Get-Member -InputObject $ApiObject -MemberType NoteProperty).Count | Should Be 5
	}
}


To address this, I thought first off to add a custom Pester assertion, as outlined here. That seemed to be too much effort for the tiny bit of tidying I though was warranted by the code so I converted the Get-Member snippet into a cmdlet, Get-FieldCount, that could be used as part of an assertion.



Function Get-FieldCount {
	[CmdletBinding()]
	Param(
		[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
		$InputObject
	)
	
	Process {
		ForEach ($apiObj in $InputObject) {
			$FieldCount = (Get-Member -InputObject $apiObj -MemberType NoteProperty).Count
			Write-Output $FieldCount
		}
	}
}




Describe 'API Get Method' {

	$ApiObject = Invoke-RestMethod -Method Get -Uri $Resource 

	It 'Contains correct number of fields' {
	
		$ApiObject | Get-FieldCount | Should Be 5
	}
}