PowerShell и Group Policy Preferences, когда счет принтеров на сотни

в 19:35, , рубрики: gpo, group policy, powershell, XML, администрирование windows, групповые политики, печать, системное администрирование

PowerShell и Group Policy Preferences, когда счет принтеров на сотни - 1

Много копий сломано вокруг управления сетевыми принтерами на пользовательских компьютерах. В основном администраторы разбились на два лагеря: подключение логон-скриптами (bat/vbs) и управление через GPP. У обоих подходов есть свои плюсы: скрипты быстрее обрабатываются, а GPP гибче и применяется чаще, чем пользователи перезагружают компьютеры. Но когда принтеров больше сотни и разбросаны они в нескольких десятках офисов и городов, сложности будут в обоих случаях.

Нужно не только подключить правильный набор принтеров каждому пользователю, с учетом его текущего местонахождения, но и ни про один не забыть. А если иногда перемещаются не только пользователи, но и сами принтеры…

В общем, мы с коллегами для себя выбрали GPP, в первую очередь для того, чтобы кто-то кроме ведущих администраторов мог разобраться в действующем конфиге, просто посмотрев отчет GPMC. Однако, кто скажет, что его штатный интерфейс удобен для управления 100+ устройствами — пусть первый бросит в меня камень. Кроме того, при вводе в эксплуатацию очередной партии нужно проделать много рутины по настройке сетевого сканирования и добавлению на сервер печати.

А всё, что делается больше одного раза, можно автоматизировать!

Что мы сегодня будем делать?

  • вести учет всех сетевых принтеров;
  • автоматизировать добавление принтеров в GPP (PS/XML);
  • автоматизировать добавление принтеров на принт-сервер, причем на кластерный (BAT/VBS)!

Итак, начнем.

1. Учет сетевых принтеров

Первая проблема, которая возникает при управлении большим количеством любых объектов — это учет. Без него легко запутаться, что где установлено и кому должно быть подключено.
Самое простое и универсальное решение — CSV. В конце концов, из любой системы инвентаризации, ServiceDesk или Excel-таблицы можно выгрузить в CSV и потом легко импортировать это в PowerShell, что мы и будем делать дальше.

У нас выгрузка выглядит вот так:

image

Поясню сразу, для чего столько полей:

  • Name — сетевое имя принтера, оно прописывается в DNS и на сервере печати
  • ByGroup — поле определяет, подключать принтер всем, кто находится в соответствующей подсети или только тем, кто входит в группу AD. Также по группе задаются ACL.
  • Subnet — подсеть, в которой должен находиться пользователь для работы с принтером
  • Location — строка расположения принтера, по которой ищет виндовый мастер добавления принтеров
    Как устроен поиск принтеров по локации

    Многие не знают, что происходит при поиске принтера стандартным мастером «Установка принтера». А происходит вот что. Мастер берет поле Location (Расположение) из атрибутов компьютера в AD, и выбирает из опубликованных в AD принтеров все, у которых расположение начинается с той же строки. То есть, если у компьютера пользователя в расположении указано «Омск/Офис на Ленина», то в поиске отобразятся принтеры с расположениями «Омск/Офис на Ленина/», «Омск/Офис на Ленина/Кабинет 404» и «Омск/Офис на Ленина/Приёмная»
  • Driver — имя драйвера, который будет указан при добавлении на сервер печати. Этот драйвер должен быть уже установлен на сервере. Обычно у всех производителей, поставляющих более-менее серьезную печатную технику, есть универсальные драйвера, и они устанавливаются максимум один раз на партию, и то, если вендор поменялся, поэтому эту часть я не автоматизировал.
  • Type — просто описание принтера, чтобы пользователи легко выбирали нужный принтер по его возможностям при необходимости.
  • Model — модель принтера или МФУ. Здесь с помощью вспомогательных таблиц по этому полю заполняются предыдущие два.
  • UID — а это часть сегодняшнего торта. Этим UID-ом помечаются объекты в GPP, упрощенно — чтобы не переустанавливать принтер при каждом обновлении групповых политик.

Этого набора данных достаточно, чтобы не хардкодить их в Powershell. Можно начинать веселье.

2. Автоматизируем GPP

В общем случае файл Printers.xml в объекте GPP выглядит примерно так:

<?xml version="1.0" encoding="utf-8"?>
<Printers clsid="{1F577D12-3D1B-471e-A1B7-060317597B9C}">
  <SharedPrinter
  clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}"
  name="PRN-BARAB-061"
  status="PRN-BARAB-061"
  image="2"
  uid="{43231EA0-A4A3-4F1A-8A25-95BC4FEFBCC6}"
  userContext="1"
  bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-BARAB-061" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-BARAB-061" sid="S-1-5-21-210359847-7924152125-768726458-48993" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.142.1" max="192.168.142.254" />
    </Filters>
  </SharedPrinter>
</Printers>

Первоисточник формата можно изучить здесь: [MS-GPPREF]: Printers и здесь: [MS-GPPREF]: Common XML Attributes.

Практически же, я думаю, вы без труда сопоставите названия полей в этом файле с чекбоксами в интерфейсе GPP, но ключевые моменты я отмечу:

clsid — это фиксированные значения классов Group Policy Preferences, их можно (и нужно) оставить как есть.

Name/Status — это отображаемые имена элементов в консоли GPP
uid — а вот это уникальный идентификатор объекта, который вы видели в таблице выше.

Если он будет меняться при каждом обновлении списка принтеров, принтер будет заново подключаться у всех пользователей, что может вызывать разные побочные эффекты.
Path — UNC-путь к принтеру
FilterGroup, FilterIpRange — элементы нацеливания (Targeting) по членству пользователя в группе AD и по нахождению компьютера в диапазоне IP-адресов.

2.1. Импортируем данные и создадим базовую структуру XML-документа:

Для этого используем системный класс [System.Xml.XmlDocument]:

$PrintersCSV = Import-Csv -Delimiter ";" ".Printers.csv" -Encoding Default

[System.Xml.XmlDocument]$PrintersGPP = New-Object System.Xml.XmlDocument
$PrintersGPP.PrependChild($PrintersGPP.CreateXmlDeclaration("1.0", "utf-8", $null)) | Out-Null
$Printers = $PrintersGPP.AppendChild($PrintersGPP.CreateElement("Printers"))
$Printers.SetAttribute("clsid","{1F577D12-3D1B-471e-A1B7-060317597B9C}")

2.2. Добавим несколько вспомогательных функций для создания необходимых элементов XML и задания их атрибутов

Создание элемента принтера:

Function NewSharedPrinter
{
    [CmdletBinding()]
    param (
        $SharedPrinter,
        $clsid = "{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}",
		$uid,
        $name,
        $action
    )

    $image
    switch ($action){
        "C" {$image = 0}
        "R" {$image = 1}
        "U" {$image = 2}
        "D" {$image = 3}
    }

    $SharedPrinter.SetAttribute("clsid",$clsid)
    $SharedPrinter.SetAttribute("name",$name)
    $SharedPrinter.SetAttribute("status",$name)
    $SharedPrinter.SetAttribute("image",$image)
	$SharedPrinter.SetAttribute("uid",$uid)
    $SharedPrinter.SetAttribute("userContext",1)
    $SharedPrinter.SetAttribute("bypassErrors",1)

    $SharedPrinterProperties = $SharedPrinter.AppendChild($PrintersGPP.CreateElement("Properties"))
    $SharedPrinterProperties.setattribute("action",$action)
    $SharedPrinterProperties.setattribute("comment","")
    $SharedPrinterProperties.setattribute("path","\print-cluster-1.common.domain$name")
    $SharedPrinterProperties.setattribute("default",0)
    $SharedPrinterProperties.setattribute("port","")
}

Создание элементов фильтрации (нацеливания) по группе и по подсети:


Function NewFilterGroup{
    [CmdletBinding()]
    param (
        $FilterGroup,
        $bool = "AND",
        $not = 0,
        $name,
        $sid
    )
    $FilterGroup.SetAttribute("bool",$bool)
    $FilterGroup.SetAttribute("not",$not)
    $FilterGroup.SetAttribute("name","COMDOM"+$name)
    $FilterGroup.SetAttribute("sid",$sid)
    $FilterGroup.SetAttribute("userContext",1)
}

Function NewFilterSubnet{
    [CmdletBinding()]
    param (
        $FilterIPRange,
        $bool = "AND",
        $not = 0,
        $start,
        $end
    )
        $FilterIPRange.SetAttribute("bool",$bool)
        $FilterIPRange.SetAttribute("not",$not)
        $FilterIPRange.SetAttribute("min",$start)
        $FilterIPRange.SetAttribute("max",$end)
}

Все три функции работают напрямую с передаваемым объектом PS и ничего не возвращают. Ремарка для перфекционистов: на момент написания скрипта так было проще и быстрее, я не стремился написать идеальный объектно-ориентированный код, мне просто нужно было, чтобы работало. Так что да, в коде есть, что улучшать, но главное — он работает!

2.3. И, наконец, основной цикл, добавление элементов управления принтерами

Для каждого принтера создается два элемента: один на добавление, если пользователь в группе и в подсети, и один на удаление принтера, если пользователь не входит в группу или находится в другой подсети.

Если по какой-то причине в CSV-файле нет UID принтера, он сгенерируется и будет выведен в консоль.

Для перевода подсети из нотации CIDR в диапазон адресов я позаимствовал замечательный командлет PSipcalc.

ForEach ($PrinterItem in ($PrintersCSV | Sort-Object Name)) {

    if (!$PrinterItem.uid){
        $uid = "{"+([guid]::NewGuid()).Guid.ToUpper()+"}"
        Write-Host "Printer $($PrinterItem.Name) is new. Policy item ID: $uid"
    } else {
        $uid = $PrinterItem.uid
    }
    
    $SharedPrinter = $PrintersGPP.CreateElement("SharedPrinter")
    NewSharedPrinter -SharedPrinter $SharedPrinter -name $PrinterItem.Name -action "U" -uid $uid
    
    $Filters = $SharedPrinter.AppendChild($PrintersGPP.CreateElement("Filters"))
    if ($PrinterItem.ByGroup -ieq "yes") {
        try {
            Get-ADGroup -Identity $PrinterItem.Name | Out-Null
        } catch {
            Write-Host "Creating group $($PrinterItem.Name)"
            New-ADGroup -Name $PrinterItem.Name `
                -Path "OU=Доступ к принтерам и МФУ,OU=User Groups,DC=DOM,DC=COM" -GroupScope DomainLocal
        }
        $FilterGroup = $PrintersGPP.CreateElement("FilterGroup")
        NewFilterGroup -FilterGroup $FilterGroup -name $PrinterItem.Name -sid (Get-ADGroup -Identity $PrinterItem.Name).SID.Value
        $Filters.AppendChild($FilterGroup) | Out-Null
    }

    $FilterNetwork = .PSipcalc.ps1 -NetworkAddress $PrinterItem.Subnet

    $FilterIPRange = $PrintersGPP.CreateElement("FilterIpRange")
    NewFilterSubnet -FilterIPRange $FilterIPRange -start $FilterNetwork.HostMin -end $FilterNetwork.HostMax
    $Filters.AppendChild($FilterIPRange) | Out-Null

    $Printers.AppendChild($SharedPrinter) | Out-Null

    $RevertSharedPrinter = $PrintersGPP.CreateElement("SharedPrinter")
    NewSharedPrinter -SharedPrinter $RevertSharedPrinter -name $PrinterItem.Name -action "D" -uid $uid
    
    if ($PrinterItem.ByGroup -ieq "yes") {
        $bool = "OR"
    } else {
        $bool = "AND"
    }

    $Filters = $RevertSharedPrinter.AppendChild($PrintersGPP.CreateElement("Filters"))
    if ($PrinterItem.ByGroup -ieq "yes") {
        $FilterGroup = $PrintersGPP.CreateElement("FilterGroup")
        NewFilterGroup -FilterGroup $FilterGroup -name $PrinterItem.Name -sid (Get-ADGroup -Identity $PrinterItem.Name).SID.Value -bool "AND" -not 1
        $Filters.AppendChild($FilterGroup) | Out-Null
    }

    $FilterIPRange = $PrintersGPP.CreateElement("FilterIpRange")
    NewFilterSubnet -FilterIPRange $FilterIPRange -start $FilterNetwork.HostMin -end $FilterNetwork.HostMax -bool $bool -not 1
    $Filters.AppendChild($FilterIPRange) | Out-Null

    $Printers.AppendChild($RevertSharedPrinter) | Out-Null
}

2.4. И теперь выводим результат в файл

Не вижу ничего плохого в том, чтобы сэкономить немного минут и сразу записать в политику:

$PrintersGPP.Save("\COMDOMSysVolCOMMON.DOMAINPolicies{f985a9ae-cb71-468b-8a99-e2c7f428aa2f}UserPreferencesPrintersPrinters.xml")

3. Добавление принтеров на сервер печати

В Windows есть довольно малоизвестный набор vbs-скриптов для управления принтерами. Причем он есть даже в клиентских версиях. Расположен он в каталоге %SystemRoot%System32Printing_Admin_Scriptsen-US, мурзилку можно почитать здесь.

Нас интересуют три утилиты из набора:

prnport.vbs — для создания порта принтера на сервере печати
prnmngr.vbs — для создания самого принтера
prncnfg.vbs — для задания настроек и включения общего доступа к принтеру
Также нам понадобится утилита SetACL для задания прав доступа на принтеры.

3.1. Создаем принтер на сервере печати

Для этого создадим скрипт CreateRemotePrinter.bat.

В качестве аргументов он будет принимать, по порядку:
%1 — Имя принтера;
%2 — Имя драйвера;
%3 — Расположение (Location);
%4 — Описание принтера.

@echo off
SET PRTOOLS="c:WindowsSystem32Printing_Admin_Scriptsen-US"
SET SETACL="SetACL.exe"

cscript /nologo %PRTOOLS%prnport.vbs -s print-cl-node-1 -a -r %1 -h %1.common.domain -t -o raw -me
cscript /nologo %PRTOOLS%prnmngr.vbs -s print-cl-node-1 -a -p %1 -m %2 -r %1
cscript /nologo %PRTOOLS%prncnfg.vbs -s print-cl-node-1 -t -p %1 -h %1 -l %3 -m %4 +shared
%SETACL% -on \print-cl-node-1%1 -ot prn -actn ace -ace "n:STN-TN%1;p:man_docs,print"

В общем случае этого достаточно. Но у нас сервер печати размещен на кластере MSCS, а указанный выше набор vbs-скриптов с кластерами работать не умеет. Поэтому придется сделать еще кое-что.

3.2. Переносим принтер с обычного сервера печати на кластеризованный

При обращении к кластеру скрипты из набора выше просто создают порт и принтер на текущей активной ноде, и на кластере они не появляются. Как раз на такой случай у Майкрософт припасен еще один инструмент — PrintBRM (Print queue Backup/Recovery/Migration). Официальной документации на него я не нашел, поэтому делюсь тем, что есть: Справка по параметрам на SS64.

Продолжаем скрипт CreateRemotePrinter.bat.

Утилита PrintBRM умеет корректно копировать конфигурацию принтера вместе с портами на кластер, однако если просто сделать копию (запустить с ключом -b) и восстановить (-r), то никакого чуда не будет.

Поэтому сейчас будет немного магии. Она подробно описана в блоге MS Performance Team Blog.
Сначала мы делаем резервную копию принтера в файл temp.printerexport.

Затем распаковываем в подкаталог printerexport, удаляем каталоги LMONS и PRTPROCS, а также обнуляем содержимое нескольких XML-файлов. Не буду вдаваться в подробности, но эти файлы содержат конфигурацию, специфичную для конкретного сервера и мешают восстановлению принтера на другой сервер, особенно кластерный.

После этого запаковываем отредактированную конфигурацию обратно в файл temp.printerexport и загружаем на кластер:

SET PRNBRM="C:WindowsSystem32spooltoolsPrintBrm.exe"

%PRNBRM% -b -nobin -s print-cl-node-1 -f temp.printerexport
%PRNBRM% -r -d printerexport -f temp.printerexport

del /f /q temp.printerexport
del /s /f /q printerexportLMONS printerexportPRTPROCS
echo ^<SpoolerAttrib /^>			> printerexportBrmSpoolerAttrib.xml
echo ^<PPROCS /^>					> printerexportPProcs.xml
echo ^<PRINTERDRIVERS /^>			> printerexportBrmDrivers.xml
echo ^<LMONS Arch="Windows x64"/^>	> printerexportBRMLMons.xml

%PRNBRM% -b -d printerexport -f temp.printerexport
del /s /f /q printerexport

%PRNBRM% -r -s print-cluster-1 -f temp.printerexport -p all -o force
del /f /q temp.printerexport

cscript /nologo %PRTOOLS%prnmngr.vbs -s print-cl-node-1 -d -p %1
cscript /nologo %PRTOOLS%prnport.vbs -s print-cl-node-1 -d -r %1

3.3. Добавляем принтеры одновременно в GPP и на сервер/кластер

Теперь нужно органично вписать этот скрипт в цикл обработки CSV-списка принтеров.

Добавим перед основным циклом запрос списка уже существующих на сервере:

$ActualPrintersList = Get-WmiObject -Class win32_share -computer print-cluster-1 | Where-Object Name -like "*PRN*" | Select-Object -ExpandProperty Name

И в теле цикла запустим наш Bat-ничек:

    if ("\print-cluster-1$($PrinterItem.Name)" -NotIn $ActualPrintersList) {
        Write-Host "Adding printer $($PrinterItem.Name) to print-cluster-1..."
        Start-Process -FilePath .CreateRemotePrinter.bat -ArgumentList "$($PrinterItem.Name) `"$($PrinterItem.Driver)`" `"$($PrinterItem.Location)`" `"$($PrinterItem.Type)`"" -Wait
    }

4. Собираем все воедино, запускаем, получаем результат

Исходные данные в CSV

Name;ByGroup;Subnet;Location;Driver;Type;Model;uid
PRN-NALTA-028;yes;192.168.192.0/24;Новоалтайск/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet Pro M425dn;HP LaserJet Pro M425dn;{35F6CF36-2A24-4A81-B061-8BE71CEC27EA}
PRN-TARUS-002;yes;192.168.128.0/23;Таруса/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet M3027 MFP;HP LaserJet M3027 MFP;{398F4A94-530C-4E3B-8A30-4288D5E8854D}
PRN-KIRILL-081;;192.168.196.0/24;Кириллов/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet 3390;HP LaserJet 3390;{421FC2DE-2E97-49FC-AEB0-3070B0166AD5}
PRN-BARAB-061;yes;192.168.142.0/24;Барабинск/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet Pro M425dn;HP LaserJet Pro M425dn;{43231EA0-A4A3-4F1A-8A25-95BC4FEFBCC6}
PRN-PYSHM-004;;192.168.143.0/24;Верхняя Пышма/;KX DRIVER for Universal Printing;МФУ А4 ЧБ — Kyocera ECOSYS M2540dn;Kyocera ECOSYS M2540dn;{45509B48-E7BC-4497-9665-86D4E1E96FE1}
PRN-BUY---001;yes;192.168.44.0/24;Буй/;HP Universal Printing PCL 6 (v5.6.0);Принтер А4 ЧБ — HP LaserJet Pro M402dn;HP LaserJet Pro M402dn;{457DB3FE-E35F-450E-B1D6-912F0D831573}
PRN-BATAY-042;yes;192.168.128.0/23;Батайск/;HP Universal Printing PCL 6 (v5.6.0);Принтер А4 ЧБ — HP LaserJet P2015 Series;HP LaserJet P2015 Series;{4740604B-C403-4511-91B0-689A197260F3}
PRN-EMPTY-002;yes;192.168.199.0/24;Пустошка/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet Pro M425dn;HP LaserJet Pro M425dn;{475578CB-689F-463B-9710-AE207973146C}
PRN-LIPKI-003;;192.168.44.0/24;Липки/;KX DRIVER for Universal Printing;МФУ А4 ЧБ — Kyocera ECOSYS M2540dn;Kyocera ECOSYS M2540dn;{4794D22E-6586-4A81-A85D-A21FB8B209CF}
PRN-KOTOV-013;yes;192.168.128.0/23;Котово/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet Pro M425dn;HP LaserJet Pro M425dn;{4DFFBA1F-48D2-4824-B2EB-0AAD12B6A9D6}
PRN-ELAB-064;;192.168.140.0/24;Елабуга/;HP Universal Printing PCL 6 (v5.6.0);МФУ А4 ЧБ — HP LaserJet Pro M425dn;HP LaserJet Pro M425dn;{52069D45-EF86-442D-A157-368D7510A1CF}
GeneratePrintersXml.ps1

$PrintersCSV = Import-Csv -Delimiter ";" ".Printers.csv" -Encoding Default

[System.Xml.XmlDocument]$PrintersGPP = New-Object System.Xml.XmlDocument
$PrintersGPP.PrependChild($PrintersGPP.CreateXmlDeclaration("1.0", "utf-8", $null)) | Out-Null
$Printers = $PrintersGPP.AppendChild($PrintersGPP.CreateElement("Printers"))
$Printers.SetAttribute("clsid","{1F577D12-3D1B-471e-A1B7-060317597B9C}")

    $DelPrinters = $Printers.AppendChild($PrintersGPP.CreateElement("SharedPrinter"))
    NewSharedPrinter -SharedPrinter $DelPrinters -name "Delete All" -action "D" -uid "{21097DBD-285D-48C3-B042-7746D7E6DA1B}"
    $Filters = $DelPrinters.AppendChild($PrintersGPP.CreateElement("Filters"))
    $FilterRunOnce = $Filters.AppendChild($PrintersGPP.CreateElement("FilterRunOnce"))
    $FilterRunOnce.SetAttribute("id","{F2537B78-C7D7-43DF-98D6-B32E90644825}")
    $FilterRunOnce.SetAttribute("hidden",1)
    $FilterRunOnce.SetAttribute("not",0)
    $FilterRunOnce.SetAttribute("bool","AND")

Function NewSharedPrinter
{
    [CmdletBinding()]
    param (
        $SharedPrinter,
        $clsid = "{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}",
        $uid,
        $name,
        $action
    )

    $image
    switch ($action){
        "C" {$image = 0}
        "R" {$image = 1}
        "U" {$image = 2}
        "D" {$image = 3}
    }

    $SharedPrinter.SetAttribute("clsid",$clsid)
    $SharedPrinter.SetAttribute("name",$name)
    $SharedPrinter.SetAttribute("status",$name)
    $SharedPrinter.SetAttribute("image",$image)
    $SharedPrinter.SetAttribute("uid",$uid)
    $SharedPrinter.SetAttribute("userContext",1)
    $SharedPrinter.SetAttribute("bypassErrors",1)

    $SharedPrinterProperties = $SharedPrinter.AppendChild($PrintersGPP.CreateElement("Properties"))
    $SharedPrinterProperties.setattribute("action",$action)
    $SharedPrinterProperties.setattribute("comment","")
    $SharedPrinterProperties.setattribute("path","\print-cluster-1.common.domain$name")
    $SharedPrinterProperties.setattribute("default",0)
    $SharedPrinterProperties.setattribute("port","")
}

Function NewFilterGroup{
    [CmdletBinding()]
    param (
        $FilterGroup,
        $bool = "AND",
        $not = 0,
        $name,
        $sid
    )
    $FilterGroup.SetAttribute("bool",$bool)
    $FilterGroup.SetAttribute("not",$not)
    $FilterGroup.SetAttribute("name","COMDOM"+$name)
    $FilterGroup.SetAttribute("sid",$sid)
    $FilterGroup.SetAttribute("userContext",1)
}

Function NewFilterSubnet{
    [CmdletBinding()]
    param (
        $FilterIPRange,
        $bool = "AND",
        $not = 0,
        $start,
        $end
    )
        $FilterIPRange.SetAttribute("bool",$bool)
        $FilterIPRange.SetAttribute("not",$not)
        $FilterIPRange.SetAttribute("min",$start)
        $FilterIPRange.SetAttribute("max",$end)
}

$ActualPrintersList = Get-WmiObject -Class win32_share -computer print-cluster-1 | Where-Object Name -like "*PRN*" | Select-Object -ExpandProperty Name

ForEach ($PrinterItem in ($PrintersCSV | Sort-Object Name)) {

    if (!$PrinterItem.uid){
        $uid = "{"+([guid]::NewGuid()).Guid.ToUpper()+"}"
        Write-Host "Printer $($PrinterItem.Name) is new. Policy item ID: $uid"
    } else {
        $uid = $PrinterItem.uid
    }
    
    $SharedPrinter = $PrintersGPP.CreateElement("SharedPrinter")
    NewSharedPrinter -SharedPrinter $SharedPrinter -name $PrinterItem.Name -action "U" -uid $uid
    
    $Filters = $SharedPrinter.AppendChild($PrintersGPP.CreateElement("Filters"))
    if ($PrinterItem.ByGroup -ieq "yes") {
        try {
            Get-ADGroup -Identity $PrinterItem.Name | Out-Null
        } catch {
            Write-Host "Creating group $($PrinterItem.Name)"
            New-ADGroup -Name $PrinterItem.Name `
                -Path "OU=Доступ к принтерам и МФУ,OU=User Groups,DC=DOM,DC=COM" -GroupScope DomainLocal
        }
        $FilterGroup = $PrintersGPP.CreateElement("FilterGroup")
        NewFilterGroup -FilterGroup $FilterGroup -name $PrinterItem.Name -sid (Get-ADGroup -Identity $PrinterItem.Name).SID.Value
        $Filters.AppendChild($FilterGroup) | Out-Null
    }

    $FilterNetwork = .PSipcalc.ps1 -NetworkAddress $PrinterItem.Subnet

    $FilterIPRange = $PrintersGPP.CreateElement("FilterIpRange")
    NewFilterSubnet -FilterIPRange $FilterIPRange -start $FilterNetwork.HostMin -end $FilterNetwork.HostMax
    $Filters.AppendChild($FilterIPRange) | Out-Null

    $Printers.AppendChild($SharedPrinter) | Out-Null

    $RevertSharedPrinter = $PrintersGPP.CreateElement("SharedPrinter")
    NewSharedPrinter -SharedPrinter $RevertSharedPrinter -name $PrinterItem.Name -action "D" -uid $uid
    
    if ($PrinterItem.ByGroup -ieq "yes") {
        $bool = "OR"
    } else {
        $bool = "AND"
    }

    $Filters = $RevertSharedPrinter.AppendChild($PrintersGPP.CreateElement("Filters"))
    if ($PrinterItem.ByGroup -ieq "yes") {
        $FilterGroup = $PrintersGPP.CreateElement("FilterGroup")
        NewFilterGroup -FilterGroup $FilterGroup -name $PrinterItem.Name -sid (Get-ADGroup -Identity $PrinterItem.Name).SID.Value -bool "AND" -not 1
        $Filters.AppendChild($FilterGroup) | Out-Null
    }

    $FilterIPRange = $PrintersGPP.CreateElement("FilterIpRange")
    NewFilterSubnet -FilterIPRange $FilterIPRange -start $FilterNetwork.HostMin -end $FilterNetwork.HostMax -bool $bool -not 1
    $Filters.AppendChild($FilterIPRange) | Out-Null

    $Printers.AppendChild($RevertSharedPrinter) | Out-Null
    
    if ("\print-cluster-1$($PrinterItem.Name)" -NotIn $ActualPrintersList) {
        Write-Host "Adding printer $($PrinterItem.Name) to print-cluster-1..."
        Start-Process -FilePath .CreateRemotePrinter.bat -ArgumentList "$($PrinterItem.Name) `"$($PrinterItem.Driver)`" `"$($PrinterItem.Location)`" `"$($PrinterItem.Type)`"" -Wait
    }
}

$PrintersGPP.Save("\COMDOMSysVolCOMMON.DOMAINPolicies{f985a9ae-cb71-468b-8a99-e2c7f428aa2f}UserPreferencesPrintersPrinters.xml")
$PrintersGPP.Save(".Printers.xml")
CreateRemotePrinter.bat

@echo off
SET PRTOOLS="c:WindowsSystem32Printing_Admin_Scriptsen-US"
SET SETACL="SetACL.exe"
SET PRNBRM="C:WindowsSystem32spooltoolsPrintBrm.exe"

cscript /nologo %PRTOOLS%prnport.vbs -s print-cl-node-1 -a -r %1 -h %1.common.domain -t -o raw -me
cscript /nologo %PRTOOLS%prnmngr.vbs -s print-cl-node-1 -a -p %1 -m %2 -r %1
cscript /nologo %PRTOOLS%prncnfg.vbs -s print-cl-node-1 -t -p %1 -h %1 -l %3 -m %4 +shared
%SETACL% -on \print-cl-node-1%1 -ot prn -actn ace -ace "n:STN-TN%1;p:man_docs,print"

%PRNBRM% -b -nobin -s print-cl-node-1 -f temp.printerexport
%PRNBRM% -r -d printerexport -f temp.printerexport

del /f /q temp.printerexport
del /s /f /q printerexportLMONS printerexportPRTPROCS
echo ^<SpoolerAttrib /^>			> printerexportBrmSpoolerAttrib.xml
echo ^<PPROCS /^>					> printerexportPProcs.xml
echo ^<PRINTERDRIVERS /^>			> printerexportBrmDrivers.xml
echo ^<LMONS Arch="Windows x64"/^>	> printerexportBRMLMons.xml

%PRNBRM% -b -d printerexport -f temp.printerexport
del /s /f /q printerexport

%PRNBRM% -r -s print-cluster-1 -f temp.printerexport -p all -o force
del /f /q temp.printerexport

cscript /nologo %PRTOOLS%prnmngr.vbs -s print-cl-node-1 -d -p %1
cscript /nologo %PRTOOLS%prnport.vbs -s print-cl-node-1 -d -r %1
Итоговый файл Printers.xml

<?xml version="1.0" encoding="utf-8"?>
<Printers clsid="{1F577D12-3D1B-471e-A1B7-060317597B9C}">
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="Delete All" status="Delete All" image="3" uid="{21097DBD-285D-48C3-B042-7746D7E6DA1B}" bypassErrors="1" disabled="1">
    <Properties action="D" path="" default="0" deleteAll="1" port="" />
    <Filters>
      <FilterRunOnce id="{F2537B78-C7D7-43DF-98D6-B32E90644825}" hidden="1" not="0" bool="AND" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-BARAB-061" status="PRN-BARAB-061" image="2" uid="{43231EA0-A4A3-4F1A-8A25-95BC4FEFBCC6}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-BARAB-061" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-BARAB-061" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.142.1" max="192.168.142.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-BARAB-061" status="PRN-BARAB-061" image="3" uid="{43231EA0-A4A3-4F1A-8A25-95BC4FEFBCC6}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-BARAB-061" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-BARAB-061" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.142.1" max="192.168.142.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-BATAY-042" status="PRN-BATAY-042" image="2" uid="{4740604B-C403-4511-91B0-689A197260F3}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-BATAY-042" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-BATAY-042" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.128.1" max="192.168.129.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-BATAY-042" status="PRN-BATAY-042" image="3" uid="{4740604B-C403-4511-91B0-689A197260F3}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-BATAY-042" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-BATAY-042" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.128.1" max="192.168.129.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-BUY---001" status="PRN-BUY---001" image="2" uid="{457DB3FE-E35F-450E-B1D6-912F0D831573}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-BUY---001" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-BUY---001" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.44.1" max="192.168.44.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-BUY---001" status="PRN-BUY---001" image="3" uid="{457DB3FE-E35F-450E-B1D6-912F0D831573}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-BUY---001" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-BUY---001" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.44.1" max="192.168.44.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-ELAB-064" status="PRN-ELAB-064" image="2" uid="{52069D45-EF86-442D-A157-368D7510A1CF}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-ELAB-064" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="0" min="192.168.140.1" max="192.168.140.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-ELAB-064" status="PRN-ELAB-064" image="3" uid="{52069D45-EF86-442D-A157-368D7510A1CF}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-ELAB-064" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="1" min="192.168.140.1" max="192.168.140.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-EMPTY-002" status="PRN-EMPTY-002" image="2" uid="{475578CB-689F-463B-9710-AE207973146C}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-EMPTY-002" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-EMPTY-002" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.199.1" max="192.168.199.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-EMPTY-002" status="PRN-EMPTY-002" image="3" uid="{475578CB-689F-463B-9710-AE207973146C}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-EMPTY-002" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-EMPTY-002" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.199.1" max="192.168.199.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-KIRILL-081" status="PRN-KIRILL-081" image="2" uid="{421FC2DE-2E97-49FC-AEB0-3070B0166AD5}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-KIRILL-081" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="0" min="192.168.196.1" max="192.168.196.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-KIRILL-081" status="PRN-KIRILL-081" image="3" uid="{421FC2DE-2E97-49FC-AEB0-3070B0166AD5}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-KIRILL-081" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="1" min="192.168.196.1" max="192.168.196.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-KOTOV-013" status="PRN-KOTOV-013" image="2" uid="{4DFFBA1F-48D2-4824-B2EB-0AAD12B6A9D6}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-KOTOV-013" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-KOTOV-013" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.128.1" max="192.168.129.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-KOTOV-013" status="PRN-KOTOV-013" image="3" uid="{4DFFBA1F-48D2-4824-B2EB-0AAD12B6A9D6}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-KOTOV-013" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-KOTOV-013" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.128.1" max="192.168.129.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-LIPKI-003" status="PRN-LIPKI-003" image="2" uid="{4794D22E-6586-4A81-A85D-A21FB8B209CF}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-LIPKI-003" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="0" min="192.168.44.1" max="192.168.44.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-LIPKI-003" status="PRN-LIPKI-003" image="3" uid="{4794D22E-6586-4A81-A85D-A21FB8B209CF}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-LIPKI-003" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="1" min="192.168.44.1" max="192.168.44.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-NALTA-028" status="PRN-NALTA-028" image="2" uid="{35F6CF36-2A24-4A81-B061-8BE71CEC27EA}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-NALTA-028" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-NALTA-028" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.192.1" max="192.168.192.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-NALTA-028" status="PRN-NALTA-028" image="3" uid="{35F6CF36-2A24-4A81-B061-8BE71CEC27EA}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-NALTA-028" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-NALTA-028" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.192.1" max="192.168.192.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-PYSHM-004" status="PRN-PYSHM-004" image="2" uid="{45509B48-E7BC-4497-9665-86D4E1E96FE1}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-PYSHM-004" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="0" min="192.168.143.1" max="192.168.143.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-PYSHM-004" status="PRN-PYSHM-004" image="3" uid="{45509B48-E7BC-4497-9665-86D4E1E96FE1}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-PYSHM-004" default="0" port="" />
    <Filters>
      <FilterIpRange bool="AND" not="1" min="192.168.143.1" max="192.168.143.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-TARUS-002" status="PRN-TARUS-002" image="2" uid="{398F4A94-530C-4E3B-8A30-4288D5E8854D}" userContext="1" bypassErrors="1">
    <Properties action="U" comment="" path="\print-cluster-1.common.domainPRN-TARUS-002" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="0" name="COMDOMPRN-TARUS-002" sid="" userContext="1" />
      <FilterIpRange bool="AND" not="0" min="192.168.128.1" max="192.168.129.254" />
    </Filters>
  </SharedPrinter>
  <SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="PRN-TARUS-002" status="PRN-TARUS-002" image="3" uid="{398F4A94-530C-4E3B-8A30-4288D5E8854D}" userContext="1" bypassErrors="1">
    <Properties action="D" comment="" path="\print-cluster-1.common.domainPRN-TARUS-002" default="0" port="" />
    <Filters>
      <FilterGroup bool="AND" not="1" name="COMDOMPRN-TARUS-002" sid="" userContext="1" />
      <FilterIpRange bool="OR" not="1" min="192.168.128.1" max="192.168.129.254" />
    </Filters>
  </SharedPrinter>
</Printers>
Результат на сервере печати

image

Автор: Михаил

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js