Displaying Toast Notifications for a Different User when PowerShell Module Updates are Available

A couple of months ago, Josh King presented a session on Using BurntToast to Display Timely Notifications for our June 2018 Mississippi PowerShell User Group virtual meeting. I was previously planning to write something to display balloon notifications in Windows and I learned that they're now called toast notifications. I also learned that Josh had created a module named BurntToast which performs most of the heavy lifting so I could simply take advantage of it instead of writing my own code.

Note: This blog article is written using Windows 10 version 1803 and Windows PowerShell version 5.1. Your mileage may vary with different operating systems and/or different versions of PowerShell.

First, install the BurntToast module from the PowerShell Gallery.

1Install-Module -Name BurntToast -Force

burnttoast1a.png

The BurntToast module contains several functions, but the one I'm interested in is New-BurntToastNotification.

1Get-Command -Module BurntToast

burnttoast2a.png

A simple test notification revealed that notifications can't be displayed for a different user. I log into my computer as a standard user and then run PowerShell elevated as a different user account.

1New-BurntToastNotification -Text 'There are new PowerShell module updates available.'

burnttoast3a.png

There didn't seem to be an option built into the BurntToast module to work around this problem so I resorted to using the Runas.exe command from within PowerShell to display the test notification for a different user.

The first time Runas.exe is used with the Savecred parameter, it prompts for the specified user's password and saves it in credential manager.

1runas.exe /user:mikefrobbins\user01 /savecred powershell.exe

burnttoast4a.png

From that point forward, it will use the saved credentials for the specified user as long as they're valid.

1runas.exe /user:mydomain\mrobbins /savecred "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -Command New-BurntToastNotification -Text 'There are new PowerShell module updates available.'"

burnttoast5a.png

Success! The previous command displays the following toast notification.

burnttoast6a.png

While using the Runas.exe command achieves the desired result, there has to be a PowerShell way to accomplish this task and indeed there is.

1$UserCred = Get-Credential
2Start-Process -FilePath "$env:windir\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -Command New-BurntToastNotification -Text 'There are new PowerShell module updates available.'" -WindowStyle Hidden -Credential $UserCred

I installed Joel Bennett's BetterCredentials PowerShell module from the PowerShell Gallery so I could easily read credentials from credential manager instead of being prompted for them or storing them in a variable or file.

1Install-Module -Name BetterCredentials -Force -AllowClobber

burnttoast8a.png

If you haven't already, store the credentials in credential manager for the user who is logged into Windows.

1Get-Credential -Store

burnttoast9a.png

Now to wire up the code to perform a check to determine if any PowerShell module updates are available. I'll simply use the Find-MrModuleUpdate function that I've previously written which is part of my MrToolkit PowerShell module.

1Install-Module -Name MrToolkit -Force

burnttoast7a.png

Finally, I'm going to embed the following code into my PowerShell profile to perform the check and display the toast notification for any/all logged in user(s) if there are any PowerShell module updates available. You also have the option of setting it up as a scheduled task or job instead of adding it to your PowerShell profile.

 1Start-Job {
 2    if (-not(Get-Module -Name MrToolkit -ListAvailable)) {
 3        Install-Module -Name MrModule -Force
 4    }
 5    if (-not(Get-Module -Name BurntToast -ListAvailable)) {
 6        Install-Module -Name BurntToast -Force
 7    }
 8
 9    if (Find-MrModuleUpdate) {
10        Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName |
11        ForEach-Object {
12            Start-Process -FilePath 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -ArgumentList "-NoProfile -Command New-BurntToastNotification -Text 'There are new PowerShell module updates available.'" -WindowStyle Hidden -Credential (Get-Credential -UserName $_.UserName -Store)
13        }
14    }
15} | Out-Null

It's being run as a job because it's not the fastest thing in the world to check each and every module for updates.

This isn't the type of thing you want running every time you open PowerShell so I've specifically added it to the all users, current host profile for the Windows PowerShell console which is located in the following location: C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1. I've also added logic to test for Internet connectivity and only have it run the first time I open the PowerShell console each Tuesday.

 1if (Test-NetConnection -ComputerName bing.com -Port 80 -InformationLevel Quiet -ErrorAction SilentlyContinue -WarningAction SilentlyContinue) {
 2    $Date = Get-Date
 3
 4    if (($Date).DayOfWeek -eq 'Tuesday') {
 5
 6        $ModuleLUCPath = "$env:ProgramFiles\WindowsPowerShell\Configuration\moduleupdates-lastchecked.txt"
 7
 8        $ModuleLastUpdateCheck = (Get-ChildItem -Path $ModuleLUCPath -ErrorAction SilentlyContinue).LastWriteTime
 9
10        if ($ModuleLastUpdateCheck.Date -ne $Date.Date) {
11
12            if ((New-Object -TypeName System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
13
14                New-Item -Path $ModuleLUCPath -ItemType File -Force | Out-Null
15
16                Start-Job {
17                    if (-not(Get-Module -Name MrToolkit -ListAvailable)){
18                        Install-Module -Name MrModule -Force
19                    }
20                    if (-not(Get-Module -Name BurntToast -ListAvailable)){
21                        Install-Module -Name BurntToast -Force
22                    }
23
24                    if (Find-MrModuleUpdate) {
25                        Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName |
26                        ForEach-Object {
27                            Start-Process -FilePath 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -ArgumentList "-NoProfile -Command New-BurntToastNotification -Text 'There are new PowerShell module updates available.'" -WindowStyle Hidden -Credential (Get-Credential -UserName $_.UserName -Store)
28                        }
29                    }
30                } | Out-Null
31
32            }
33            else {
34                Write-Warning -Message 'Aborting due to PowerShell not being run elevated as a local admin!'
35            }
36
37        }
38
39    }
40
41}

I've gone down a rabbit hole and jumped through some hoops in this blog article to display the notifications because of running PowerShell as a different user than I'm logged into Windows as, but I've learned quite a few things in the process that I'm sure will translate to other future tasks.

µ