Drucker verteilen - Besser wird es nicht

10.04.2022 | Autor: Mark Heitbrink

Wer mich kennt, der weiss, das Ich es persönlich verabscheue, es mir fast körperliche Schmerzen bereitet, Drucker per Gruppenrichtlinie in irgendeiner Form zu verteilen. Am Ende ist es nicht nur die Technik, die ein Problem hat, sondern das Regelwerk, das es benötigt um jede Situation per GPO abzufangen.
Es ist einfach unglaublich aufwendig.

Mein Lieblingsweg, die Drucker zu verteilen ist und bleibt ein mündiger Anwender und ein simples Namenskonzept. Shortcut zum Printserver auf den Desktop, fertig.
Drucker verteilen und bereitstellen - Gruppenrichtlinien

... und denkt dran: Summer of 2021 #printnightmare!
PrintNightmare - 3 Richtlinien werden benötigt - Gruppenrichtlinien

Neben dem Aufwand, den ihr betreiben müsst, steht Performance als Killerargument gegen das Deployment per Gruppenrichtlinie. Die CSE "Group Policy Preference Printer" oder "Bereitgestellte Drucker" laufen beide synchron im Anmeldeprozess. Das kostet extrem Zeit, wenn es eine große Anzahl von Druckern ist, die per Item Level Targeting gefiltert werden. Zusätzlich müssen die Druckertreiber in das System hinzugefügt werden. 

ILT - Item Level Targeting ist ein Regelwerk, das super flexibel auf eure Anforderungen angepasst werden kann. Leider ist es ein XML. 

  • XML generiert relativ viel Volumen im Verhlätnis zum Kontent (Tag auf/Tag zu, viel Text). Das XML muss über das Netzwerk transportiert werden. Im LAN ist das kein Problem. Im WLAN/WAN kann das aufgrund von Latenzen schon sehr langsam werden. Wir haben alle gelernt, das 30 Geräte im selben Hotspot keine gute Idee sind und kein WLAN Router wirklich gut SMB spricht.
  • Ein XML muss immer interpretiert/geparst werden. Es ist keine Binary, das direkt verstanden wird. Ein intelligentes System (.exe/.dll) muss den Text Inhalt lesen, verstehen und verarbeiten. Das kostet einfach Zeit.
  • 5 Drucker in Summe die mit ILT gefiltert und an die Anwender verteilt werden, erzeugen keine Last oder Zeit, die sich langsam für den Anwender anfühlt.
  • 400+ Drucker per ILT filtern, von denen nur 1 oder 2 installiert werden, das ist spürbar bei der Anmeldung. Der Anwender sitzt direkt vor dem System, schaut zu und wartet.
    (Nein, der geht nicht Kaffee holen, das darf er nicht, weil dann das System ungesperrt rumsteht!)

Fakt:

Das Regelwerk lässt sich nicht verbessern. Wer 400-500 Drucker verteilen möchte, muss 400-500 Regeln bauen, bzw. mehr, wenn es Überschneidungen gibt. Egal ob in der GPO, in einer CSV, per Excel, whatever. 500 Drucker benötigen 500 Zuweisungen. Das ist der Deal den man eingehen muss. Wenn es soviele Drucker sind, die geordnet an ein Ziel übergeben werden müssen, dann ist es eben so. Punkt. Deal with it. Ihr habt es so gewollt.

Einfacher wäre die Verteilung eines Desktop ShortCuts, aber was weiss schon ich ...
Drucker verteilen und bereitstellen - Gruppenrichtlinien

Ok, ihr wollt 500 Drucker per GPO verteilen, euren Anwendern ist ein Doppelklick nicht zuzumuten und ihr wollt das, weil ihr das schon immer gemacht habt und euer Lehrling gerade nichts zu tun hat. 

"Spürbare" Performance Optimierung:

Wie gesagt, das Regelwerk kann nicht vereinfacht werden. Ebenfalls ist die Netzwerklast nicht zu reduzieren. Die Druckertreiber müssen übergeben werden, der Dateitransfer/Copyjob kostet Zeit die nicht reduziert werden kann, sie sind durch eure Netzwerkinfrastruktur begrenzt.

Wir können die "fühlbare Zeit" des Anwenders reduzieren in dem wir die Druckerverteilung aus dem Anmelde Prozess herausnehmen und sie asynchron hintendran hängen. Ausserhalb der Anmeldung gewinnen wir für den Anwender wieder einen hochperformanten Anmeldeprozess. Die Drucker inkl. Treiber kommen einfach 30 Sekunden später im Hintergrund, wenn der Desktop schon da ist und der Anwender Programme starten kann. Der einzige Nachteil ist, das die Drucker nicht sofort(!) drucken können, aber ich glaube nicht, das es eine Anwendung gibt, in der ein User direkt 5 Sekunden nach Anmeldung drucken möchte.

Eine Lösung:

Die Drucker werden weiterhin per Group Policy Preference Printer organisiert, die Übergabe der Drucker und des Regelwerks erfolgt als Geplanter Task per Skript. 
Der Vorteil ist, das Frontend der GPP Printer ist klickbar und leicht zu administrieren. 

Beispiel:

  • Es gibt pro Drucker eine Sicherheitsgruppe, die den Drucker erhalten soll.
  • Der Computer ist Mitglied dieser Gruppe, da Rechner und Drucker statisch im Büro stehen. "Wenn der Rechner in Raum X steht, dann Drucker Y".
  • Wichtig! Ihr müsst wissen welcher Rechner "wo" steht. Ihr müsst wissen "wo" jeder Drucker steht. 
  • Die Drucker Richtlinie wird nicht verlinkt, bzw. wenn sie verlinkt ist, wird der Link deaktivert. Die GPO dient nur der Konfiguration für den Administrator und aschnliessend zur Datenermittlung und Auswertung, wer welchen Drucker erhält. Das erstellte XML ist unsere Datenbank, egal wo sie verlinkt ist. Es geht nur im die GPO, als reines Objekt. Ihr Verwaltungsbereich ist in unserem speziellen Fall im weiteren Verarbeitungsprozess nicht gefragt.

Da die Powershell gerne durch die Executionpolicy ausgebremst wird, verpacke ich sie meist in eine Batch. Ich baue einen "Install-Warpper" um den Aufruf und umgehe damit die Execution Policy. 

powershell.exe -executionpolicy bypass %~dp0Connect-Printer.ps1

Aprospos Execution Policy, wer es noch nicht kannte, dem empfehle ich: 15 Ways to Bypass the PowerShell Execution Policy (netspi.com) von Scott Sutherland

Oder ihr ruft das Script nicht direkt als PS1 auf, sondern über das Programm mit Parametern
Programm: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Parameter: -executionpolicy bypass \\gallier.ads\netlogon\Printer\Connect-Printer.ps1

# Ermittlung der Domain 
$Domain=(Get-WmiObject Win32_ComputerSystem).Domain

# !!! Namen der GPO anpassen, oder den Pfad direkt eintragen, wenn er bekannt ist.
# Am DC: Ermittlung der ID des GPO. -> (Get-GPO -Name $Name).ID
# Alternativ: Siehe Reiter "Details" in der GPMC
# Anschliessend ID zwischen die geschweiften Klammern einsetzen
$AllPrinter = [XML] (Get-Content -Path "\\$Domain\SYSVOL\$Domain\Policies\{D96ACC82-F4A0-4ACC-804C-F39112018E42}\User\Preferences\Printers\Printers.xml")

# Ermittlung der Drucker, deren Item nicht deaktiviert ist.
$Printer=$AllPrinter.Printers.SharedPrinter | where {$_.Disabled -ne "1"}

# Die Drucker werden idR per Sicherheitsgruppe gefiltert. 
# Die Frage ist, ist der Benutzer oder der Computer bei euch Mitglied der Filter Gruppe?
# "SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\GroupMembership" gibt es in HKCU und HKLM
$KeyPath='HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\GroupMembership'
$DomainGroups=(Get-ItemProperty -path $KeyPath).PSObject.Properties.Value | Where {($_ -match 'S-1-5-21-*') -or ($_ -match 'S-1-5-32-*')}

# Drucker die zu löschen sind, anhand der Action "Delete" ermitteln
$RemovePrinters=$Printer | Where {$_.Properties.Action -eq "D"}

# Jeden Drucker mit Action "D" löschen, Erfolg/Fehler reporten.
# Das Script wird idR nicht sichtbar ausgeführt. Die Textausgabe 
# ist nur für den manuellen Aufruf integriert.
foreach ($Remove in $RemovePrinters) {
        Remove-Printer -Name $Remove.Properties.Path -erroraction SilentlyContinue
        if ($? -eq $true) {
            Write-host Drucker $Remove.Properties.Path entfernt
        }
        if ($? -eq $False) {
            Write-host Drucker $Remove.Properties.Path lokal nicht vorhanden, oder wurde schon gelöscht
        }
    }

# Drucker hinzufügen, wenn das Objekt in der Sicherheitsgruppe ist 
# und der Drucker nicht gelöscht werden soll.
foreach ($Group in $DomainGroups) {
    $MatchingPrinters=$Printer | Where {($_.Filters.FilterGroup.sid -match $Group) -and ($_.Properties.Action -ne "D")}
    foreach ($Match in $MatchingPrinters) {
        Add-Printer -ConnectionName $Match.Properties.Path -erroraction SilentlyContinue
        # Default Drucker setzen, wenn er als dieser definiert wurde.
        if ($Match.Properties.Default -eq "1") {
                $Name=$Match.Name
                $Printer=Get-CimInstance -Class Win32_Printer -Filter "ShareName='$Name'"
                Invoke-CimMethod -InputObject $Printer -MethodName SetDefaultPrinter 
                Write-host Setze $Match.Name als StandardDrucker
                }
        if ($? -eq $true) {
            Write-host Drucker $Match.Properties.Path wurde erfolgreich verbunden
        }
        if ($? -eq $False) {
        if ($? -eq $False) {
            Write-host Drucker $Match.Properties.Path nicht verbunden: $Error[0]
            # Der Drucker existiert nicht? Freigabe, Treiber und XML kontrollieren oder Printnightmare?
        }
    }
}