Здравствуйте уважаемые читатели. Эта небольшая статья больше шпаргалка для меня, но, надеюсь, она будет полезна и другим. Всем, кто проявит интерес к статье буду признателен, особенно тем, кто укажет на недочеты. Жду ваши комментарии.

Итак начнем. Задача, которую пытались решить — это установка принтеров на терминальные серверы (нынче серверы узла сеансов) фермы. Было несколько путей:

  • установка с помощью групповых политик

  • установка скриптом

  • установка вручную

Установка вручную — конечно, самый простой путь, но если серверов несколько, то вероятность ошибки велика, да и установка вручную — это скучно. Нужна была автоматизация, поэтому сначала выбрал групповые политики — разворачивание принтера на компьютер (на rdsh).

Выбрал установку на компьютер, потому что AD, которое мне досталось по сути не имело структуры — все пользователи размещались в одном OU. Чтобы навести порядок в AD и распределить пользователей на OU, которые соответствуют либо структуре здания либо штатному расписанию потребуется много времени.

В результате использования GPO выявил следующие нюансы:

  • драйверы принтеров не устанавливаются автоматически или это происходит долго;

  • даже если установить драйверы принтера на rdsh, то установка принтера через gpo происходит только после перезагрузки сервера;

  • после входа пользователя на сервер — список принтеров пуст примерно минут пять бывает больше;

  • принтеры очень часто не отображаются в панели управления, но отображаются в диалоговом окне печати, например в блокноте. В оснастке управления печатью развернутые принтеры не отображаются;

  • При удалении принтера из групповой политики принтер частенько не удаляется сразу, даже после gpupdate force.

Самое критичное — это установка принтера только после перезагрузки — довольно неприятная ситуация, когда необходимо выкинуть с терминала 50 пользователей. Поэтому начал смотреть в сторону скрипта на Powershell.

Организацию работы по установке принтера предполагал такой: принтер устанавливается вручную на сервер печати, затем запускается скрипт, который устанавливает этот принтер на все терминальные серверы фермы, с такими же настройками что и на сервере печати.

Удаленная установка принтера
Удаленная установка принтера

В результате сбора информации нашел хорошую статью про PrintBRM - Отказоустойчивый сервер печати на базе Windows, благодарю автора - информация была полезна. Испытал, метод рабочий, но хотелось больше контроля над алгоритмом установки, поэтому использовал Powershell.

Алгоритм скрипта
Алгоритм скрипта

Сам скрипт:

$driversFile= "\\storage\printDrivers\drivers.txt"
$printersFile= "\\storage\printDrivers\printers.txt"
$ACLfolder = "\\storage\printDrivers\acl\"
$csvfile= "\\storage\printDrivers\printers.csv"
$folderDrivers= "\\storage\printDrivers\drivers\"
$terminals = @("tspd-01", "tspd-02", "its-01", "its-02")


$exportFlag=0
#удаляем файл списка названий драйверов
if(Test-Path $ACLfolder){Remove-Item -Path $ACLfolder -Recurse}
if(Test-Path $driversFile){Remove-Item -Path $driversFile}
if(Test-Path $printersFile){Remove-Item -Path $printersFile}
if(Test-Path $csvfile){Remove-Item -Path $csvfile}

Start-Sleep -Seconds 5
#формируем список названий драйверов
$drivers=Get-PrinterDriver | select Name | where {$_.Name -notlike "*Microsoft*" -and  $_.Name -notlike "Remote*"}
foreach ($driver in $drivers) {
write $driver.Name | out-file $driversFile -Append
}

#формируем список принтеров
$printers = get-printer | where {$_.Name -notlike "*Microsoft*" -and  $_.Name -notlike "Remote*"}
#создаем директорию для ACL
Mkdir $ACLfolder
foreach($printer in $printers){
Write $printer.Name | out-file $printersFile -Append
#экспортируем ACL принтера в текстовый файл
$aclfile=$ACLfolder+$printer.Name+".txt"
(Get-Printer $printer.Name -Full).PermissionSDDL | Out-File $aclfile
}

#формируем csv файл с информацией о принтере
get-printer | where {$_.Name -notlike "*Microsoft*" -and  $_.Name -notlike "Remote*"} |Export-CSV $csvfile -NoTypeInformation -Encoding UTF8


#функция экспорта драйверов
function ExportDrivers($folderDrivers){
    #очищаем папку с драйверами
    if(Test-Path $folderDrivers){Remove-Item -Path $folderDrivers -Recurse}
    #получаем список драйверов принтеров
    $PrintDriver = Get-WindowsDriver -Online | where {($_.ClassName -like "Printer")}
    foreach ($print in $PrintDriver){
        write $print
        #Записываем название драйверов НЕ от микрософта
        if($print.ProviderName -ne "Microsoft"){
            #Создаем имя папки
            $folderName=($folderDrivers + $print.Driver).replace('.inf','')
            write $folderName
            #Создаем папку
            Mkdir $folderName
            #экспортируем драйвера принтеров в папку
            pnputil.exe /export-driver $print.Driver $folderName
        }
    }
}

#Запуск экспорта
if($exportFlag -eq 1){ExportDrivers -folderDrivers $folderDrivers}


Write "_____________________"
$pass = "Morkovka10"
$us="Domain\mylogin"
$password = ConvertTo-SecureString -String $pass -AsPlainText -Force
$cred= New-Object System.Management.Automation.PSCredential ($us, $password )
#Запуск удаленной сесии
foreach($server in $terminals){
$s = New-PSSession -computerName $server -authentication CredSSP -credential $cred
Invoke-Command -Session $s -Scriptblock {
             #проброс перемнных в сессию
             $csvfile=$using:csvfile 
             $ACLfolder= $using:ACLfolder
             $folderDrivers=$using:folderDrivers
             $driversFile=$using:driversFile
             $h = hostname
             write "____________________________________ $h ________________________________________________________________________"

             #устанавливаем драйвера в хранилище драйверов
             write "install drivers!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
             $folderDrivers=$folderDrivers+"*.inf"
             pnputil.exe /add-driver $folderDrivers /subdirs /install
             write "install drivers!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
             
             #добавляем отсутствующие драйвера 
             write "_______________________________"
             Write "добавляем отсутствующие драйвера принтеров из хранилища драйверов"
             $drivers = Get-Content -Path $driversFile 
             foreach ($driver in $drivers){
               if((Get-PrinterDriver -Name $driver).Count -eq 0){
                Add-PrinterDriver -Name $driver
               }
             }
               write "_______________________________"


             #Получаем массив объектов - список принтеров на принтсервере из csv
             $printers = $printers =Import-CSV -Path $csvfile

             #Получаем массив локальных принтеров
             $localPrinters = get-printer -Full | where {$_.Name -notlike "*Microsoft*" -and  $_.Name -notlike "Remote*"}

             #удаляем локальные принтеры которых нет на принтсервере

             foreach ($localP in $localPrinters){
                 $match = $printers -match $localP.Name
                 if($match.Count -eq 0){
                     $pname=$localP.Name
                     write "REMOVE $pname - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
                     Remove-Printer -Name $localP.Name
                 }
             }

             Foreach($p in $printers){
                write $p.Name
                write $p.DriverName
                write $p.PortName 
                $aclfile=$ACLfolder+$p.Name+".txt"
                $perms = Get-Content -Path $aclfile
                if((Get-Printer -Name $p.Name).Count -ne 0){
                #синхронизируем ACL
                Set-Printer $p.Name -PermissionSDDL $perms 
                
                #Получаем локальный принтер 
                $localprinter= Get-Printer -Name $p.Name
                
                #Проверяем порт
                if($localprinter.PortName -ne $p.PortName){
                    write "порт НЕ совпадает"
                    #Добавляем порт если его нет
                    if((Get-PrinterPort -Name $p.PortName).Count -eq 0){
                     Add-PrinterPort -Name $p.PortName -PrinterHostAddress $p.PortName
                    }
                    #Добавляем порт принтеру
                    Set-Printer -Name $p.Name -PortName $p.PortName
                }else{write "порт совпадает"}
                #Проверяем драйвер
                if($localprinter.DriverName -ne $p.DriverName){
                 Set-Printer -Name $p.Name -DriverName $p.DriverName
                }
                } else {
                    
                    #Добавляем порт
                    if((Get-PrinterPort -Name $p.Name).Count -eq 0){
                     Add-PrinterPort -Name $p.PortName -PrinterHostAddress $p.PortName
                    }
                    #Добавляем принтер
                    Add-Printer -Name $p.Name -DriverName $p.DriverName -PortName $p.PortName
                    #Добавляем ACL
                    Set-Printer $p.Name -PermissionSDDL $perms
                }
             }
             }
}
             
             

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

В результате мы получили:

  • удаленную установку принтеров на терминальные серверы без необходимости их перезагрузки;

  • единый сервер администрирования печати — сервер‑печати, который является эталонным — именно с него копируются все настройки устанавливаемых принтеров;

  • простой метод развертывания принтеров, поэтому его можно передать специалистам службы поддержки — установил принтер на сервер печати, настроил права и затем запустил скрипт — все просто!

  • Сервер печати — занимает мало места (в виде ВМ), поэтому можно быстро и легко его бекапить каждый день, поэтому не страшно, если техподдержка начудит — его быстро можно восстановить.

Задачка решена, способ конечно немного велосипедоизобретательный, но рабочий. Жду Ваших комментарий, делитесь опытом! Всем Добра!

Комментарии (12)


  1. Stillgray
    18.09.2023 12:59

    Всё же проще устанавливать опубликованные в АД принтеры через gpo\Политики пользователя\предпочтения, с нацеливанием на вхождение пользователя в определённую доменную группу.
    Для автоматической установки достаточно включения в доменную группу и релогин пользователя.


    1. MagicEx
      18.09.2023 12:59
      +1

      Проще ровно до того момента, пока не начнете ловить приколы с расшаренными принтерами. А они проявляются довольно быстро. Навскидку:

      1. Забивание реестра расшаренными принтерами.

      2. Забивание механизма CSR удаленными\старыми\ненужными принтерами.

      3. Исчезновение всех принтеров у пользователя до перелогина. Иногда до удаления профиля.

      Это основные проблемы, которые заставили сделать свое решение, примерно такое же как в статье. Отличия:

      • В качестве хранилища используется MSSQL, поскольку уже развернут в компании.

      • Импорты разделены на фулл\дифф, поскольку сравнение занимает много времени. Дифф вычисляется при экспорте сравнением на стороне SQL.

      • Инициатор установки не принт-сервер, а каждый RDSH, из-за их количества.

      Данный механизм позволил полностью решить все проблемы с принтерами, работает успешно уже более 5 лет. Принтеров около 2000+, 100+ серверов.


      1. shmelfrol Автор
        18.09.2023 12:59

        я думал про хранение в базе, но это наверное будет уже следующий этап. Кстати Вы очень правы - если инициатор rdsh, то экономится много времени так как установка идет одновременно на все серверы...


        1. Stillgray
          18.09.2023 12:59

          Ну, идеальным решением стало бы follow printing, когда в ОС устанавливается только один виртуальный принтер, а печать определяется политиками организации.
          Вот тут, к примеру, статья у КРОК с описанием преимуществ.


          1. MagicEx
            18.09.2023 12:59

            Очень узкое решение, рассчитано на офис, где стоит большое МФУ, к которому все ходят

            Нереализуемо для организации печати на складах и россыпи филиалов, думаю, что вы понимаете почему


            1. Stillgray
              18.09.2023 12:59

              Я бы не сказал, что этажные МФУ - большие. Равно, как бы и то, что ниша использования этого ПО - узкая.
              Скорее - наоборот, судя по высокой конкуренции в области печати по требованию. Маржинальность этого рынка больше, чем рынка печати для soho.

              Разумеется, нет смысла городить Follow printing в компании из пяти человек и одного принтера, но начиная с определённого момента роста размера компании, приобретение данного ПО становится выгодным, хотя бы за счёт экономии на бумаге.

              Кстати, иметь один этажный принтер выгоднее со всех сторон, чем россыпь локальных.

              С другой стороны, соорудить подобную систему на базе сервера печати Windows не так уж и сложно. Ведь, по сути, опубликованные принтеры - это сетевые шары, куда можно просто скопировать сформированный PCL\PS\PDF (в зависимости от принтера) файл из БД, по запросу от нужного устройства.


              1. MagicEx
                18.09.2023 12:59

                Мы немного про разные вещи говорим. Я про склады, где есть рабочие места с термопринтерами и А4, где сотрудник работает на одном месте и должен получать результаты печати чуть ли не мгновенно, от этого зависит скорость обработки. Эта скорость достигается принтером на каждом рабочем месте, а то и не одним. Как вы понимаете - follow printing это не вариант))

                Другой сценарий - сотни филиалов по 2-3 сотрудника на каждом. Сотрудники в терминалках, хотя тут не сильно важно. Обслуживание также максимально ускорено и ожидание печати с одного принтера недопустимо.

                Выливаются данные сценарии в тысячи принтеров, которые надо раздавать, что и решают подобные скрипты


  1. IDDQDesnik
    18.09.2023 12:59
    +2

    Скрипт просит, что бы его причесали: команды и переменные идут то с большой буквы, то с маленькой, то как попало.

    С проблемой не отображения принтеров тоже сталкивались. У нас принтеры прокидывались стандартным образом с клиента в сеанс RDP при коннекте, но в какой то момент все сломалось. Проблема оказалась в имени компьютера клиента, дефолтное DESKTOP-XXXXXXX оказывается слишком долинное, должно быть 14 символов или меньше.


  1. awaydownwego
    18.09.2023 12:59

    для таких дел есть entry-должность в ИТ называется эникейщик (или техник по русски), вот он ходит и делает (дёшего и сердито))


    1. shmelfrol Автор
      18.09.2023 12:59

      Да, но если дело касается серверов, а не рабочих станций, то необходимо эникейщику дать механизм работы с принтерами на rds, чтобы он не сломал терминальный сервер и не парализовал работу пользователей. Это же касается и удаленной установки и на рабочие станции. В принципе, на это и нацеливался создавая скрипт. Нужно сделать именно дешево и именно сердито - все должно быть четко и понятно, тогда получится функция, бизнес-процесс, которую можно передать сотруднику, за которую он будет ответственен.


  1. mc2
    18.09.2023 12:59

    На схеме, если драйвер совпадает, то что происходит дальше?)


    1. shmelfrol Автор
      18.09.2023 12:59

      ничего не делаем, переходим к следующему принтеру