Deploying Printers via GPO/GPP – Is There a Programmatic Option?

active-directorygroup-policynetwork-printerscripting

For reasons beyond my control, I've been tasked with setting up GPO/GPPs to deploy our 100+ printers to our 1000+ clients.

The good news is that we have over a dozen sites, and for the most part, I'm allowed to push out all printers at site X to all client PCs at site X.

The bad news is that the two ways I know how to do it ("Deploy with Group Policy…", from the print server" and using GPP/Group Policy Preferences) involve vastly more manual work than I'm willing to for this many printers. I can't even seem to select all the printers on a print server and use the Deploy with Group Policy... option, for example – it expects me to do that one by one, which isn't going to happen. The GPPs are even worse, as it expects me to select a printer's path from the print server and then manually punch in a bunch of information (such as printer IP) that it should be able to get from the printer connection.

My Google-Fu for a script to add all printers on a print server to a GPO/GPP came up empty, and I can't seem to see another way to do this in even a semi-automated fashion, but I'm sticking with the belief that I'm missing something, because there's no way any sane person would chose to manually add hundreds of printers into GPOs.

Ideally, I'd want to find a programmatic way to use the GPPs, but under the circumstances, any solution that doesn't involve dozens of hours manually adding printers would be just great.

Does anyone have a way to do this, or am I going to need to build a PowerShell script and/or trick a subordinate into doing this?

Best Answer

I googled pretty hard, and even toyed with backup-GPO in hopes of being able to hack the resultant XML file and reimport it, but I suspect that a PowerShell script is in your future.

It's not that bad. You can generate the printer list from the nearest server and then loop through that and map them.

Something like this:

$net = New-Object -COMObject WScript.Network
$printserver = 'yourserver'

$printerlist = Get-WMIObject -class Win32_Printer -computer $printserver | Where-Object {$_.ShareName -ne $null} | Select-Object ShareName
foreach ($printer in $printerlist) 
{
    $printerpath = '\\' + $printserver + '\' + $printer.ShareName
    #echo $printerpath
    $net.AddWindowsPrinterConnection($printerpath)
}

If the printers are named logical things, and there's some logical way to identify the machines, you might be able to refine it more. I used to pick the nearest server based on pulling up the client IP address, for example. If IP address like 10.20.*, go to server1. Etc.

I hope that helps.

Edit:

Looking at @EvanAnderson's documentation, I'm pretty sure that XML is hackable.

Relevant bit of my exported file (with redactions):

<DSObject bkp:Path="CN={GUIDHERE},CN=PushedPrinterConnections,%GPO_MACH_DSPATH%" bkp:SourceExpandedPath="CN={13B9B596-452C-4652-A05D-78EF06610134},CN=PushedPrinterConnections,CN=Machine,CN={44A99FBA-0DB3-484C-808E-3DDAE9932A2B},CN=Policies,CN=System,DC=Domainname,DC=extension" bkp:ObjectClass="msPrint-ConnectionPolicy">
    <DSAttributeMultiString bkp:DSAttrName="showInAdvancedViewOnly">
        <DSValue><![CDATA[TRUE]]></DSValue>
    </DSAttributeMultiString>
    <DSAttributeMultiString bkp:DSAttrName="uNCName">
        <DSValue><![CDATA[\\Servername\PrinterShareName]]></DSValue>
    </DSAttributeMultiString>
    <DSAttributeMultiString bkp:DSAttrName="serverName">
        <DSValue><![CDATA[\\Servername]]></DSValue></DSAttributeMultiString>
    <DSAttributeMultiString bkp:DSAttrName="printAttributes">
        <DSValue><![CDATA[0]]></DSValue>
    </DSAttributeMultiString>
    <DSAttributeMultiString bkp:DSAttrName="printerName">
        <DSValue><![CDATA[PrinterShareName]]></DSValue>
    </DSAttributeMultiString>
</DSObject>