Citrix TEMP profiles

Creation date: 7/29/2026 8:52 AM    Updated: 7/29/2026 8:54 AM   citrix
Run this from your local machine as an admin in PowerShell
It will ask you to authenticate 

Domain\Username
Password
 



<#
.SYNOPSIS
    Runs the TEMP profile cleanup remotely across the HURCO Citrix VDA farm.

.DESCRIPTION
    Uses PowerShell Remoting (WinRM) to connect to each target server in
    parallel and remove stale TEMP profile folders under C:\Users - matching
    TEMP* (e.g. TEMP.HURCO.055, TEMP.UK.004, TEMP.FR.000) as well as the
    legacy "<username>.TEMP" style - plus their orphaned entries under
    HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList.

    Deletion is permanent (Remove-Item bypasses the Recycle Bin).

.PARAMETER ComputerName
    Target server names. Defaults to the HURCO Citrix VDA farm.

.PARAMETER Path
    Remote path to scan on each server. Defaults to C:\Users.

.PARAMETER Credential
    Credential with local admin rights on the target servers. Prompted for if
    not supplied.

.PARAMETER WhatIf
    Preview only - reports what would be deleted on each server without
    deleting anything.

.EXAMPLE
    .\Invoke-RemoteTempProfileCleanup.ps1 -WhatIf
    Preview across all 8 default servers.

.EXAMPLE
    .\Invoke-RemoteTempProfileCleanup.ps1
    Actually deletes across all 8 default servers (prompts for credentials).

.EXAMPLE
    .\Invoke-RemoteTempProfileCleanup.ps1 -ComputerName HURCO-CX33 -WhatIf
    Preview against a single server.

.NOTES
    Requires:
      - WinRM enabled on each target (Enable-PSRemoting) and reachable on
        TCP 5985 (HTTP) or 5986 (HTTPS) from this machine.
      - An account with local Administrator rights on each target server.
      - If this machine is NOT domain-joined, the targets must be added to
        this machine's WinRM TrustedHosts list (Kerberos won't be available):
          Set-Item WSMan:\localhost\Client\TrustedHosts -Value "HURCO-CX*" -Force
#>

[CmdletBinding()]
param(
    [string[]]$ComputerName = @(
        'HURCO-CX32', 'HURCO-CX33', 'HURCO-CX34', 'HURCO-CX35',
        'HURCO-CX42', 'HURCO-CX43', 'HURCO-CX44', 'HURCO-CX45'
    ),
    [string]$Path = 'C:\Users',
    [pscredential]$Credential,
    [switch]$WhatIf
)

if (-not $Credential) {
    $Credential = Get-Credential -Message "Enter an account with local admin rights on the target servers"
}

# This scriptblock runs ON EACH REMOTE SERVER. It mirrors the logic in
# Remove-TempProfiles.ps1, but reports results back as objects instead of
# writing to a local log file, and uses a plain boolean instead of the
# ShouldProcess/-WhatIf machinery (more reliable across a remoting boundary).
$remoteScript = {
    param($Path, $PreviewOnly)

    $result = [System.Collections.Generic.List[object]]::new()
    $profileListPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'

    $activeSessionUsers = @()
    try {
        $quserOutput = quser 2>$null
        if ($quserOutput) {
            $activeSessionUsers = $quserOutput | Select-Object -Skip 1 | ForEach-Object {
                ($_ -replace '^\s*>', '').Trim() -split '\s+' | Select-Object -First 1
            } | Where-Object { $_ }
        }
    } catch {}

    $tempFolders = Get-ChildItem -Path $Path -Directory -ErrorAction SilentlyContinue |
        Where-Object {
            $_.Name -like 'TEMP*' -or
            $_.Name -like '*.TEMP' -or
            $_.Name -like '*.TEMP.*'
        }

    foreach ($folder in $tempFolders) {
        $baseName = $folder.Name -replace '\.TEMP(\.\d+)?$', ''

        if ($activeSessionUsers -contains $baseName) {
            $result.Add([pscustomobject]@{ Folder = $folder.FullName; Status = 'Skipped (active session)' })
            continue
        }

        $inUse = $false
        $lockCheckFile = Join-Path $folder.FullName '.lockcheck.tmp'
        try {
            Set-Content -Path $lockCheckFile -Value 'x' -ErrorAction Stop
            Remove-Item -Path $lockCheckFile -ErrorAction SilentlyContinue
        } catch {
            $inUse = $true
        }

        if ($inUse) {
            $result.Add([pscustomobject]@{ Folder = $folder.FullName; Status = 'Skipped (locked/in use)' })
            continue
        }

        if ($PreviewOnly) {
            $result.Add([pscustomobject]@{ Folder = $folder.FullName; Status = 'Would delete (WhatIf)' })
            continue
        }

        try {
            Remove-Item -Path $folder.FullName -Recurse -Force -ErrorAction Stop
        } catch {
            $result.Add([pscustomobject]@{ Folder = $folder.FullName; Status = "Error: $($_.Exception.Message)" })
            continue
        }

        Get-ChildItem $profileListPath -ErrorAction SilentlyContinue | ForEach-Object {
            $props = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue
            if ($props.ProfileImagePath -and $props.ProfileImagePath -ieq $folder.FullName) {
                try { Remove-Item -Path $_.PSPath -Recurse -Force -ErrorAction Stop } catch {}
            }
        }

        $result.Add([pscustomobject]@{ Folder = $folder.FullName; Status = 'Deleted' })
    }

    if ($result.Count -eq 0) {
        $result.Add([pscustomobject]@{ Folder = '(none found)'; Status = 'N/A' })
    }

    $result
}

Write-Host "Connecting to $($ComputerName.Count) server(s)$(if ($WhatIf) { ' (WhatIf - preview only)' })..." -ForegroundColor Cyan

$jobs = Invoke-Command -ComputerName $ComputerName -Credential $Credential `
    -ScriptBlock $remoteScript -ArgumentList $Path, $WhatIf.IsPresent `
    -AsJob -JobName 'TempProfileCleanup'

$jobs | Wait-Job | Out-Null

$report = foreach ($job in $jobs.ChildJobs) {
    $computer = $job.Location
    try {
        Receive-Job -Job $job -ErrorAction Stop | ForEach-Object {
            [pscustomobject]@{
                Server = $computer
                Folder = $_.Folder
                Status = $_.Status
            }
        }
    } catch {
        [pscustomobject]@{
            Server = $computer
            Folder = '(connection failed)'
            Status = $_.Exception.Message
        }
    }
}

$stamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$csvPath = Join-Path $PWD "TempProfileCleanup_Report_$stamp.csv"

$report = $report | Sort-Object Server, Folder
$report | Format-Table -AutoSize
$report | Export-Csv -Path $csvPath -NoTypeInformation

Write-Host "`nReport saved to: $csvPath" -ForegroundColor Green

Get-Job -Name 'TempProfileCleanup' -ErrorAction SilentlyContinue | Remove-Job -Force

Citrix Issues