Коллеги, а также все, кто интересуется темой системного администрирования и автоматизацией, приветствую!
В этой статье мы будем автоматизировать виртуальное окружение для различных задач тестирования при помощи Vagrant и PowerShell DSC. В этой части мы начнем с минимального набора любой доменной инфраструктуры Windows. В конкретном случае песочница будет состоять из 2-х узлов: контроллер домена и хост-член домена.

А зачем оно вообще нужно?

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

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

Иными словами — если появилась задача смоделировать работу реальных компонентов инфрастуктуры, перед нами встает первый вопрос:

Как это сделать?

Хорошо если необходимо проверить работу какого-нибудь компонента или ПО на одной виртуальной машине, но что делать, если для этого требуются несколько (напр. сложная топология Active Directory)?

Поняв, сколько ручных действий придется для этого выполнить возникает следующий вопрос:

Как это автоматизировать?

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

Стек используемых технологий и скучная терминология

Для начала разберемся с набором ПО и необходимой терминологией которые понадобятся для реализации нашей задумки.

  • Поставщик виртуализации — ПО или средства реализующие функционал виртуализации (напр. VirtualBox, VmWare, Hyper-V).
  • VirtualBox — один из примеров поставщика технологии виртуализации. Благодаря этому компоненту, мы сможем создавать и конфигурировать виртуальные машины. В нашей статье мы используем в качестве поставщика виртуализации именно VirtualBox. В зависимости от провайдера, настройки файла конфигурации Vagrant могут отличаться. Чтобы избежать этой ситуации, а также для универсальности и кроссплатформенности (можно использовать как на хостах Windows, так и на Linux), выбор был сделан в пользу VirtualBox. Конечно, никто не мешает использовать любой другой провайдер.
  • Vagrant — компонент, позволяющий автоматизировать конфигурацию виртуальных машин. Слой или уровень, позволяющий конфигурировать виртуальные машины в едином стиле.
  • PowerShell DSC (Desired State Configuration) — средство автоматизации задач, в основе которого лежит декларативная парадигма (декларативный подход). Иными словами, мы описываем состояние, которое хотим получить, но не описываем процесс получения этого состояния. В процессе выполнения сценариев DSC происходит магия 🙂
  • Ресурсы DSC — специальные модули позволяющие конфигурировать систему.

Установка необходимого ПО на хост Windows

Автоматическая установка

Существует отличная возможность автоматизировать процесс путем установки ПО через пакетный менеджер Chocolatey.

  • Windows 7+ / Windows Server 2003+
  • PowerShell v2+
  • .NET Framework 4+

Если требования к установке Chocolatey удовлетворены, то предлагаю открыть cmd.exe от имени администратора, вставить код ниже и чего-нибудь выпить, пока выполняется установка 🙂

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" && choco install -y vagrant & choco install -y virtualbox

Ручная установка

Также нам никто не мешает выполнить установку в ручном режиме.

  • Скачиваем и устанавливаем VirtualBox
  • Скачиваем и устанавливаем Vagrant

Сам процесс установки ПО несложен, не будем останавливаться на этом.

Разворачиваем и автоматизируем песочницу

Небольшое отступление. Сначала выполним некоторые вещи без особых разбирательств, а уже потом разберем, что же мы там наворотили и почему оно работает или не работает 🙂
Начинаем.

Создаем проект нашей тестовой лаборатории и формируем нижеуказанную структуру папок и файлов, где:

  • LAB — название корневой папки проекта.
  • Vagrantfile — файл (подробнее дальше) с указаниями по разворачиванию виртуальных машин.
  • provision — папка, где будут лежать сценарии DSC (будут использоваться для конфигурации виртуальных машин).
  • Файлы с расширением .ps1 — непосредственно сценарии DSC для конфигурирования ОС.

Ниже определим назначение каждого сценария .ps1

  • ConfigureRebootOnNode.ps1 — конфигурирует перезапуск хоста, если это необходимо.
  • DSCInstallDHCP.ps1 — устанавливает роль DHCP, RSAT-DHCP, авторизует DHCP сервер в AD после установки и конфигурирует область.
  • DSCInstallDomain.ps1 — устанавливает роль AD DS, RSAT-ADDS, конфигурирует домен PARTY.HARD.
  • DSCJoinDomain.ps1 — добавляет хост в домен Active Directory.
  • PreDSCInstall.ps1 — устанавливает необходимые для работы модули DSC.

Ниже прикрепляю визуализацию структуры.

LAB
│ Vagrantfile

└───provision
ConfigureRebootOnNode.ps1
DSCInstallDHCP.ps1
DSCInstallDomain.ps1
DSCJoinDomain.ps1
PreDSCInstall.ps1

Копируем файл Vagrantfile в папку с проектом.

Vagrantfile

# -*- mode: ruby -*-
# # vi: set ft=ruby :

Vagrant.configure("2") do |config|	
	config.vagrant.plugins = "vagrant-reload"
	config.vm.define "lab-dc1" do |subconfig|
		subconfig.vm.box = "gusztavvargadr/windows-server"
		subconfig.vm.hostname = "lab-dc1"
		subconfig.vm.provider :virtualbox do |vb|
			vb.gui = false
		end
		subconfig.vm.network "private_network", ip: "192.168.11.2", 
			virtualbox__intnet: true
		subconfig.winrm.username = "vagrant"
		subconfig.winrm.password = "vagrant"
		subconfig.winrm.transport = :plaintext
		subconfig.winrm.basic_auth_only = true 
		#Install DSC Modules
		subconfig.vm.provision "shell",
			path: "provision\\PreDSCInstall.ps1"
		#https://github.com/dsccommunity/ActiveDirectoryDsc
		subconfig.vm.provision "shell",
			path: "provision\\DSCInstallDomain.ps1"
		#Restart VM to finish Active Directory Domain Services installation
		subconfig.vm.provision :reload
		#https://github.com/dsccommunity/xDhcpServer
		subconfig.vm.provision "shell",
			path: "provision\\DSCInstallDHCP.ps1"
	end
	  
	config.vm.define "lab-test" do |subconfig|
		subconfig.vm.box = "gusztavvargadr/windows-server"
		subconfig.vm.hostname = "lab-test"
		subconfig.vm.provider :virtualbox do |vb|
			vb.gui = false
		end
		subconfig.vm.network "private_network", ip: "127.0.0.2", 
			auto_config: false,
			virtualbox__intnet: true
		subconfig.winrm.username = "vagrant"
		subconfig.winrm.password = "vagrant"
		subconfig.winrm.transport = :plaintext
		subconfig.winrm.basic_auth_only = true
		subconfig.vm.provision "shell",
			path: "provision\\ConfigureRebootOnNode.ps1"		
		subconfig.vm.provision "shell",
			path: "provision\\PreDSCInstall.ps1"
		subconfig.vm.provision "shell",
			path: "provision\\DSCJoinDomain.ps1"
	end
end

Копируем файл PreDSCInstall.ps1 в папку provision

PreDSCInstall.ps1

Install-Module -Name ActiveDirectoryDsc -Force
Install-Module -Name PSDscResources -Force
Install-Module -Name ComputerManagementDsc -Force
Install-Module -Name xDhcpServer -Force

Копируем файл DSCInstallDomain.ps1 в папку provision

DSCInstallDomain.ps1

Configuration ADDomain_NewForest_Config
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential]
        $SafeModePassword
    )

    Import-DscResource -ModuleName PSDscResources
    Import-DscResource -ModuleName ActiveDirectoryDsc
    Import-DscResource -ModuleName ComputerManagementDsc

    Node 'localhost'
    {
        WindowsFeature 'Active Directory Domain Services'
        {
            Name   = 'AD-Domain-Services'
            Ensure = 'Present'
        }

        WindowsFeature 'RSAT-ADDS'
        {
            Name   = 'RSAT-ADDS'
            Ensure = 'Present'
        }
        
        ADDomain 'party.hard'
        {
            DomainName                    = 'party.hard'
            Credential                    = $Credential
            SafemodeAdministratorPassword = $SafeModePassword
            ForestMode                    = 'WinThreshold'
        }
    }
}

#Next block is using to allow password as plain text
$cd = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
         }
    )
}

#Define user and password for ADDomain deployment (also used for restore).
$password = ConvertTo-SecureString "RestorePassword123$" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('vagrant',$password)

#Create MOF
ADDomain_NewForest_Config -Credential $cred -SafeModePassword $cred -ConfigurationData $cd

#Execute MOF
Start-DscConfiguration -Path .\ADDomain_NewForest_Config -Force -Wait -Verbose

Копируем файл DSCInstallDHCP.ps1 в папку provision

Configuration xDhcpsServerScope_NewScope
{
    Import-DscResource -ModuleName xDHCpServer
    #Define NIC IP
    $IP = Get-NetIPAddress -InterfaceAlias "Ethernet 2" | Where-Object {$_.IPAddress -notlike "*:*" } | select -ExpandProperty IPAddress

    Node 'localhost'
    {
        WindowsFeature 'RSAT-DHCP'
        {
            Name   = 'RSAT-DHCP'
            Ensure = 'Present'
        }

        WindowsFeature 'DHCP'
        {
            Name   = 'DHCP'
            Ensure = 'Present'
        }
                
        xDhcpServerAuthorization RemoteServerActivation
        {
            Ensure = 'Present'
            DnsName = $env:COMPUTERNAME + '.party.hard'
            IPAddress = $IP
        }       
                  
        
        xDhcpServerScope Scope
        {
            ScopeId = '192.168.11.0'
            Ensure = 'Present'
            IPEndRange = '192.168.11.254'
            IPStartRange = '192.168.11.10'
            Name = '11.0/24'
            SubnetMask = '255.255.255.0'
            LeaseDuration = ((New-TimeSpan -Hours 8 ).ToString())
            State = 'Active'
            AddressFamily = 'IPv4'
        }     
        
        xDhcpServerOption Option
        {
            Ensure = 'Present'
            ScopeID = '192.168.11.0'
            DnsDomain = 'party.hard'
            DnsServerIPAddress = '192.168.11.2'
            AddressFamily = 'IPv4'
            Router = '192.168.11.1'
        }
    }
}

xDhcpsServerScope_NewScope
Start-DscConfiguration -Path .\xDhcpsServerScope_NewScope -Force -Wait -Verbose

Копируем файл DSCJoinDomain.ps1 в папку provision

DSCJoinDomain.ps1

Configuration JoinDomainConfiguration
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullorEmpty()]
        [System.Management.Automation.PSCredential]
        $Credential
    )

    Import-DscResource -Module ComputerManagementDsc

    Node 'localhost'
    {
        Computer JoinDomain
        {
            Name       = $env:COMPUTERNAME
            DomainName = 'PARTY'
            Credential = $Credential # Credential to join to domain
        }
    }
}


#Next block is using to allow password as plain text
$cd = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
         }
    )
}

#Define user and password for ADDomain deployment (also used for restore).
$password = ConvertTo-SecureString "vagrant" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('party.hard\vagrant',$password)

#Create MOF
JoinDomainConfiguration -Credential $cred -ConfigurationData $cd

#Execute MOF
Start-DscConfiguration -Path .\JoinDomainConfiguration -Force -Wait -Verbose

Копируем файл ConfigureRebootOnNode.ps1 в папку provision

Configuration ConfigureRebootOnNode
{
    Node 'localhost'
    {
        LocalConfigurationManager
        {
            RebootNodeIfNeeded = $true
        }
    }
}

#Create MOF
ConfigureRebootOnNode
Set-DscLocalConfigurationManager .\ConfigureRebootOnNode -Verbose
Get-DscLocalConfigurationManager

Структура проекта сформирована, что дальше?

Мы с вами выполнили, пожалуй, самую сложную часть, поздравляю. Теперь, чтобы автоматически развернуть нашу небольшую тестовую лабораторию достаточно выполнить команду:

vagrant up

Все. Идем пить чай. В зависимости от производительности вашей машины и скорости интернет, возможно, придется выпить не одну кружку 🙂

По возвращению Вы обнаружите маленькое виртуальное окружение, которое состоит из двух узлов. Мы можем убедиться в этом выполнив команду:

vagrant status

Мы увидим список запущенных виртуальных машин

Current machine states:

lab-dc1                   running (virtualbox)
lab-test                  running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.

Либо, открыв менеджер управления виртуальными машинами VirtualBox. Мы увидим те же ВМ и тот же статус.

Что же случилось и что происходило под капотом?

Vagrant

Давайте поэтапно разберем что произошло. Вся ключевая логика и построчные комментарии описаны в файле Vagrantfile. Для удобства я добавлю комментарии к строкам на русском языке. Вернемся к файлу Vagrantfile и рассмотрим конфигурацию одной из машин.

config.vm.define "lab-dc1" do |subconfig|
    #Определяем готовый образ ОС для виртуальной машины. Vagrant скачает указанный образ из публичного репозитория (https://app.vagrantup.com/boxes/search)
    subconfig.vm.box = "gusztavvargadr/windows-server"
    #Определяем название хоста виртуальной машины
    subconfig.vm.hostname = "lab-dc1"
    #Определяем настройки провайдера виртуализации (необязательно)
    subconfig.vm.provider :virtualbox do |vb|
        #Запускам виртуальную машину в фоне
        vb.gui = false
    end
    #Определяем конфигурацию сети: тип адаптера, ip.
    subconfig.vm.network "private_network", ip: "192.168.11.2", 
        virtualbox__intnet: true
    #Определяем логин и пароль для подключения и дальнейшей конфигурации ВМ
    subconfig.winrm.username = "vagrant"
    subconfig.winrm.password = "vagrant"
    #Определяем параметры подключения.
    subconfig.winrm.transport = :plaintext
    subconfig.winrm.basic_auth_only = true 
    #Выполняем Powershell сценарий, который установит необходимые DSC модули
    subconfig.vm.provision "shell",
        path: "provision\\PreDSCInstall.ps1"
    #Выполняем Powershell DSC сценарий, который развернет службы AD
    #https://github.com/dsccommunity/ActiveDirectoryDsc
    subconfig.vm.provision "shell",
        path: "provision\\DSCInstallDomain.ps1"
    #Выполняем перезагрузку ВМ после инсталляции служб AD
    subconfig.vm.provision :reload
    #Выполняем Powershell DSC сценарий, который развернет и настроит службу DHCP
    #https://github.com/dsccommunity/xDhcpServer
    subconfig.vm.provision "shell",
        path: "provision\\DSCInstallDHCP.ps1"
end

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

Дополнительная литература

https://www.vagrantup.com/docs/
https://youtu.be/Q8baD4TS_l0

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

PowerShell DSC

В начале статьи уже упоминалось, что в отличии от привычных нам PowerShell сценариев, DSC использует декларативный подход. Обратите внимание, что мы не указываем как именно мы будем приводить систему к желаемому состоянию. Фактически нам необходимо только задекларировать (описать) желаемое состояние.

Также в начале статьи упоминалось понятие — ресурсы Powershell. Для технологии PowerShell DSC, понятие ресурсов является ключевым. Именно благодаря ним определяется и выполняется конфигурация. Существуют как стандартные ресурсы (входящие в систему), так и пользовательские (импортируются отдельно).

Вы, возможно, обратили внимание, что в некоторых сценариях импортируются ресурсы. Подобные как раз являются примером пользовательских (расширяемых) ресурсов, которые были скачаны с репозитория github сообщества dsccommunity.

Для примера возьмем DSCInstallDHCP.ps1, который устанавливает и конфигурирует DHCP.
Также, для удобства, я прокомментирую строки на русском языке.

#Декларируем название конфигурации
Configuration xDhcpsServerScope_NewScope
{
    #Импортируем необходимые ресурсы
    Import-DscResource -ModuleName xDHCpServer
    #Декларируем хосты на которых будет применяться конфигурация
    Node 'localhost'
    {
        #Декларируем установку компонента RSAT-DHCP при помощи ресурса WindowsFeature (пример стандартного ресурса)
        WindowsFeature 'RSAT-DHCP'
        {
            #Декларируем необходимые имя и статус инсталляции компонента.
            Name   = 'RSAT-DHCP'
            Ensure = 'Present'
        }
        #Декларируем установку компонента DHCP при помощи ресурса WindowsFeature (пример стандартного ресурса)
        WindowsFeature 'DHCP'
        {
            #Декларируем необходимые имя и статус инсталляции компонента.
            Name   = 'DHCP'
            Ensure = 'Present'
        }
        #Декларируем необходимость авторизации сервера при помощи ресурса xDhcpServerAuthorization (пример пользовательского ресурса)    
        xDhcpServerAuthorization LocalServerActivation
        {
            Ensure = 'Present'
        }     
        #Декларируем необходимость создания области DHCP при помощи ресурса xDhcpServerScope (пример пользовательского ресурса)
        xDhcpServerScope Scope
        {
            #Декларируем настройки области
            ScopeId = '192.168.11.0'
            Ensure = 'Present'
            IPEndRange = '192.168.11.254'
            IPStartRange = '192.168.11.10'
            Name = '11.0/24'
            SubnetMask = '255.255.255.0'
            LeaseDuration = ((New-TimeSpan -Hours 8 ).ToString())
            State = 'Active'
            AddressFamily = 'IPv4'
        }     
        #Декларируем необходимость создания параметров области DHCP при помощи ресурса xDhcpServerOption (пример пользовательского ресурса)
        xDhcpServerOption Option
        {
            Ensure = 'Present'
            ScopeID = '192.168.11.0'
            DnsDomain = 'party.hard'
            DnsServerIPAddress = '192.168.11.2'
            AddressFamily = 'IPv4'
            Router = '192.168.11.1'
        }
    }
}
#Формируем MOF файл для применения конфигурации
xDhcpsServerScope_NewScope
#Применяем конфигурацию на хосте
Start-DscConfiguration -Path .\xDhcpsServerScope_NewScope -Force -Wait -Verbose

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

Дополнительная литература

https://channel9.msdn.com/Series/PowerShell-Desired-State-Configuration https://habr.com/ru/company/microsoft/blog/253497/

А оно точно работает?

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

Какого функционала мы ожидаем от песочницы?
Глобально, мы ожидаем два хоста в локальной сети 192.168.11.0/24. Один — контроллер домена PARTY.HARD, другой — член этого домена.

LAB-DC1 — 192.168.11.2

  • Active Directory Domain Services
  • AD RSAT
  • Конфигурация домена PARTY.HARD
  • DHCP
  • DHCP RSAT
  • Авторизация DHCP
  • Конфигурация области DHCP

LAB-TEST — 192.168.11.10 (выдано DHCP сервером)

  • Присоединение к домену PARTY.HARD

После развертывания домена, стала доступна доменная учетная запись Administrator со стандартным паролем vagrant. Попробую воспользоваться этой учеткой и зайти в систему используя VirtualBox Manager.

Я зашел в диспетчер серверов на контроллере домена. Как мы видим службы были инсталлированы.
Также я зашел в оснастку ADUC. Домен существует и хосты-члены находятся в соответствующих Organizational Units.
Похоже, что с DHCP также не возникло проблем. Мы видим, что область была добавлена.
В списке арендованных адресов мы можем увидеть нашу вторую ВМ.

Итоги

Похоже, что нам удалось развернуть виртуальное окружение. Различных сценариев применения можно придумать великое множество, все зависит от ваших задач, хотелок, размера оперативной памяти и скорости дисковой подсистемы 🙂

В следующей части статьи мы с вами попробуем увеличить размеры песочницы и расширить сценарии применения.
До встречи!

2 комментария к “Создаем автоматически разворачиваемую лабораторию Windows с помощью Vagrant и PowerShell DSC. Часть 1. AD.

  1. Приветствую!
    Отлично, спасибо за проделанную работу.

    Но у меня сходу не поднялось
    Хотя бы, куда оно не может распаковать образ?

    «`
    PS D:\Scripts\LAB> vagrant up
    Bringing machine ‘lab-dc1’ up with ‘virtualbox’ provider…
    Bringing machine ‘lab-test’ up with ‘virtualbox’ provider…
    ==> lab-dc1: Box ‘gusztavvargadr/windows-server’ could not be found. Attempting to find and install…
    lab-dc1: Box Provider: virtualbox
    lab-dc1: Box Version: >= 0
    ==> lab-dc1: Loading metadata for box ‘gusztavvargadr/windows-server’
    lab-dc1: URL: https://vagrantcloud.com/gusztavvargadr/windows-server
    ==> lab-dc1: Adding box ‘gusztavvargadr/windows-server’ (v1809.0.1912) for provider: virtualbox
    lab-dc1: Downloading: https://vagrantcloud.com/gusztavvargadr/boxes/windows-server/versions/1809.0.1912/providers/virtualbox.box
    lab-dc1: Download redirected to host: vagrantcloud-files-production.s3.amazonaws.com
    lab-dc1:
    The box failed to unpackage properly. Please verify that the box
    file you’re trying to add is not corrupted and that enough disk space
    is available and then try again.
    The output from attempting to unpackage (if any):

    x Vagrantfile
    x box.ovf
    x gusztavvargadr-ws2019s-1912.0.0-1578083647-disk001.vmdk: Write failed
    x metadata.json
    bsdtar.EXE: Error exit delayed from previous errors.

    1. Иван, приветствую Вас!

      Стандартное расположение для скачиваемых боксов для ОС Windows — C:\Users\USERNAME\vagrant.d\boxes.
      Его можно можно изменить путем добавления в среду пользователя или системы переменной с именем VAGRANT_HOME и указанием папки (имеет смысл размещать боксы на дисках побыстрее).
      Судя по вашему логу, очень похоже, что закончилось место на диске C.

      Для решения проблемы можно попробовать:
      1) Освободить место под боксы на диске C.
      2) Запустить cmd и ввести команду — setx VAGRANT_HOME «D:\LAB\boxes» (возможно потребуется дополнительно создать папку boxes по этому пути), перезапустите cmd и попробуйте выполнить команду vagrant up еще раз.
      Команда setx добавит переменную в среде пользователя. Если проблема действительно была в свободном месте и в новом расположении его будет достаточно, то процесс должен пойти дальше.

      Не могли бы Вы проверить это? Попробуем разобраться и добавим информацию в статью для наших коллег.
      Спасибо!

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *