diff --git a/New-ToastNotification.ps1 b/New-ToastNotification.ps1
index 8b6d593..1ea1cab 100644
--- a/New-ToastNotification.ps1
+++ b/New-ToastNotification.ps1
@@ -82,18 +82,18 @@
** 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
- -
- -
- -
- -
- 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:
- -
- -
- Added support for getting deadline date/time dynamically for software updates
+ 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
+ -
+ -
+ -
+ -
+ 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:
+ -
+ -
+ Added support for getting deadline date/time dynamically for software updates
- Configure DynamicDeadline with the UpdateID
2.0.1 - Updated custom action scripts!
@@ -106,7 +106,7 @@
- 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
+ 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
@@ -252,23 +252,23 @@ function Get-GivenName() {
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
Write-Log -Message "Looking for given name in WMI with CCM client"
$LoggedOnSID = Get-CimInstance -Namespace ROOT\CCM -Class CCM_UserLogonEvents -Filter "LogoffTime=null" | Select -ExpandProperty UserSID
- if ($LoggedOnSID.GetType().IsArray) {
- Write-Log -Message "Multiple SID's found. Skipping"
- $GivenName = ""
- $GivenName
+ if ($LoggedOnSID.GetType().IsArray) {
+ Write-Log -Message "Multiple SID's found. Skipping"
+ $GivenName = ""
+ $GivenName
}
- else {
- $RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
- $DisplayName = (Get-ChildItem -Path $RegKey | Where-Object {$_.GetValue("LoggedOnUserSID") -eq $LoggedOnSID} | Select-Object -First 1).GetValue("LoggedOnDisplayName")
- if ($DisplayName) {
- Write-Log -Message "Given name found in WMI with the CCM client: $GivenName"
- $GivenName = $DisplayName.Split()[0].Trim()
- $GivenName
- }
- else {
- $GivenName = ""
- $GivenName
- }
+ else {
+ $RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
+ $DisplayName = (Get-ChildItem -Path $RegKey | Where-Object {$_.GetValue("LoggedOnUserSID") -eq $LoggedOnSID} | Select-Object -First 1).GetValue("LoggedOnDisplayName")
+ if ($DisplayName) {
+ Write-Log -Message "Given name found in WMI with the CCM client: $GivenName"
+ $GivenName = $DisplayName.Split()[0].Trim()
+ $GivenName
+ }
+ else {
+ $GivenName = ""
+ $GivenName
+ }
}
}
}
@@ -1081,6 +1081,89 @@ exit 0
}
}
+function Write-FullCustomAction {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ [string]
+ $ActionName,
+ [Parameter()]
+ [string]
+ $ScriptDirectory,
+ [Parameter()]
+ [string]
+ $ExecutionScript
+ )
+ # Create CMD File
+ try {
+ $CMDFileName = $ActionName + '.cmd'
+ $CMDFilePath = $ScriptDirectory + '\' + $CMDFileName
+ Write-Log -Level Info -Message "Creating CMD File [$CMDFilePath]"
+ New-item -Path $ScriptDirectory -Name $CMDFileName -Force -ErrorAction Stop -OutVariable PathInfo | Out-Null
+ $GetCustomScriptPath = $PathInfo.FullName
+ Write-Log -Level Info -Message "File created. Writing content for CMD File [$CMDFilePath]"
+ [String]$Script = "powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File `"$ScriptDirectory\$ActionName.ps1`""
+ if (-NOT[string]::IsNullOrEmpty($Script)) {
+ Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force -ErrorAction Stop
+ }
+ Write-Log -Level Info -Message "CMD File [$CMDFilePath] created and content written successfully!"
+ }
+ catch {
+ Write-Log -Level Error "Failed to create or write content to custom CMD script [$CMDFilePath] for action [$ActionName]. Action button will not work."
+ $ErrorMessage = $_.Exception.Message
+ Write-Log -Level Error -Message "Error message: $ErrorMessage"
+ }
+ #Create PS1 File
+ try {
+ $PS1FileName = $ActionName + '.ps1'
+ $PS1FilePath = $ScriptDirectory + '\' + $PS1FileName
+ Write-Log -Level Info -Message "Creating PS1 File [$PS1FilePath]"
+ New-item -Path $ScriptDirectory -Name $PS1FileName -Force -ErrorAction Stop -OutVariable PathInfo | Out-Null
+ $GetCustomScriptPath = $PathInfo.FullName
+ Write-Log -Level Info -Message "File created. Writing content for PS1 File [$PS1FilePath]"
+ [String]$Script = @"
+ $ExecutionScript
+"@
+ if (-NOT[string]::IsNullOrEmpty($Script)) {
+ Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force
+ }
+ Write-Log -Level Info -Message "PS1 File [$PS1FilePath] created and content written successfully!"
+ }
+ catch {
+ Write-Log -Level Error "Failed to create or write content to custom PS1 script [$PS1FilePath] for action [$ActionName]. Action button will not work."
+ $ErrorMessage = $_.Exception.Message
+ Write-Log -Level Error -Message "Error message: $ErrorMessage"
+ }
+}
+
+function Write-FullCustomProtocol {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ [string]
+ $ActionName,
+ [Parameter()]
+ [string]
+ $ScriptDirectory
+ )
+ # Build out registry for custom action for running packages and task sequences via the action button
+ try {
+ Write-Log -Level Info -Message "Creating protocol in current user [HKCU:\Software\Classes\$($ActionName)] for action [$ActionName]"
+ New-Item "HKCU:\Software\Classes\$($ActionName)\shell\open\command" -Force -ErrorAction Stop | Out-Null
+ New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionName)" -Name 'URL Protocol' -Value '' -PropertyType String -Force -ErrorAction Stop | Out-Null
+ New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionName)" -Name '(default)' -Value "URL:$($ActionName) Protocol" -PropertyType String -Force -ErrorAction Stop | Out-Null
+ $RegCommandValue = $ScriptDirectory + '\' + "$($ActionName).cmd"
+ New-ItemProperty -LiteralPath "HKCU:\Software\Classes\$($ActionName)\shell\open\command" -Name '(default)' -Value $RegCommandValue -PropertyType String -Force -ErrorAction Stop | Out-Null
+ }
+ catch {
+ Write-Log -Level Error -Message "Failed to create the $ActionName custom protocol in HKCU\Software\Classes. Action button might not work"
+ $ErrorMessage = $_.Exception.Message
+ Write-Log -Level Error -Message "Error message: $ErrorMessage"
+ }
+
+}
+
+
######### GENERAL VARIABLES #########
# Global variables
# Setting global script version
@@ -1220,6 +1303,7 @@ if(-NOT[string]::IsNullOrEmpty($Xml)) {
$PendingRebootUptime = $Xml.Configuration.Feature | Where-Object {$_.Name -like 'PendingRebootUptime'} | Select-Object -ExpandProperty 'Enabled'
$PendingRebootCheck = $Xml.Configuration.Feature | Where-Object {$_.Name -like 'PendingRebootCheck'} | Select-Object -ExpandProperty 'Enabled'
$ADPasswordExpiration = $Xml.Configuration.Feature | Where-Object {$_.Name -like 'ADPasswordExpiration'} | Select-Object -ExpandProperty 'Enabled'
+ $CustomActionsEnabled = $Xml.Configuration.CustomActions.Enabled
# Load Toast Notification options
$PendingRebootUptimeTextEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'PendingRebootUptimeText'} | Select-Object -ExpandProperty 'Enabled'
$MaxUptimeDays = $Xml.Configuration.Option | Where-Object {$_.Name -like 'MaxUptimeDays'} | Select-Object -ExpandProperty 'Value'
@@ -1261,6 +1345,10 @@ if(-NOT[string]::IsNullOrEmpty($Xml)) {
$Action = $Xml.Configuration.Option | Where-Object {$_.Name -like 'Action'} | Select-Object -ExpandProperty 'Value'
$GreetGivenName = $Xml.Configuration.Text | Where-Object {$_.Option -like 'GreetGivenName'} | Select-Object -ExpandProperty 'Enabled'
$MultiLanguageSupport = $Xml.Configuration.Text | Where-Object {$_.Option -like 'MultiLanguageSupport'} | Select-Object -ExpandProperty 'Enabled'
+ # Load Custom Action Details
+ $CustomDetection = $Xml.Configuration.CustomActions.DetectionScript
+ $CustomAction = $Xml.Configuration.CustomActions.Action
+ $CustomActionName = $Xml.Configuration.CustomActions.Action.Name
# Load Toast Notification buttons
$ActionButtonEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'ActionButton'} | Select-Object -ExpandProperty 'Enabled'
$DismissButtonEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'DismissButton'} | Select-Object -ExpandProperty 'Enabled'
@@ -1490,6 +1578,13 @@ if (($Action -eq "ToastReboot:") -AND ($RunApplicationIDEnabled -eq "True")) {
Write-Log -Level Error -Message "This seems like an unintended configuration"
Exit 1
}
+# New checks for Custom Actions
+if (($CustomActionsEnabled -eq "True") -AND (($UpgradeOS -eq "True") -or ($PendingRebootUptime -eq "True") -or ($PendingRebootCheck -eq "True") -or ($ADPasswordExpiration -eq "True"))){
+ Write-Log -Level Error -Message "Error. Conflicting selection in the $Config file"
+ Write-Log -Level Error -Message "Error. You can't have CustomActionsEnabled set to True and other features set to True at the same time"
+ Write-Log -Level Error -Message "You should only enable one of the features or CustomActions"
+ Exit 1
+}
# Downloading images into user's temp folder if images are hosted online
if (($LogoImageFileName.StartsWith("https://")) -OR ($LogoImageFileName.StartsWith("http://"))) {
@@ -1613,6 +1708,35 @@ if ($PendingRebootUptime -eq "True") {
Write-Log -Message "PendingRebootUptime set to True. Checking for device uptime. Current uptime is: $Uptime days"
}
+# Determin the results of custom actions, if enabled.
+If ($CustomActionsEnabled -eq "True"){
+ # convert the string to a scriptblock
+ $DetectionScript = [Scriptblock]::Create($CustomDetection)
+ write-log -Message "Running custom detection script..."
+ Try {
+ #Run the script block. It should return a boolean true or false.
+ $CustomDetectionResult = Invoke-Command -ScriptBlock $DetectionScript
+ Write-Log -Message "Custom detection script seemed to execute successfully and returned [$CustomDetectionResult]"
+ }
+ Catch {
+ Write-Log -Level Error -Message "Custom detection script seemed to execute unsuccessfully and returned [$CustomDetectionResult]"
+ Write-Log -Level Warn -Message "Setting Result to false because of failure."
+ $CustomDetectionResult = $False
+ }
+
+ If($CustomDetectionResult -eq $true) {
+ IF ($CustomAction.ExecutionScript.Enabled -eq "True"){
+ Write-Log -Message "Custom Action Execution Script set to 'True'. Creating protocol and action script"
+ Write-FullCustomProtocol -ActionName $CustomActionName -ScriptDirectory $global:CustomScriptsPath
+ Write-FullCustomAction -ActionName $CustomActionName -ScriptDirectory $global:CustomScriptsPath -ExecutionScript $CustomAction.ExecutionScript.'#text'
+ } else {
+ Write-Log -Message "Custom Action Execution Script NOT set to 'True'. Skipping creating protocol and action script"
+ }
+ }
+}
+
+
+
# Check for required entries in registry for when using Software Center as application for the toast
if ($SCAppStatus -eq "True") {
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
@@ -1978,14 +2102,24 @@ if (($ADPasswordExpiration -eq "True") -AND ($ADPasswordExpirationResult -eq $Tr
else {
Write-Log -Level Warn -Message "Conditions for displaying toast notification for ADPasswordExpiration are not fulfilled"
}
+# Toast Used for Custom Action
+if (($CustomActionsEnabled -eq "True") -AND ($CustomDetectionResult -eq $True)) {
+ Write-Log -Message "Toast notification is used in regards to CustomActionsEnabled. CustomDetection returned [$CustomDetectionResult]"
+ Display-ToastNotification
+ # Stopping script. No need to accidently run further toasts
+ break
+}
+else {
+ Write-Log -Level Warn -Message "Conditions for displaying toast notification for CustomActionsEnabled are not fulfilled. CustomDetection returned [$CustomDetectionResult]"
+}
# Toast not used for either OS upgrade or Pending reboot OR ADPasswordExpiration. Run this if all features are set to false in config.xml
-if (($UpgradeOS -ne "True") -AND ($PendingRebootCheck -ne "True") -AND ($PendingRebootUptime -ne "True") -AND ($ADPasswordExpiration -ne "True")) {
+if (($UpgradeOS -ne "True") -AND ($PendingRebootCheck -ne "True") -AND ($PendingRebootUptime -ne "True") -AND ($ADPasswordExpiration -ne "True") -AND ($CustomActionsEnabled -ne "True")) {
Write-Log -Message "Toast notification is not used in regards to OS upgrade OR Pending Reboots OR ADPasswordExpiration. Displaying default toast"
Display-ToastNotification
# Stopping script. No need to accidently run further toasts
break
}
else {
- Write-Log -Level Warn -Message "Conditions for displaying default toast notification are not fulfilled"
+ Write-Log -Level Warn -Message "Conditions for displaying default toast notification are not fulfilled. Either One of the other scenarios match or, CustomActionsEnabled is set to true."
}
\ No newline at end of file
diff --git a/config-toast-custom.xml b/config-toast-custom.xml
new file mode 100644
index 0000000..134e0d3
--- /dev/null
+++ b/config-toast-custom.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $PathExists = Test-Path 'C:\Windows\System32' -ErrorAction Ignore
+ If ($PathExists){
+ $True
+ } else {
+ $False
+ }
+
+
+
+$App = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"
+[xml]$Toast = @"
+<toast scenario="Reminder">
+ <visual>
+ <binding template="ToastGeneric">
+ <text placement="attribution">The Servicedesk</text>
+ <text>Doing the Needful...</text>
+ <group>
+ <subgroup>
+ <text hint-style="body" hint-wrap="true">Thanks for clicking to do the needful. This was a lot of fun.</text>
+ </subgroup>
+ </group>
+ <group>
+ <subgroup>
+ <text hint-style="base" hint-wrap="true">Just another paragragh on doing the needful.</text>
+ </subgroup>
+ </group>
+ <group>
+ <subgroup>
+ <text hint-style="body" hint-wrap="true">I've really got nothing else. Just wanting to babble on a little more.</text>
+ </subgroup>
+ </group>
+ </binding>
+ </visual>
+ <actions>
+ <action activationType="system" arguments="dismiss" content="Thanks!" />
+ </actions>
+</toast>
+"@
+[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $nul
+[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $nul
+
+# Load the notification into the required format
+$ToastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
+$ToastXml.LoadXml($Toast.OuterXml)
+[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($App).Show($ToastXml)
+
+
+
+
+
+
+ Your computer is required to restart due to having exceeded the maximum allowed uptime.
+ Reason: Pending reboots was found in registry or WMI.
+ Your password will expire on:
+ Hey you - wake up. Your computer needs to restart. Do it now.
+ Do it!
+ Later
+ Snooze
+ www.imab.dk
+ Helpdesk kindly reminds you...
+ Computer requires the needful!
+ The directory C:\Windows\System32 exists. So, looks like your computer is ready to do the needful.
+ To proceed with doing the needful, click the "Do it!" link below. It will be amazing.
+ Click snooze to be reminded again in:
+ Your deadline is:
+ Good morning
+ Good afternoon
+ Good evening
+ Minutes
+ Hour
+ Hours
+ Computer uptime:
+ days
+
+
\ No newline at end of file
diff --git a/config-toast-rebootpending.xml b/config-toast-rebootpending.xml
index 8b019fd..3cf0d65 100644
--- a/config-toast-rebootpending.xml
+++ b/config-toast-rebootpending.xml
@@ -23,11 +23,21 @@
-
+
-
+
+
+
+
+ Test-Path 'C:\Windows\System32' -ErrorAction Ignore
+
+
+
+
+
+