Citrix Virtual Apps and Desktops

Remove deprecated policy settings after an upgrade

When Citrix® deprecates a product component, its associated policy settings become invalid. Use GpoClean.ps1 to identify and remove these deprecated settings from Citrix Virtual Apps and Desktops™ policies after an upgrade, including any policies that become empty as a result.

Before you begin

Make sure you meet these requirements before you run the script:

  • A supported version of Citrix Virtual Apps and Desktops with Broker SDK and Group Policy Management installed
  • Citrix policy administration permissions on a Delivery Controller™

Note:

The script automatically loads the Citrix.Broker.Admin.V2 and Citrix.Common.GroupPolicy snap-ins.

Remove deprecated policy settings

Run GpoClean.ps1 to remove deprecated policy settings:

  1. Copy GpoClean.ps1 to a Delivery Controller on which you have Citrix policy administration permissions.
  2. Open a PowerShell session on the Delivery Controller.
  3. Run the script. No parameters are required.

    .\GpoClean.ps1
    <!--NeedCopy-->
    

    Note:

    Add -Verbose for detailed per-setting logging: .\GpoClean.ps1 -Verbose.

The script runs in two phases:

Phase What happens
Phase 1 Runs Test-BrokerDesktopPolicy, identifies DeprecatedSetting errors, and sets each affected setting to NotConfigured
Phase 2 Re-runs Test-BrokerDesktopPolicy, identifies policies with no configured settings (PolicyHasNoSettings), and removes them

All changes are saved in a single write operation. A summary reports the number of settings and policies removed. If no deprecated settings are found, the script exits with no changes.

Note:

  • The Unfiltered policy is never removed, even if it contains no configured settings.

Script

Save the following content as GpoClean.ps1, then run it as described in Remove deprecated policy settings:

<#
.Synopsis

    Cleans up deprecated settings from Citrix policies and removes policies that no longer have any settings.

.Description

    This script runs Test-BrokerDesktopPolicy to identify deprecated settings in policies. When a setting is
    deprecated (DeprecatedSetting error), the script removes the setting from the policy using the Citrix Group Policy
    PowerShell Provider. After removing deprecated settings, if a policy has no settings remaining
    (PolicyHasNoSettings error), the policy is removed automatically, except for the 'Unfiltered' policy which is
    preserved.

    This script is useful for cleaning up policies after upgrading to a newer version where some settings have been
    deprecated.

.Requirements

    Run this script on a DDC that has the Citrix Group Policy Management and Citrix Broker SDK installed.
#>

[CmdletBinding()]
param()

Write-Host "Checking Citrix policies for deprecated settings..."

# Load the Citrix Broker PowerShell Snapin.
$BrokerSnapin = "Citrix.Broker.Admin.V2"
$loaded = Get-PSSnapin $BrokerSnapin -ErrorAction SilentlyContinue
if ($loaded -eq $null)
{
    Add-PSSnapin $BrokerSnapin
}

# First pass: Run Test-BrokerDesktopPolicy to check for errors.
Write-Host "Running policy validation..."
$testResults = Test-BrokerDesktopPolicy | Select-Object -Property *

if ($testResults -eq $null -or $testResults.Count -eq 0)
{
    Write-Host "No policy errors found. All policies are clean."
    exit 0
}

# Check if there are any DeprecatedSetting errors.
$hasDeprecatedSettings = $false
foreach ($result in $testResults)
{
    if ([string]$result.ErrorCode -eq 'DeprecatedSetting')
    {
        $hasDeprecatedSettings = $true
        break
    }
}

if (-not $hasDeprecatedSettings)
{
    Write-Host "No deprecated settings found. Policy errors exist but do not require cleaning."
    Write-Host "Run 'Test-BrokerDesktopPolicy' to see other policy issues."
    exit 0
}

# Load the Citrix Group Policy PowerShell Provider (only if we need to clean).
Write-Host "`nDeprecated settings found. Loading Group Policy provider for cleanup..."
$GpSnapin = "Citrix.Common.GroupPolicy"
$Provider = "CitrixGroupPolicy"
$SiteName = "CitrixGP"

$loaded = Get-PSSnapin $GpSnapin -ErrorAction SilentlyContinue
if ($loaded -eq $null)
{
    Add-PSSnapin $GpSnapin
}

# Mount the provider drive if it has not been mounted.
$GpDrive = Get-PSDrive $SiteName -ErrorAction SilentlyContinue
if ($GpDrive -eq $null)
{
    [void]($GpDrive = New-PSDrive -PSProvider $Provider -Name $SiteName -Root \ -Controller localhost)
}

$uRoot = $SiteName + ":\User\"
$cRoot = $SiteName + ":\Computer\"

$GpDrive.AutoWriteBack = $false

$settingsRemoved = 0
$policiesRemoved = 0

$settingNameMappings = @{
    'PvsIntegrationEnabled' = 'ImageProviderIntegrationEnabled'
}

function Remove-SettingFromPolicy
{
    param(
        [string] $policyName,
        [string] $settingName
    )

    $targetSettingName = Split-Path -Path $settingName -Leaf

    if ($settingNameMappings.ContainsKey($targetSettingName))
    {
        $mappedSettingName = $settingNameMappings[$targetSettingName]
        Write-Verbose "Mapping deprecated setting name '$targetSettingName' to '$mappedSettingName'"
        $targetSettingName = $mappedSettingName
    }

    $searchPattern = $SiteName + ":\" + "*\" + $policyName + "\Settings\*"
    Write-Verbose "Searching for settings matching '$targetSettingName' in policy '$policyName'..."
    $allSettings = Get-ChildItem -Path $searchPattern -Recurse -ErrorAction SilentlyContinue |
        Where-Object {
            ($_.PSObject.Properties['State'] -and $_.State -ne 'NotConfigured') -or
            ($_.Name -eq 'Values' -and $_.Values -ne $null -and $_.Values -ne '')
        }

    $found = $false
    foreach ($setting in $allSettings)
    {
        $RootPath = "{0}\{1}::{2}:" -f $GpSnapin, $Provider, $SiteName
        $relativePath = $setting.PSPath.Substring($RootPath.Length)
        Write-Verbose "  Checking configured setting: $relativePath"
        $currentSettingName = Split-Path -Path $setting.PSPath -Leaf
        if ($currentSettingName -ne $targetSettingName) { continue }
        Write-Verbose "  Found matching setting: $relativePath"
        Set-ItemProperty -Path $setting.PSPath -Name State -Value NotConfigured
        Write-Host "  Set setting '$relativePath' to NotConfigured"
        $Script:settingsRemoved++
        $found = $true
    }

    if (-not $found)
    {
        Write-Warning "Could not find setting '$targetSettingName' in policy '$policyName'"
    }
}

function Remove-PolicyByName
{
    param(
        [string] $policyName
    )

    if ($policyName -eq 'Unfiltered')
    {
        Write-Host "  Skipping removal of 'Unfiltered' policy (reserved policy)"
        return
    }

    $uPath = $uRoot + $policyName
    $cPath = $cRoot + $policyName

    if (Test-Path $uPath)
    {
        $uSettings = Get-ChildItem -Path ($uPath + "\Settings") -Recurse -ErrorAction SilentlyContinue |
            Where-Object {
                ($_.PSObject.Properties['State'] -and $_.State -ne 'NotConfigured') -or
                ($_.Name -eq 'Values' -and $_.Values -ne $null -and $_.Values -ne '')
            }
        if ($uSettings.Count -eq 0)
        {
            Remove-Item -Path $uPath -Force -Recurse
            Write-Host "  Removed policy: \User\$policyName"
            $Script:policiesRemoved++
        }
    }

    if (Test-Path $cPath)
    {
        $cSettings = Get-ChildItem -Path ($cPath + "\Settings") -Recurse -ErrorAction SilentlyContinue |
            Where-Object {
                ($_.PSObject.Properties['State'] -and $_.State -ne 'NotConfigured') -or
                ($_.Name -eq 'Values' -and $_.Values -ne $null -and $_.Values -ne '')
            }
        if ($cSettings.Count -eq 0)
        {
            Remove-Item -Path $cPath -Force -Recurse
            Write-Host "  Removed policy: \Computer\$policyName"
            $Script:policiesRemoved++
        }
    }
}

# Phase 1: Remove deprecated settings.
Write-Host "`nPhase 1: Removing deprecated settings (this may take some time)..."
foreach ($result in $testResults)
{
    if ([string]$result.ErrorCode -eq 'DeprecatedSetting')
    {
        Write-Host "`nPolicy '$($result.PolicyName)' has deprecated settings:"
        if ($result.Settings -ne $null)
        {
            foreach ($setting in $result.Settings)
            {
                Write-Host "  - Setting '$($setting.SettingName)' is deprecated"
                Remove-SettingFromPolicy -policyName $result.PolicyName -settingName $setting.SettingName
            }
        }
    }
}

# Phase 2: Remove empty policies.
Write-Host "`nPhase 2: Checking for policies with no settings..."
$testResults2 = Test-BrokerDesktopPolicy | Select-Object -Property *

if ($testResults2 -eq $null -or $testResults2.Count -eq 0)
{
    Write-Host "No policy errors found."
}
else
{
    foreach ($result in $testResults2)
    {
        if ([string]$result.ErrorCode -eq 'PolicyHasNoSettings')
        {
            Write-Host "`nPolicy '$($result.PolicyName)' has no settings remaining:"
            Remove-PolicyByName -policyName $result.PolicyName
        }
    }
}

# Save all changes.
if ($settingsRemoved -gt 0 -or $policiesRemoved -gt 0)
{
    Write-Host "`nSaving changes..."
    $GpDrive.Save()
}

Remove-PSDrive $SiteName

Write-Host "`n=========================================="
Write-Host "Policy Cleanup Summary:"
Write-Host "  Settings removed: $settingsRemoved"
Write-Host "  Policies removed: $policiesRemoved"
Write-Host "=========================================="

if ($settingsRemoved -eq 0 -and $policiesRemoved -eq 0)
{
    Write-Host "`nNo issues found. All policies are clean."
}
else
{
    Write-Host "`nCleanup completed successfully."
}

exit 0
<!--NeedCopy-->

For more information about deprecated settings in each release, see Policy changes.

Remove deprecated policy settings after an upgrade