Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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?

share|improve this question
1  
As I mentioned to you in chat earlier today, there is a COM interface for programmatically creating and linking GPOs... all starting with something like $GPM = New-Object -ComObject GPMgmt.Gpm. I think that interface, while being an absolute pig to work with, might provide you with a slightly more supportable method than simply hacking some XML that comes out looking and smelling like a real GPO. No one likes COM though. Least of all we Powershellers. –  Ryan Ries Feb 18 at 3:03
1  
@RyanRies Yes, I'm still kicking it a round in my mind, and one of the things I haven't decided on is whether or not my scrip needs to be supportable, or just a one time thing for setting it up in the first place. Pretty sure I'll post it here, whatever I end up doing. –  HopelessN00b Feb 18 at 3:09
    
When you say you need to deploy 100 printers, you do not mean all computers (1..1000) should have all 100 printers, right? –  Adil Hindistan Feb 18 at 3:29
1  
@AdilHindistan No. Each computer should get all the printers available at its site. (More or less.) But separating them isn't the hard part. It's getting the printers into GPOs in the first place that's turning out to be more of a pain than you'd think it should be. –  HopelessN00b Feb 18 at 3:33
add comment

3 Answers

up vote 7 down vote accepted

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>
share|improve this answer
    
It's a good answer (and I might end up using something like this to meet some of our deadlines), but I suspect that an intern or subordinate is in this question's future, actually, since we are being required to use GPOs/GPPs to assign printers at some point. And while a PS logon script would technically fit that requirement, I expect that management would merp at me enough to make it not worth my while to do it that way. –  HopelessN00b Feb 17 at 23:28
    
Alas. :( The GPO powershell cmdlets seem to back up, restore, create blank objects... but not add to GPOs. The XML objects that have the printer policies look completely hackable except for the GUID. –  Katherine Villyard Feb 17 at 23:32
1  
Yeah, without 3rd party tools, GPO/GPP automation via PowerShell is pretty painful. I found something that looks pretty good, save for the fact that it requires a 3rd party software suite we probably won't be allowed to buy... so it will probably be some script like your to edit the XML in existing site-specific GPOs... which I imagine I'll post here once I'm done. –  HopelessN00b Feb 17 at 23:49
add comment

Powershell management of Group Policy sucks w/o third-party (commercial) products, in my opinion.

I think you're stuck slinging through the XML (or in HTML if you prefer) in Group Policy Objects to do what you're looking for.

Fortunately the XML doesn't look that terrifying. The per-printer UID value (which I believe is what @KatherineVillyard is referring to in her comment) is just a random GUID generated for each printer referenced in the XML.

Here's some sample Powershell code, shamelessly modeled after Katherine's code:

@"
<?xml version="1.0" encoding="utf-8"?>
<Printers clsid="{1F577D12-3D1B-471e-A1B7-060317597B9C}" disabled="0">
"@

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

$printerlist = Get-WMIObject -class Win32_Printer -computer $printserver | Where-Object {$_.ShareName -ne $null} | Select-Object ShareName
foreach ($printer in $printerlist) 
{
    $date = Get-Date
    echo '  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}"'
    '    name="' + $printer.ShareName + '"' | echo
    '    status="' + $printer.ShareName + '"' | echo
    echo '    image="2"'
    '    changed="' + $date + '"' | echo
    $ng = [GUID]::NewGuid().ToString('B')
    '    uid="' + $ng + '">' | echo

    echo '    <Properties'
    echo '      action="R"'
    echo '      comment=""'
    '      path="\\' + $printserver + '\' + $printer.ShareName + '"' | echo
    echo '      location=""'
    echo '      default="1"' 
    echo '      skipLocal="1"'
    echo '      deleteAll="0"'
    echo '      persistent="0"'
    echo '      deleteMaps="0"'
    echo '      port=""/>'
    echo '  </SharedPrinter>'
}

@"
</Printers>
"@

(I write really, really ugly Powershell code.)

I have not actually tried to have the GPP CSE parse this XML. The XML does validate, at least.

I'm starting to think about writing some monstrosity using Get-GPO and parsing out the GUID to get to the filesystem path for the GPO in the SYSVOL but, given that I need to do some real work this evening, I think I'm going to leave that as an exercise for the reader. >smile< It should be highly feasible, though.

share|improve this answer
    
If it's a random GUID, it's definitely hackable. I just updated mine, too. Heh. –  Katherine Villyard Feb 18 at 0:10
    
Modifying the XML in a Group Policy backup would certainly work, but changing it "live" in the SYSVOL should also be feasible (provided one is either brave, stupid, or, like me, both). Since I have real work to do this evening (and since I'm back over @ewwhite for the rep rankings this quarter again) I think I'll save loading that particular gun and pointing it at their feet for someone else. >giggle< –  Evan Anderson Feb 18 at 0:25
    
I'm brave, but I'm not that brave. ;) Generate file, test importing it in test environment, etc. –  Katherine Villyard Feb 18 at 2:07
2  
Powershell really needs more GPO options. Get-Link, Set-Link, New-GPO and Set-GPORegistry are not really good enough... –  Mark Henderson Feb 18 at 3:27
    
Built-in PowerShell commands are sometimes not enough. In that case, I would suggest to look at 'The GPO Guy's solutions. He has a company: sdmsoftware.com –  Adil Hindistan Feb 18 at 4:28
add comment

I recently undertook a similar project and after looking into good ol' GPO push method vs. newer GPP vs. scripting, I chose for scripting the whole thing. I have no idea what would work best for you but here are a couple of pointer for you:

  • Ideally use a client with a new OS (Windows 8/2012+) to connect to print servers and get Printer information from Print Servers:

    Get-Printer -computer PrintServerName

  • Use AD Security groups to map print queues to computers. So let's say you have a print queue (you gathered from above command) named \PrintServer1\MyColorPrinter123, create a security group like printer.group.PrintServer1.MyColorPrinter123, and add the computers to that group

  • In the startup script, have a function to check group membership of the computer when it is coming up, and see if it is part of any printer groups. If it is , use the built-in printui.exe (or printui.dll) commands to map the printer like this

    Invoke-Expression 'rundll32 printui.dll,PrintUIEntry /ga /n"\PrintServer1\MyColorPrinter123" /q'

  • Once the computer is up, that print spooler service will push the 'print connection', which is what GPOs used to do, to any user that's going to login.

You can go a lot more detailed** but on a high level that's what it takes.

** I created a GUI that allows users (techs really) to choose any Print Server, and it gives them list of printers on that server. If they choose one, they can see all the properties of it. That info comes from Get-Printer I mentioned above. If you export that data as a csv, you could then re-use it to display information..

** Techs use that GUI to send a request to add a computer to printers that it is supposed to connect. This is a 'request', b/c they do not have permissions in AD.

** A simple backend script watches the folder and add the computer to the printer group I mentioned above. So, if you already know who should get which printer, great, you can easily do it. Adding computers to groups is a simple job with AD Cmdlets.

** You can also schedule a job to check the print servers to see if there are any new print queues, and compare them to you AD groups.

So, creating a 'managed' solution is a bit involved, but it's easy to start with the basics and keep on adding to have a very flexible, easy to use system that does not involve GPOs... just a bit PowerShell

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.