On Demand Toast Notifications

Profile picture for user PaulW
Posted by PaulW on Sat, 12/26/2020 - 11:04pm

Sample Toast NotificationNot too long ago, there was an issue with Azure AD that broke basically everything in the cloud.  So, for many organizations, it was difficult to communicate to everyone and let them know that most everything was broken because.... Most everything was broken.  Heck, even the 911 emergency services weren't working in some parts of the country.  But, I'm sure this won't happen again...This got me thinking about creating some sort of on demand notifications from CM...

For Windows 10 feature updates, I solicited the end user with Toast notifications.  So, since they were used to toasts from IT, I thought this was another good use for toasting.  So, the logical choice was scripts as they could be triggered on demand.  And really, since it is time sensitive, you want users to get the alert now, or not at all.  So, scripts fit this very well except for one major problem.  Scripts run as system and don't run interactively.  So, there'd be a few hurdles to overcome to toast from the system context.

So, lets look at the first hurdle, getting a script running as system to interact in the logged on user's context.  There is a tool I use all the time for application deployments that handles this quite well.  The PowerShell App Deployment Toolkit.  But, I don't need the entire toolkit, just the parts to make it run interactively. So, I deconstructed it and trimmed it down to the functions needed to run the process as a user.  And then I trimmed those functions down the the minimums, removing all the logging and such.

The second hurdle is that scripts don't have content other than the script.  So, anything that I need on the PC needs to be shipped in the script.  For this, I need at least a script to trigger in the user context on the machine.  And then, with toasts, you'll want the logo and the image.  The script is simple enough to drop a string into a file.  the images can be done by Base64 encoding them and including them as a string in the script as well.  The script to create the toast and the images can then be written to a user accessible location on the computer, something original like C:\Temp.

Ok.  This all seems easy enough to do.  So, let's do it!

Script

I don't want to get too far into the weeds on the script as it over 1300 lines long.   So just an overview.

  • The script parameters will give details on the contents of the toast notice.
  • The script starts running as system account and detects the active console session.
  • The script creates temporary files in a common location that can be read by any user (C:\Temp)/
    • Invoke-ITAlertToast.ps1 -- The script that includes the needed files for the toast notice.
    • alertconfig.json -- The configuration file that is created to populate the toast.  These are the parameters from the initial script.
  • The Actual notification script creates the image files in the user's temp directory.

Update Images

Sample Toast Image LocationsThe images in the script are probably the first items you will want to update to brand to your environment.  The logo image and the hero image are the two types of images.

The hero image is the banner of the toast notice.  The optimal resolution for this is 364 x 180 pixels.  This includes the text at the top ("IT ALERT" in this example).  So, keep in mind that this text cannot be modified easily.

The logo image is the logo that appears in the circle.  The optimal size for this image is 256 x 256 pixels.

I'll leave creating your image up to you, but once it is ready, follow these steps to encode the files in Base64.

$Path = 'C:\PathToImage\Image.jpg'
[convert]::ToBase64String((get-content $Path -encoding byte))

The string that is output from this can then be copied into the script.  The text is a few thousand characters long. Copy it into the script and replace the $B64HeroImage and $B64LogoImage encoded text.

One thing to note, that string is thousands of characters long.  So, it may be worthwhile to split the string every X number of characters.  Lets say every 128 characters to make it look better in the script.  Use the following adaptation to complete that.  I think it just makes it cleaner to not have to scroll across thousands of characters.

$Path = 'C:\PathToImage\Image.jpg'
$b64text = [convert]::ToBase64String((get-content $Path -encoding byte))
While ($b64text)
{
   $x,$b64text = ([char[]]$b64text).where({$_},'Split',128)
   $x -join ''
}

You can test the encoding by sending the string back into these 2 lines:

[byte[]]$Bytes = [convert]::FromBase64String($B64LogoImage)
[System.IO.File]::WriteAllBytes('C:\Temp\Logoimage.jpg,$Bytes)

Parameters

The script has 8 parameters that build the text content in the toast.  As explained above ,the images are hard coded into the script. So, lets take a look at each of these.

  • HeaderText -- The header text is the text next to the logo. This could be similar to a from tag.
  • TitleText -- This is the heading first line in the toast notification. Sort of like the subject line.
  • BodyText1 -- This is the first paragraph in the body of the toast and is a required field.
  • BodyText2 -- This is the second paragraph in the body of the toast and is not required.
  • AlertTime -- This is like the Sent time in an email, just in case the toast is sitting on the computer for hours.
  • Expiration -- This is the date/time that the toast will no longer display after and is an optional field. So, if something is very time sensitive and you don't want it to deliver more than an hour after it has been sent, this can be used to confirm that will happen.
  • Scenario -- Possible values are: reminder | short | long
    • How long displayed:
      • Reminder: Until Dismissed
      • Short: 5 seconds
      • Long: 25 seconds
  • DismissButtonText -- This is the text that is displayed in the single button at the bottom of the toast message. Dismiss is the default text.

Implement CM Script

Now that you have the script laid out, let's import it into CM and set up the parameters.

  1. Browse to \Software Library\Overview\Scripts.Creating the CM Script
  2. Click Create Script.
  3. Name the script Invoke-ToastAsUser, or whatever you want to call it.
  4. Paste all 1300+ lines of code into the script field.
  5. Click next to configure the parameters.
  6. All 8 parameters should be available to configure.  Configure with the following settings:
    1. HeaderText -- Leave all settings default.  I'd recommend limiting the character length to about 32 characters to avoid wrapping.
    2. TitleText -- Leave this at the default as well.  I'd recommend limiting the length to about 24 characters to avoid wrapping.
    3. BodyText1 -- No change.
    4. BodyText2 -- No change.
    5. AlertTime -- Clear the default value from this field. Set max length to 19 characters. to account for a date/time value of something like 12/12/2020 11:12 PM.
    6. Expiration -- Set the max length to 19 characters for this field as well.
    7. Scenario -- Change the data type to a list. Add ReminderShort, and Long to the list.  Make sure Reminder is at the top of the list as this will be your default.
    8. DismissButtonText -- Clean up the the single quotes.  Change the text to whatever you like if Dismiss isn't to your liking.
  7. So, you should see something like this.CM Script parameters
  8. Now, finish up the wizard, approve the script and give it a shot!

See it in Action

So, lets take a look at this thing in action...  In less than a minute, you can have an alert out to your systems that are online!

Animation of the script in action

Get the Script

All the code can be found out on my github repo here.

 

Or download the script directly with this link, Invoke-ToastAsUser.ps1 (right-click and Save Link As).

Related Technology

Comments

Incredible work, congratulations and thanks for sharing with us.
When implementing and running the script in my environment (a full mesh WAN network with 115 physical locations), the script took almost 30 minutes to run on a machine that is on a remote site other than the ConfigMgr Primary Site. Running the script locally on the same machine, it was almost immediate.
Perhaps it was possible in a new version where the script is sent to the stations in a SCCM package, the scheduled task would be created, but it would be disabled.
Then, another package would be used only to activate the scheduled task and run the script locally and then disable the scheduled task again, each time it needed to send the notification.
Just speculation.
Another difficulty I encountered was that if the focus assist setting is enabled, the notification does not appear.

In reply to by Rodrigo Morato… (not verified)

Thanks for the feedback.  From my perspective, this was designed for time sensitive notifications. nothing will really get something to computers from CM faster than scripts as it uses the client fast channel (BGB).  So, it should hit most systems within a few minutes.

If you sent and triggered it via a package, you'd be talking about delivering content to DPs as well before you toast. And then also waiting for clients to check in for policy.

My goal was delivering a notification to users that email, etc was down, since I couldn't use email, etc to deliver the message.

Focus Assist is a problem that is difficult to address from everything I've read and dug into.  If you want to be sure to get something to display, you could instead use a WPF form triggered from the Powershell script, or something like that.  Really wouldn't be too hard to do that instead.  But again, this is more of a "best effort" notification rather than a "must deliver" notice.

Great work! Thanks! 

I've been looking for this solution for a week now, thank you so much for this. Works like a charm!

I'm trying to implement this and running into an error. If I run the script as is on my computer, it runs fine. However when I try running the script from MECM, I get errors in the script log:

Parameter hash verification for script 829222B1-676F-4627-BFA7-16CD51329233 failed.    Scripts    7/21/2021 3:12:11 PM    14888 (0x3A28)
CScriptsEndpoint:Parameter hash verification failed with error 0x87d0029e    Scripts    7/21/2021 3:12:11 PM    14888 (0x3A28)
Parameter hash verification failed for script with guid 829222B1-676F-4627-BFA7-16CD51329233. Error 0x87d0029e    Scripts    7/21/2021 3:12:11 PM    14888 (0x3A28)
 

Any thoughts on why this is happening? I have no finger nails left cause I've been scratching my head so much on this one...

 

I'm trying to implement this. For testing purposes only, I'm just using your script with setting the parameters and just using your images that you provided. If I run it locally, it works just fine. However when I put the script into MECM and try to run it, it fails. I get the following errors in the scripts.log file.

Parameter hash verification for script D1ECAEB8-F722-4BC5-A3C4-C87AFE060F81 failed.    Scripts    1/19/2021 12:17:54 PM    16276 (0x3F94)
CScriptsEndpoint:Parameter hash verification failed with error 0x87d0029e    Scripts    1/19/2021 12:17:54 PM    16276 (0x3F94)
Parameter hash verifiction failed for script with guid D1ECAEB8-F722-4BC5-A3C4-C87AFE060F81. Error 0x87d0029e    Scripts    1/19/2021 12:17:54 PM    16276 (0x3F94)
 

Any thoughts on that?

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
Verify you are a human.