Why choose Ansible? Are there any other tools for Windows Server Configuration Management? Probably, but the Red Hat software is ageless, idempotent and with YAML-based automation syntax which is quick to learn. There is no need for a more complex solution. This is automation of Windows Servers, not rocket science.
Setting up connection with Windows Server
If you have prior experience with Ansible or if you have read our beginner’s guide, you will know that the connectivity between Ansible controller and the hosts is made via SSH. However, when dealing with Windows servers, we should use something called WINRM. Yeah, there is OpenSSH for Windows, but WinRM is preinstalled on every Windows Server.
Also, we have to use PowerShell for this automation and modules for Windows, but first let’s connect a Linux controller with Windows Server 2019.
Windows Server 2019 Setup:
- First we have to make sure that the WinRm is running on the Server:
PS C:\Users\itgix\Downloads> winrm quickconfig
WinRM service is already running on this machine.
WinRM is already set up for remote management on this computer.
If it’s not running you can always enable it with:
PS C:\Users\itgix\Downloads> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-RemoteManagement-Client
2. Next we can see if there are any Listeners configured to the Server:
PS C:\Users\itgix\Downloads> winrm enumerate winrm/config/Listener
Listener
Address = *
Transport = HTTP
Port = 5985
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 10.240.1.100, 127.0.0.1, ::1, fe80::9745:976c:b3e1:74bc%2
This should be the default Listener configuration. You can always Set Up a new Listener. Also there is something we should point out. You can see the example that the listener is configured on port 5985, this is not a secure port (it’s not using SSL certificate). The secure WinRM port is 5986, but you’ll have to have and set up an SSL certificate to perform this type of connection.
If you have a certificate you can configure it by following this guide from Microsoft. However our focus is not the security in this case, but we highly recommend caution when ssl is not used.
3. We need a dedicated Windows user to perform the tasks. In Ansible, we always have a user that performs the tasks that the controller sends. In that Windows case the user should be Administrator. Every admin user can be used, but normally when we use Ansible, we use a dedicated user. That’s just best practice.
4. Install Chocolatey
This will be needed so the Ansible module will use chocolatey package manager to download stuff:
choco install --package-parameters=/SSHServerFeature openssh
Linux Set Up:
As everything has to be user friendly at the end, the Ansible playbook will be runned by a GitLab Pipeline, so it will be executed on a GitLab runner. The one we have is running on a Linux container, more specifically Alpine Container.
- Check the Windows Server connection first.
As Linux is going to connect to another virtual machine, first we have to make sure that they can connect to each other (check port availability).
Our Windows Server is located in Azure Cloud, so we need to make an inbound port rule to access port 5985 / or 5986
If it’s in another Cloud the concept is the same: AWS – Security Group inbound rule, GCP firewall rule, etc.
If you’re using a virtual machine on your own server or local hypervisor, you should create a firewall rule to open the exact port.
After that we can check availability of the port from the Alpine Container:
# apk add net-tools
# telnet /WINDOWS IP/ 5985
Connected to /WINDOWS IP/
2. Install the needed dependencies:
# apk add python3 py3-pip gcc libffi-dev ansible
# pip3 install "pywinrm>=0.3.0"
3. Set up the inventory file
Now for the project we have to set up Ansible inventory (host) file and needed variables for the connection (port, user, password):
[win]
{ip addresses for the windows vms (one per row)}
[win:vars]
ansible_port=5985
ansible_user={Windows Admin User}
ansible_password={Password}
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
Check hosts:
# ansible -i hosts all --list-hosts
hosts (2):
windows1
windows2
Create Ansible Playbook
Now is the time to do the configuration part of building an Ansible pipeline. Something simple can look like this:
---
- name: Set Up Automation of Windows Server 2019
hosts: win
tasks:
# installing dependencies and packages
- name: Install C#
win_chocolatey:
name: "{{ item }}"
state: present
loop:
- dotnet
- name: Install Firefox using Chocolatey
win_chocolatey:
name: firefox
state: present
- name: Install Notepad++ using Chocolatey
win_chocolatey:
name: notepadplusplus
version: 8.5.2
state: present
- name: Install Prometheus Windows Exporter using Chocolatey
win_chocolatey:
name: prometheus-windows-exporter.install
state: present
This Ansible Playbook is installing some packages and dependencies via win_chocolatey.
So to test Ansible playbook run:
# ansible-playbook -i hosts main.yml
PLAY [Set Up Automation of Windows Server 2019] ********************************
TASK [Gathering Facts] *********************************************************
changed: [windows1]
TASK [Install C#] **************************************************************
changed: [windows1] => (item=dotnet)
TASK [Install Firefox using Chocolatey] ****************************************
changed: [windows1]
TASK [Install Notepad++ using Chocolatey] **************************************
changed: [windows1]
TASK [Install Prometheus Windows Exporter using Chocolatey] ********************
changed: [windows1]
PLAY RECAP ******************************
20.56.144.225 : ok=5 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
As you can see, the dependencies are successfully installed on the Windows Server from the Alpine container. For the test it is used only one Windows VM – windows1.
GitLab Pipeline
One final step. We have to create a Pipeline to run the playbook from.
Here is an example of pipeline you can use:
default:
image:
name: registry.gitlab.com/gitlab-org/terraform-images/releases/1.4
entrypoint:
- "/usr/bin/env"
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
stages:
- windows-ansible-playbook
windows-ansible-start:
stage: windows-ansible-playbook
before_script:
- export ANSIBLE_PORT=${ANSIBLE_PORT}
- export ANSIBLE_USER=${ANSIBLE_USER}
- export ANSIBLE_PASSWORD=${ANSIBLE_PASSWORD}
- ansible --version
script:
- ansible-playbook -i hosts main.yml
when: manual
Then we can run the pipeline:
Conclusion
Automation is a goal for every DevOps Engineer. But this doesn’t need to be complex and everything to be automated at all costs. There are always the right tools for every situation and infrastructure.
This example of configuring Windows servers could be done with Server Templating tools like Packer for example. Depends on the situation and environment. Architecture is the key.