Improve Group Policy Preferences Processing Time

| June 27, 2022

Update 27-Jun-2022

As this has been a top post for years, I’m re-posting it on the new blog with the final edit below. Note from the old blog comments, someone has added additional functionality to this and is available here:

Github version.

Update 5/16/2017:

Thanks to tabularasa for some improvements to this, fixing some missing slashes among other things!

Original post (edited code in block)

In years past, logon scripts were the bane of many logon experiences due to their synchronous nature. Beginning with Windows 7, logon scripts can be processed asynchronously. We glossed over this fact: “Nice feature, but how will it help us? We’re heavily invested into GP Preferences now!” However, now that we’re 1078 virtual desktops deep, logon time is a serious concern. Citrix has failed to provide us with a way to extend the time on the automatic “log off if log on takes more than 90 seconds” feature. In most cases, this isn’t an issue, but this limitation highlighted a serious flaw: Even under normal circumstances, there are some cases where some users might exceed 90s.

Our organization uses the HP Universal Print driver, Ricoh Universal Print driver and the Lexmark Universal print driver and we map printers with Group Policy Preferences. This mapping process takes, on an “average” printer, 6-7 seconds. Unfortunately, this processing does in fact run synchronously (possibly due to GPP Drive mapping – I just found this article but it requires further testing! Would be interesting if this whole mess could be avoided by moving drive mapping out of GPO!) and therefore can add substantial time to the logon process. Some users have 5-15 printers available to them, bringing the practical limit to the number of printers to something like 10 (given that portions of the logon process require some time).

As we are heavily invested in preferences, item level targeting and (as everyone is) short on staff / training, we decided to leverage that investment in GPP but still gain the benefits of the logon script. A colleague of mine, Matt Heckman, created a rather ingenious Powershell script that effectively uses GPP Printer entries from a “dummy” GPO to map printers for our VDI environment. This means that our level 1 admins can still create printers using GPP with simple filters and the users in VDI will automatically map them. The results were substantial: A logon that took 60 seconds to reach a desktop was shortened by 45 seconds! In K-12, logon time is king as it directly translates into instructional time.

So, how does it work?

Create a new GPO. It is not required to link it anywhere in the domain. Be sure to clearly name it so that someone doesn’t think that it is an unused GPO!

Copy your existing printer preferences into the new GPO.

Modify the logon script to reflect the name/GUID of your new GPO. To obtain the GUID, in GPMC click the “details” tab, then copy the “Unique ID”. If you use any filters other than computer name or security group name you will need to extend the script.

Without further delay, give it a whirl!

#$GPOGuid = "{7B4605FA-3435-4A2B-8A33-A101944C5E47}"
$GPOGuidObject = Get-GPO "VOA - Printer GPP Policy"
$GPOGuid = $GPOGuidObject.Id
$GPOGuidDomain = $GPOGuidObject.DomainName
$LogDir="H:\Windows\PrinterLogs"
$LogPath="$($LogDir)\$($env:USERNAME)_$($env:COMPUTERNAME)_LogonScript.log"
write-host "Writing logs to $($LogPath)"
 
#Check to see if the log directory exists and create it it doesn't yet exist.
    #$LogPathExist=false
    #Is the H drive already mapped
    $LogPathExist=Test-Path H:
    if ($LogPathExist) {
           write-host "H Drive Exists"
    } else {
        write-host "H Drive not Mapped"
        return
    }
    $LogPathExist=Test-Path $LogDir
    if ($logPathExist) {
        write-host "$($LogDir) Exists"
    } else {
        write-host "$($LogDir) Does not exist"
        #create directory for for logfile
        mkdir $LogDir
    }
 
 
    #Is the folder created
        #create the folder if its not already there
 
 
 
 
Start-Transcript -Path $LogPath
 
[xml]$printersXml = Get-Content "\\$GPOGuidDomain\sysvol\$GPOGuidDomain\Policies\{$GPOGuid}\User\Preferences\Printers\Printers.xml"
 
$userGroups = ([Security.Principal.WindowsIdentity]"$($env:USERNAME)").Groups.Translate([System.Security.Principal.NTAccount])
$computerGroups = ([Security.Principal.WindowsIdentity]"$($env:COMPUTERNAME)").Groups.Translate([System.Security.Principal.NTAccount])
 
Function Process-FilterComputer {
    Param(
        $filter
    )
 
    $result = $false
 
    if ($filter.type -eq "NETBIOS") {
        if ($env:COMPUTERNAME -like $filter.name) {
            $result = $true
        }
    }
 
    if ($filter.not -eq 0) {
        return $result
    } else {
        return !$result
    }
}
 
Function Process-FilterUser {
    Param(
        $filter
    )
 
    $result = $false
 
    if ("$env:USERDOMAIN\$env:USERNAME" -like $filter.name) {
        $result = $true
    }
 
    if ($filter.not -eq 0) {
        return $result
    } else {
        return !$result
    }
}
 
Function Process-FilterGroup {
    Param(
        $filter
    )
 
    $result = $false
 
    if ($filter.userContext -eq 1) {
        if ($userGroups -contains $filter.name) {
            $result = $true
        }
    } else {
        if ($computerGroups -contains $filter.name) {
            $result = $true
        }
    }
 
    if ($filter.not -eq 0) {
        return $result
    } else {
        return !$result
    }
}
 
Function Process-FilterCollection {
    Param(
        $filter
    )
 
    if ($filter.HasChildNodes) {
        $result = $true
        $childFilter = $filter.FirstChild
 
        while ($childFilter -ne $null) {
            if (($childFilter.bool -eq "OR") -or ($childFilter.bool -eq "AND" -and $result -eq $true)) {
                if ($childFilter.LocalName -eq "FilterComputer") {                    
                    $result = Process-FilterComputer $childFilter
                } elseif ($childFilter.LocalName -eq "FilterUser") {
                    $result = Process-FilterUser $childFilter
                } elseif ($childFilter.LocalName -eq "FilterGroup") {
                    $result = Process-FilterGroup $childFilter
                } elseif ($childFilter.LocalName -eq "FilterCollection") {
                    $result = Process-FilterCollection $childFilter
                }
 
                #Write-Host "Process-$($childFilter.LocalName) $($childFilter.name): $($result)"
            } else {
                #Write-Host "Process-$($childFilter.LocalName) $($childFilter.name): skipped"
            }
 
            if (($childFilter.NextSibling.bool -eq "OR") -and ($result -eq $true)) {
                break
            } else {
                $childFilter = $childFilter.NextSibling
            }
        }
    }
 
    if ($filter.not -eq 1) {
        return !$result
    } else {
        return $result
    }
}
 
$com = New-Object -ComObject WScript.Network
 
$installedPrinterDrivers = get-printerdriver
 
#Get-Content "\\$GPOGuidDomain\sysvol\$GPOGuidDomain\Policies\{$GPOGuid}\User\Preferences\Printers\Printers.xml"
 
foreach ($sharedPrinter in $printersXml.Printers.SharedPrinter) {
    $filterResult = Process-FilterCollection $sharedPrinter.Filters
    Write-Host "$($sharedPrinter.name) filters passed: $($filterResult)"
 
    if ($filterResult -eq $true) {
        if ($sharedPrinter.Properties.action -eq 'C') {
            #check to see if the driver is present on the XenAppServer
           
           $printServer = $sharedPrinter.Properties.path
           $printServer = $printServer.Split("\\")[2]
           $driverName = Get-Printer -ComputerName $($PrintServer) -Name $($sharedPrinter.name)
 
            if ($installedPrinterDrivers | where {$_.name -eq $driverName.drivername}) {
            #Create the printer in the session
                $com.AddWindowsPrinterConnection($sharedPrinter.Properties.path)
                "AddWindowsPrinterConnection:$($sharedPrinter.Properties.path)"
                if ($sharedPrinter.Properties.default -eq 1) {
                    #$com.SetDefaultPrinter($sharedPrinter.Properties.path)
                    (Get-WmiObject -ComputerName . -Class Win32_Printer -Filter "Name='$($($sharedPrinter.Properties.path) -replace "\\","\\")'").SetDefaultPrinter()
                    "SetDefaultPrinter:$($sharedPrinter.Properties.path)"
                }
            } else {
            #alert that the driver isn't present
            write-host "`r`n  The driver for $($sharedPrinter.Properties.path) doesn't appear to be present.  The driver that needs to be installed is $($driverName.drivername).  Please install this driver on the XenApp Server.  `n`r"
            }
        } elseif ($sharedPrinter.Properties.action -eq 'D') {
            $com.RemovePrinterConnection($sharedPrinter.Properties.path, $true, $true)
            "RemovePrinterConnection:$($sharedPrinter.Properties.path)"
        }
    }
}
 
Stop-Transcript
comments powered by Disqus