diff --git a/Hidden.vbs b/Hidden.vbs new file mode 100644 index 0000000..fb7cb76 --- /dev/null +++ b/Hidden.vbs @@ -0,0 +1 @@ +CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False \ No newline at end of file diff --git a/New-ToastNotification.ps1 b/New-ToastNotification.ps1 new file mode 100644 index 0000000..34b7989 --- /dev/null +++ b/New-ToastNotification.ps1 @@ -0,0 +1,1223 @@ +<# +.SYNOPSIS + Create nice Windows 10 toast notifications for the logged on user in Windows 10. + +.DESCRIPTION + Everything is customizeable through config-toast.xml. + Config-toast.xml can be locally 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. + All actions are logged to a local log file in appdata\roaming\ToastNotification\New-Toastnotificaion.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: 1.7.1 + 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 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 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 + + +.LINK + https://www.imab.dk/windows-10-toast-notification-script/ +#> + +[CmdletBinding()] +param( + [Parameter(HelpMessage='Path to XML Configuration File')] + [string]$Config +) + +######### FUNCTIONS ######### + +# Create write log function +function Write-Log() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [ValidateNotNullOrEmpty()] + [Alias("LogContent")] + [string]$Message, + + # EDIT with your location for the local log file + [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!" + return $true + } + Write-Log -Message "Check returned FALSE on ANY of the registry checks: Reboot is NOT pending!" + return $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) { + Write-Log -Message "Check returned TRUE on checking WMI for pending reboot: Reboot is pending!" + return $true + } + Write-Log -Message "Check returned FALSE on checking WMI for pending reboot: Reboot is NOT pending!" + return $false + } + else { + Write-Log -Level Warn -Message "Computer has no ConfigMgr client installed - skipping checking WMI for pending reboots" + return $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" + # Thanks to Trevor Jones @ http://smsagent.blog + Clear-Variable -Name GivenName -ErrorAction SilentlyContinue + 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 Warn -Message "$_." + } + + if ($GivenName) { + Write-Log -Message "Given name retrieved from Active Directory" + $GivenName + } + + elseif (-NOT($GivenName)) { + Write-Log -Message "Given name not found in AD or no local AD available. Continuing looking for given name elsewhere" + 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 + } + 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 = $DisplayName.Split()[0].Trim() + $GivenName + } + else { + $GivenName = "" + $GivenName + } + } + } + } + + elseif (-NOT($GivenName)) { + # More options for given name here + + } + + else { + Write-Log -Message "No given name found. Using nothing as placeholder" + $GivenName = "" + $GivenName + } +} + +# 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 -Message "Not running supported version of Windows" + $false + } + else { + Write-Log -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 are enabled in Windows" + $true + } + elseif ($ToastEnabledKey -eq "0") { + Write-Log -Message "Toast notifications are not enabled in Windows. The script will run, but toasts might not be displayed" + $false + } + else { + Write-Log -Message "The registry key for determining if toast notifications are enabled does not exist. The script will run, but toasts might not be displayed" + $false + } +} + +# 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 Warn -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 Warn -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 Warn -Message "No password expiration date found. Returning False" + $false + } + } + + elseif (-NOT($SamAccountName) -OR ($DomainName)) { + Write-Log -Level Warn -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. +# This works for Task Sequences and regular packages +# 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) { + try { + # Get task sequence program information from WMI. This is the same location used by Software Center + $PackageID = Get-CimInstance -Namespace root\ccm\clientsdk -Query "SELECT * FROM CCM_Program where PackageID = '$DynDeadlineValue'" + } + catch { + Write-Log -Message "Failed to get PackageID from WMI" -Level Warn + } + + if ($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 -Message "Failed to get deadline date and time from WMI" -Level Warn + Write-Log -Message "Please check if there really is a deadline configured" -Level Warn + Write-Log -Message "The script is continuing, but the toast is displayed without deadline date and time" -Level Warn + } + } + else { + Write-Log -Message "Appears that the specified package ID: $DynDeadlineValue is not deployed to the device." + # Nothing to see here yet + } + } + else { + Write-Log -Message -Level Warn "ConfigMgr service not found" + } +} + +# 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 -Level Warn -Message "Error. Could not create ToastNotificationScript registry path" + } + } + + # 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 Warn -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 Warn -Message "Failed to write PackageID: $RunPackageIDValue to registry" + } + } + } + else { + Write-Log -Level Warn -Message "PackageID: $RunPackageIDValue was not found in WMI as deployed to the client. Please check the config.xml or deployment in ConfigMgr" + } + } + else { + Write-Log -Level Warn -Message "No ConfigMgr service found. This functon 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 Warn -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 Warn -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 Warn -Message "Failed to write ApplicationID: $RunApplicationIDValue to registry" + } + } + } + else { + Write-Log -Level Warn -Message "ApplicationID: $RunApplicationIDValue was not found in WMI as deployed to the client. Please check the config.xml or deployment in ConfigMgr" + } + } + else { + Write-Log -Level Warn -Message "No ConfigMgr service found. This functon requires the ConfigMgr client to be installed" + } + } +} + +# Create Display-ToastNotification function +function Display-ToastNotification() { + + $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 + try { + Write-Log -Message "All good. Displaying the toast notification" + [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($App).Show($ToastXml) + } + catch { + Write-Log -Message "Something went wrong when displaying the toast notification" -Level Warn + Write-Log -Message "Make sure the script is running as the logged on user" -Level Warn + } + if ($CustomAudio -eq "True") { + Invoke-Command -ScriptBlock { + Add-Type -AssemblyName System.Speech + $speak = New-Object System.Speech.Synthesis.SpeechSynthesizer + $speak.Speak($CustomAudioTextToSpeech) + $speak.Dispose() + } + } +} + +# Create Test-NTSystem function +# If the script is being run as SYSTEM, the toast notification won't display +function Test-NTSystem() { + $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() + if ($currentUser.IsSystem -eq $true) { + Write-Log -Message "The script is being run as SYSTEM. This is not supported. The script needs the current user's context" -Level Warn + $true + } + elseif ($currentUser.IsSystem -eq $false) { + $false + } +} + +######### GENERAL VARIABLES ######### + +# Getting executing directory +$global:ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +# Setting path to images +$ImagesPath = "file:///$global:ScriptPath/Images" +# Get running OS build +$RunningOS = Get-CimInstance -Class Win32_OperatingSystem | Select-Object BuildNumber +# Get user culture for multilanguage support +$userCulture = (Get-Culture).Name +# Setting the default culture to en-US. This will be the default language if MultiLanguageSupport is not enabled in the config +$defaultUserCulture = "en-US" + +# Testing for prerequisites +# Testing if script is being run as SYSTEM. This is not supported as the toast notification needs the current user's context +$isSystem = Test-NTSystem +if ($isSystem -eq $True) { + Write-Log -Message "Aborting script" -Level Warn + Exit 1 +} +# Test if the script is being run on a supported version of Windows. Windows 10 AND workstation OS is required +$SupportedWindowsVersion = Get-WindowsVersion +if ($SupportedWindowsVersion -eq $False) { + Write-Log -Message "Aborting script" -Level Warn + Exit 1 +} +# Testing for blockers of toast notifications in Windows +$WindowsPushNotificationsEnabled = Test-WindowsPushNotificationsEnabled + +# If no config file is set as parameter, use the default. +# Default is executing directory. In this case, the config-toast.xml must exist in same directory as the New-ToastNotification.ps1 file +if (-NOT($Config)) { + Write-Log -Message "No config file set as parameter. Using local config file" + $Config = Join-Path ($global:ScriptPath) "config-toast.xml" +} + +# Load config.xml +if (Test-Path $Config) { + try { + $Xml = [xml](Get-Content -Path $Config -Encoding UTF8) + Write-Log -Message "Successfully loaded $Config" + } + catch { + $ErrorMessage = $_.Exception.Message + Write-Log -Message "Error, could not read $Config" + Write-Log -Message "Error message: $ErrorMessage" + Exit 1 + } +} +else { + Write-Log -Message "Error, could not find or access $Config" + Exit 1 +} + +# Load xml content into variables +try { + Write-Log -Message "Loading xml content from $Config into variables" + + # Load Toast Notification features + $ToastEnabled = $Xml.Configuration.Feature | Where-Object {$_.Name -like 'Toast'} | Select-Object -ExpandProperty 'Enabled' + $UpgradeOS = $Xml.Configuration.Feature | Where-Object {$_.Name -like 'UpgradeOS'} | Select-Object -ExpandProperty 'Enabled' + $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' + + # 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' + $PendingRebootCheckTextEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'PendingRebootCheckText'} | Select-Object -ExpandProperty 'Enabled' + $ADPasswordExpirationTextEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'ADPasswordExpirationText'} | Select-Object -ExpandProperty 'Enabled' + $ADPasswordExpirationDays = $Xml.Configuration.Option | Where-Object {$_.Name -like 'ADPasswordExpirationDays'} | Select-Object -ExpandProperty 'Value' + $TargetOS = $Xml.Configuration.Option | Where-Object {$_.Name -like 'TargetOS'} | Select-Object -ExpandProperty 'Build' + $DeadlineEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'Deadline'} | Select-Object -ExpandProperty 'Enabled' + $DeadlineContent = $Xml.Configuration.Option | Where-Object {$_.Name -like 'Deadline'} | Select-Object -ExpandProperty 'Value' + $DynDeadlineEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'DynamicDeadline'} | Select-Object -ExpandProperty 'Enabled' + $DynDeadlineValue = $Xml.Configuration.Option | Where-Object {$_.Name -like 'DynamicDeadline'} | Select-Object -ExpandProperty 'Value' + $RunPackageIDEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'RunPackageID'} | Select-Object -ExpandProperty 'Enabled' + $RunPackageIDValue = $Xml.Configuration.Option | Where-Object {$_.Name -like 'RunPackageID'} | Select-Object -ExpandProperty 'Value' + $RunApplicationIDEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'RunApplicationID'} | Select-Object -ExpandProperty 'Enabled' + $RunApplicationIDValue = $Xml.Configuration.Option | Where-Object {$_.Name -like 'RunApplicationID'} | Select-Object -ExpandProperty 'Value' + $SCAppName = $Xml.Configuration.Option | Where-Object {$_.Name -like 'UseSoftwareCenterApp'} | Select-Object -ExpandProperty 'Name' + $SCAppStatus = $Xml.Configuration.Option | Where-Object {$_.Name -like 'UseSoftwareCenterApp'} | Select-Object -ExpandProperty 'Enabled' + $PSAppName = $Xml.Configuration.Option | Where-Object {$_.Name -like 'UsePowershellApp'} | Select-Object -ExpandProperty 'Name' + $PSAppStatus = $Xml.Configuration.Option | Where-Object {$_.Name -like 'UsePowershellApp'} | Select-Object -ExpandProperty 'Enabled' + $CustomAudio = $Xml.Configuration.Option | Where-Object {$_.Name -like 'CustomAudio'} | Select-Object -ExpandProperty 'Enabled' + $LogoImageFileName = $Xml.Configuration.Option | Where-Object {$_.Name -like 'LogoImageName'} | Select-Object -ExpandProperty 'Value' + $HeroImageFileName = $Xml.Configuration.Option | Where-Object {$_.Name -like 'HeroImageName'} | Select-Object -ExpandProperty 'Value' + $Scenario = $Xml.Configuration.Option | Where-Object {$_.Name -like 'Scenario'} | Select-Object -ExpandProperty 'Type' + $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 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' + $SnoozeButtonEnabled = $Xml.Configuration.Option | Where-Object {$_.Name -like 'SnoozeButton'} | Select-Object -ExpandProperty 'Enabled' + + if ($MultiLanguageSupport -eq "True") { + Write-Log "MultiLanguageSupport set to True. Checking for language support" + # Check xml if language support is added for the users culture, if not fallback to "en-US" // Added by Matt Benninge + if(![string]::IsNullOrEmpty($xml.Configuration.$userCulture)) { + Write-Log "Support for the users language culture found, localizing text using $userCulture" + $XmlLang = $xml.Configuration.$userCulture + } + elseif(![string]::IsNullOrEmpty($xml.Configuration.$defaultUserCulture)) { + Write-Log "No support for the users language culture found, using $defaultUserCulture as default fallback language" + $XmlLang = $xml.Configuration.$defaultUserCulture + } + } + elseif ($MultiLanguageSupport -eq "False") { + $XmlLang = $xml.Configuration.$defaultUserCulture + } + else { + $XmlLang = $xml.Configuration.$defaultUserCulture + } + + # Load Toast Notification text + $PendingRebootUptimeTextValue = $XmlLang.Text | Where-Object {$_.Name -like 'PendingRebootUptimeText'} | Select-Object -ExpandProperty '#text' + $PendingRebootCheckTextValue = $XmlLang.Text | Where-Object {$_.Name -like 'PendingRebootCheckText'} | Select-Object -ExpandProperty '#text' + $ADPasswordExpirationTextValue = $XmlLang.Text | Where-Object {$_.Name -like 'ADPasswordExpirationText'} | Select-Object -ExpandProperty '#text' + $CustomAudioTextToSpeech = $XmlLang.Text | Where-Object {$_.Name -like 'CustomAudio'} | Select-Object -ExpandProperty '#text' + $ActionButtonContent = $XmlLang.Text | Where-Object {$_.Name -like 'ActionButton'} | Select-Object -ExpandProperty '#text' + $DismissButtonContent = $XmlLang.Text | Where-Object {$_.Name -like 'DismissButton'} | Select-Object -ExpandProperty '#text' + $SnoozeButtonContent = $XmlLang.Text | Where-Object {$_.Name -like 'SnoozeButton'} | Select-Object -ExpandProperty '#text' + $AttributionText = $XmlLang.Text | Where-Object {$_.Name -like 'AttributionText'} | Select-Object -ExpandProperty '#text' + $HeaderText = $XmlLang.Text | Where-Object {$_.Name -like 'HeaderText'} | Select-Object -ExpandProperty '#text' + $TitleText = $XmlLang.Text | Where-Object {$_.Name -like 'TitleText'} | Select-Object -ExpandProperty '#text' + $BodyText1 = $XmlLang.Text | Where-Object {$_.Name -like 'BodyText1'} | Select-Object -ExpandProperty '#text' + $BodyText2 = $XmlLang.Text | Where-Object {$_.Name -like 'BodyText2'} | Select-Object -ExpandProperty '#text' + + # New text options + $SnoozeText = $XmlLang.Text | Where-Object {$_.Name -like 'SnoozeText'} | Select-Object -ExpandProperty '#text' + $DeadlineText = $XmlLang.Text | Where-Object {$_.Name -like 'DeadlineText'} | Select-Object -ExpandProperty '#text' + $GreetMorningText = $XmlLang.Text | Where-Object {$_.Name -like 'GreetMorningText'} | Select-Object -ExpandProperty '#text' + $GreetAfternoonText = $XmlLang.Text | Where-Object {$_.Name -like 'GreetAfternoonText'} | Select-Object -ExpandProperty '#text' + $GreetEveningText = $XmlLang.Text | Where-Object {$_.Name -like 'GreetEveningText'} | Select-Object -ExpandProperty '#text' + $MinutesText = $XmlLang.Text | Where-Object {$_.Name -like 'MinutesText'} | Select-Object -ExpandProperty '#text' + $HourText = $XmlLang.Text | Where-Object {$_.Name -like 'HourText'} | Select-Object -ExpandProperty '#text' + $HoursText = $XmlLang.Text | Where-Object {$_.Name -like 'HoursText'} | Select-Object -ExpandProperty '#text' + $ComputerUptimeText = $XmlLang.Text | Where-Object {$_.Name -like 'ComputerUptimeText'} | Select-Object -ExpandProperty '#text' + $ComputerUptimeDaysText = $XmlLang.Text | Where-Object {$_.Name -like 'ComputerUptimeDaysText'} | Select-Object -ExpandProperty '#text' + + Write-Log -Message "Successfully loaded xml content from $Config" +} + +catch { + Write-Log -Message "Xml content from $Config was not loaded properly" + Exit 1 +} + +# Check if toast is enabled in config.xml +if ($ToastEnabled -ne "True") { + Write-Log -Message "Toast notification is not enabled. Please check $Config file" + Exit 1 +} + +# Checking for conflicts in config. Some combinations makes no sense, thus trying to prevent those from happening +if (($UpgradeOS -eq "True") -AND ($PendingRebootCheck -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have both ÜpgradeOS feature set to True AND PendingRebootCheck feature set to True at the same time" + Exit 1 +} +if (($UpgradeOS -eq "True") -AND ($PendingRebootUptime -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have both ÜpgradeOS feature set to True AND PendingRebootUptime feature set to True at the same time" + Exit 1 +} +if (($PendingRebootCheck -eq "True") -AND ($PendingRebootUptime -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You currently can't have both PendingReboot features set to True. Please use them seperately" + Exit 1 +} +if (($ADPasswordExpiration -eq "True") -AND ($UpgradeOS -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have both ADPasswordExpiration AND UpgradeOS set to True at the same time" + Exit 1 +} +if (($ADPasswordExpiration -eq "True") -AND ($PendingRebootCheck -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have both ADPasswordExpiration AND PendingRebootCheck set to True at the same time" + Exit 1 +} +if (($ADPasswordExpiration -eq "True") -AND ($PendingRebootUptime -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have both ADPasswordExpiration AND PendingRebootUptime set to True at the same time" + Exit 1 +} +if (($SCAppStatus -eq "True") -AND (-NOT(Get-Service -Name ccmexec))) { + Write-Log -Level Warn -Message "Error. Using Software Center app for the notification requires the ConfigMgr client installed" + Write-Log -Level Warn -Message "Error. Please install the ConfigMgr cient or use Powershell as app doing the notification" + Exit 1 +} +if (($SCAppStatus -eq "True") -AND ($PSAppStatus -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have both SoftwareCenter app set to True AND PowershellApp set to True at the same time" + Exit 1 +} +if (($SCAppStatus -ne "True") -AND ($PSAppStatus -ne "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You need to enable at least 1 app in the config doing the notification. ie. Software Center or Powershell" + Exit 1 +} +if (($UpgradeOS -eq "True") -AND ($PendingRebootUptimeTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have UpgradeOS set to True and PendingRebootUptimeText set to True at the same time" + Exit 1 +} +if (($UpgradeOS -eq "True") -AND ($PendingRebootCheckTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have UpgradeOS set to True and PendingRebootCheckText set to True at the same time" + Exit 1 +} +if (($PendingRebootUptimeTextEnabled -eq "True") -AND ($PendingRebootCheckTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have PendingRebootUptimeText set to True and PendingRebootCheckText set to True at the same time" + Write-Log -Level Warn -Message "You should only enable one of the text options" + Exit 1 +} +if (($PendingRebootCheck -eq "True") -AND ($PendingRebootUptimeTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have PendingRebootCheck set to True and PendingRebootUptimeText set to True at the same time" + Write-Log -Level Warn -Message "You should use PendingRebootCheck with the PendingRebootCheckText option instead" + Exit 1 +} +if (($PendingRebootUptime -eq "True") -AND ($PendingRebootCheckTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have PendingRebootUptime set to True and PendingRebootCheckText set to True at the same time" + Write-Log -Level Warn -Message "You should use PendingRebootUptime with the PendingRebootUptimeText option instead" + Exit 1 +} +if (($ADPasswordExpirationTextEnabled -eq "True") -AND ($PendingRebootCheckTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have ADPasswordExpirationTextEnabled set to True and PendingRebootCheckText set to True at the same time" + Write-Log -Level Warn -Message "You should only enable one of the text options" + Exit 1 +} +if (($ADPasswordExpirationTextEnabled -eq "True") -AND ($PendingRebootUptimeTextEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have ADPasswordExpirationTextEnabled set to True and PendingRebootUptimeTextEnabled set to True at the same time" + Write-Log -Level Warn -Message "You should only enable one of the text options" + Exit 1 +} +if (($DeadlineEnabled -eq "True") -AND ($DynDeadlineEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have DeadlineEnabled set to True and DynamicDeadlineEnabled set to True at the same time" + Write-Log -Level Warn -Message "You should only enable one of the deadline options" + Exit 1 +} +if (($RunApplicationIDEnabled -eq "True") -AND ($RunPackageIDEnabled -eq "True")) { + Write-Log -Level Warn -Message "Error. Conflicting selection in the $Config file" + Write-Log -Level Warn -Message "Error. You can't have RunApplicationIDEnabled set to True and RunPackageIDEnabled set to True at the same time" + Write-Log -Level Warn -Message "You should only enable one of the options" + Exit 1 +} + +# Running RunApplicationIDEnabled function +if ($RunApplicationIDEnabled -eq "True") { + Write-Log -Message "RunApplicationID set to True. Will allow execution of ApplicationID directly from the toast action button" + Write-ApplicationIDRegistry +} + +# Running RunPackageIDEnabled function +if ($RunPackageIDEnabled -eq "True") { + Write-Log -Message "RunPackageID set to True. Will allow execution of PackageID directly from the toast action button" + Write-PackageIDRegistry +} + +# Running DynamicDeadline function +if ($DynDeadlineEnabled -eq "True") { + Write-Log -Message "DynDeadlineEnabled set to True. Overriding deadline details using date and time from WMI" + $DeadlineContent = Get-DynamicDeadline +} + +# Running ADPasswordExpiration Check +if ($ADPasswordExpiration -eq "True") { + Write-Log -Message "ADPasswordExpiration set to True. Checking for expiring AD password" + $TestADPasswordExpiration = Get-ADPasswordExpiration $ADPasswordExpirationDays + $ADPasswordExpirationResult = $TestADPasswordExpiration[0] + $ADPasswordExpirationDate = $TestADPasswordExpiration[1] + $ADPasswordExpirationDiff = $TestADPasswordExpiration[2] +} + +# Running Pending Reboot Checks +if ($PendingRebootCheck -eq "True") { + Write-Log -Message "PendingRebootCheck set to True. Checking for pending reboots" + $TestPendingRebootRegistry = Test-PendingRebootRegistry + $TestPendingRebootWMI = Test-PendingRebootWMI +} +if ($PendingRebootUptime -eq "True") { + $Uptime = Get-DeviceUptime + Write-Log -Message "PendingRebootUptime set to True. Checking for device uptime. Current uptime is: $Uptime days" +} + +# Check for required entries in registry for when using Software Center as application for the toast +if ($SCAppStatus -eq "True") { + + # Path to the notification app doing the actual toast + $RegPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings" + $App = "Microsoft.SoftwareCenter.DesktopToasts" + + # Creating registry entries if they don't exists + if (-NOT(Test-Path -Path "$RegPath\$App")) { + New-Item -Path "$RegPath\$App" -Force + New-ItemProperty -Path "$RegPath\$App" -Name "ShowInActionCenter" -Value 1 -PropertyType "DWORD" -Force + New-ItemProperty -Path "$RegPath\$App" -Name "Enabled" -Value 1 -PropertyType "DWORD" -Force + } + + # Make sure the app used with the action center is enabled + if ((Get-ItemProperty -Path "$RegPath\$App" -Name "Enabled" -ErrorAction SilentlyContinue).Enabled -ne "1") { + New-ItemProperty -Path "$RegPath\$App" -Name "Enabled" -Value 1 -PropertyType "DWORD" -Force + } +} + +# Check for required entries in registry for when using Powershell as application for the toast +if ($PSAppStatus -eq "True") { + + # Register the AppID in the registry for use with the Action Center, if required + $RegPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings" + $App = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe" + + # Creating registry entries if they don't exists + if (-NOT(Test-Path -Path "$RegPath\$App")) { + New-Item -Path "$RegPath\$App" -Force + New-ItemProperty -Path "$RegPath\$App" -Name "ShowInActionCenter" -Value 1 -PropertyType "DWORD" + } + + # Make sure the app used with the action center is enabled + if ((Get-ItemProperty -Path "$RegPath\$App" -Name "ShowInActionCenter" -ErrorAction SilentlyContinue).ShowInActionCenter -ne "1") { + New-ItemProperty -Path "$RegPath\$App" -Name "ShowInActionCenter" -Value 1 -PropertyType "DWORD" -Force + } +} + +# Checking if running toast with personal greeting with given name +if ($GreetGivenName -eq "True") { + Write-Log -Message "Greeting with given name selected. Replacing HeaderText" + $Hour = (Get-Date).TimeOfDay.Hours + if ($Hour –ge 0 –AND $Hour –lt 12) { + $Greeting = $GreetMorningText + } + elseif ($Hour –ge 12 –AND $Hour –lt 16) { + $Greeting = $GreetAfternoonText + } + else { + $Greeting = $GreetEveningText + } + + $GivenName = Get-GivenName + $HeaderText = "$Greeting $GivenName" +} + +# Formatting the toast notification XML +# Create the default toast notification XML with action button and dismiss button +if (($ActionButtonEnabled -eq "True") -AND ($DismissButtonEnabled -eq "True")) { + Write-Log -Message "Creating the xml for displaying both action button and dismiss button" +[xml]$Toast = @" + + + + + + $AttributionText + $HeaderText + + + $TitleText + + + + + $BodyText1 + + + + + $BodyText2 + + + + + + + + + +"@ +} + +# NO action button and NO dismiss button +if (($ActionButtonEnabled -ne "True") -AND ($DismissButtonEnabled -ne "True")) { + Write-Log -Message "Creating the xml for no action button and no dismiss button" +[xml]$Toast = @" + + + + + + $AttributionText + $HeaderText + + + $TitleText + + + + + $BodyText1 + + + + + $BodyText2 + + + + + + + +"@ +} + +# Action button and NO dismiss button +if (($ActionButtonEnabled -eq "True") -AND ($DismissButtonEnabled -ne "True")) { + Write-Log -Message "Creating the xml for no dismiss button" +[xml]$Toast = @" + + + + + + $AttributionText + $HeaderText + + + $TitleText + + + + + $BodyText1 + + + + + $BodyText2 + + + + + + + + +"@ +} + +# Dismiss button and NO action button +if (($ActionButtonEnabled -ne "True") -AND ($DismissButtonEnabled -eq "True")) { + Write-Log -Message "Creating the xml for no action button" +[xml]$Toast = @" + + + + + + $AttributionText + $HeaderText + + + $TitleText + + + + + $BodyText1 + + + + + $BodyText2 + + + + + + + + +"@ +} + +# Snooze button - this option will always enable both action button and dismiss button regardless of config settings +if ($SnoozeButtonEnabled -eq "True") { + Write-Log -Message "Creating the xml for snooze button" +[xml]$Toast = @" + + + + + + $AttributionText + $HeaderText + + + $TitleText + + + + + $BodyText1 + + + + + $BodyText2 + + + + + + + + + + + + + + + + + +"@ +} + +# Add an additional group and text to the toast xml used for notifying about possible deadline. Used with UpgradeOS option +if (($DeadlineEnabled -eq "True") -OR ($DynDeadlineEnabled -eq "True")) { + + if ($DeadlineContent) { + # Format the date time to match local culture of the running OS. Thanks @osdsune.com + $LocalCulture = Get-Culture + $RegionDateFormat = [System.Globalization.CultureInfo]::GetCultureInfo($LocalCulture.LCID).DateTimeFormat.LongDatePattern + $RegionTimeFormat = [System.Globalization.CultureInfo]::GetCultureInfo($LocalCulture.LCID).DateTimeFormat.ShortTimePattern + $LocalDateFormat = $DeadlineContent + $LocalDateFormat = Get-Date $LocalDateFormat -f "$RegionDateFormat $RegionTimeFormat" + +$DeadlineGroup = @" + + + $DeadlineText + $LocalDateFormat + + +"@ + $Toast.toast.visual.binding.InnerXml = $Toast.toast.visual.binding.InnerXml + $DeadlineGroup + } +} + +# Add an additional group and text to the toast xml for PendingRebootCheck +if ($PendingRebootCheckTextEnabled -eq "True") { +$PendingRebootGroup = @" + + + $PendingRebootCheckTextValue + + +"@ + $Toast.toast.visual.binding.InnerXml = $Toast.toast.visual.binding.InnerXml + $PendingRebootGroup +} + +# Add an additional group and text to the toast xml for ADpasswordExpiration +if ($ADPasswordExpirationTextEnabled -eq "True") { +$ADPasswordExpirationGroup = @" + + + $ADPasswordExpirationTextValue $ADPasswordExpirationDate + + +"@ + $Toast.toast.visual.binding.InnerXml = $Toast.toast.visual.binding.InnerXml + $ADPasswordExpirationGroup +} + +# Add an additional group and text to the toast xml used for notifying about computer uptime. Only add this if the computer uptime exceeds MaxUptimeDays. +if (($PendingRebootUptimeTextEnabled -eq "True") -AND ($Uptime -gt $MaxUptimeDays)) { +$UptimeGroup = @" + + + $PendingRebootUptimeTextValue + + + + + $ComputerUptimeText $Uptime $ComputerUptimeDaysText + + +"@ + $Toast.toast.visual.binding.InnerXml = $Toast.toast.visual.binding.InnerXml + $UptimeGroup +} + +# Running the Display-notification function depending on selections and variables +# Toast used for upgrading OS. Checking running OS buildnumber. No need to display toast, if the OS is already running on TargetOS +if (($UpgradeOS -eq "True") -AND ($RunningOS.BuildNumber -lt $TargetOS)) { + Write-Log -Message "Toast notification is used in regards to OS upgrade. Taking running OS build into account" + Display-ToastNotification + # Stopping script. No need to accidently run further toasts + break +} +else { + Write-Log -Level Warn -Message "Conditions for displaying toast notifications for UpgradeOS are not fulfilled" +} + +# Toast used for PendingReboot check and considering OS uptime +if (($PendingRebootUptime -eq "True") -AND ($Uptime -gt $MaxUptimeDays)) { + Write-Log -Message "Toast notification is used in regards to pending reboot. Uptime count is greater than $MaxUptimeDays" + Display-ToastNotification + # Stopping script. No need to accidently run further toasts + break +} +else { + Write-Log -Level Warn -Message "Conditions for displaying toast notifications for pending reboot uptime are not fulfilled" +} + +# Toast used for pendingReboot check and considering checks in registry +if (($PendingRebootCheck -eq "True") -AND ($TestPendingRebootRegistry -eq $True)) { + Write-Log -Message "Toast notification is used in regards to pending reboot registry. TestPendingRebootRegistry returned $TestPendingRebootRegistry" + Display-ToastNotification + # Stopping script. No need to accidently run further toasts + break +} +else { + Write-Log -Level Warn -Message "Conditions for displaying toast notifications for pending reboot registry are not fulfilled" +} + +# Toast used for pendingReboot check and considering checks in WMI +if (($PendingRebootCheck -eq "True") -AND ($TestPendingRebootWMI -eq $True)) { + Write-Log -Message "Toast notification is used in regards to pending reboot WMI. TestPendingRebootWMI returned $TestPendingRebootWMI" + Display-ToastNotification + # Stopping script. No need to accidently run further toasts + break +} +else { + Write-Log -Level Warn -Message "Conditions for displaying toast notifications for pending reboot WMI are not fulfilled" +} + +# Toast used for ADPasswordExpiration +if (($ADPasswordExpiration -eq "True") -AND ($ADPasswordExpirationResult -eq $True)) { + Write-Log -Message "Toast notification is used in regards to ADPasswordExpiration. ADPasswordExpirationResult returned $ADPasswordExpirationResult" + Display-ToastNotification + # Stopping script. No need to accidently run further toasts + break +} +else { + Write-Log -Level Warn -Message "Conditions for displaying toast notification for ADPasswordExpiration are not fulfilled" +} + +# 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")) { + 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" +} \ No newline at end of file diff --git a/RunToastHidden.cmd b/RunToastHidden.cmd new file mode 100644 index 0000000..cd0e9b4 --- /dev/null +++ b/RunToastHidden.cmd @@ -0,0 +1 @@ +powershell.exe -file "C:\ProgramData\ToastNotificationScript\PendingReboot\New-ToastNotification.ps1" -Config "\\YourNetworkPath\ToastNotificationScript\Configs\config-toast-pendingreboot.xml" \ No newline at end of file diff --git a/Windows 10 Toast Notification Script Custom Action Protocols.msi b/Windows 10 Toast Notification Script Custom Action Protocols.msi new file mode 100644 index 0000000..91d3b15 Binary files /dev/null and b/Windows 10 Toast Notification Script Custom Action Protocols.msi differ diff --git a/Windows 10 Toast Notification Script documentation.docx b/Windows 10 Toast Notification Script documentation.docx new file mode 100644 index 0000000..f3c305a Binary files /dev/null and b/Windows 10 Toast Notification Script documentation.docx differ diff --git a/Windows 10 Toast Notification Script documentation.pdf b/Windows 10 Toast Notification Script documentation.pdf new file mode 100644 index 0000000..7b93370 Binary files /dev/null and b/Windows 10 Toast Notification Script documentation.pdf differ diff --git a/config-toast-adpwexpiration.xml b/config-toast-adpwexpiration.xml new file mode 100644 index 0000000..46a9413 --- /dev/null +++ b/config-toast-adpwexpiration.xml @@ -0,0 +1,102 @@ + + + + + + + + \ No newline at end of file diff --git a/config-toast-officeupgrade.xml b/config-toast-officeupgrade.xml new file mode 100644 index 0000000..44af1eb --- /dev/null +++ b/config-toast-officeupgrade.xml @@ -0,0 +1,102 @@ + + + + + + + + \ No newline at end of file diff --git a/config-toast-osupgrade.xml b/config-toast-osupgrade.xml new file mode 100644 index 0000000..2ca570a --- /dev/null +++ b/config-toast-osupgrade.xml @@ -0,0 +1,102 @@ + + + + + + + + \ No newline at end of file diff --git a/config-toast-rebootpending.xml b/config-toast-rebootpending.xml new file mode 100644 index 0000000..e7feb6d --- /dev/null +++ b/config-toast-rebootpending.xml @@ -0,0 +1,102 @@ + + + + + + + + \ No newline at end of file diff --git a/config-toast.xml b/config-toast.xml new file mode 100644 index 0000000..340c4d1 --- /dev/null +++ b/config-toast.xml @@ -0,0 +1,102 @@ + + + + + + + + \ No newline at end of file