Update Printer Mappings And Keep The Default Printer

Hi all,

I am a big time newbie with scripting in general and looking for some help.

I have found the following script on another website to update printers and there is a part in that script that it is supposed to keep the default printer. The only error I get is the following:

Exception calling “SetDefaultPrinter” with “1” argument(s): “There is no printer called “Lex-Finance”.”

At \server1\SYSVOL\domain.org\scripts\remap-printers-printserver.ps1:70 char:67

  •                              (New-Object -ComObject WScript.Network).SetDefaultPrinter <<<< ($default.S
    

hareName)

+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

+ FullyQualifiedErrorId : ComMethodTargetInvocation

Another question I had is how can I move multiple print servers in this script? I currently have 2 print servers and I am consolidating to 1 server. Can I add two old print server names?

This script is run from the logon script and run on all users.

Thank you very much for all help!

Michael

This is the script:


Param (
$newPrintServer = “svrprint1”,
$PrinterLog = “\file\share\PrintMigration.csv”
)

Try {
Write-Verbose (“{0}: Checking for printers mapped to old print server” -f $Env:USERNAME)
$default = Get-WmiObject -Class Win32_Printer -Filter “Default=‘True’” -ErrorAction Stop
$printers = @(Get-WmiObject -Class Win32_Printer -Filter “SystemName=‘\\svrprint’” -ErrorAction Stop)

If ($printers.count -gt 0) {        
    ForEach ($printer in $printers) {
        Write-Verbose ("{0}: Replacing with new print server name: {1}" -f $Printer.Name,$newPrintServer)
        $newPrinter = $printer.Name -replace "svrprint",$newPrintServer  
        $returnValue = ([wmiclass]"Win32_Printer").AddPrinterConnection($newPrinter).ReturnValue                
        If ($returnValue -eq 0) {
            "{0},{1},{2},{3},{4},{5}" -f $Env:COMPUTERNAME,
                                         $env:USERNAME,
                                         $newPrinter,
                                         $returnValue,
                                         (Get-Date),
                                         "Added Printer" | Out-File -FilePath $PrinterLog -Append -Encoding ASCII            
            Write-Verbose ("{0}: Removing" -f $printer.name)
            $printer.Delete()
            "{0},{1},{2},{3},{4},{5}" -f $Env:COMPUTERNAME,
                                         $env:USERNAME,
                                         $printer.Name,
                                         $returnValue,
                                         (Get-Date),
                                         "Removed Printer" | Out-File -FilePath $PrinterLog -Append -Encoding ASCII
        } Else {
            Write-Verbose ("{0} returned error code: {1}" -f $newPrinter,$returnValue) -Verbose
            "{0},{1},{2},{3},{4},{5}" -f $Env:COMPUTERNAME,
                                         $env:USERNAME,
                                         $newPrinter,
                                         $returnValue,
                                         (Get-Date),
                                         "Error Adding Printer" | Out-File -FilePath $PrinterLog -Append -Encoding ASCII
        }
    }
}

} Catch {
“{0},{1},{2},{3},{4},{5}” -f $Env:COMPUTERNAME,
$env:USERNAME,
“WMIERROR”,
$_.Exception.Message,
(Get-Date),
“Error Querying Printers” | Out-File -FilePath $PrinterLog -Append -Encoding ASCII

							 }
							 (New-Object -ComObject WScript.Network).SetDefaultPrinter($default.ShareName)

First, it’s good you are giving Powershell a shot. If you are an administrator, it’s a necessity to know some scripting, especially Powershell with products today.

To answer your question directly, the .SetDefaultPrinter() method is looking for the Name of the printer, not the ShareName. You didn’t mention what kind of numbers you have in your environment, but you are using a SINGLE log file for every computer running a login script. So, if you have 300 people in your environment and Monday @ 8:00 there are 100 people logging in, that file is going to open by someone and the script is going to try to access the log causing that computer to lose it’s logging. You might get 80% logging, but 20% are going to get errors. A better practice is to create a log just for that computer (e.g. MYCOMPUTER_PrintMigration.csv) and then have another script compile all of the logs into a single CSV for review. (If you need assistance with that script, we can assist too, but you should at least give it a whack first :slight_smile: )

By default, Powershell has the ability to Import, Export and even ConvertTo CSV format. It should be leveraged if you want to dump to a CSV, to generate a PSObject versus a string. So, I took a whack at updating your code to generate a custom PSObject. I added some columns to make it easier to filter your results. You can go into your Powershell console and just do:

$csv = Import-CSV "\\file\share\Final_PrintMigration.csv" #final_ would be the compiled CSV of all logs
$csv | Where{$_Added -ne $true} | Select ComputerName, UserName, Date, StatusMessage

Viola! You have all entries that have failed. You could do Export-CSV and send to Desktop Support or run a script to re-mediate the issue you’re having. As far as the default printer, what you need to do is check if the current printer ShareName you are processing is the same ShareName of the default printer you gathered and if it matches attempt to set it as the default printer. Take a look at the script below and give it a shot. I did not test it, but it should be close to what you are looking to do:

Param (
    $newPrintServer = "svrprint1",
    $PrinterLog = "\\file\share\{0}_PrintMigration.csv" -f $Env:COMPUTERNAME
)

$log = @()

if (Test-Path $PrinterLog) {
    #pull in the current log to 'Append' new logging
    $log += Import-CSV $printerLog
}

Try {
    Write-Verbose ("{0}: Checking for printers mapped to old print server" -f $Env:USERNAME)
    $printers = @(Get-WmiObject -Class Win32_Printer -Filter "SystemName='\\\\svrprint'" -ErrorAction Stop)
    $default = Get-WmiObject -Class Win32_Printer -Filter "Default='True'" -ErrorAction Stop

    If ($printers.count -gt 0) {
        ForEach ($printer in $printers) {
            Write-Verbose ("{0}: Replacing with new print server name: {1}" -f $Printer.Name,$newPrintServer)
            $newPrinter = $printer.Name -replace "svrprint",$newPrintServer
            $returnValue = ([wmiclass]"Win32_Printer").AddPrinterConnection($newPrinter).ReturnValue
            If ($returnValue -eq 0) {
                try {
                    #Deleting the printer is usually the biggest issue
                    #when doing this type of migration.  You need to capture
                    #if the delete was successful during your testing if the
                    #event your code needs updated.  Many scripts use WMI to enumerate
                    #and WScript.Network to map and delete the old printer
                    $printer.Delete()
                    $deleted = $true
                }
                catch {
                    $deleted = $false
                }
                $added = $true

                $defaultPrt = $null
                if ($default.ShareName -eq $printer.ShareName) {
                    (New-Object -ComObject WScript.Network).SetDefaultPrinter($default.ShareName)
                    $defaultPrt = " [DEFAULT] "
                }

                $StatusMsg = $("Successfully added {0} {1} with return value {2}" -f $newPrinter,$defaultPrt,$returnValue)
            }
            Else {
                Write-Verbose ("{0} returned error code: {1}" -f $newPrinter,$returnValue) -Verbose
                $added = $false
                $StatusMsg = $("Failed adding {0} with return value {1}" -f $newPrinter,$returnValue)
            } #if ret val
        } #if count

        $props = @{
            ComputerName=$Env:COMPUTERNAME
            UserName=$Env:USERNAME
            OldPrinter = $("\\{0}\{1}" -f $printer.SystemName, $printer.ShareName)
            Printer = $newPrinter
            Added = $added
            Deleted = $deleted
            StatusMessage = $StatusMsg
            Date = $(Get-Date)
        }

        $log += New-Object -TypeName PSObject -Property $props
    } #foreach printer
}#try
Catch {
    $props = @{
        ComputerName=$Env:COMPUTERNAME
        UserName=$Env:USERNAME
        OldPrinter = "N\A"
        Printer = "N\A"
        Added = "N\A"
        Deleted = "N\A"
        StatusMessage = $StatusMsg = "WMIERROR: {0}" -f $_.Exception.Message
        Date = $(Get-Date)
    }
        
    $log += New-Object -TypeName PSObject -Property $props
}

$log | Export-CSV $PrinterLog -Force -NoTypeInformation

Edit: Updated $log = Import-CSV $printerLog to use +=

I wish I can help but I hate working with printers because of my years as a Citrix MetaFrame Administrator.

Printers became a huge hassle.

I can quickly offer this idea the “default” printer is a registry setting but because each driver creates a very different looking key I can’t provide an example that would work every time for you. However I prefer to work with the PSProvider “HKLM:”

[url]http://support.microsoft.com/kb/156212[/url]

-VERN

Get-PSProvider

Hello Rob,

Thanks for the encouragement. I am trying to understand bit by bit how powershell works. When scripts get too evolved, I get totally lost.LOL. I tested the code you added and keep getting this error:
ethod invocation failed because [System.Management.Automation.PSObject] doesn’t contain a method named ‘op_Addition’.
t \dc1\NETLOGON\remap-printers-all-printserver.ps1:79 char:12
$log += <<<< New-Object -TypeName PSObject -Property $props

  • CategoryInfo : InvalidOperation: (op_Addition:String) , RuntimeException

Also, I have two print servers to migrate. Would it be a good idea to run 2 scripts each using 1 print server?

As for the sizing of the environment, we have around 100 users in total.

After running teh script, It removes all printers but does not reconnect them on the new print server.

Any idea why? The code I had did remap them, but did not set the default printer.

Let me know your thoughts,

Michael

The issue with op_Addition was most likely caused by line 10 because I didn’t have += to add to the empty object created on line 6. So, I have updated the script above.

There is no reason one script cannot handle both print servers. That is the purpose of passing the parameters to the script. Are you consolidating two print servers into a single server? If a user had \oldserver1\printer1 and \oldserver2\printer1 mapped, you would have to handle deleting both and only mapping 1 server. So, would need to get details on logic, but yes it could be a single script (and should be).

I did not actually update the code on line 21 or 22, which is your replace and .AddPrinterConnection. Did you update the “svrPrint” to the actual print server name and pass a parameter for $newPrintServer (or update the default) to reflect actual servers in your environment.

As far as the default printer, even after I talked about using the wrong property (ShareName versus Name), I didn’t update the code. Additionally, thinking about it, you would need to re-enumerate the printers collection to get the new printers actual name, so I updated the code to pull printers that match the share and new printserver name.

Param (
    $newPrintServer = "svrprint1",
    $PrinterLog = "\\file\share\{0}_PrintMigration.csv" -f $Env:COMPUTERNAME
)

$log = @()

if (Test-Path $PrinterLog) {
    #pull in the current log to 'Append' new logging
    $log += Import-CSV $printerLog
}

Try {
    Write-Verbose ("{0}: Checking for printers mapped to old print server" -f $Env:USERNAME)
    $printers = @(Get-WmiObject -Class Win32_Printer -Filter "SystemName='\\\\svrprint'" -ErrorAction Stop)
    $default = Get-WmiObject -Class Win32_Printer -Filter "Default='True'" -ErrorAction Stop

    If ($printers.count -gt 0) {
        ForEach ($printer in $printers) {
            Write-Verbose ("{0}: Replacing with new print server name: {1}" -f $Printer.Name,$newPrintServer)
            $newPrinter = $printer.Name -replace "svrprint",$newPrintServer
            $returnValue = ([wmiclass]"Win32_Printer").AddPrinterConnection($newPrinter).ReturnValue
            If ($returnValue -eq 0) {
                try {
                    #Deleting the printer is usually the biggest issue
                    #when doing this type of migration.  You need to capture
                    #if the delete was successful during your testing if the
                    #event your code needs updated.  Many scripts use WMI to enumerate
                    #and WScript.Network to map and delete the old printer
                    $printer.Delete()
                    $deleted = $true
                }
                catch {
                    $deleted = $false
                }
                $added = $true

                $defaultPrt = $null
                if ($default.ShareName -eq $printer.ShareName) {
                    Get-WmiObject -Class Win32_Printer -Filter ("SystemName='\\\\{0}' And ShareName ='{0}' " -f $newPrintServer,$($printer.ShareName)) | foreach {
                        (New-Object -ComObject WScript.Network).SetDefaultPrinter($_.Name)
                    } #foreach 
                    $defaultPrt = " [DEFAULT] "
                }

                $StatusMsg = $("Successfully added {0} {1} with return value {2}" -f $newPrinter,$defaultPrt,$returnValue)
            }
            Else {
                Write-Verbose ("{0} returned error code: {1}" -f $newPrinter,$returnValue) -Verbose
                $added = $false
                $StatusMsg = $("Failed adding {0} with return value {1}" -f $newPrinter,$returnValue)
            } #if ret val
        } #if count

        $props = @{
            ComputerName=$Env:COMPUTERNAME
            UserName=$Env:USERNAME
            OldPrinter = $("\\{0}\{1}" -f $printer.SystemName, $printer.ShareName)
            Printer = $newPrinter
            Added = $added
            Deleted = $deleted
            StatusMessage = $StatusMsg
            Date = $(Get-Date)
        }

        $log += New-Object -TypeName PSObject -Property $props
    } #foreach printer
}#try
Catch {
    $props = @{
        ComputerName=$Env:COMPUTERNAME
        UserName=$Env:USERNAME
        OldPrinter = "N\A"
        Printer = "N\A"
        Added = "N\A"
        Deleted = "N\A"
        StatusMessage = $StatusMsg = "WMIERROR: {0}" -f $_.Exception.Message
        Date = $(Get-Date)
    }
        
    $log += New-Object -TypeName PSObject -Property $props
}

$log | Export-CSV $PrinterLog -Force -NoTypeInformation

retested and still removes all printers. But in the csv it says it added the printers:
Successfully added \hqfp00\Lex-Finance [DEFAULT] with return value 0

I had changed the srvprintX for the new print server and the old print server names. I also changed the path for the log files

Hi There

So, i ran your script and what i see on my end is

  • Printerlog created - csv file has been created but is blank
  • newPrintServer = added - \printerservername\ didn’t change when i checked print mapping under device and printers
  • no errors while running script

How i can fix this?

Thx, M