-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathNew-ToastNotification-AppLocker-Edition.ps1
2629 lines (2485 loc) · 147 KB
/
New-ToastNotification-AppLocker-Edition.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<#
.SYNOPSIS
Create nice toast notifications for the logged on user in Windows 10.
.DESCRIPTION
Everything is customizeable through config-toast.xml.
Config-toast.xml can be locally, hosted online in blob storage or set to an UNC path with the -Config parameter.
This way you can quickly modify the configuration without the need to push new files to the computer running the toast.
Can be used for improving the numbers in Windows Servicing as well as kindly reminding users of pending reboots (and a bunch of other use cases as well).
All actions are logged to a local log file in AppData\Roaming\ToastNotificationScript\New-ToastNotification.log.
.PARAMETER Config
Specify the path for the config.xml. If none is specified, the script uses the local config.xml
.NOTES
Filename: New-ToastNotification.ps1
Version: 2.3.0
Author: Martin Bengtsson
Blog: www.imab.dk
Twitter: @mwbengtsson
Version history:
1.0 - Script created
1.1 - Separated checks for pending reboot in registry/WMI from OS uptime.
More checks for conflicting options in config.xml.
The content of the config.xml is now imported with UTF-8 encoding enabling other characters to be used in the text boxes.
1.2 - Added option for personal greeting using given name retrieved from Active Directory. If no AD available, the script will use a placeholder.
Added ToastReboot protocol example, enabling the toast to carry out a potential reboot.
1.3 - All text elements in the toast notification is now customizeable through the config.xml
Expanded the options for finding given name. Now also looking in WMI if no local AD is available.
Added Get-WindowsVersion function: Testing for supported Windows version
Added Test-WindowsPushNotificationsEnabled function: Testing for OS toast blockers
Added some more detailed logging
Added contributions from @SuneThomsenDK @ https://www.osdsune.com
- Date formatting in deadline group
- Fixed a few script errors
- More text options
1.4 - Added new feature for checking for local active directory password expiration.
If the password is about to expire (days configured in config.xml), the toast notification will display reminding the users to change their password
1.4.1 - Get-ADPasswordExpiration function modified to not requiring the AD Powershell module. Thank you @ Andrew Wells :-)
Improved logging for when no toast notifications are displayed
More commenting
1.4.2 - Bug fixes to the date formatting of ADPasswordExpiration now correctly supporting different cultures
1.4.3 - Some minor corrections to the get-givenname function when retreiving first name from WMI and registry
Moved the default location for New-ToastNotification.log file to the user's profile
Added contribution from @kevmjohnston @ https://ccmcache.wordpress.com
- Added function for retrieving deadline date and time dynamically in WMI with ConfigMgr
1.5 - Added new option to run task sequences (PackageID) directly from the toast notification action button. Enable the option <RunPackageID> in the config.xml
Fixed a few script errors when running the script on a device without ConfigMgr client
1.6 - Added new option to run applications (ApplicationID) directly from the toast notification action button. Enable the option <RunApplicationID> in the config.xml
Created Display-ToastNotification function
- Displaying the toast notification as been trimmed and merged into its own function
Created Test-NTsystem function
- Testing if the script is being run as SYSTEM. This is not supported
Converted all Get-WMIObject to Get-CimInstance
- Get-WMIObject has been deprecated and is replaced with Get-CimInstance
1.7 - Added multilanguage support. Thank you Matt Benninge @matbe
- Script and config files now support multiple languages
- Note that old config xml files needs to be updated to support this
- Moved text values from option to the text-section for consistency
1.7.1 - Added 2 new options (LogoImageName and HeroImageName) to the config file, allowing switching of images more easily and dynamically
1.8.0 - Added support for using Windows 10 Toast Notification Script with Endpoint Analytics Proactive Remediation
- Added support for having config.xml file hosted online
- Added support for having images used in the script hosted online
** Most of the work done in version 2.0.0 is done by Chad Brower // @Brower_Cha on Twitter **
** I have added the additional protocols/scripts and rewritten some minor things **
** As well as added support for dynamic deadline retrieval for software updates **
** Stuff has been rewritten to suit my understanding and thoughts of the script **
2.0.0 - Huge changes to how this script handles custom protocols
Added Support for Custom Actions/Protocols within the script under user context removing the need for that to be run under SYSTEM/ADMIN
- <Option Name="Action" Value="ToastRunUpdateID:" />
- <Option Name="Action" Value="ToastRunPackageID:" />
- <Option Name="Action" Value="ToastRunApplicationID:" />
- <Option Name="Action" Value="ToastReboot:" />
Added Support to dynamically create Custom Action Scripts to support Custom Protocols
Added Support for Software (Feature) Updates : Searches for an update and will store in variable
Added new XML Types for Software Updates:
- <Option Name="RunUpdateID" Enabled="True" Value="3012973" />
- <Option Name="RunUpdateTitle" Enabled="True" Value="Version 1909" />
Added support for getting deadline date/time dynamically for software updates
- Configure DynamicDeadline with the UpdateID
2.0.1 - Updated custom action scripts!
- Moved all custom action scripts into the user's profile in $env:APPDATA\ToastNotificationScript
- $env:ALLUSERSPROFILE was used previously. This is a bad location if device is used by multiple users due to permission issues
- Updated all custom action scripts to invoke their respective action via WMI
- Rewritten all custom action scripts
- Added logic allowing new custom action scripts to be created if necessary
- Now checks script version in registry
- If newer version is available from the script, new custom action scripts will be created
- This allows me to make sure the relevant scripts are in place in case I change something along the way
- Modified script output of custom script for RunPackageID to pick up Program ID dynamically
Added support for getting deadline date/time dynamically for applications
- Configure DynamicDeadline with the Application ID
2.0.2 - Fixed an error in the custom protocols
- The path to the custom scripts was incomplete
2.1.0 - Added a second action button: ActionButton2
- This allows you to have 2 separate actions. Example: Action1 starts a task sequence, action2 sends the user to a web page for more info
- This will require new config.xml files
Reworked Get-GivenName function
- Now looks for given name in 1) local Active Directory 2) with WMI and the ConfigMgr client 3) directly in registry
- Now checks 3 places for given name, and if no given name found at all, a placeholder will be used
Fixed CustomAudioToSpeech option
- This part haven't worked for a while it seems
- Only works properly with en-US language
Added Enable-WindowsPushNotifications function // Thank you @ Trevor Jones: https://smsagent.blog/2020/11/12/prevent-users-from-disabling-toast-notifications-can-it-be-done/
- This will force enable Windows toast notification for the logged on user, if generally disabled
- A Windows service will be restarted in the process in the context of the user
2.2.0 - Added built-in prevention of having multiple toast notifications to be displayed in a row
- This is something that can happen, if a device misses a schedule in configmgr.
- The nature of configmgr is to catch up on the missed schedule, and this can lead to multiple toast notifications being displayed
Added the ability to run the script coming from SYSTEM context // Thank you @ Andrew: https://twitter.com/AndrewZtrhgf :-)
- This has proven to only work with packages/programs/task sequences and when testing with psexec.
- Running the script in SYSTEM, with the script feature in configmgr and proactive remediations in Intune, still yields unexpected results
2.3.0 - Added the Register-CustomNotificationApp function
- This function retrieves the value of the CustomNotificationApp option from the config.xml
- The function then uses this name, to create a custom app for doing the notification.
- This will reflect in the shown toast notification, instead of Software Center or PowerShell
- This also creates the custom notifcation app with a prevention from disabling the toast notifications via the UI
2.3.1 - Dedicated version tailored to use when running AppLocker, constrained language mode and having the script digitally signed
- For now this only works with ConfigMgr, as I haven't found a way to dot-source the script when coming from Intune (Proactive Remediations)
.LINK
https://www.imab.dk/windows-10-toast-notification-script/
#>
[CmdletBinding()]
param(
[Parameter(HelpMessage='Path to XML Configuration File')]
[string]$Config
)
#region Functions
# Create Write-Log function
function Write-Log() {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path = "$env:APPDATA\ToastNotificationScript\New-ToastNotification.log",
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level = "Info"
)
Begin {
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process {
if (Test-Path $Path) {
$LogSize = (Get-Item -Path $Path).Length/1MB
$MaxLogSize = 5
}
# Check for file size of the log. If greater than 5MB, it will create a new one and delete the old.
if ((Test-Path $Path) -AND $LogSize -gt $MaxLogSize) {
Write-Error "Log file $Path already exists and file exceeds maximum file size. Deleting the log and starting fresh."
Remove-Item $Path -Force
$NewLogFile = New-Item $Path -Force -ItemType File
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (-NOT(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End {
}
}
# Create Pending Reboot function for registry
function Test-PendingRebootRegistry() {
Write-Log -Message "Running Test-PendingRebootRegistry function"
$CBSRebootKey = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -ErrorAction Ignore
$WURebootKey = Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -ErrorAction Ignore
$FileRebootKey = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -ErrorAction Ignore
if (($CBSRebootKey -ne $null) -OR ($WURebootKey -ne $null) -OR ($FileRebootKey -ne $null)) {
Write-Log -Message "Check returned TRUE on ANY of the registry checks: Reboot is pending!"
$true
}
else {
Write-Log -Message "Check returned FALSE on ANY of the registry checks: Reboot is NOT pending!"
$false
}
}
# Create Pending Reboot function for WMI via ConfigMgr client
function Test-PendingRebootWMI() {
Write-Log -Message "Running Test-PendingRebootWMI function"
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
Write-Log -Message "Computer has ConfigMgr client installed - checking for pending reboots in WMI"
$Util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
$Status = $Util.DetermineIfRebootPending()
if (($Status -ne $null) -AND ($Status.RebootPending -eq $True)) {
Write-Log -Message "Check returned TRUE on checking WMI for pending reboot: Reboot is pending!"
$true
}
else {
Write-Log -Message "Check returned FALSE on checking WMI for pending reboot: Reboot is NOT pending!"
$false
}
}
else {
Write-Log -Level Error -Message "Computer has no ConfigMgr client installed - skipping checking WMI for pending reboots"
$false
}
}
# Create Get Device Uptime function
function Get-DeviceUptime() {
Write-Log -Message "Running Get-DeviceUptime function"
$OS = Get-CimInstance Win32_OperatingSystem
$Uptime = (Get-Date) - ($OS.LastBootUpTime)
$Uptime.Days
}
# Create Get GivenName function
function Get-GivenName() {
Write-Log -Message "Running Get-GivenName function"
try {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$PrincipalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain, [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain())
$GivenName = ([System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($PrincipalContext,[System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName,[Environment]::UserName)).GivenName
$PrincipalContext.Dispose()
}
catch [System.Exception] {
Write-Log -Level Error -Message "$_"
}
if (-NOT[string]::IsNullOrEmpty($GivenName)) {
Write-Log -Message "Given name retrieved from Active Directory: $GivenName"
$GivenName
}
# This is the last resort of trying to find a given name. This part will be used if device is not joined to a local AD, and is not having the configmgr client installed
elseif ([string]::IsNullOrEmpty($GivenName)) {
Write-Log -Message "Given name not found in AD or no local AD is available. Continuing looking for given name elsewhere"
$RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
if ((Get-ItemProperty $RegKey).LastLoggedOnDisplayName) {
$LoggedOnUserDisplayName = Get-Itemproperty -Path $RegKey -Name "LastLoggedOnDisplayName" | Select-Object -ExpandProperty LastLoggedOnDisplayName
if (-NOT[string]::IsNullOrEmpty($LoggedOnUserDisplayName)) {
$DisplayName = $LoggedOnUserDisplayName.Split(" ")
$GivenName = $DisplayName[0]
Write-Log -Message "Given name found directly in registry: $GivenName"
$GivenName
}
else {
Write-Log -Message "Given name not found in registry. Using nothing as placeholder"
$GivenName = $null
}
}
else {
Write-Log -Message "Given name not found in registry. Using nothing as placeholder"
$GivenName = $null
}
}
}
# Create Get-WindowsVersion function
# This is used to determine if the script is running on Windows 10 or not
function Get-WindowsVersion() {
$OS = Get-CimInstance Win32_OperatingSystem
if (($OS.Version -like "10.0.*") -AND ($OS.ProductType -eq 1)) {
Write-Log -Message "Running supported version of Windows. Windows 10 and workstation OS detected"
$true
}
elseif ($OS.Version -notlike "10.0.*") {
Write-Log -Level Error -Message "Not running supported version of Windows"
$false
}
else {
Write-Log -Level Error -Message "Not running supported version of Windows"
$false
}
}
# Create Windows Push Notification function.
# This is testing if toast notifications generally are disabled within Windows 10
function Test-WindowsPushNotificationsEnabled() {
$ToastEnabledKey = (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\PushNotifications" -Name ToastEnabled -ErrorAction Ignore).ToastEnabled
if ($ToastEnabledKey -eq "1") {
Write-Log -Message "Toast notifications for the logged on user are enabled in Windows"
$true
}
elseif ($ToastEnabledKey -eq "0") {
Write-Log -Level Error -Message "Toast notifications for the logged on user are not enabled in Windows. The script will try to enable toast notifications for the logged on user"
$false
}
}
# Create Enable-WindowsPushNotifications
# This is used to re-enable toast notifications if the user disabled them generally in Windows
function Enable-WindowsPushNotifications() {
$ToastEnabledKeyPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications"
Write-Log -Message "Trying to enable toast notifications for the logged on user"
try {
Set-ItemProperty -Path $ToastEnabledKeyPath -Name ToastEnabled -Value 1 -Force
Get-Service -Name WpnUserService** | Restart-Service -Force
Write-Log -Message "Successfully enabled toast notifications for the logged on user"
}
catch {
Write-Log -Level Error -Message "Failed to enable toast notifications for the logged on user. Toast notifications will probably not be displayed"
}
}
# Create function for testing for local Active Directory password expiration
# Thank you @ Andrew Wells :-)
function Get-ADPasswordExpiration([string]$fADPasswordExpirationDays) {
Write-Log -Message "Running Get-ADPasswordExpiration function"
try {
Write-Log -Message "Looking up SamAccountName and DomainName in local Active Directory"
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$PrincipalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain,[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain())
$SamAccountName = ([System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($PrincipalContext,[System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName,[Environment]::UserName)).SamAccountName
$DomainName = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
$PrincipalContext.Dispose()
}
catch [System.Exception] {
Write-Log -Level Error -Message "$_"
}
if (($SamAccountName) -AND ($DomainName)) {
Write-Log -Message "SamAccountName found: $SamAccountName and DomainName found: $DomainName. Continuing looking for AD password expiration date"
try {
$Root = [ADSI] "LDAP://$($DomainName)"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Root, "(SamAccountName=$($SamAccountName))")
$Searcher.PropertiesToLoad.Add("msDS-UserPasswordExpiryTimeComputed") | Out-Null
$Result = $Searcher.FindOne();
$ExpiryDate = [DateTime]::FromFileTime([Int64]::Parse((($Result.Properties["msDS-UserPasswordExpiryTimeComputed"])[0]).ToString()))
}
catch {
Write-Log -Level Error -Message "Failed to retrieve password expiration date from Active Directory. Script is continuing, but without password expiration date"
}
if ($ExpiryDate) {
Write-Log -Message "Password expiration date found. Password is expiring on $ExpiryDate. Calculating time to expiration"
$LocalCulture = Get-Culture
$RegionDateFormat = [System.Globalization.CultureInfo]::GetCultureInfo($LocalCulture.LCID).DateTimeFormat.LongDatePattern
$ExpiryDate = Get-Date $ExpiryDate -f "$RegionDateFormat"
$Today = Get-Date -f "$RegionDateFormat"
$DateDiff = New-TimeSpan -Start $Today -End $ExpiryDate
if ($DateDiff.Days -le $fADPasswordExpirationDays -AND $DateDiff.Days -ge 0) {
Write-Log -Message "Password is expiring within the set period. Returning True"
Write-Log -Message "ADPasswordExpirationDays is set to: $fADPasswordExpirationDays"
# Return status, date and days until expiration
$true
$ExpiryDate
$DateDiff
}
else {
Write-Log -Message "Password is not expiring anytime soon. Returning False"
Write-Log -Message "ADPasswordExpirationDays is set to: $fADPasswordExpirationDays"
$false
}
}
elseif (-NOT($ExpiryDate)) {
Write-Log -Level Error -Message "No password expiration date found. Returning False"
$false
}
}
elseif (-NOT($SamAccountName) -OR ($DomainName)) {
Write-Log -Level Error -Message "Failed to retrieve SamAccountName or DomainName from local Active Directory. Script is continuing, but password expiration date cannot be retrieved"
$false
}
}
# Create function for retrieving deadline directly from WMI based on the PackageID, UpdateID or ApplicationID
# This works for Task Sequences, regular packages, software updates and applications
# Thank you @kevmjohnston :-)
function Get-DynamicDeadline() {
Write-Log -Message "Running Get-DynamicDeadline function. Trying to get deadline details from WMI and ConfigMgr"
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
# Getting package or task sequence information from WMI
if ($RunPackageIDEnabled -eq "True") {
Write-Log -Message "RunPackageIDEnabled is True. Trying to get deadline information based on package id"
try {
$PackageID = Get-CimInstance -Namespace root\ccm\clientsdk -Query "SELECT * FROM CCM_Program where PackageID = '$DynDeadlineValue'"
}
catch {
Write-Log -Level Error -Message "Failed to get Package ID from WMI"
}
}
# Getting software update information from WMI
elseif ($RunUpdateIDEnabled -eq "True") {
Write-Log -Message "RunUpdateIDEnabled is True. Trying to get deadline information based on update id"
$UpdateID = Get-CMUpdate
}
# Getting application information from WMI
elseif ($RunApplicationIDEnabled -eq "True") {
Write-Log -Message "RunApplicationIDEnabled is True. Trying to get deadline information based on application id"
try {
$ApplicationID = Get-CimInstance -Namespace root\ccm\clientsdk -Query "SELECT * FROM CCM_Application where ID = '$DynDeadlineValue'"
}
catch {
Write-Log -Level Error -Message "Failed to get Application ID from WMI"
}
}
# If not used with any of the options which supports getting the deadline dynamically
else {
Write-Log -Level Error -Message "Currently no option enabled within the toast configuration which supports getting the deadline retrieved dynamically"
Write-Log -Level Error -Message "This currently only works for packages/task sequences and software updates"
}
# If a package ID was retrieved, get deadline information
if (-NOT[string]::IsNullOrEmpty($PackageID)) {
# Get the deadline based on the package id
# The Where-Object clause filters out any old/dummy deadline values
# The Measure-Object clause returns only the earliest deadline if multiple program instances are found. In testing, I've only seen one instance
# per package ID even if multiple deployments of the same task sequence with different deadlines are targeted, so this is more of a failsafe
Write-Log -Message "PackageID retrieved. PackageID is: $DynDeadlineValue. Now getting deadline date and time"
$Deadline = ($PackageID | Where-Object {$_.Deadline -gt (Get-Date).AddDays(-1)} | Measure-Object -Property Deadline -Minimum).Minimum
if ($Deadline) {
# Deadline date and time retrieved. I'm formatting the date later on in the actual toast xml
Write-Log -Message "Deadline date and time successfully retrieved from WMI. Deadline is: $Deadline"
$Deadline.ToUniversalTime()
}
else {
Write-Log -Level Error -Message "Failed to get deadline date and time from WMI"
Write-Log -Level Error -Message "Please check if there really is a deadline configured"
Write-Log -Level Error -Message "The script is continuing, but the toast is displayed without deadline date and time"
}
}
# If a software update ID was retrieved, get deadline information
elseif (-NOT[string]::IsNullOrEmpty($UpdateID)) {
Write-Log -Message "Update ID retrieved. Update ID is: $DynDeadlineValue. Now getting deadline date and time"
if (-NOT[string]::IsNullOrEmpty($UpdateID.Deadline)) {
Write-Log -Message "Deadline date and time successfully retrieved from WMI. Deadline is: $($UpdateID.Deadline)"
$UpdateID.Deadline.ToUniversalTime()
}
else {
Write-Log -Level Error -Message "Failed to get deadline date and time from WMI"
Write-Log -Level Error -Message "Please check if there really is a deadline configured"
Write-Log -Level Error -Message "The script is continuing, but the toast is displayed without deadline date and time"
}
}
# If a application ID was retrieved, get deadline information
elseif (-NOT[string]::IsNullOrEmpty($ApplicationID)) {
Write-Log -Message "Application ID retrieved. Application ID is: $DynDeadlineValue. Now getting deadline date and time"
if (-NOT[string]::IsNullOrEmpty($ApplicationID.Deadline)) {
Write-Log -Message "Deadline date and time successfully retrieved from WMI. Deadline is: $($ApplicationID.Deadline)"
$ApplicationID.Deadline.ToUniversalTime()
}
else {
Write-Log -Level Error -Message "Failed to get deadline date and time from WMI"
Write-Log -Level Error -Message "Please check if there really is a deadline configured"
Write-Log -Level Error -Message "The script is continuing, but the toast is displayed without deadline date and time"
}
}
else {
Write-Log -Level Warn -Message "Appears that the specified Package ID or Update ID or Application ID: $DynDeadlineValue is not deployed to the device"
}
}
else {
Write-Log -Level Error -Message "ConfigMgr service not found. This function requires the ConfigMgr client to be installed"
}
}
# Create Get-CMUpdate function
# This gets information about a deployed software update from WMI on the device
# HUGE shout-out to Chad Brower // @Brower_Cha on Twitter
# Added in version 2.0.0
function Get-CMUpdate() {
Write-Log -Message "Running Get-CMUpdate function"
# If the ConfigMgr service exist
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
try {
# Get update information from WMI based on UpdateID and UpdateTitle
$GetCMUpdate = Get-CimInstance -Namespace root\ccm\clientSDK -Query "SELECT * FROM CCM_SoftwareUpdate WHERE ArticleID = '$RunUpdateIDValue' AND Name LIKE '%$RunUpdateTitleValue%'"
}
catch {
Write-Log -Level Error -Message "Failed to retrieve UpdateID from WMI with the CM client"
}
# If an update is based on the details from config.xml is deployed to the computer
if (-NOT[string]::IsNullOrEmpty($GetCMUpdate)) {
# Check EvaluationState: https://docs.microsoft.com/en-us/mem/configmgr/develop/reference/core/clients/sdk/ccm_softwareupdate-client-wmi-class
# There are 28 eval states. Perhaps add them all in the future
switch ($GetCMUpdate.EvaluationState) {
0 { $EvaluationState = 'None' }
1 { $EvaluationState = 'Available' }
2 { $EvaluationState = 'Submitted' }
7 { $EvaluationState = 'Installing' }
8 { $EvaluationState = 'Reboot' }
9 { $EvaluationState = 'Reboot' }
13 { $EvaluationState = 'Error' }
}
# If the evaluation of the update is in a desired state, write the details to output
if ($EvaluationState -eq "None" -OR $EvaluationState -eq "Available" -OR $EvaluationState -eq "Submitted") {
Write-Log -Level Info -Message "Found update that matches UpdateID: $($GetCMUpdate.ArticleID) and name: $($GetCMUpdate.Name)"
$GetCMUpdate
}
# If the evaluation state is in error, retry the installation of the software update, but write this to log separately
elseif ($EvaluationState -eq "Error") {
Write-Log -Message "UpdateID: $($GetCMUpdate.ArticleID) is in evaluation state: $EvaluationState. Retrying installation"
$GetCMUpdate
}
# If the evalutation is not in a desired state, do not display toast notification and exit script
else {
Write-Log -Level Error -Message "EvalutationState of UpdateID: $($GetCMUpdate.ArticleID) is not set to available. EvaluationState is: $EvaluationState"
Write-Log -Level Error -Message "Script will exit here. Not displaying toast notification when when EvaluationState is: $EvaluationState"
# Terminating initiating powershell process due to requirement from using AppLocker and constrained language mode
Terminate-ToastProcess
}
}
# If the software update defined in the config.xml file is not found on the system, do not display toast notification and exit script
else {
Write-Log -Level Error -Message "Specified update was not found on system. UpdateID: $RunUpdateIDValue and name: $RunUpdateTitleValue. Please check deployment in ConfigMgr"
Write-Log -Level Error -Message "Script will exit here. Not displaying toast notification when specified update is not deployed"
# Terminating initiating powershell process due to requirement from using AppLocker and constrained language mode
Terminate-ToastProcess
}
}
else {
Write-Log -Level Error -Message "ConfigMgr service not found. This function requires the ConfigMgr client to be installed"
}
}
# Create Write-PackageIDRegistry function
function Write-PackageIDRegistry() {
Write-Log -Message "Running Write-PackageIDRegistry function"
$RegistryPath = "HKCU:\SOFTWARE\ToastNotificationScript"
$RegistryName = "RunPackageID"
# Making sure that the registry path being used exists
if (-NOT(Test-Path -Path $RegistryPath)) {
try {
New-Item -Path $RegistryPath -Force
}
catch {
Write-Log -Message "Error. Could not create ToastNotificationScript registry path" -Level Error
}
}
# If the PackageID specified in the config.xml is picked up
if ($RunPackageIDValue) {
# If the ConfigMgr service exist
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
# Testing if the PackageID specified in the config.xml actually is deployed to the device
try {
$TestPackageID = Get-CimInstance -Namespace root\ccm\clientsdk -Query "SELECT * FROM CCM_Program WHERE PackageID = '$RunPackageIDValue'"
}
catch {
Write-Log -Level Error -Message "Failed to retrieve $RunPackageIDValue from WMI"
}
# If the PackageID is found in WMI with the ConfigMgr client, tattoo that PackageID into registry
if ($TestPackageID) {
Write-Log -Message "PackageID: $RunPackageIDValue was found in WMI as deployed to the client"
Write-Log -Message "Writing the PackageID to registry"
if ((Get-ItemProperty -Path $RegistryPath -Name $RegistryName -ErrorAction SilentlyContinue).$RegistryName -ne $RunPackageIDValue) {
try {
New-ItemProperty -Path $RegistryPath -Name $RegistryName -Value $RunPackageIDValue -PropertyType "String" -Force
}
catch {
Write-Log -Level Error -Message "Failed to write PackageID: $RunPackageIDValue to registry"
}
}
}
else {
Write-Log -Level Error -Message "PackageID: $RunPackageIDValue was not found in WMI as deployed to the client. Please check the config.xml or deployment in ConfigMgr"
Write-Log -Level Error -Message "Script will exit here. Not displaying toast notification when specified package is not deployed"
# Terminating initiating powershell process due to requirement from using AppLocker and constrained language mode
Terminate-ToastProcess
}
}
else {
Write-Log -Level Error -Message "No ConfigMgr service found. This function requires the ConfigMgr client to be installed"
}
}
}
# Create Write-ApplicationIDRegistry function
function Write-ApplicationIDRegistry() {
Write-Log -Message "Running Write-ApplicationIDRegistry function"
$RegistryPath = "HKCU:\SOFTWARE\ToastNotificationScript"
$RegistryName = "RunApplicationID"
# Making sure that the registry path being used exists
if (-NOT(Test-Path -Path $RegistryPath)) {
try {
New-Item -Path $RegistryPath -Force
}
catch {
Write-Log -Level Error -Message "Error. Could not create ToastNotificationScript registry path"
}
}
# If the ApplicationID specified in the config.xml is picked up
if ($RunApplicationIDValue) {
# If the ConfigMgr service exist
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
# Testing if the ApplicationID specified in the config.xml actually is deployed to the device
try {
$TestApplicationID = Get-CimInstance -ClassName CCM_Application -Namespace root\ccm\clientsdk | Where-Object {$_.Id -eq $RunApplicationIDValue}
}
catch {
Write-Log -Level Error -Message "Failed to retrieve $RunApplicationIDValue from WMI"
}
# If the ApplicationID is found in WMI with the ConfigMgr client, tattoo that ApplicationID into registry
if ($TestApplicationID) {
Write-Log -Message "ApplicationID: $RunApplicationIDValue was found in WMI as deployed to the client"
Write-Log -Message "Writing the ApplicationID to registry"
if ((Get-ItemProperty -Path $RegistryPath -Name $RegistryName -ErrorAction SilentlyContinue).$RegistryName -ne $RunApplicationIDValue) {
try {
New-ItemProperty -Path $RegistryPath -Name $RegistryName -Value $RunApplicationIDValue -PropertyType "String" -Force
}
catch {
Write-Log -Level Error -Message "Failed to write ApplicationID: $RunApplicationIDValue to registry"
}
}
}
else {
Write-Log -Level Error -Message "ApplicationID: $RunApplicationIDValue was not found in WMI as deployed to the client. Please check the config.xml or deployment in ConfigMgr"
Write-Log -Level Error -Message "Script will exit here. Not displaying toast notification when specified application is not deployed"
# Terminating initiating powershell process due to requirement from using AppLocker and constrained language mode
Terminate-ToastProcess
}
}
else {
Write-Log -Level Error -Message "No ConfigMgr service found. This function requires the ConfigMgr client to be installed"
}
}
}
# Create Write-UpdateIDRegistry
# This function writes the UpdateID to registry when used with Software (Feature) Updates in ConfigMgr
# HUGE shout-out to Chad Brower // @Brower_Cha on Twitter
# Added in version 2.0.0
function Write-UpdateIDRegistry() {
Write-Log -Message "Running Write-UpdateIDRegistry function"
$RegistryPath = "HKCU:\SOFTWARE\ToastNotificationScript"
$RegistryName = "RunUpdateID"
# Making sure that the registry path being used exists
if (-NOT(Test-Path -Path $RegistryPath)) {
try {
New-Item -Path $RegistryPath -Force
}
catch {
Write-Log -Level Error -Message "Error. Could not create ToastNotificationScript registry path"
}
}
# If the UpdateID specified in the config.xml is picked up
if (-NOT[string]::IsNullOrEmpty($RunUpdateIDValue)) {
# If the ConfigMgr service exist
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
# Getting the UpdateID specified in the config.xml via Get-CMUpdate function
try {
$GetUpdateID = Get-CMUpdate
}
catch {
Write-Log -Level Error -Message "Failed to successfully run the Get-CMUpdate function"
}
# If the UpdateID is found in WMI with the ConfigMgr client, tattoo that UpdateID into registry
if (-NOT[string]::IsNullOrEmpty($GetUpdateID.UpdateID)) {
Write-Log -Message "Get-CMDUpdate was successfully run and UpdateID was retrieved"
Write-Log -Message "Writing the UpdateID to registry"
if ((Get-ItemProperty -Path $RegistryPath -Name $RegistryName -ErrorAction SilentlyContinue).$RegistryName -ne $GetUpdateID.UpdateID) {
try {
New-ItemProperty -Path $RegistryPath -Name $RegistryName -Value $GetUpdateID.UpdateID -PropertyType "String" -Force
}
catch {
Write-Log -Level Error -Message "Failed to write UpdateID: $($GetUpdateID.UpdateID) to registry"
}
}
}
else {
Write-Log -Level Error -Message "UpdateID: $RunUpdateIDValue was not found in WMI as deployed to the client. Please check the config.xml or deployment in ConfigMgr"
}
}
else {
Write-Log -Level Error -Message "No ConfigMgr service found. This function requires the ConfigMgr client to be installed"
}
}
}
# Create Display-ToastNotification function
# Updated in version 2.2.0
function Display-ToastNotification() {
try {
if ($isSystem -eq $true) {
Write-Log -Message "Confirmed SYSTEM context before displaying toast"
# is running under SYSTEM context
# show notification to all logged on users
& (Join-Path -Path $global:CustomScriptsPath -ChildPath "InvokePSScriptAsUser.ps1") "$PSCommandPath" "$Config"
}
else {
Write-Log -Message "Confirmed USER context before displaying toast"
# is running under user context
$Load = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$Load = [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime]
# Load the notification into the required format
$ToastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
$ToastXml.LoadXml($Toast.OuterXml)
# Display the toast notification
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($App).Show($ToastXml)
}
if ($CustomAudio -eq "True") {
Invoke-Command -ScriptBlock {
Add-Type -AssemblyName System.Speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
sleep 1.25
$speak.SelectVoiceByHints("female",65)
$speak.Speak($CustomAudioTextToSpeech)
$speak.Dispose()
}
}
Write-Log -Message "All good. Toast notification was displayed"
# Saving time stamp of when toast notification was run into registry
Save-NotificationLastRunTime
}
catch {
Write-Log -Message "Something went wrong when displaying the toast notification" -Level Error
Write-Log -Message "Make sure the script is running as the logged on user" -Level Error
}
}
# Create Test-NTSystem function
# Testing to see if the script is being run as SYSTEM
# Updated in version 2.2.0
function Test-NTSystem() {
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
if ($currentUser.IsSystem -eq $true) {
Write-Log -Message "Script is initially running in SYSTEM context. Please be vary, that this has limitations and may not work!"
$true
}
elseif ($currentUser.IsSystem -eq $false) {
Write-Log -Message "Script is initially running in USER context"
$false
}
}
# Create Write-CustomActionRegistry function
# This function creates custom protocols for the logged on user in HKCU.
# This will remove the need to create the protocols outside of the toast notification script
# HUGE shout-out to Chad Brower // @Brower_Cha on Twitter
# Added in version 2.0.0
function Write-CustomActionRegistry() {
[CmdletBinding()]
param (
[Parameter(Position="0")]
[ValidateSet("ToastRunApplicationID","ToastRunPackageID","ToastRunUpdateID","ToastReboot")]
[string]$ActionType,
[Parameter(Position="1")]
[string]$RegCommandPath = $global:CustomScriptsPath
)
Write-Log -Message "Running Write-CustomActionRegistry function: $ActionType"
switch ($ActionType) {
ToastReboot {
# Build out registry for custom action for rebooting the device via the action button
try {
New-Item "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name 'URL Protocol' -Value '' -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name '(default)' -Value "URL:$($ActionType) Protocol" -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
$RegCommandValue = $RegCommandPath + '\' + "$($ActionType).cmd"
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Name '(default)' -Value $RegCommandValue -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
}
catch {
Write-Log -Level Error "Failed to create the $ActionType custom protocol in HKCU\Software\Classes. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
ToastRunUpdateID {
# Build out registry for custom action for running software update via the action button
try {
New-Item "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name 'URL Protocol' -Value '' -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name '(default)' -Value "URL:$($ActionType) Protocol" -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
$RegCommandValue = $RegCommandPath + '\' + "$($ActionType).cmd"
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Name '(default)' -Value $RegCommandValue -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
}
catch {
Write-Log -Level Error "Failed to create the $ActionType custom protocol in HKCU\Software\Classes. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
ToastRunPackageID {
# Build out registry for custom action for running packages and task sequences via the action button
try {
New-Item "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name 'URL Protocol' -Value '' -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name '(default)' -Value "URL:$($ActionType) Protocol" -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
$RegCommandValue = $RegCommandPath + '\' + "$($ActionType).cmd"
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Name '(default)' -Value $RegCommandValue -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
}
catch {
Write-Log -Level Error "Failed to create the $ActionType custom protocol in HKCU\Software\Classes. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
ToastRunApplicationID {
# Build out registry for custom action for running applications via the action button
try {
New-Item "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name 'URL Protocol' -Value '' -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)" -Name '(default)' -Value "URL:$($ActionType) Protocol" -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
$RegCommandValue = $RegCommandPath + '\' + "$($ActionType).cmd"
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionType)\shell\open\command" -Name '(default)' -Value $RegCommandValue -PropertyType String -Force -ErrorAction SilentlyContinue | Out-Null
}
catch {
Write-Log -Level Error "Failed to create the $ActionType custom protocol in HKCU\Software\Classes. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
}
}
# Create Write-CustomActionScript function
# This function creates the custom scripts in ProgramData\ToastNotificationScript which is used to carry out custom protocol actions
# HUGE shout-out to Chad Brower // @Brower_Cha on Twitter
# Added in version 2.0.0
# Updated in version 2.2.0
function Write-CustomActionScript() {
[CmdletBinding()]
param (
[Parameter(Position="0")]
[ValidateSet("ToastRunApplicationID","ToastRunPackageID","ToastRunUpdateID","ToastReboot","InvokePSScriptAsUser")]
[string]$Type,
[Parameter(Position="1")]
[String]$Path = $global:CustomScriptsPath
)
Write-Log -Message "Running Write-CustomActionScript function: $Type"
switch ($Type) {
# Create custom scripts for running software updates via the action button
ToastRunUpdateID {
try {
$CMDFileName = $Type + '.cmd'
$CMDFilePath = $Path + '\' + $CMDFileName
try {
New-item -Path $Path -Name $CMDFileName -Force -OutVariable PathInfo | Out-Null
}
catch {
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
try {
$GetCustomScriptPath = $PathInfo.FullName
[String]$Script = "powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File `"$global:CustomScriptsPath\ToastRunUpdateID.ps1`""
if (-NOT[string]::IsNullOrEmpty($Script)) {
Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force
}
}
catch {
Write-Log -Level Error "Failed to create the custom .cmd script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
catch {
Write-Log -Level Error "Failed to create the custom .cmd script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
try {
$PS1FileName = $Type + '.ps1'
$PS1FilePath = $Path + '\' + $PS1FileName
try {
New-item -Path $Path -Name $PS1FileName -Force -OutVariable PathInfo | Out-Null
}
catch {
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
try {
$GetCustomScriptPath = $PathInfo.FullName
[String]$Script = @'
$RegistryPath = "HKCU:\SOFTWARE\ToastNotificationScript"
$UpdateID = (Get-ItemProperty -Path $RegistryPath -Name "RunUpdateID").RunUpdateID
$TestUpdateID = Get-WmiObject -Namespace ROOT\ccm\ClientSDK -Query "SELECT * FROM CCM_SoftwareUpdate WHERE UpdateID = '$UpdateID'"
if (-NOT[string]::IsNullOrEmpty($TestUpdateID)) {
Invoke-WmiMethod -Namespace ROOT\ccm\ClientSDK -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$TestUpdateID)
if (Test-Path -Path "$env:windir\CCM\ClientUX\SCClient.exe") { Start-Process -FilePath "$env:windir\CCM\ClientUX\SCClient.exe" -ArgumentList "SoftwareCenter:Page=Updates" -WindowStyle Maximized }
}
exit 0
'@
if (-NOT[string]::IsNullOrEmpty($Script)) {
Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force
}
}
catch {
Write-Log -Level Error "Failed to create the custom .ps1 script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
catch {
Write-Log -Level Error "Failed to create the custom .ps1 script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
# Do not run another type; break
Break
}
# Create custom script for rebooting the device directly from the action button
ToastReboot {
try {
$CMDFileName = $Type + '.cmd'
$CMDFilePath = $Path + '\' + $CMDFileName
try {
New-item -Path $Path -Name $CMDFileName -Force -OutVariable PathInfo | Out-Null
}
catch {
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
try {
$GetCustomScriptPath = $PathInfo.FullName
[String]$Script = 'shutdown /r /t 0 /d p:0:0 /c "Toast Notification Reboot"'
if (-NOT[string]::IsNullOrEmpty($Script)) {
Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force
}
}
catch {
Write-Log -Level Error "Failed to create the custom .cmd script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
catch {
Write-Log -Level Error "Failed to create the custom .cmd script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
# Do not run another type; break
Break
}
# Script output updated in 2.0.1 to dynamically pick up the Program ID.
# Previously this was hard coded to '*', making it work for task sequences only. Now also works for regular packages (only one program).
# Create custom scripts to run packages and task sequences directly from the action button
ToastRunPackageID {
try {
$CMDFileName = $Type + '.cmd'
$CMDFilePath = $Path + '\' + $CMDFileName
try {
New-item -Path $Path -Name $CMDFileName -Force -OutVariable PathInfo | Out-Null
}
catch {
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
try {
$GetCustomScriptPath = $PathInfo.FullName
[String]$Script = "powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File `"$global:CustomScriptsPath\ToastRunPackageID.ps1`""
if (-NOT[string]::IsNullOrEmpty($Script)) {
Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force
}
}
catch {
Write-Log -Level Error "Failed to create the custom .cmd script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"
}
}
catch {
Write-Log -Level Error "Failed to create the custom .cmd script for $Type. Action button might not work"
$ErrorMessage = $_.Exception.Message
Write-Log -Level Error -Message "Error message: $ErrorMessage"