Prevent Terminal Server to use C:\Windows\Temp (%windir%\temp)

The Terminal Server employs %windir%\temp as a temporary storage location. Occasionally, customer restrictions or complete directory erasure can lead to Terminal Server failures.

Applies to YSoft SAFEQ 6 and YSoft SAFEQ 5.

Following scenarios were observed:

  1. YSoft SafeQ Terminal Service uses %windir%\temp to store a vital configuration files to its HWC web server. It may stop working in rare cases when %windir%\temp\webserver\applicationHost.config and %windir%\temp\webserver\web.config are deleted.

    Example in Terminal Server log file:
    2022-10-10 04:25:28.9338 WARN 52| HwcWebServer | ConfigurationChanged | Web server [HWC] configuration has been damaged by 3rd party.
  2. The temp directory is used for storing the Thumbnails for embedded terminal images that could cause Terminal Server service to fail in case the thumbnail is not accessible for any reason. This may in rare cases cause an unhandled exception that results in a seemingly correct stop of Terminal Server. However, the Event Viewer message says otherwise.

    Event Viewer - Error - YSoftSQ-TS

    Failed to stop service. System.InvalidOperationException: An unhandled exception was detected

    ---> System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Windows\TEMP\Sqta\Thumbnails'.

    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)

    at System.IO.FileSystemEnumerableIterator`1...

Resolution:

Reconfigure Terminal Server to prevent usage of %windir%\temp directory. You can do it either manually (Option 1) or by automated PowerShell script (Option 2).

Explanation of parameters to be changed

useWebserverRootToStoreConfigurationFiles

  • true forces Terminal Server to use <SPOC>\terminalserver\webserver instead of %windir%\temp\webserver

pathToResizedImagesTempFolder

  • the path defined in this parameter must exist, verify it in advance
  • this redirects the cache to a specified folder rather than %windir%\temp

cacheResizedImages

  • true means caching for small images will remain enabled (default value)
  • false would disable caching of small images for embedded terminals, this could increase network traffic but on the other hand there would be no need to define pathToResizedImagesTempFolder

Option 1

  1. Backup file <SPOC>\terminalserver\TerminalServer.exe.config
  2. Edit <SPOC>\terminalserver\TerminalServer.exe.config
    • add the following lines between the tags <appSettings> and </appSettings> :
      <add key="useWebserverRootToStoreConfigurationFiles" value="true" />
      <add key="cacheResizedImages" value="true" />
      <add key="pathToResizedImagesTempFolder" value="C:\SafeQ6\temp\" />
  3. Save the file
  4. Restart the Terminal Server service

Option 2

Run this PowerShell script:

<#
.SYNOPSIS
  The script adds useWebserverRootToStoreConfigurationFiles, cacheResizedImages, pathToResizedImagesTempFolder to TerminalServer.exe.config file.
          
.DESCRIPTION
  The script is part of o KB article related to an issue with usage of C:\Windows\Temp by Terminal Server, which might lead to issues in some environments.
  The script does the following:
  - it makes a backup of TerminalServer.exe.config
  - it makes a backup of TerminalServer\logs directory
  - it adds/replaces parameter parameters as follows:
    <add key="useWebserverRootToStoreConfigurationFiles" value="true" />
    <add key="cacheResizedImages" value="true" />
    <add key="pathToResizedImagesTempFolder" value="<SPOC>\terminalserver\temp" />
  - it creates <SPOC>\terminalserver\temp directory
  - it restarts Terminal Server service to apply the change
  - it verifies that the change was properly made
  - it waits till the Terminal Server service gets fully initialized
  
  It is enough to run the script just once. Running the script multiple times on the server causes no harm, but it also brings no benefit when the change is already in place.
  The script must be launched on every server hosting Terminal Server service of YSoft SafeQ.
  The script exists right after its completion.
  The best practice is to:
  - save the script (e.g. script.ps1)
  - launch PowerShell as an administrator
  - go to directory with the saved script (e.g. cd c:\temp\)
  - run the script (e.g.  .\script.ps1)
  
.PREREQUISITE        
  PowerShell 3.0 or higher is required, current version can be listed by command: $PSVersionTable.PSVersion.Major
  The script must be launched using PowerShell as an Administrator.
   
.NOTES
  Version:        1.01
  Last Modified:  29/08/2022
          
#>
  
#-----------------------------------------------------------[Execution]------------------------------------------------------------
  
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force
  
  
  
Function SetAttribute {
    Param(
        [string]$Key,
        [string]$Value
    )
    $ConfFile.configuration.appSettings.AppendChild($ConfFile.CreateElement("add")) | % {$_.SetAttribute("key", $Key),$_.SetAttribute("value", $Value)}
}
  
Function StopService {
    Param(
        [string]$Name
    )
    $Svc = Get-Service -Name $Name
    $Svc | Stop-Service -Force
    Do { (Get-Date).ToString("HH:mm:ss") + ' ' + $Svc.Name + ' ' + $Svc.Status }
    Until ($Svc.Status -eq "Stopped")
}
  
Function StartService {
    Param(
        [string]$Name
    )
    $Svc = Get-Service -Name $Name
    $Svc | Start-Service
    Do { (Get-Date).ToString("HH:mm:ss") + ' ' + $Svc.Name + ' ' + $Svc.Status }
    Until ($Svc.Status -eq "Running")
}
  
# Check the PowerShell version
If ($PSVersionTable.PSVersion.Major -lt 3) {
    Write-Host "Your version of Powershell is not up to date, please download and install Windows Management Framework 3.0 or higher, then run the script again :" -ForegroundColor Red
    Write-Host "https://www.microsoft.com/en-us/download/details.aspx?id=54616" -ForegroundColor Red
    Pause;Exit
}
  
# Check the path of "YSoftSQ-TS"
$TSPath = (Get-ItemProperty -Path HKLM:System\CurrentControlSet\Services\YSoftSQ-TS).ImagePath | % {$_.Split('"')[1].Replace("terminalserver\TerminalServer.exe","")}
If ([string]::IsNullOrEmpty($TSPath)) {
    Write-Host "No Terminal Server detected, please check that you are running the script on a YSoft SafeQ server having a Terminal Server installed" -ForegroundColor Red
    Pause;Exit
}
  
# Stop service
(Get-Date).ToString("HH:mm:ss") + ' YSoftSQ-TS Stopping'
StopService -Name "YSoftSQ-TS"
  
# Get conf file parameters and make a backup of the file
(Get-Date).ToString("HH:mm:ss") + ' Backing up the TerminalServer.exe.config'
$ConfPath = $TSPath+"terminalserver\TerminalServer.exe.config"
$ConfPathBck = $ConfPath+'.'+$(get-date -format "yyyy-MM-dd_HHmmss")
Copy-Item $ConfPath $ConfPathBck
(Get-Date).ToString("HH:mm:ss") + ' Backup stored at: ' + $ConfPathBck
$ConfFile = [xml](Get-Content $ConfPath)
 
#Create temp dir
$tempDir = $TSPath+"terminalserver\temp"
If (!(Test-Path $tempDir)) {
    New-Item -Path $tempDir -ItemType Directory | Out-Null
}
If (!(Test-Path $tempDir)) {
    Throw "    Creation of the following directory failed, create it manually and launch the script again. `
    Path: $tempDir"
} else {
    (Get-Date).ToString("HH:mm:ss") + ' ImagesTempFolder created at: ' + $tempDir
}
 
# Modify TerminalServer.exe.config
 
$ConfigChanges = @()
 
$change1 = New-Object -TypeName psobject @{
    key    = 'useWebserverRootToStoreConfigurationFiles'
    value  = 'true'
}
$ConfigChanges += $change1
 
$change2 = New-Object -TypeName psobject @{
    key    = 'cacheResizedImages'
    value  = 'true'
}
$ConfigChanges += $change2
 
$change3 = New-Object -TypeName psobject @{
    key    = 'pathToResizedImagesTempFolder'
    value  = $tempDir
}
$ConfigChanges += $change3
 
(Get-Date).ToString("HH:mm:ss") + ' Modifying TerminalServer.exe.config'
foreach ($change in $ConfigChanges){
    ($ConfFile.configuration.appSettings.ChildNodes | ? {$change.key -contains $_.key }) | % {[void]$_.ParentNode.RemoveChild($_)}
    SetAttribute -Key $change.key -Value $change.value
    $ConfFile.Save($ConfPath)
}
 
#Backup log  directories
Write-Output ((Get-Date).ToString("HH:mm:ss") + " Backing up the Terminal Server log directory")
$pm_folder = "$TSPath\terminalserver\logs"
$timestamp = (Get-Date).ToString("yyyy-MM-dd_HH-mm-ss")
$pm_foldernewname = $pm_folder+"_backup_"+$timestamp
Rename-Item -Path $pm_folder -NewName $pm_foldernewname -ErrorAction SilentlyContinue
if (Test-Path $pm_foldernewname) {(Get-Date).ToString("HH:mm:ss") +" Backup stored at: $($pm_foldernewname.Replace('\\','\'))"}
  
# Start TS service
(Get-Date).ToString("HH:mm:ss") + ' YSoftSQ-TS Starting'
StartService -Name "YSoftSQ-TS"
  
# Checking whether the configuration was properly loaded
 
 
foreach ($change in $ConfigChanges){
    (Get-Date).ToString("HH:mm:ss") + " Checking whether the $($change.key) was properly applied, please wait"
    do
    {
        Start-Sleep -Seconds 2
        $tslog = Get-Content -Encoding String -Path $TSPath\terminalserver\logs\terminalserver.log
        [array]::Reverse($tslog)
        $tslogMatch = $tslog | Where-Object { $_ -match $($change.key +'.*'+ $change.value.Replace('\','\\')) } | Select-Object -First 1
    }
    until (($tslogMatch | Measure-Object).Count -gt 0)
    Write-Output ((Get-Date).ToString("HH:mm:ss") + " Check finished with success")
}
  
  
# Wait for TS to be active
(Get-Date).ToString("HH:mm:ss") + " Terminal Server initialization in progress, please wait"
do
{
    Start-Sleep -Seconds 10
    $tslog = Get-Content -Encoding String -Path $TSPath\terminalserver\logs\terminalserver.log
    [array]::Reverse($tslog)
    $tslogMatch = $tslog | Where-Object { $_ -match 'TS\sfully\sstarted' } | Select-Object -First 1
}
until (($tslogMatch | Measure-Object).Count -gt 0)
Write-Output ((Get-Date).ToString("HH:mm:ss") + " Terminal Server initialization finished")
  
Write-Output ((Get-Date).ToString("HH:mm:ss") + " Script finished")