Create a new Windows Domain with Ansible using the win_powershell module

In this entry, we are going to use Ansible to manage a Windows host. To create a control node, the machine that is going to run Ansible, you have to use a UNIX-like system. This opens the possibility to use the Windows Subsystem for Linux if an independent control node is not an option for you. In our case, we will use a Debian GNU/Linux 11 (bullseye) machine as the control node and a Windows Server 2019 Standard as the host. In the control node we have the following relevant packages installed:

  • ansible [core 2.12.6]
  • Python 3.9.2
  • pip 22.1.2
  • pywinrm-0.4.3

The first thing is to create the inventory file which is going to contain all the information Ansible needs to successfully connect to the Windows hosts:

andrey@debian:~$ vi inventory.yml

Then, we will add the following variables:

---
Windows2019:
 hosts:
  192.168.100.130
 vars:
  ansible_connection: winrm
  ansible_user: Administrator
  ansible_password: P@ssword!
  ansible_winrm_server_cert_validation: ignore
  ansible_winrm_transport: basic
...

YML files follow a certain pattern that can be a bit tricky if a proper structure is not followed. For that I am currently using the Red Hat extension for “Ansible language support” that is available for Visual Studio Code. The standard hosts file where the Ansible inventory lives by default follows a different syntax (INI like one). In this case, we chose to use YML for the inventory. By default, Windows uses winrm to exchange management data with any computer, in this case with our Ansible control node. Let us quickly review each element of the inventory.yml file:

  • The first three lines provide the information to identify the host.
  • ansible_connection: refers to the connection type to host. Since we are going to connect to a Windows host, we are using winrm (keep in mind is also possible to use SSH if it is installed on the host).
  • ansible_user and ansible_pasword are the credentials used to connect to the host. In this case, we used the local admin account that has permissions to use the Remote Management Service. Keep in mind if that if you don’t want to store credentials in plain text, the ansible-vault command provides a solution for those cases.
  • ansible_winrm_server_cert_validation: determines the certification validation mode. In our case, this must be set to ignore since we do not have a verifiable certificate available.

To use the winrm service, we also need pywinrm installed on our ansible control node. In Ansible [core 2.12.6], the standard installation contains the ansible.windows.win_powershell module that you can search by running “ansible-doc -l | grep ansible.windows.win_powershell”. This module is not part of older versions, but in that case you may have win_shell and win_command available to accomplish what we are trying to do here.

To configure the Windows host, the easiest way is to run the .ps1 script mentioned in the documentation. Once that’s covered, you should be good to go. If you experience issues, check the documentation about it. To check if everything is working, we can call the win_ping module:

Succesful result of “ansible -i inventory.yml Windows2019 -m win_ping”

In this example the -i switch tells ansible to use a different inventory and not the default one. Windows2019 is just the name we used in the inventory to refer to our Windows host. The -m is to call the win_ping module.

A playbook to create a domain and install AD services

Playbooks are like recipes that Ansible can follow, and they can also be created with the YML syntax. Since we are using the PS module, Ansible is just a way to carry the PowerShell commands for us. With that in mind, we need to do two things on the Windows host: the first one is to install AD-Domain-Services and then, we need to create the domain. So, let us create a playbook with a task for each thing we have to do:

---
- name: Create a new domain and new domain controller
  hosts: Windows2019 

  tasks:
  - name: Install AD-Domain-Services
    ansible.windows.win_powershell:
      script: |
       Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools

  - name: Creating andreypicado.com
    ansible.windows.win_powershell:
      script: |

       $domainProperties = @{
        DomainName           = "andreypicado.com"
        DomainNetbiosName    = "ANDREYPICADO"
        ForestMode           = "Win2012R2"
        DomainMode           = "Win2012R2"
        CreateDnsDelegation  = $false
        InstallDns           = $true
        Force                = $true
       }

       $SafeModePass = ConvertTo-SecureString "P@ssword01!!!" -AsPlainText -Force
       Install-ADDSForest @domainProperties -SafeModeAdministratorPassword $SafeModePass 
...

First we specify the host for the playbook (line 3). Then one task installs the domain services and the other creates the domain with the details and passes them as an array to the Install-ADDSForest cmdlet. This is a basic configuration, but take a look a the documentation for Install-ADDSForest and Install-WindowsFeature to see what options are available.

To run the playbook we just created with the custom inventory file, we can use:

ansible-playbook -i inventory.yml create_domain.yml

Ansible will take care of the rest and it will show the progress for each task:

Successful run of the create_domain.yml playbook

At the end, the host computer is going to restart (that can be turned off with the Install-ADDSForest options) and after that we should be able to see the domain login screen:

Then we can check the computer is part of the Domain Controller OU and see if everything is working. Since we did not create an administrator account for the domain, we still rely on the local administrator account and that could cause some issues. Is a good a idea to add a domain admin user as part of the domain controller installation.

Domain Controllers OU inside AD Users and Computer in the Windows host.
| Theme: UPortfolio