Now that we’ve created the gold server and version control repositories, it’s time to set up some core services. Since Microsoft Active Directory, DNS, and DHCP are common in the sorts of environments I’m trying to emulate, I’ll use Windows Server Core to set those up here, too. If I was focusing on Windows systems administration, I’d set up multiple domain controllers, multiple DHCP servers, and separate all the critical services. But since I’m focusing on Linux administration and configuration management, I’ll start with Active Directory and DHCP on one virtual machine. If I need to scale out in the future, I can adjust as needed.
I’ll use this as a proof of concept for using Puppet Bolt as an ad hoc change tool on the gold server. This will let me keep the scripts for creating an AD domain, managing DNS records, and managing DHCP options and reservations in version control. It will also let me run those scripts from the gold server, without requiring PowerShell on Linux, Puppet Agent on Windows, or ssh on Windows.
Creating the Domain Controller Virtual Machine
Review the posts on installing pfSense and installing a management virtual machine if necessary, but we’ll be creating a VM for Windows Server.
Create a new virtual machine connected to the lab network.
I named mine dc1.blab.renf.ro
.
For Server Core 2016, I used:
- 2 CPUs
- 4 GB RAM
- 40 GB thin-provisioned hard disk
Installing Server Core
In the Windows Server 2016 installation process, a traditional server with a graphical user interface is labeled as “(Desktop Experience)”, and Server Core installations are marked with no additional labels.
On first boot, Windows will need you to set an administrator password.
Do so, and you’ll be at an administrative command prompt.
Run sconfig
to set the following:
- Computer Name:
dc1
- Windows Update Settings: Automatic
- Network Settings:
- static IP address: 192.168.1.3
- subnet mask: 255.255.255.0
- gateway: 192.168.1.1
- Primary DNS server: 192.168.1.1
- Alternate DNS server: none
- Download and Install Updates: All updates
Installing all the updates will typically require a reboot, which would also cause the new computer name to take effect. There may also be additional rounds of available updates.
Post-Installation Tasks
Installing Bolt and Configuring a Bolt Project
Bolt is Puppet’s agentless orchestration tool, similar to Red Hat’s Ansible. Since Bolt uses Windows Remote Management (WinRM) for communication, and Server Core 2016 enables WinRM by default, there’s nothing additional to do on the Server Core console to prepare for using Bolt.
On the gold server, I followed the Installing Bolt documentation, specifically:
yum install https://yum.puppet.com/puppet-tools-release-el-7.noarch.rpm yum install puppet-bolt
I created a bolt repository in the /opt/gitrepos
folder, cloned it to a folder in root’s home directory, and created a Boltdir
to hold the bolt project data:
git init --bare --shared=umask /opt/gitrepos/bolt.git git clone /opt/gitrepos/bolt.git ~/bolt mkdir ~/bolt/Boltdir
Creating an Active Directory Domain with Bolt
To make a basic Active Directory domain without Bolt, I would start PowerShell on the domain controller virtual machine’s console and run:
install-windowsfeature ad-domain-services install-addsforest -domainname ad.blab.renf.ro -installdns
I would then enter the Safe Mode Administrator password twice when prompted, then let the server reboot.
With Bolt, I could run those commands with bolt command run
, but I’d rather build a PowerShell script and an associated Bolt task to automate things.
I’d also like to create an inventory file to avoid having to specify IP addresses, usernames, protocols, and other connection details each time I run Bolt.
Creating a Bolt Inventory
For the inventory file, I created a Boltdir/inventory.yaml
file with the contents:
groups: - name: windows groups: - name: dc targets: - 192.168.1.3 config: transport: winrm winrm: ssl: false user: Administrator password: _plugin: prompt message: Password
This will let me run things on the domain controller by specifying the --target
parameter for bolt
.
Once DNS is up and running, I can replace the IP address with a hostname.
Once the inventory is created, I can test it by running a built-in task with:
bolt task run reboot::last_boot_time --targets dc
and seeing the domain controller’s last boot time.
Creating Bolt Tasks
To make my own tasks for creating the Active Directory domain, I created a directory structure for the task:
mkdir ~/bolt/Boltdir/site-modules/renfro/tasks cd ~/bolt/Boltdir/site-modules/renfro/tasks
and then created a JSON file and a PowerShell script for each task.
The PowerShell script (create_ad_domain.ps1
) is like any oher PowerShell script developed and run on Windows, it’s just stored in the Bolt directory tree:
[CmdletBinding(PositionalBinding=$false)] param ( [Parameter(Mandatory=$true)] [string] $domainname, [Parameter(Mandatory=$true)] [string] $safemodepassword ) try { install-windowsfeature ad-domain-services } catch { Write-Output "Cannot install ad-domain-services windows feature" exit 1 } try { $securepassword = ConvertTo-SecureString $safemodepassword ` -AsPlainText -Force Install-ADDSForest -DomainName $domainname -InstallDns ` -SafeModeAdministratorPassword $securepassword -Force } catch { exit 2 }
The JSON metadata file (create_ad_domain.json
) contains:
{ "puppet_task_version": 1, "supports_noop": false, "description": "Create Active Directory domain.", "parameters": { "domainname": { "description": "The Active Directory domain name.", "type": "Variant[String]" }, "safemodepassword": { "description": "The Active Directory Safe Mode administrator password.", "type": "Variant[String]" } } }
Consider Running the Bolt Task to Create the Active Directory Domain, but Don’t
Once both files are created, the task could be run from any directory in the bolt project:
bolt task run --targets dc renfro::create_ad_domain domainname=ad.blab.renf.ro safemodepassword=ENTER_PASSWORD_HERE
But we’d like a way to not have a password recorded in the command history. We could get around that by disabling the bash history or deleting specific entries, but it would be better to prompt for the password instead.
We can’t have the PowerShell script prompt for input, and tasks are limited in how they can interact with users, so we have to go one step farther and set up a Bolt plan.
Writing a Bolt Plan Wrapper Around the Active Directory Domain Creation Task
Plans are typically used for higher-level orchestration across tasks and hosts, but we’ll use it here because it has a prompt function that can hide sensitive input like passwords. Sensitive results from the prompt command can be converted back to plain text for running tasks using the unwrap method.
The plan wrapper is pretty short, just enough to accept one parameter from the command line, prompt for another parameter, and call the task.
In Boltdir/site-modules/renfro/plans/create_ad_domain.pp
, add the following:
plan renfro::create_ad_domain( TargetSpec $dc, String $domain, ) { $safemodepassword = prompt('Enter the safe-mode administrator password', 'sensitive' => true).unwrap run_task('renfro::create_ad_domain', $dc, domainname => $domain, safemodepassword => $safemodepassword) }
Run this plan with:
bolt plan run renfro::create_ad_domain dc=192.168.1.3 domain=ad.blab.renf.ro
and you’ll be prompted for the administrator password for the domain controller (with a prompt of Password:
), and prompted for the safe-mode administrator password for Active Directory (with a prompt of Enter the safe-mode administrator password:
).
It’ll take a while for this task to complete.
If you have a view of the domain controller’s console, you’ll see it reboot after the task completes.
Testing the Active Directory Domain
Once the domain controller has rebooted, verify that Active Directory is functional by running:
bolt command run "Get-Aduser Administrator" --target dc bolt command run "Get-Adcomputer dc1" --target dc
from any directory in the bolt project. Both commands should return objects with distinguished names and object classes.