Перейти к содержимому

Infrastructure as Code? Это очень просто!

20.05.2019

Если принято считать, что для реализации концепции Infrastructure as Code (программно определяемая инфраструктура) обязательно потребуются среды виртуализации, облака и ПО управления уровня Enterprise, то уверяю, все намного проще. Даже одного сервера и одного сценария на PowerShell, причем без продвинутых расширений типа Desired State Configuration достаточно для реального внедрения решения с элементами Infrastructure as Code.

Как компонент IaC, мы также рассмотрим автоматизацию создания групповых политик, а точнее Group Policy Preferences. Казалось бы, групповые политики сами по себе предназначены для автоматизации распространения конфигураций, зачем автоматизировать еще и их? Однако в подходе IaC требуется либо автоматически пересоздавать, либо тиражировать управляющие конфигурации, а групповые политики являются их частью.

Решение, о котором пойдет речь, возникло из, казалось бы, стандартной задачи по настройке серверов печати в региональных офисах. При этом хотелось, чтобы региональные администраторы не настраивали принтеры каждый по-своему, а следовали регламенту, принятому в головном офисе. С этой целью написал для региональных администраторов подробную инструкцию с картинками, в которой было описано, как следует устанавливать принтер на Windows Server с ролью принт-сервера, настраивать TCP/IP-порт принтера, создавать доменную группу и добавлять принтер в Group Policy Preferences для последующего распространения. Поскольку квалификация администраторов в региональных офисах могла быть разной, делал описание максимально подробным и иллюстрированным. Но после того как объем инструкции превысил 20 страниц, появилось сомнение, что у кого-либо хватит желания дочитать ее до конца. Да и следование инструкции не гарантировало от случайных ошибок.

Тем не менее, инструкция оказалась полезной, она выступила в роли технического задания. Именно по ней был разработан сценарий, который мы будем рассматривать далее в этой статье. Но сначала перечислим шаги, которые следует предпринять для полноценного развертывания сервера печати:

  1. Установка Windows Server, назначение IP-адреса, имени и т.п.
  2. Добавление роли сервера печати
  3. Установка драйверов принтеров, причем для всех версий и архитектур клиентских операционных систем, которые будут обращаться к данному принт-серверу
  4. Добавление TCP/IP-портов принтеров
  5. Установка принтеров с использованием ранее созданных портов и заранее добавленных драйверов
  6. Создание доменных групп, разграничивающих установку принтеров на клиентские устройства
  7. Создание объекта групповых политик, в котором будут создаваться Group Policy Preferences для назначения принтеров.
  8. Создание Group Policy Preferences, управляющих распространением каждого из принтеров
  9. Назначение (link) созданного объекта групповых политик на заданную OU, содержащую объекты компьютеров
  10. Добавление компьютеров или пользователей в созданные в п. 6 доменные группы

Довольно много пунктов, не правда ли? Некоторые шаги намеренно разделены, например пп. 3, 4 и 5 при ручной установке не разделяются, а выполняются в едином мастере конфигурации принтера. Также возможны варианты, например разграничивать установку принтеров не по группам безопасности, а по расположению объектов компьютеров или пользователей в Active Directory, но такой вариант мы не будем рассматривать здесь.

Забегая вперед, описываемый ниже сценарий предназначен для автоматизации шагов с 4 по 9 включительно. Из оставшихся пунктов, может возникнуть вопрос по шагу номер 3. Почему установка драйверов принтеров не была включена в автоматическое развертывание сервера печати? На то есть две причины.

Драйвера принтеров нередко являются частью специализированного ПО принтеров, которое бывает очень разным и устанавливается по-разному. Извлечь собственно драйвера из такого ПО возможно, но трудозатраты оказываются сравнимыми или даже превышают объем работы по простой установке подобного ПО. Кроме того, некоторые производители принтеров, такие как HP и Kyocera выпускают универсальные драйвера принтеров, совместимые с большим числом производимых и ранее выпускавшихся моделей. Примеры наименований универсальных драйверов: HP Universal Printing PCL 6, Kyocera Classic Universaldriver соответственно. Важно, что достаточно однократно установить ПО драйвера принтера на принт-сервер, а затем использовать с любым числом совместимых принтеров.

Ниже представлен сценарий printinst.ps1, который реально используется в работе и позволяет почти автоматически развертывать инфраструктуру печати. «Почти» означает, что все же остается одна операция, которую приходится выполнить вручную, а точнее полуавтоматически. Речь идет об импорте XML-файла, подготовленного сценарием, в Group Policy Preferences,, что будет рассмотрено отдельно. Пояснения по выполняемым действиям сценария даны в комментариях.

# printinst.ps1
# Развертывание принтеров в филиале на основе CSV-файла конфигураци, версия 1.5

Param (
[Parameter (Mandatory=$true)]
    [string]$ConfigCSV
)

# Читаем конфигурационный файл в переменную
$rawconf = Get-Content -Path $ConfigCSV
$config = @{}

# Список принтеров для импорта будем формировать в виде объекта
$colprinters = New-Object System.Collections.ArrayList

# Выполняем парсинг конфигурационного файла
$pass = 1
foreach ($line in $rawconf) {
    $items = $line -split(";")
    If (-Not (($items[0] -eq "") -Or ($items[0] -eq $null))) {
        If ($items[0] -eq "Имя принтера") {
            $pass = 2
        } else {
            If ($pass -eq 1) {
                $config.Add($items[0],$items[1])
            } else {
                $oPrinter = New-Object System.Object
                $oPrinter | Add-Member -MemberType NoteProperty -Name "Name" -Value ($items[0]).Trim()
                $oPrinter | Add-Member -MemberType NoteProperty -Name "ip" -Value ($items[1]).Trim()
                $oPrinter | Add-Member -MemberType NoteProperty -Name "Driver" -Value ($items[2]).Trim()
                $oPrinter | Add-Member -MemberType NoteProperty -Name "Location" -Value ($items[3]).Trim()
                $colprinters.Add($oPrinter)
            }
        }
    }
}


If ($colprinters.Count -eq 0) {
    Write-Host "Список принтеров не найден!`r`n"
    Break
}

if(@(get-module | where-object {$_.Name -eq "ActiveDirectory"} ).count -eq 0) {import-module ActiveDirectory}

# Set-PSDebug -Trace 1

# Назначаем переменные из конфигурационного файла
# Имена домена вычисляем из расположения домен-контроллера
$prnsrv = $config["Принт-сервер"]
$dc = $config["Домен-контроллер"]
$domain = (Get-AdDomainController -Identity $dc -Server $dc).domain
$NetBiosDomain = (Get-ADDomain -Server $dc).NetBiosName

$outFolder = $config["Папка для XML"]
$fil = $config["Код филиала"]

$LDAPgrp = $config["OU групп"]
$LDAPgpo = $config["OU компьютеров"]

$gpName = $fil + " Printers GPO"

# Проверяем, существует ли объект групповых политик, если нет - создаем и ожидаем создание
$error.Clear()
$gpo = get-gpo -Name $gpName -Domain $domain -Server $dc -ErrorAction SilentlyContinue
If ($error.Count -gt 0) {
    $error.Clear()
    Write-Host "`r`nСоздаем объект групповых политик "$gpname"..."
    New-GPO -Name $gpname -Domain $domain -Server $dc
    If ($error.Count -gt 0) {
        Break
    } else {
        Do {
            Start-Sleep -Seconds 1
            $error.Clear()
            $gpo = Get-GPO -Name $gpName -Domain $domain -Server $dc -ErrorAction SilentlyContinue
        } Until ($error.Count -eq 0)
    }
    New-GPLink -Name $gpName -Target $LDAPgpo -Domain $domain -Server $dc
}

# Получаем путь к GPO в папке SYSVOL
$gpoPath = "\\" + $dc + "\sysvol\" + $domain + "\Policies\{" + $gpo.Id.ToString() + "}"

# В переменной $txt начинаем формировать XML-структуры для последующего импорта в GPO 
$txt = "<?xml version=""1.0"" encoding=""utf-8""?>`r`n<Printers clsid=""{1F577D12-3D1B-471e-A1B7-060317597B9C}"">"
$writeXML = $false

# Выполняем для всех перечисленных принтеров
foreach ($oPrinter in $colPrinters) {

    $printer = $oPrinter.Name
    Write-Host "`r`nДобавляем принтер "$printer"..."
    $ip = $oPrinter.ip
    # Для установки принтера требуется, чтобы драйвер был уже установлен на принт-сервере
    $driver = $oPrinter.Driver
    # Удаляем любые кавычки в поле Расположение, если они есть
    $loc = $oPrinter.Location -replace """",""

    $groupName = $fil + "-prn-" + $printer
    $grpdescr = "Установка принтера " + $printer

    # Проверяем, создан ли TCP/IP-порт на принт-сервере, если нет, создаем
    $error.Clear()
    Get-PrinterPort -Name "$ip" -ComputerName $prnsrv -ErrorAction SilentlyContinue
    If ($error.Count -gt 0) {
        Add-PrinterPort -name "$ip" -PrinterHostAddress "$ip" -ComputerName $prnsrv -SNMP 1 -SNMPCommunity "public"
    }
    
    # Получаем список принтеров на принт-сервере, если локальное имя отсуствует в списке,
    # то добавляем новый принтер и предоставляем в совместное использование
    $printers = (get-printer -ComputerName $prnsrv).name
    If ($printers -eq $null) {$printers = @()}

    If ($printers.Contains($printer)) {
        $error.Clear()
    } else {
        # Если принтер не создается, то повторяем команду несколько раз, пока не пройдет 🙂
        $i = 0
        Do {
            $i++
            $error.Clear()
            Add-Printer -ComputerName $prnsrv -Name $printer -DriverName "$driver" -PortName "$ip" -Shared -ShareName $printer -Location "$loc"
        } Until (($i -eq 7) -Or ($error.Count -eq 0))
    }

    If ($error.Count -eq 0) {

        # Проверяем, существует ли доменная группа, соответствующая принтеру, если нет, создаем
        # $error.Clear()
        try {Get-ADGroup -Identity $groupName -Server $dc} catch {
        # If ($error.Count -gt 0) {
            New-ADGroup -Name $groupName -GroupScope Universal -Path $LDAPgrp -Description $grpdescr -Server $dc
        }
        # SID группы потребуется для формирования XML
        $sid = (Get-AdGroup -Identity $groupname -Server $dc).sid.value

        # Читаем в GPO существующую XNL-запись, чтобы выяснить, какие принтеры уже назначены политикой
        $prnxml = $gpopath + "\Machine\Preferences\Printers\printers.xml"
        If (Test-Path -Path $prnxml) {
            $PrnLocalNames = ([xml](get-content $prnxml -Raw)).Printers.PortPrinter.Properties.localName
        } else {
            $PrnLocalNames = @()
        }

        # Если принтер новый, то добавляем информацию о принтере в XML-структуру
        If ($PrnLocalNames.Contains($printer)) {
            Write-Host "Принтер "$printer" уже назначен политикой!"
        } else {
            $writeXML = $true
            $currentDate = Get-Date -Format "yyyy-MM-dd hh-mm-ss"
            $uid = ([guid]::NewGuid()).ToString()

            $txt += "<PortPrinter clsid=""{C3A739D2-4A44-401e-9F9D-88E5E77DFB3E}"" name=""$ip"" status=""$ip"" image=""0"" changed=""$currentDate"" uid=""{$uid}"" bypassErrors=""1""><Properties ipAddress=""$ip"" action=""C"" location=""$loc"" localName=""$printer"" comment="""" default=""0"" skipLocal=""0"" useDNS=""0"" useIPv6=""0"" path=""\\$prnsrv\$printer"" deleteAll=""0"" lprQueue="""" snmpCommunity=""public"" protocol=""PROTOCOL_RAWTCP_TYPE"" portNumber=""9100"" doubleSpool=""0"" snmpEnabled=""0"" snmpDevIndex=""1""/><Filters><FilterGroup bool=""AND"" not=""0"" name=""$NetBiosDomain\$groupName"" sid=""$sid"" userContext=""0"" primaryGroup=""0"" localGroup=""0""/></Filters></PortPrinter>`r`n"
        }

    } else {
        Write-Host "Ошибка, принтер"$printer" не создан!"
    }
}

# Если предстоит добавление принтеров, то сохраняем подготовленный XML в файле для импорта...
If ($writeXML) {
    If (-Not (Test-Path -Path $outFolder"\"$gpName)) {
        New-Item -Path $outFolder"\"$gpName -ItemType "directory"
    }
    $txt += "</Printers>`r`n"
    $txt | Out-File -FilePath $outFolder"\"$gpName"\Printers.xml" -Encoding utf8

# ...и напоминаем, что файл следует импортировать в политику вручную    
    Write-Host "`r`nТеперь следует импортировать файл "$outFolder"\"$gpName"\Printers.xml в объект групповых политик"
    Write-Host $gpName" в раздел Computer Configuration/Preferences/Control Panel Settings/Printers"
}

Архив со сценарием и примерами конфигурационных файлов можно загрузить отсюда. Для запуска сценария следует запустить PowerShell от имени администратора и с правами администратора домена и ввести в командной строке

<путь к сценарию>\printinst.ps1 <путь к конфигурационному файлу>\<имя файла>.csv

Работа сценария начинается с разбора текстового конфигурационного файла, содержащего всю необходимую входную информацию, поэтому стоит подробно остановиться на структуре и подготовке конфигурационного файла. Он текстовый, с разделителями «точка с запятой», поэтому его можно редактировать непосредственно, однако более удобно добавлять информацию по принтерам в таблице Excel, а затем выгрузить таблицу в формате CSV. Пример заполнения таблицы показан на скриншоте.

Таблица состоит из общего раздела и списка принтеров. Перечислим назначение каждого из полей.

Домен-контроллер — FQDN любого writable домен-контроллера. Имя домена извлекается сценарием из FQDN-имени. Именно в этом домене будут созданы группы и объект групповых политик. Важно именно указание определенного контроллера домена, а не общего имени домена, чтобы исключить ошибки из-за задержек репликации создаваемых объектов Active Directory, Поскольку в сценарии выполняются обращения к только что созданным объектам в AD, при работе с выделенным домен-контроллером задержки минимальны.

Принт-сервер — FQDN-имя сервера (Windows Server) с установленной ролью сервера печати, в конфигурации которого будут созданы принтеры. Принт-сервер и домен-контроллер даже могут располагаться в разных доменах.

Папка для XML — папка, в которой будет создан XML-файл для импорта в Group Policy Preferences.

Код филиала — любая комбинация букв, помогающая идентифицировать расположение. Данный код будет добавляться в качестве префикса при создании групп для установки принтеров, а также объекта групповых политик.

OU групп — LDAP-путь к организационной единице, где будут созданы группы для инсталляции принтеров. Для установки на клиентский компьютер того или иного принтера необходимо включить объект компьютера в соответствующую группу. Инсталляция будет выполнена через механизм Group Policy Preferences. Обратите внимание, что в представленной версии сценария установка сетевых принтеров происходит в контексте компьютера.

OU компьютеров — LDAP-путь к организационной единице, содержащей объекты компьютеров. К ней будет прилинкован объект групповых политик, создаваемый для инсталляции принтеров через Group Policy Preferences.

Перечисленные имена полей не должны меняться (если не править сценарий), поскольку по ним выполняется поиск соответствующих значений в конфигурационном файле.

Следующий раздел представляет собой список принтеров для установки. Он может включать как новые, так и уже установленные принтеры. Ранее установленные принтеры можно не удалять из списка — они не будут устанавливаться повторно. Для каждого принтера следует заполнить 4 параметра: имя принтера, которое также будет его сетевым именем, IP-адрес порта, драйвер и расположение.

Сетевому порту принтера можно было бы назначить имя в DNS и обращаться к нему по имени. Однако для упрощения конфигурации и с учетом того, что к порту принтера будет обращаться только принт-сервер, было решено не регистрировать имена портов в DNS. Наименование драйвера в списке должно в точности совпадать с аналогичным именем в свойствах Сервера печати на закладке Drivers. Заполнение таблицы в Excel позволяет исключить опечатки при заполнении данного поля. Для этого следует создать на отдельном листе список наименований драйверов, установленных на принт-сервере, объявить данный список именованным диапазоном и наложить условие на вводимые наименования драйверов, так чтобы их можно было выбирать только из указанного диапазона (Данные — Проверка данных — Проверка вводимых значений — Параметры). В поле Расположение следует добавлять информацию, связанную с точным местоположением принтера. Она будет появляться в описании принтера.

Подготовленную таким образом таблицу следует экспортировать в текстовом формате.

CSV-файл будет иметь вид, примерно как на скриншоте.

В принципе, он и в таком виде поддается редактированию, однако более правильно вносить все изменения в конфигурацию в таблице Excel.

Сценарий printinst.ps1 рассчитан на повторные запуски. Если какие-либо объекты в AD, порты или принтеры были созданы ранее, они не будут создаваться повторно. В процессе работы сценария могут появляться сообщения об ошибках (красного цвета). Они не обязательно означают ошибки в выполнении сценария, поскольку обрабатываются, но их вывод не подавляется. Сценарий не удаляет ранее созданные принтеры и объекты в AD. Результатом работы сценария, в числе прочего, будет XML-файл, который следует импортировать в Group Policy Preferences. В XML-файле будет содержаться конфигурационная информация только что установленных принтеров, для которых нужно создать предпочтения в объекте групповых политик в разделе Computer Configuration/Preferences/Control Panel Settings/Printers. Ранее созданные предпочтения  (для уже установленных принтеров) затронуты не будут.

Чтобы импортировать XML-файл, в Проводнике откройте папку, в которой он был создан, щелкните по файлу правой кнопкой мыши и выберите в контекстном меню пункт Copy. Затем откройте на редактирование объект групповых политик, в котором должны быть назначены принтеры. Напомню, после отработки сценария он будет иметь имя <Код филиала> Printers GPO и будет прилинкован к OU, путь к которому указан в поле OU компьютеров конфигурационного файла. Далее перейдите в раздел Computer Configuration/Preferences/Control Panel Settings/Printers, щелкните по пустому полю правой кнопкой мыши и выберите пункт меню Paste.

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

Представленное решение позволяет тиражировать типовую конфигурацию печати для множества удаленных и региональных офисов. И даже при наличии всего одного сервера печати решение оказывается эффективным, поскольку обеспечивает соответствие принятым стандартам, соблюдение регламентов и простое восстановление конфигурации печати в случае сбоев. Да и подход Infrastructure as Code больше не кажется чем-то заОблачным — как видим, он легко реализуется даже на простых земных инфраструктурах. 🙂

Реклама
Добавить комментарий

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google photo

Для комментария используется ваша учётная запись Google. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s

%d такие блоггеры, как: