Tag Archives: Joining A Windows Domain

Puppet – Creating A Domain Join Declaration For All Windows Machines

Puppet – Creating A Domain Join Declaration For All Windows Machines

Introduction

Puppet is a Configuration Management tool that is used for deploying, configuring and managing servers. Puppet uses a Master Slave architecture in which the Master and Slave communicate through a secure encrypted channel with the help of SSL.

This article explains how to configure a Puppet manifest to join all Windows agents to a Windows Domain using the domain_membership module by trinkin available at https://forge.puppet.com/trlinkin/domain_membership. The puppet code uses facts from the agent to assess if the machine is a Windows machine or not before applying the class declaration.

Test Environment

For this article I’ll be using an already deployed Puppet Master server on CentOS which has a very basic manifest configured to Domain Join any Windows machines when the puppet agent contacts the server.

Active Directory is also configured in the environment with DNS on a server named Lab-Lokse (The reason I mention it’s name will come in to play later).

The Domain Controller was deployed using Puppet also by following the article below which also provides the steps to encrypt a password using hiera: https://geekdudes.wordpress.com/2018/11/01/installing-domain-controller-using-puppet/

Installing The Domain Membership Module

To implement the domain membership module perform the following steps:

  • Logon to the Puppet Master
  • Install the module by executing the following
puppet module install trlinkin-domain_membership --version 1.1.2
  • The module will also install the modules it depends upon
  • Once installed confirm that module and it’s dependencies are installed (Highlighted in bold below) by executing puppet module list:
/etc/puppetlabs/code/environments/production/modules
├── jriviere-windows_ad (v0.3.2)
├── puppet-download_file (v3.2.0)
├── puppet-windows_env (v3.2.0)
├── puppet-windowsfeature (v3.2.2)
├── puppetlabs-acl (v2.1.0)
├── puppetlabs-chocolatey (v3.3.0)
├── puppetlabs-dsc (v1.9.4)
├── puppetlabs-iis (v4.5.1)
├── puppetlabs-powershell (v2.3.0)
├── puppetlabs-reboot (v2.4.0)
├── puppetlabs-registry (v2.1.0)
├── puppetlabs-stdlib (v5.2.0)
├── puppetlabs-windows (v6.1.0)
├── puppetlabs-wsus_client (v1.1.0)
└── trlinkin-domain_membership (v1.1.2)

Adding The Domain Join Resource Declaration

Once the domain membership module is installed the next step is to edit the puppet manifest and add the domain join resource declaration. I added it at the top of my manifest for convenience after a default node declaration. I also learned recently that the ordering setting in puppet.conf has been depreciated and it now defaults to use the order the declarations are listed in the manifest.

As my Puppet server is running on CentOS my manifest files are stored under /etc/puppetlabs/code/environment/production/manifests and I’m starting a new manifest file called lab-setup.pp.

To add the declaration to the puppet manifest perform the following steps:

  • Open the new manifest file (I use vi as I’m on CentOS)
  • Insert the following at the top of the new file:

#########################################
# Default node to catch undefined nodes
#########################################

node default {}
  • Next add in a lookup for the Domain Admin password which has been stored using hiera
#########################################
# Importing Domain Password from hiera
#########################################
$adminpassword = lookup('windowspassword')
  • Using a lookup for the Domain Admin password is useful as it means we don’t have to hard code it into the manifest !
  • Next add the puppet code and class declaration below to the manifest:
#########################################
# Domain join logic for Windows servers
#########################################
if $facts['os']['family'] == 'windows' {
   if $facts['networking']['hostname'] == 'Lab-Lokse' {
      }
   else {
      class { 'domain_membership': 
         domain => 'lab.lost-it.org',
         username => 'Administrator', 
         password => $adminpassword, 
         join_options => '3',
         } 
      }
   }
}
  • Save and exit the manifest file

Puppet Code Explanation

Below is a breakdown of the Domain Join resource declaration piece by piece to show what means what and why it’s being used:

  1. node default {} – Not strictly necessary but without it my Foreman reports show errors for any deployed servers which haven’t been added to the manifest yet.
  2. $adminpassword = lookup(‘windowspassword’) – Used to lookup the encrypted Domain Admin password and store it as a variable in the manifest for later use.
  3. if $facts[‘os’][‘family’] == ‘windows’ { – This checks the facts reported from the agent and if it’s OS Family matches windows (Lower case was important in my case) it continues to execute the puppet code.
  4. if $facts[‘networking’][‘hostname’] == ‘Lab-Lokse’ { – I added this second piece of logic so that if the agent is my Domain Controller it doesn’t attempt to join it. Again, not strictly necessary but stopped any errors in logs and reports to Foreman !
  5. class { ‘domain_membership’: – Invokes the domain_membership class from the domain_membership module.
  6. username => ‘Administrator’, – The username to use when joining the Domain. For the sake of ease I used the Domain Administrator account but this could be changed to a restricted account for security.
  7. password => $adminpassword, – Passes the hiera encrypted Domain Administrator password to the module using the $adminpassword variable.
  8. join_options => ‘3’, – Joins the computer to the Domain and creates the computer object.

Final Thoughts

Now when I deploy out my Windows sysprep’d image with the puppet agent installed it contacts the Puppet master, joins the Domain and reboots without me needing to do anything !

My next task is to make it more corporate friendly and change the logic when checking if the server is a Domain Controller using a naming convention like Lab-DC0x so it can handle multiple Domain Controllers. I’ve already done it in a manifest to match nodes to a naming convention for different roles in my lab so fingers crossed it should be too difficult.

Tested On

This simple piece of puppet code has been tested successfully on the following Operating Systems:

  • CentOS 7 (Tested to ensure a non Windows server ignores the logic
  • Windows Server 2012 R2
  • Windows Server 2012 R2 Core
  • Windows Server 2016
  • Windows Server 2016 Core
  • Windows Server 2019
  • Windows Server 2019 Core