Skip to main content

Validate Machine Build by Pester v5 and Send the result to MS Teams Channel

· 3 min read
Naw Awn

This has been one on my list to do for a while. What you want to do is test your machine build after the deployment from SCCM, MDT or Intune Windows AutoPilot. The script will check

  • if the machine has the list of software you exptect
  • if the specific file or folder eixsts
  • if the registry key and value pairs are correct
  • if the services are running or stopped

Then send the result (Passed or Failed) to your Microsoft Teams channel via the incoming webhook so that you can monitor the result.

The script is divided into three components. The first one is the actual test script (.Tests.ps1) that will perform the check. The second one is the data file (.psd1) where you define what you want to test: Software, File and Folder, Registry and Windows Service. The third one is the script (.ps1) that invokes the first powershell script and send the result to your Microsoft Teams channel.

Test-MachineBuild.Tests.ps1
Param(
[Parameter()]
$ConfigFile = '.\MachineBuild-Config.psd1'
)
BeforeDiscovery {
$Data = Import-PowerShellDataFile $ConfigFile
}
BeforeAll{
Function Get-InstalledProgram{
Param(
[String]$ComputerName
)
Begin{
$RegistryView = @([Microsoft.Win32.RegistryView]::Registry32,[Microsoft.Win32.RegistryView]::Registry64)
$RegistryHive = [Microsoft.Win32.RegistryHive]::LocalMachine
$SoftwareHKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
}
Process{
Foreach($View in $RegistryView){
try{
$Key = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryHive, $ComputerName, $View)
}catch{
$MsgBody = "Unable to access registry on {0}. Error code: {1:x}" -f $ComputerName, $_.Exception.HResult
Write-Warning $MsgBody
continue
}
$SubKey = $Key.OpenSubKey($SoftwareHKey)
$KeyList = $SubKey.GetSubKeyNames()
Foreach($Item in $KeyList){
[PSCustomObject][Ordered]@{
DisplayName = $SubKey.OpenSubKey($Item).GetValue('DisplayName')
RegHive = $SubKey.OpenSubKey($Item).View
Publisher = $SubKey.OpenSubKey($Item).GetValue('Publisher')
InstallDate = $SubKey.OpenSubKey($Item).GetValue('InstallDate')
Version = $SubKey.OpenSubKey($Item).GetValue('DisplayVersion')
InstallSource = $SubKey.OpenSubKey($Item).GetValue('InstallSource')
UninstallString = $SubKey.OpenSubKey($Item).GetValue('UninstallString')
}
}
}
}
}

Function Test-Registry{
[OutputType([Bool])]
Param(
$Path,
$Name,
$Value
)
Process{
Try{ $Result = Get-ItemPropertyValue -Path $Path -Name $Name }
catch{ $Result = $null }
return ($Result -eq $Value)
}
}
[Void](New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT)
$InstalledSW = Get-InstalledProgram -ComputerName $Env:ComputerName | where{$_.DisplayName}
}

Describe "Software Installation" {
It "<_> should be installed" -Foreach $($Data.Software) {
($_ -in $($InstalledSW.DisplayName)) | Should -Be $true
}
}
Describe "Files and Folders"{
It "<_> should exist" -Foreach $($Data.Folder) {
Test-Path -Path $_ | Should -Be $true
}
}
Describe "Registry key values"{
It "<Name> Should have <Value>" -Foreach $($Data.Registry) {
Test-Registry -Path $_.Path -Name $_.Name -Value $_.Value | Should -Be $true
}
}
Describe "Service Status"{
It "<Name> Should be <Status>" -Foreach $($Data.Service) {
(Get-Service -Name $_.Name).Status | Should -Be $_.Status
}
}

Due to the separation of Data and Script, all the test data are compiled into this psd1 file. This would make it easier to add or remove what you want to test without touching the test script file.

MachineBuild-Config.psd1
@{
Software = @(
'Microsoft Visual Studio Code',
'Adobe Acrobat DC (64-bit)',
'Git',
'VLC media player',
'Node.js',
'Google Chrome',
'Microsoft 365 Apps for enterprise - en-us'
)
Folder = @(
'D:\MySoftware',
'C:\Temp'
)
Registry = @(
@{
Path = 'HKLM:\System\CurrentControlSet\Control\Terminal Server'
Name = 'fDenyTSConnections'
Value = 0
},
@{
Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
Name = 'SecurityHealth'
Value = 'C:\WINDOWS\system32\SecurityHealthSystray.exe'
}
)
Service = @(
@{
Name = 'TermService'
Status = 'Running'
},
@{
Name = 'mpssvc'
Status = 'Running'
},
@{
Name = 'XboxGipSvc'
Status = 'Stopped'
}
)
}
Validate-Build.ps1
Function Send-TeamAlert{
Param(
[String]$Message
)
Begin{
$Uri = 'Paste your MS Teams Uri here https://outlook.office.com/webhook/guid@guid/incomingwebhook/somemoreguid'
}
Process{
$Body = @{
text = $Message
} | ConvertTo-Json
Invoke-RestMethod -Uri $Uri -Method Post -Body $Body -ContentType 'application/json'
}
}
#Thumbsup for passed, Thumbsdown for failed
$Emoji = @{
Passed = '&#x1f44d'
Failed = '&#x1f44e'
}
$Result = (Invoke-Pester .\Test-MachineBuild.Tests.ps1 -PassThru).Result
$Message = "**{0}** - Build QC Result: {1}" -f $($Evn:ComputerName), $($Emoji[$Result])

Send-TeamAlert -Message $Message