<# This script will configure some of the Citrix VDA post install tasks by: - Disabling the Telemetry Service - Disabling the VDA Ceip Service - Disabling the CtxAppVService (from 7.14 to 7.15 CU4/1906) - Disabling the App-V Package Cleanup (from 7.15 CU5/1909) - Disabling the Smart Card Services and Launcher - Configuring the Citrix Desktop Service (BrokerAgent) Scheduled Task - Configuring the UviProcessExcludes - Configuring the CtxHooks - Configuring the UPMEvent - Updating the BrokerAgent.exe.config file - Enables the SaveRsopToFile registry value if it exists Note that although some of these items can be disabled/removed/excluded during the VDA installation, actioning them here ensures we have consistency between installations and VDA versions. Script name: VDA-PostInstall.ps1 Release 2.1 Written by Jeremy Saunders (jeremy@jhouseconsulting.com) 2nd February 2018 Modified by Jeremy Saunders (jeremy@jhouseconsulting.com) 20th January 2023 #> #------------------------------------------------------------- # Set Powershell Compatibility Mode Set-StrictMode -Version 2.0 # Enable verbose, warning and error mode $VerbosePreference = 'Continue' $WarningPreference = 'Continue' $ErrorPreference = 'Continue' $StartDTM = (Get-Date) #------------------------------------------------------------- # Set the actions this script will take... $DisableTelemetryService = $True $DisableVDACeipService = $True $DisableCtxAppVService = $False $DisableAppVPackageCleanup = $True $DisableSmartCardServicesAndLauncher = $True $ConfigureBrokerAgentService = $True $ConfigureUviProcessExcludes = $True $ConfigureCtxHooks = $True $ConfigureUPMEvent = $True $UpdateBrokerAgentConfig = $False $EnableSaveRsopToFileValue = $True #------------------------------------ $Vendor = "Citrix" $Product = "VDA" $Version = "Post Install" $LogPS = "${env:SystemRoot}" + "\Temp\$Vendor $Product $Version PS Wrapper.log" Start-Transcript $LogPS # Bypass the "Open File – Security Warning" dialog box. # For more information refer to http://support.microsoft.com/kb/889815 $env:SEE_MASK_NOZONECHECKS = 1 # Get the current script path $ScriptPath = {Split-Path $MyInvocation.ScriptName} $ScriptPath = $(&$ScriptPath) # Push the current location onto a location stack and then change the current location to the location specified Push-Location "$ScriptPath" #------------------------------------ # The Citrix Telemetry Service is essentially a service for Citrix to collect data so they can more easily see how # their customers are using their product(s), which may be good in the long term, but in the short term it doesn't # add any benefit other than utilise CPU/RAM/bandwidth and/or cause delays on boot at the "please wait" or "getting # devices ready" points. # - https://support.citrix.com/article/CTX212998 # - https://discussions.citrix.com/topic/379694-provisioned-server-2012-r2-images-stuck-at-getting-devices-ready/#entry1936442 # - https://discussions.citrix.com/topic/380372-vda-upgrade-cmdlet/#entry1938844 # Note that it defaults to "Automatic (Delayed Start)" # VDA 7.12 and newer the Customer Experience Improvement Program (CEIP) is enabled by default. To disable it, we create # a registry value HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\Telemetry\CEIP\Enabled (DWORD) and set it to 0 (zero). We also # disable the CitrixVDACeipService service. If ($DisableTelemetryService -eq $True) { write-verbose "Disabling the Citrix Telemetry Service" -verbose Invoke-Command {cmd /c sc.exe config CitrixTelemetryService start= disabled} | out-null } If ($DisableVDACeipService -eq $True) { write-verbose "Disabling the Citrix CEIP Service for VDA Service" -verbose Invoke-Command {cmd /c sc.exe config CitrixVDACeipService start= disabled} | out-null $Path = "HKLM:\SOFTWARE\Citrix\Telemetry\CEIP" $KeyExists = $False $ErrorActionPreference = "stop" try { Get-Item -Path "$Path" | Out-Null $KeyExists = $true } catch { # } $ErrorActionPreference = "Continue" If ($KeyExists -eq $False) { New-Item -Path "$path" -Force | Out-Null } write-verbose "Disabling the Citrix CEIP automatic enrolment" -verbose Set-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\Telemetry\CEIP" -Name Enabled -Type DWORD -Value 0 –Force } # Possible results using the sc.exe command line tool: # [SC] ChangeServiceConfig SUCCESS # [SC] OpenSCManager FAILED 5: Access is denied. # [SC] OpenSCManager FAILED 1722: The RPC server is unavailable." --> Computer shutdown # [SC] OpenService FAILED 1060: The specified service does not exist as an installed service." --> Service not installed #------------------------------------ # The Citrix App-V component software, installed and enabled by default when you install the VDA, was removing existing App-V packages # when the Citrix Desktop Service (BrokerAgent) starts prior to the release of 7.15 CU5 and 1909. Whilst there are multiple ways to # potentially control this behaviour, it was easier to disable the CtxAppVService service altogether. This is no longer being used, # but left here for reference. If ($DisableCtxAppVService -eq $True) { write-verbose "Disabling the CtxAppVService Service" -verbose Invoke-Command {cmd /c sc.exe config CtxAppVService start= disabled} | out-null } #------------------------------------ # The App-V packages deployed (pre-cached) to VDAs might be incorrectly removed from the VDAs after a reboot when the service starts. # This fix introduces a registry value under "HKEY_LOCAL_MACHINE\Software\Citrix\AppV\Features" called RedundantPackageCleanup. # The value was added from 7.15 CU5 and 1909 to control whether to enable or disable the clean-up. Whilst it is disabled by default, # we still add the value and set it to False to ensure it remains disabled. This will reduce risk should the issue regress into new # VDA releases. If ($DisableAppVPackageCleanup -eq $True) { write-verbose "Disabling the automatic cleanup of App-V packages" -verbose $Path = "HKLM:\Software\Citrix\AppV\Features" $KeyExists = $False $ErrorActionPreference = "stop" try { Get-Item -Path "$Path" | Out-Null $KeyExists = $true } catch { # } $ErrorActionPreference = "Continue" If ($KeyExists -eq $False) { New-Item -Path "$path" -Force | Out-Null } write-verbose "Disabling the Citrix CEIP automatic enrolment" -verbose Set-ItemProperty -Path "HKLM:\Software\Citrix\AppV\Features" -Name "RedundantPackageCleanup" -Type STRING -Value "False" –Force } #------------------------------------ # Disable the Citrix Smart Card Services and and remove the Launcher from the Run key to speed up the logon process. # - Disable the Citrix Smart Card Certificate Propagation Service (workstation VDA only) # - Disable the Citrix Smart Card Removal Policy Service (workstation VDA only) # - Disable the Citrix Smart Card Service # - Remove the Citrix Virtual Smart Card launcher from the Run key. # It is set to the following by default: # - C:\Program Files\Citrix\Virtual Smart Card\Citrix.Authentication.VirtualSmartcard.Launcher.exe If ($DisableSmartCardServicesAndLauncher -eq $True) { write-verbose "Disabling the Citrix Smart Card Certificate Propagation Service" -verbose Invoke-Command {cmd /c sc.exe config CtxSCardCertPropSvc start= disabled} | out-null write-verbose "Disabling the Citrix Smart Card Removal Policy Service" -verbose Invoke-Command {cmd /c sc.exe config CtxSCardRemovalPolicySvc start= disabled} | out-null write-verbose "Disabling the Citrix Smart Card Service" -verbose Invoke-Command {cmd /c sc.exe config CtxSmartCardSvc start= disabled} | out-null write-verbose "Removing the Citrix Virtual Smart Card launcher from the Run key" -verbose $path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" $value = "Citrix Virtual Smart Card" $ValueExist = $False $ErrorActionPreference = "stop" try { If ((Get-ItemProperty -Path "$Path" | Select-Object -ExpandProperty "$Value") -ne $null) { $ValueExist = $True } } catch { # } $ErrorActionPreference = "Continue" If ($ValueExist) { Remove-ItemProperty -path "$path" -name "$value" -Force } } #------------------------------------ # Configure the start method for the Citrix Desktop Service (BrokerAgent) service # We do this because the Citrix Desktop Service (BrokerAgent) service starts and registers with the Delivery Controllers before the boot # process is complete. Therefore a user can potentially launch an application during the tail end of the boot process. When this happens # it potentially fails the session launch amongst other things. If ($ConfigureBrokerAgentService -eq $True) { # As documented here: https://www.jhouseconsulting.com/2019/03/04/controlling-the-starting-of-the-citrix-desktop-service-brokeragent-1894 # This implements a delay for the VDA registration process. # Copy the script into place $Scripts = "$env:SystemDrive\Scripts" If (-not(Test-Path -Path "$Scripts")) { New-Item -Path "$Scripts" -ItemType Directory | Out-Null } # Push the current location onto a location stack and then change the current location to the location specified Push-Location "$ScriptPath" $CreateTask = $True $DisableService = $True If (Test-Path -path "$ScriptPath\StartCitrixDesktopService.ps1") { copy-item -path ".\StartCitrixDesktopService.ps1" -Destination "$Scripts" -Recurse -Force -Verbose } Else { $CreateTask = $False $DisableService = $True write-warning "The StartCitrixDesktopService.ps1 script is missing!" -verbose } # Change the current location back to the location most recently pushed onto the stack Pop-Location # Disable the Service If ($DisableService) { write-verbose "Set the Citrix Desktop Service (BrokerAgent) to Disabled" -verbose Invoke-Command {cmd /c sc.exe config BrokerAgent start= disabled} | out-null } # Create the Scheduled Task If ($CreateTask) { write-verbose "Creating a Scheduled Task to start the Citrix Desktop Service (BrokerAgent) via a script" -verbose # The name of the scheduled task $taskName = "Start the Citrix Desktop Service" # The task description $taskDescription = "This task is created to enable and start the Citrix Desktop Service" # We delay the task by x minutes to give the Session Host a chance to complete it's startup process before allowing the BrokerAgent to register $AddDelayTrigger = $False $DelayedStartInMinutes = 2 # The Task Action command #$TaskCommand = "${env:SystemRoot}\system32\WindowsPowerShell\v1.0\powershell.exe" $TaskCommand = @(Get-Command powershell.exe)[0].Definition # The script to be executed $TaskScript = "$Scripts\StartCitrixDesktopService.ps1" # The Task Action command argument #$TaskArguments = '-Executionpolicy bypass -Command "& ' + " '" + $TaskScript + "'" $TaskArguments = '-Executionpolicy bypass -Command "& ' + " '" + $TaskScript + "'" + '"' # Create the TaskService object. Try { [Object] $service = new-object -com("Schedule.Service") If (!($service.Connected)){ Try { $service.Connect() # Get a folder to create a task definition in # This is actually the %SystemRoot%\System32\Tasks folder. $rootFolder = $service.GetFolder("\") # Delete the task if already present $ScheduledTasks = $rootFolder.GetTasks(0) $Task = $ScheduledTasks | Where-Object{$_.Name -eq "$TaskName"} If ($Task -ne $Null){ Try { $rootFolder.DeleteTask($Task.Name,0) # 'Success' } Catch [System.Exception]{ # 'Exception Returned' } } Else { # "Task Not Found" } # Create the new task $taskDefinition = $service.NewTask(0) # Create a registration trigger with a trigger type of (8) at startup $triggers = $taskDefinition.Triggers $trigger = $triggers.Create(8) If ($AddDelayTrigger) { # The delay time in minutes before the task runs once it's been triggered $trigger.Delay = "PT${DelayedStartInMinutes}M" } $trigger.Id = "BootTriggerId" $trigger.Enabled = $true # Create the action for the task to execute. $Action = $taskDefinition.Actions.Create(0) $Action.Path = $TaskCommand $Action.Arguments = $TaskArguments $Action.WorkingDirectory = "" # Register (create) the task. $Settings = $taskDefinition.Settings # Set the Task Compatibility to V2 (Windows 7/2008R2) $Settings.Compatibility = 3 # The default task priority 7 (below normal), so we set this back to normal $Settings.Priority = 6 $Settings.AllowDemandStart = $true $Settings.StopIfGoingOnBatteries = $false $Settings.DisallowStartIfOnBatteries = $false $regInfo = $taskDefinition.RegistrationInfo $regInfo.Description = $taskDescription $regInfo.Author = $Env:Username # Note that the task is created as an XML file under the %SystemRoot%\System32\Tasks folder # 6 == Task Create or Update # 5 == A Local System, Local Service, or Network Service account is being used as a security context to run the task. $rootFolder.RegisterTaskDefinition($taskName, $taskDefinition, 6, "System", $null , 5) | out-null write-verbose "- Scheduled Task Created Successfully" -verbose $rootFolder.GetTasks(0) | Where-Object{$_.Name -eq "$TaskName"} | ForEach-Object { write-verbose "- Disabled task" -verbose $_.Enabled = $False } } Catch [System.Exception]{ write-warning "- Scheduled Task Creation Failed" -verbose } } } Catch [System.Exception]{ write-warning "- Scheduled Task Creation Failed" -verbose } } } Else { write-verbose "Set the Citrix Desktop Service (BrokerAgent) service to Automatic (Delayed Start)" -verbose # This will delay the VDA Registration after a reboot so that it will start about 2 minutes after the last "Automatic" service has started. Invoke-Command {cmd /c sc.exe config BrokerAgent start= delayed-auto} | out-null } #------------------------------------ # XenDesktop/XenApp VDA 7.9 and above utilises Kernel APC Hooking as a replacement of AppInit_DLLs. # The KAPC Hooking DLL Injection Driver (CtxUvi) verifies that the hook DLLs configuration in the # registry is not changed at runtime (i.e. HKLM\SOFTWARE\Citrix\CtxHook\AppInit_DLLs\). # If a change to the configuration is detected, the CtxUvi driver disables itself until the next # reboot, resulting in none of the Citrix Hooks being properly loaded. So it is recommended not to # use Group Policies to control these registry keys and placing them in the master PVS/MCS image. # As defined under the $ProcessesToAdd variable, I add the following processes: sppsvc.exe, # RAserver.exe, SelfService.exe, CtxWebBrowser.exe, Receiver.exe, msedge.exe, msedgewebview2.exe, # AcroCef.exe, RdrCEF.exe, QtWebEngineProcess.exe, chrome.exe, nacl64.exe # The script only appends the first 14 characters of these values, or whatever values are missing, # and does not duplicate or wipe an existing value or values in the list. Each VDA version may have # a default list. This covers many different known issues across the VDA and process versions # documented by Citrix and the various support forums. # References: # - https://support.citrix.com/article/CTX220418 # - https://support.citrix.com/article/CTX226605 # - https://support.citrix.com/article/CTX223973 # - https://support.citrix.com/article/CTX465105 $ProductVersion = (Get-Item "${env:ProgramFiles}\Citrix\Virtual Desktop Agent\BrokerAgent.exe").VersionInfo.ProductVersion [int]$ProductVersionMajor = $ProductVersion.Split('.')[0] [int]$ProductVersionMinor = $ProductVersion.Split('.')[1] $ContainsGPU = $False Try { $ContainsGPU = ((Get-WmiObject -Query "SELECT * FROM Win32_PNPEntity WHERE DEVICEID LIKE '%VEN_10DE%'").Manufacturer -eq "NVIDIA") } Catch { # } $ProductType = (Get-WMIObject Win32_OperatingSystem).ProductType If ($ConfigureUviProcessExcludes) { If (($ProductVersionMajor -eq 7 -AND $ProductVersionMinor -ge 9) -OR $ProductVersionMajor -gt 7) { # Prevent the CtxUvi Driver disabling. Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CtxUvi" UviEnabled -Value 1 –Force # Add a list of processes to the UviProcesExcludes registry value under the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\CtxUvi # Add the full process here, but the code will only add the first 14 characters to the UviProcesExcludes registry value. $ProcessesToAdd = @("sppsvc.exe","RAserver.exe","SelfService.exe","CtxWebBrowser.exe","Receiver.exe","msedge.exe","msedgewebview2.exe","AcroCef.exe","RdrCEF.exe","QtWebEngineProcess.exe","chrome.exe","nacl64.exe") $ErrorActionPreference = "stop" try { If ((Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CtxUvi" | Select-Object -ExpandProperty "UviProcessExcludes") -ne $null) { $UviProcessExcludes = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CtxUvi" -Name "UviProcessExcludes").UviProcessExcludes } } catch { # } $ErrorActionPreference = "Continue" $AddUviProcessExcludes = $False write-verbose "Checking the UviProcessExcludes value..." -verbose If (!([String]::IsNullOrEmpty($UviProcessExcludes))) { write-verbose "- The current values are: `"$UviProcessExcludes`"" -verbose ForEach ($ProcessToAdd in $ProcessesToAdd) { If ($ProcessToAdd.Length -gt 14) { $ProcessToAdd = $ProcessToAdd.SubString(0,14) } If ($UviProcessExcludes -like "*$ProcessToAdd*") { write-verbose "- The $ProcessToAdd process has already been added" -verbose } Else { write-verbose "- The $ProcessToAdd process is being added to the string" -verbose $UviProcessExcludes = $UviProcessExcludes + $ProcessToAdd + ";" $AddUviProcessExcludes = $True } } } Else { ForEach ($ProcessToAdd in $ProcessesToAdd) { If ($ProcessToAdd.Length -gt 14) { $ProcessToAdd = $ProcessToAdd.SubString(0,14) } $AddUviProcessExcludes = $True If ([String]::IsNullOrEmpty($UviProcessExcludes)) { $UviProcessExcludes = $ProcessToAdd + ";" } Else { $UviProcessExcludes = $UviProcessExcludes + $ProcessToAdd + ";" } } } If ($AddUviProcessExcludes) { write-verbose "- Setting the new values: `"$UviProcessExcludes`"" -verbose Set-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Services\CtxUvi" -name "UviProcessExcludes" -value "$UviProcessExcludes" -Type STRING -Force } } } If ($ConfigureCtxHooks) { If ($ContainsGPU -AND $ProductType -eq 3) { Set-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\CtxHook\AppInit_Dlls\Graphics Helper" OpenCL -Value 1 –Force Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Citrix\CtxHook\AppInit_Dlls\Graphics Helper" OpenCL -Value 1 –Force Set-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\CtxHook\AppInit_Dlls\Graphics Helper" CUDA -Value 1 –Force Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Citrix\CtxHook\AppInit_Dlls\Graphics Helper" CUDA -Value 1 –Force Set-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\CtxHook\AppInit_DLLs\Multiple Monitor Hook" EnableWPFHook -Value 1 –Force Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Citrix\CtxHook\AppInit_DLLs\Multiple Monitor Hook" EnableWPFHook -Value 1 –Force } } #------------------------------------ # This task was driven by the great documentation from George Spiers (https://www.jgspiers.com/). # upmEvent.exe needs to run to generate Event ID 1000. This is needed for seeing the logon duration in # Citrix Director. If Event ID 1000 is not generated, the logon duration is NULL in the database. # For a default location: # - VDA 7.15 and lower it is under the run key, which was a bad idea as documented by George, so we move # the Citrix UPMEvent.exe process from the 'Run' key to a Scheduled Task so that it starts up faster # and improves the logon time as recorded in Citrix Director. We also append the .exe to the upmEvent # process to avoid quirky issues where the file cannot be found. One added configuration process I do # here is set the priority of the scheduled task to normal. # - VDA 7.16 to 7.18 it is under the userinit key. This change results in upmEvent.exe running much # quicker than previous versions because Citrix have allowed Winlogon to run the .exe, moving # upmEvent.exe away from the Run registry key. # - VDA 1808 and above the upmEvent is processed by the Citrix Profile Management service. So if it # exists under the Run registry key, a logon script or Scheduled Task, it should be removed. If not, # it can create a timing conflict (race condition) where it may result with a logon session getting # stuck with a black screen. # References: # - https://www.jgspiers.com/citrix-director-reduce-logon-times/ # - https://www.jgspiers.com/reduce-citrix-director-interactive-session-ti If ($ConfigureUPMEvent) { $ProductVersion = (Get-Item "${env:ProgramFiles}\Citrix\Virtual Desktop Agent\BrokerAgent.exe").VersionInfo.ProductVersion [int]$ProductVersionMajor = $ProductVersion.Split('.')[0] [int]$ProductVersionMinor = $ProductVersion.Split('.')[1] $AddToUserinit = $True $upmEventEXE = "${env:ProgramFiles}\Citrix\Virtual Desktop Agent\upmEvent.exe" If (TEST-PATH "$upmEventEXE") { $path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" $value = "Citrix UPM UserMsg" $ValueExist = $False $ErrorActionPreference = "stop" try { If ((Get-ItemProperty -Path "$Path" | Select-Object -ExpandProperty "$Value") -ne $null) { $ValueExist = $True } } catch { # } $ErrorActionPreference = "Continue" If ($ValueExist) { write-verbose "Removing `"Citrix UPM UserMsg`" (upmEvent.exe) from the Run key" -verbose Remove-ItemProperty -path "$path" -name "$value" -Force } If ($ProductVersionMajor -eq 7 -AND ($ProductVersionMinor -ge 15 -AND $ProductVersionMinor -lt 19)) { If ($AddToUserinit) { write-verbose "Adding the upmEvent.exe process to the Userinit registry value" -verbose # Add the upmEvent.exe to the Userinit value. $ErrorActionPreference = "stop" try { If ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" | Select-Object -ExpandProperty "Userinit") -ne $null) { $Userinit = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "Userinit").Userinit } } catch { # } $ErrorActionPreference = "Continue" $AddUserinit = $True write-verbose "Checking the Userinit value..." -verbose If ($Userinit -ne $Null -AND $Userinit -ne "") { write-verbose "- The current values are: `"$Userinit`"" -verbose If ($Userinit -like "*upmEvent*") { write-verbose "- The upmEvent.exe process has already been added" -verbose $AddUserinit = $False } Else { $Userinit = $Userinit + "$upmEventEXE wait," } } Else { $Userinit = "$upmEventEXE wait," } If ($AddUserinit) { write-verbose "- Setting the new values: `"$Userinit`"" -verbose Set-ItemProperty -path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -name "Userinit" -value "$Userinit" -Type STRING -Force } } Else { write-verbose "Creating a Scheduled Task to start the upmEvent.exe process" -verbose # The name of the scheduled task $TaskName = "Citrix UPMEvent" # The task description $TaskDescription = "We move the Citrix UPMEvent.exe process from the 'Run' key to a Scheduled Task so that it starts up faster and improves the logon time as recorded in Citrix Director." # The Task Action command $TaskCommand = """${env:ProgramFiles}\Citrix\Virtual Desktop Agent\upmEvent.exe""" # The Task Action command argument $TaskArguments = "wait" # Create the TaskService object. Try { [Object] $service = new-object -com("Schedule.Service") If (!($service.Connected)){ Try { $service.Connect() # Get a folder to create a task definition in # This is actually the %SystemRoot%\System32\Tasks folder. $rootFolder = $service.GetFolder("\") # Delete the task if already present $ScheduledTasks = $rootFolder.GetTasks(0) $Task = $ScheduledTasks | Where-Object{$_.Name -eq "$TaskName"} If ($Task -ne $Null){ Try { $rootFolder.DeleteTask($Task.Name,0) # 'Success' } Catch [System.Exception]{ # 'Exception Returned' } } Else { # "Task Not Found" } # Create the new task $taskDefinition = $service.NewTask(0) # Create a registration trigger with a trigger type of (9) LogonTrigger $triggers = $taskDefinition.Triggers $trigger = $triggers.Create(9) $trigger.ExecutionTimeLimit = "PT30M" $trigger.Enabled = $true # Create the action for the task to execute. $Action = $taskDefinition.Actions.Create(0) $Action.Path = $TaskCommand $Action.Arguments = $TaskArguments $taskPrincipal = $taskDefinition.Principal # Must be a valid user account or group. # Here we use BUILTIN\Users so that it runs for all users. # BUILTIN\Users translates to a SID of S-1-5-32-545 $taskPrincipal.GroupID = "BUILTIN\Users" $taskPrincipal.RunLevel = 0 # Register (create) the task. $Settings = $taskDefinition.Settings # Set the Task Compatibility to V2 (Windows 7/2008R2) $Settings.Compatibility = 3 # The default task priority 7 (below normal), so we set this back to normal $Settings.Priority = 6 $Settings.AllowDemandStart = $true $Settings.StopIfGoingOnBatteries = $false $Settings.DisallowStartIfOnBatteries = $false # Note that the task is created as an XML file under the %SystemRoot%\System32\Tasks folder $regInfo = $taskDefinition.RegistrationInfo $regInfo.Description = $TaskDescription # 6 == Task Create or Update # 3 == LogonTypeInteractive $rootFolder.RegisterTaskDefinition($TaskName, $TaskDefinition, 6, '', '', 3) | Out-Null write-verbose "- Scheduled Task Created Successfully" -verbose } Catch [System.Exception]{ write-warning "- Scheduled Task Creation Failed" -verbose } } } Catch [System.Exception]{ write-warning "- Scheduled Task Creation Failed" -verbose } } } Else { # The version of BrokerAgent.exe is not in scope for this fix. } } Else { write-verbose "The `"${env:ProgramFiles}\Citrix\Virtual Desktop Agent\upmEvent.exe`" executable does not exist. This" -verbose write-verbose "has been written to work with VDA versions 7.7 and above. Earlier version used upmUserMsg.exe included" -verbose write-verbose "with Citrix Profile Management located under the `"${env:ProgramFiles}\Citrix\User Profile Manager`"" -verbose write-verbose "folder instead of UPMEvent.exe included with the VDA binaries." -verbose } } #------------------------------------ # I was testing a config change in a large multi-domain environment by changing the allowNtlm="false" # setting to allowNtlm="true" in the BrokerAgent.exe.config file. Leaving the UpdateBrokerAgentConfig # variable set to False will not apply this change. However, I've left the code in the script for future # reference in case the BrokerAgent.exe.config file needs to be modified again as it took a while to # figure out the best way to manipulate this XML file. If ($UpdateBrokerAgentConfig) { $filePath = "${env:ProgramFiles}\Citrix\Virtual Desktop Agent" $configFile = "BrokerAgent.exe.config" $setting = "allowNtlm=" # This XML file has an unusual format. I found that the only way to successfully read it, was to not cast it as XML. # Reading it in using ReadAllText and StreamReader were the only two methods that would not disrupt the format. # Then I could simply do a string replace and writing it back out again instead of managing is via the XML nodes and elements. $invalidChars = [io.path]::GetInvalidFileNamechars() $datestampforfilename = ((Get-Date -format s).ToString() -replace "[$invalidChars]","-") $Reader = new-object System.IO.StreamReader("$filePath\$configFile") $content = @() While (-not $Reader.EndOfStream) { $line = $Reader.ReadLine() If ($line -match ([regex]::Escape($setting))) { $content += $line.replace("false", "true") } Else { $content += $line } } $Reader.Close() $Reader.Dispose() $Writer = new-object System.IO.StreamWriter("$filePath\$configFile.tmp") $Writer.Write(($content | Out-String)) $Writer.Close() $Writer.Dispose() Get-ChildItem -path "$filePath\" | where {$_.Name -eq "$configFile"} | Rename-Item -newname ("$filePath\$configFile" + "_" + "$datestampforfilename") -force Get-ChildItem -path "$filePath\" | where {$_.Name -eq "$configFile.tmp"} | Rename-Item -newname ("$filePath\$configFile") -force } #------------------------------------ # This checks for the SaveRsopToFile registry value, and then sets it to 1, which enables it. This # addresses a bug with 7.15 LTSR CU6 [LCM-8201] with a change of security model where the rsop.gpf # is either missing or 0 bytes and therefore the applied policies do not appear in Director under # Session Details, providing misleading information. We apply it at post install instead of Group # Policy to ensure this fix has been applied before the CitrixCseEngine (Citrix Group Policy Engine) # service starts. I continue to apply this to avoid regression, but will review this again with the # release of 2203. # Reference: https://support.citrix.com/article/CTX286890 If ($EnableSaveRsopToFileValue) { $SaveRsopToFileValueExist = $False $ErrorActionPreference = "stop" try { If ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\GroupPolicy" | Select-Object -ExpandProperty "SaveRsopToFile") -ne $null) { $SaveRsopToFileValueExist = $True } } catch { # } $ErrorActionPreference = "Continue" If ($SaveRsopToFileValueExist) { write-verbose "Enabling the SaveRsopToFile registry value" -verbose Set-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\GroupPolicy" -Name SaveRsopToFile -Type DWORD -Value 1 –Force } Else { write-verbose "The SaveRsopToFile registry value does not exist" -verbose } } #------------------------------------ # Change the current location back to the location most recently pushed onto the stack, which will be defined by the $ScriptPath variable Pop-Location # Enable File Security Remove-Item env:\SEE_MASK_NOZONECHECKS Write-Verbose "Stop logging" -Verbose $EndDTM = (Get-Date) Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalSeconds) Seconds" -Verbose Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose Stop-Transcript