On Demand Toast Notifications

Profile picture for user PaulW

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.  You can replace the text with your own default.  I'd recommend limiting the character length to about 32 characters to avoid wrapping.  If there are still single quotes ( ' ) in this field they should be removed.
    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?

In reply to by Todd Christensen (not verified)

I got the same problem.  Finally figured out that parameters must NOT be in single quotes.  After comparing the animated GIF to what I'm doing, getting rid of the single quotes fixes the problem and the toast goes through.

Hey, thanks for the comment! Yes, single quotes could definitely mess with the parameter parsing. If you want them, you could try escaping them with a back tick.  But generally, try avoiding the special characters like these all together. 

This may have worked on older W10 versions but on W10 21H2 placement="attribution" no longer puts it next to the app display name in the attribution area...

Hi. Perfect solution for me. But when i run the script, no Toast appear. The temporary files will be created, but no Message appears. What do i wrong?

In reply to by Robert (not verified)

It may not be working in newer versions of windows 10, for sure doesn't work in win 11 as they have changed that a bunch.  No longer in this role so, haven't using it or updated since.  If I get a chance sometime, i'll take a look at it. :-)

Yah, this script is what I modeled from partly, or got the idea from.  I actually ended up testing in my lab last night.  It was working on 21H1 and Windows 11 in my lab.  So, windows 11 does work surprisingly :).  And I upgraded the Win 10 machine to 22H2 this morning, and that one is working as well.  So, must be something in your environment maybe?  Are you seeing the temp files created and deleted in C:\Temp?  And do you see the image files get created in the logged in user's temp (%temp%) directory?  If you don't see the image files, then the task isn't triggering or is failing for some reason.  PowerShell restrictions in your environment at all (Group Policy)?

But when you run it as a script from CM it doesn't work?  Running as admin and running as a script are very different.  And what about the temp files? are they created in both c:\Temp and %temp% in the logged on user's profile. I asked this because it would help with troubleshooting how far along it gets.

when i run this script in CM, nothing happens! Firewall Ports 5985 and 5986 are open.
When i run the scrip local with user rights, files appear in c:\temp but no images in %temp%
When i run the script local as admin, it runs like a charm. 
 

This would be expected then.  It creates a scheduled task that would require admin rights.  CM Scripts run as local system when executed through CM.  Either some bad data being passed into the script, usually a ' or an issue with your fast channel.  Check the scripts.log client side, see if anything shows up there.

script.log

Parameter hash verification for script 0D97D34D-21E1-4267-87C2-CB4E5876AD2C failed.    Scripts    10.01.2023 07:40:54    19348 (0x4B94)
CScriptsEndpoint:Parameter hash verification failed with error 0x87d0029e

Parameter hash verification failed for script with guid 0D97D34D-21E1-4267-87C2-CB4E5876AD2C. Error 0x87d0029e

Yup, that typically means there were special characters in one or more of the script parameters.  Avoid the special characters.  Especially’ ‘ “ ( ) { } |

When the script imports into cm , it brings a few of them with it from the default parameters of the script.  This may have been the case. These have to be cleaned up during the import.  I wish it was better at validating the inputs. 

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.