Worklet - Toast Notifications with Branding and Ignores Focus Assist

  • 24 January 2022
  • 8 replies
  • 1092 views

Userlevel 5
Badge +1

Yup. Another Toast Notification post :-) What makes this one unique is how customizable it can be with company branding and links to internal sites, interactive buttons and so on. Once you get the hang of this one you can apply this to multiple scenarios that fit your needs.

  1. Really need to read Martin Bengtsson blog before you get started. https://www.imab.dk/windows-10-toast-notification-script/ 
  2. Download the scripts here https://github.com/imabdk/Toast-Notification-Script
  3. Edit config-toast.xml to your satisfaction or desired outcome.
  4. Create a new worklet and associate to the right groups
  5. Set the Evaluation Code to whatever you want
    exit 1
  6. Set the Remediation Code Payload
    # Scheduled Task Name
    $schdtaskname = 'ToastNotify'

    # Working Directory
    $workdir = 'C:\ProgramData\ToastNotify'
    IF((Test-Path $workdir) -eq $false){mkdir $workdir -Force | Out-Null}
    IF((Test-Path $workdir\Images) -eq $false){mkdir $workdir\Images -Force | Out-Null}

    # Cleanup Prior Scheduled tasks if they exist
    Unregister-ScheduledTask -TaskName "$schdtaskname" -Confirm:$false | Out-Null

    # Build RunToastHidden.cmd
    $cmd = @"
    powershell.exe -NoProfile -NoLogo -NonInteractive -ExecutionPolicy Bypass -File "$workdir\New-ToastNotification.ps1"
    "@
    New-Item -Path "$workdir" -Name "RunToastHidden.cmd" -ItemType "file" -Value $cmd -force | Out-Null

    # Copy Toast Notify Files
    $collection = 'ToastLogoImageDefault.jpg','ToastHeroImageDefault.jpg'
    foreach ($file in $collection){
    IF(!(Test-Path $workdir\$file)){ Copy-Item ".\$file" $workdir\Images | out-null }
    }
    $collection = 'New-ToastNotification.ps1','config-toast.xml','Hidden.vbs'
    foreach ($file in $collection){
    IF((Get-ChildItem $workdir\$file -ErrorAction SilentlyContinue).LastWriteTime -lt (Get-ChildItem $file).LastWriteTime){
    Copy-Item ".\$file" $workdir | out-null
    }
    }

    # Scheduled Task Functions
    Function Schedule-ToastNotify{
    $TaskStartTime = (Get-Date 12pm)
    $SchedService = New-Object -ComObject Schedule.Service
    $SchedService.Connect()
    $Task = $SchedService.NewTask(0)
    $Task.RegistrationInfo.Description = 'ToastNotify'
    $Task.Settings.Enabled = $true
    $Task.Settings.AllowDemandStart = $true
    $Task.Settings.WakeToRun = $true
    $trigger = $Task.triggers.Create(1) # https://docs.microsoft.com/en-us/windows/win32/taskschd/triggercollection-create
    $trigger.StartBoundary = $TaskStartTime.ToString("yyyy-MM-dd'T'HH:mm:ss")
    $trigger.Enabled = $true
    $action = $Task.Actions.Create(0)
    $action.Path = "wscript"
    $action.Arguments = "`"$workdir\Hidden.vbs`" `"$workdir\RunToastHidden.cmd`""
    $taskFolder = $SchedService.GetFolder('\')
    $taskFolder.RegisterTaskDefinition("$schdtaskname", $Task , 6, 'Users', $null, 4) | out-null
    }

    # Running Scheduled Task Function
    function Run-ToastNotify{
    IF($TaskSchd -eq $true){
    $time = New-ScheduledTaskTrigger -At (Get-Date).AddSeconds(3) -Once
    Set-ScheduledTask -TaskName "$schdtaskname" -Trigger $time | Out-Null
    }
    }

    # Schedule and Run the Scheduled Task
    Schedule-ToastNotify
    Run-ToastNotify
  7. Upload ALL the files to the worklet (config-toast.xml, Hidden.vbs, New-ToastNotification.ps1, ToastHeroImageDefault.jpg, ToastLogoImageDefault.jpg):
  8. Run worklet

 

After the worklet runs the end user should see this. Take note of the notification icon in lower right with the “moon” logo indicating that Focus Assist is on.
 

 

 

Here you can see where the worklet put data on the local drive

 

Troubleshooting

log data for the toast notification is stored under C:\Users\%username%\AppData\Roaming\ToastNotficationScript\

 

 


8 replies

Userlevel 1
Badge

Hi,

We are currently looking for something like this, but I can´t get this worklet to run.

Through Powershell, on my machine, it works, but through Automox it´s not working, and we can´t debug why.

Thanks

Userlevel 5
Badge +1

Sometimes I will drop in this line at the top of the script to get an idea of what is happening when the worklet runs. After the worklet runs I review output of C:\windows\temp\worklet.txt

 

Start-Transcript C:\Windows\Temp\worklet.txt

Another option is try and see what is not happening by evaluating the various parts the script accomplishes

  1. Does it create the directory C:\ProgramData\ToastNotify
  2. Does it create a file C:\ProgramData\ToastNotify\RunToastHidden.cmd
  3. Did these files “config-toast.xml, Hidden.vbs, New-ToastNotification.ps1, ToastHeroImageDefault.jpg, ToastLogoImageDefault.jpg” get copied under C:\ProgramData\ToastNotify
  4. Does a schedule task called ToastNotify exist
  5. Is there a history of the Scheduled Task running
  6. If the scheduled task is running, did any output get generated here C:\Users\%username%\AppData\Roaming\ToastNotficationScript\ for the currently logged in user...
Userlevel 1
Badge

Thanks for the ideas! 

We’ll keep debugging and update here with our final output. 

Hi Jack, after suggesting using this for reboots on another thread, I’ve been pondering what else to use it for.

 

The obvious one seems to be when you need a user to close an application before running an update. Looking at the Toast script, you can add functionality to invoke an install from SCCM, but would there be a way to trigger an Automox policy to run? Do you know if the locally installed agent for example could be triggered to run a particular policy?

Thanks!

Danny

Userlevel 5
Badge +1

I’d like to think it would be possible. To run an Automox policy one would need to know the Client ID and the Policy ID.

 

Here is sample code that would let you run a policy from Automox remotely. Assumes a few things:

  • assets.csv is a payload containing every asset under columns: hostname,id (otherwise during runtime you would have to query all assets just to filter on the asset or have the id pre-staged on each client)
  • PolicyID which you is in the URL of any policy you find in the console. 
$assetid = (Import-Csv assets.csv | where-object hostname -eq $env:COMPUTERNAME).id
$policy = '123456'
$orgID = '12345'

$headers = @{ "Authorization" = "Bearer $apiKey" } # $apiKey being stored as secret in Automox

$url = "https://console.automox.com/api/policies/$policy/action?o=$orgID&action=remediateServer&serverId=$AssetId"
Invoke-WebRequest -Method POST -Uri $url -Headers $headers -UseBasicParsing

The next part, I’m going to assume will require a lot of trial and error to get just right. 

In the script “New-ToastNotification.ps1” around line 1998, you might be able to just replace the code under the section “Running RunApplicationID function” with the code block above instead of having it call that function “Write-ApplicationIDRegistry”. I’m about 90% sure that would work. I’ve not tried it, but would think it possible. 

Ultimately to solution this it requires time and a deep understanding of how this XML configuration file and “New-ToastNotification.ps1” script runs. Paying close attention to those action buttons and how that flow works. 

 

 

Userlevel 5
Badge +1

On second thought… perhaps just code in the remediation necessary over trying to figure out Policy and Agent IDs. I mean, at this point a script is running to display all the content to the end user. 

Hi Jack, thanks for this, I’ll see what I can do and will report back.

 

So just put the remediation script in “New-ToastNotification.ps1” rather than calling back to Automox (which I guess will report on success insomuch as the application will show as installed?)?

 

Of course, any third or fourth thoughts you have along the way would be greatly received as I’m flying by the seat of my pants!

Userlevel 5
Badge +1

The actions run during the New-ToastNotification.ps1 script runtime would not reflect in the Automox worklet activity log. Only exit codes from the code dropped in the remediation section of the worklet reflect in the activity log. Which makes a feedback loop for anything executed beyond that a bit harder.  

 

To get a desired feedback I’d focus on the evaluation code in the worklet itself, which in a way can help us see successful or not in the console. Given you may be waiting for user input, the immediate evaluation run post remediation code is not likely to yield anything great. Will have to rely on the scan cadence (default is 24 hours) or manually do a re-scan of the agent. 

You definitely have a pretty cool use case here. re-purposing this tool to act in a similar way  as it already does with SCCM would likely close a lot of gaps I think all of us Automox customers would love to have. Complex workflows with on-demand end user interactions.

 

 

Reply