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
The BurntToast module contains several functions, but the one I'm interested in is
New-BurntToastNotification
.
1Get-Command -Module BurntToast
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.'
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
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.'"
Success! The previous command displays the following toast notification.
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
If you haven't already, store the credentials in credential manager for the user who is logged into Windows.
1Get-Credential -Store
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
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.
µ