Powershell – XenApp Printer mapping issue workaround

Let me first describe the issue.
The remote office users are on Windows 7, there are several local RICOH printers configured over the local network.
When users want to connect to the enterprise they login to the XenApp Web Interface and then run a Pulished Desktop.
Office applications are used within the Published Desktop and threfore when there is a need to print a document it have to be redirected to the locally attached/configured RICOH printer. In other words, when user tries to print from the Published Desktop he/she should be able to see/select a locally configured printer.

Now, here is the problem – user can actually see the printer from within XenApp desktop but the printer can not be selected. The printer or I should probably say the printer driver is not initialized correctly. If I look at the Printers console on the server I see the printer being created for the user session, but if I would try to open the properties the following “0x00000709” error message would appear:
XPrint1

As it was found later the issue was related to the missing Windows updates. After installing recommended updates the issue went away.
But in the mean time a workaround was discovered – if we restart a Print Spooler and Citrix Printing services then the remote printers become available.
To automate this workaround a Powershell script was developed with the following logic: every time somebody logs in to a server a scheduled task is triggered. Trigger calls a Powershell script. First of all script scans the registries and extracts a list of the currently connected users. Then it compares this fresh list to the one that was collected last time. If the connected computer is from the affected remote site [computer names are known – line 55] and it was not processed last time then we need to restart the Print Spooler service. I’m also checking whether there is a running printing job in a queue and if there is then restart is postponed:

# This script must be run on a target server as a scheduled task every x minutes
# Script identifies a new user session, performs the PC name check and restarts Print Spooler services if needed 

cls

# Preparing variables
$LogFile = 'C:\Temp\SpoolerRestartLog.txt'
$SessionPCsList = 'C:\Temp\SessionPCs.txt'
$UserDetails = @()
$SessionPCs = @()

# Information about the connected users is retrieved from the registries
$Users = Get-ChildItem "Registry::HKEY_USERS"

foreach ($User in $Users){

    # We only intersted in AD users, their SID length can be 55 or 56 symbols
    if (($User.Name.Length -gt 54) -or ($User.Name.Length -lt 57)){
        $path1 = "Registry::"+$User.Name+"\Volatile Environment"; 
        #Test-Path $path1
        
        # If the path is valid then collect data
        if (Test-Path $path1){
            #Get-Item -Path $a; 
            $Username = Get-ItemProperty -Path $path1 | where-object {$_.USERNAME} | Foreach-Object {$_.USERNAME}
            #$Username
            
            # Now we need to extract PC name, the one that user is connecting to RDS server
            $UserSessionID = Get-ChildItem $path1
            
            foreach ($ID in $UserSessionID){
                # Creating a path to the session ID key
                $path2 = $path1+'\'+$ID.PSChildName;
                $PC = Get-ItemProperty -Path $path2 | where-object {$_.CLIENTNAME} | Foreach-Object {$_.CLIENTNAME}
                #$PC
            }

            # Lets store a Username and a Clientname in the Arrays for the further use
            $UserDetails += $Username
            $SessionPCs += $PC
        }
    }
}

'' >> $LogFile

# Now we need to check whether there is a user [actually his CLIENTNAME property] who is connected after the last check
# Getting a list of previous sessions
$PreviousSessionPCs = Get-Content 'C:\Temp\SessionPCs.txt'

$Count = 0
foreach ($SessionPC in $SessionPCs){
    
    # We are only interested in particular site computers
    if ($SessionPC.StartsWith("PCNAME")){
        
        "$(Get-Date): We have a computer location match: " + $SessionPC + ", owned by: " + $UserDetails[$Count]  >> $LogFile

        $Detected = $PreviousSessionPCs -Contains $SessionPC
        #$Detected
        if (!$Detected){
            "$(Get-Date): This Computer did not exist before, we have to restart Print Spooler!" >> $LogFile
            $AllowSpoolerRestart = 1
        }
        else
        {
            "$(Get-Date): This Computer was processed last time, no need to restart the Print Spooler!" >> $LogFile
            '' >> $LogFile
        }
    }
    $Count = $Count + 1
}

# If the previous step identified a new session from the wanted site then we need to restart a Spooler service
if ($AllowSpoolerRestart){
    "$(Get-Date): Preparing for the Spooler restart, collecting current Print Jobs" >> $LogFile

    # Query WMI for currently running Print Jobs
    $computer = "localhost"
    $namespace = "root\CIMV2"
    $PrintJobs = Get-WmiObject -class Win32_PrintJob -computername $computer -namespace $namespace
    #$PrintJobs

    if ($PrintJobs -eq $Null)
    {
        "$(Get-Date): No Printing Jobs are present - restarting Printer Spooler immediatelly" >> $LogFile
        # Making a short delay, just in case
        #Start-Sleep -s 3

        $ProcessActive = Get-Service Spooler
            "$(Get-Date): Current Print Spooler state is: " + $ProcessActive.status >> $LogFile

            # Stopping. Force is used because there are dependencies.
            stop-service -Name Spooler -Force
            do{
                "$(Get-Date): Waiting for service to be stopped" >> $LogFile
                Start-Sleep -s 2
                $ProcessActive = Get-Service Spooler
            }
            until ($ProcessActive.status -eq "Stopped")
            
            "$(Get-Date): Current Print Spooler state is: " + $ProcessActive.status >> $LogFile

            # Starting
            restart-service -Name Spooler -Force
            do{
                "$(Get-Date): Waiting for service to be started" >> $LogFile
                Start-Sleep -s 2
                $ProcessActive = Get-Service Spooler
            }
            until ($ProcessActive.status -eq "Running")
            
            "$(Get-Date): Current Print Spooler state is: " + $ProcessActive.status >> $LogFile

        # Now we can refresh computer list stored on the disk for the next script execution
        $SessionPCs > $SessionPCsList

    }
    else
    {
        "$(Get-Date): There are printing jobs in the queue" >> $LogFile
        $PrintJobs  >> $LogFile

        # First lets check whether there is a job having status of OK
        $ValidJob = 0
        foreach ($Job in $PrintJobs) 
        {
            write-host "Status: " $Job.Status 
            if ($Job.Status -eq 'OK')
            {
                "$(Get-Date): There is a valid ongoing job" >> $LogFile
                $ValidJob = $ValidJob + 1
            }
        }

        # If there are jobs having OK status then we have to delay restart until the queue is empty
        if ($ValidJob -gt 0)
        {
            "$(Get-Date): Canceling Spooler restart, there are Jobs in the queue, will try at next execution" >> $LogFile
            # Computer list will not be updated
        }

        # If there are no OK jobs then we can continue with restart 
        if ($ValidJob -eq 0)
        {
            "$(Get-Date): There are only invalid print jobs in the queue, restarting anyway!" >> $LogFile
            
            # Making a short delay just in case
            #Start-Sleep -s 3

            $ProcessActive = Get-Service Spooler
            "$(Get-Date): Current Print Spooler state is: " + $ProcessActive.status >> $LogFile

            # Stopping
            stop-service -Name Spooler -Force
            do{
                "$(Get-Date): Waiting for service to be stopped" >> $LogFile
                Start-Sleep -s 2
                $ProcessActive = Get-Service Spooler
            }
            until ($ProcessActive.status -eq "Stopped")
            
            "$(Get-Date): Current Print Spooler state is: " + $ProcessActive.status >> $LogFile

            # Starting
            restart-service -Name Spooler -Force
            do{
                "$(Get-Date): Waiting for service to be started" >> $LogFile
                Start-Sleep -s 2
                $ProcessActive = Get-Service Spooler
            }
            until ($ProcessActive.status -eq "Running")
            
            "$(Get-Date): Current Print Spooler state is: " + $ProcessActive.status >> $LogFile

            # Now we can refresh computer list stored on the disk for the next script execution
            $SessionPCs > $SessionPCsList
        }    
    }
}
Advertisements

Leave a Comment here

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s