‘No Windows, No Problem … What?’ | Altenar Explores How Ansible Allows Users To Manage Windows Infrastructure
Altenar, a sportsbook software provider, details in the below article, its experience with Ansible, Windows and modern stacks, to illuminate the insights it’s learned over the years that could be beneficial, if not utterly intriguing, to you.
Altenar, a sportsbook software provider, aims to create usable, informative and interactive content that can help bring the technically minded closer together.
As a prelude to this article, an Altenar spokesperson notes, “although we have been running a modern stack (k8s, helm, .net core, etc) in production for about four years, that’s not how it has always been. Our first version of Sportsbook was Windows-based (.NET + MSSQL), and we managed 100+ servers via Ansible.”
Adding, “No Windows, no problems.” - that is the answer I got by asking a guru of Ansible "How do you manage Windows?" on one of the local Ansible meetups.”
Continue reading ‘No Windows, No Problem … what?!’ to discover more from a sports betting industry leader…
Prepare Your Servers for Ansible
Unlike Linux, Windows requires you to prepare for Ansible to connect to it (more details available here and all the details are out of the scope of this article). However, there is one key point to stress here. The ConfigureRemotingForAnsible.ps1 script - that is pretty much what you need to run before you'll be able to connect to a VM via Ansible.
It's worth mentioning that we, Altenar, use Terraform for VM provisioning and this is where run_once_command_list becomes very handy. We saved ConfigureRemotingForAnsible.ps1 on the C:/ of the base VM template and used the following option in Terraform:
resource "vsphere_virtual_machine" "vm" {
# ... other configuration ...
clone {
# ... other configuration ...
customize {
# ... other configuration ...
windows_options {
computer_name = "${lower(var.envname)}${var.vmname}${format("%03d", count.index)}"
organization_name = "Altenar"
join_domain = "${var.domain}"
domain_admin_user = "${var.domain_user}"
domain_admin_password = "${var.domain_password}"
admin_password = "${var.admin_password}"
auto_logon = true
run_once_command_list = [
"powershell.exe -version 4 -ExecutionPolicy Bypass -File C:\\ConfigureRemotingForAnsible.ps1",
]
}
}
}
}
Once a new server is provisioned it is ready to be set up by Ansible according to the role we assign to the server.
How To Set Up Ansible
From the Ansible side, a few initial preparations are also required.
Step One: Make sure all Windows servers belong to the "Windows" group in your inventory, we use groups but you can list each server individually here or automate the inventory creation process. Inventory management is out of this article scope, but it is well described in Ansible documentation:
[windows:children]
frontend
client_api
Backoffice
Step Two: Create windows.yaml file in group_vars folder of each environment and/or in the main group_vars folder and add the following content:
ansible_user: "{{ windows_user|default ('Administrator') }}"
ansible_password: "{{ windows_password }}"
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_transport: credssp
Step Three: Include this role as dependent into the meta/main.yaml file of any of the roles you are planning to execute, for instance:
# cat roles/infra/IIS/meta/main.yml
---
dependencies:
- { role: infra/fetch-credentials/windows }
This role checks appropriate environment variables and fails if a Username or Password is not provided. These environment variables are then copied to Ansible variables and used in the settings we defined in p2.
Step Four: At this time, we have our servers configured and Ansible set up. We are good to go, the only thing left is actually setting up Windows username and password.
You can achieve this through one of two ways.
1st Way: You can do it using export WINDOWS_USERNAMEand export WINDOWS_PASSWORD commands:
export WINDOWS_USERNAME=Administrator
export WINDOWS_PASSWORD=******
2nd Way: or if you have more than one set of credentials you can use a small bash script-helper:
# source set_env.sh
Set Value for WINDOWS_DOMAIN_USER: Administrator
Set Value for WINDOWS_DOMAIN_PASSWORD:
Set Value for WINDOWS_USER: Administrator
Set Value for WINDOWS_PASSWORD:
Our preparations are done. We are ready to start Ansibling.
Windows tips and tricks
Ansible has much fewer modules for Windows than for Linux. There are 104 modules for windows out of 2832 in total yet it is enough for most of the common cases. But sometimes we have to be creative.
DSL is your friend
The first thing to remember - DSL is your friend. There are many DSL modules for windows, for example using this module you can install MSSQL by just one task:
- name: Configure SqlSetup
win_dsc:
resource_name: SqlSetup
ForceReboot: False
UpdateEnabled: False
SourcePath: "{{ disk_image_out.mount_path|default(database_settings.mssql_installation_source) }}"
InstanceName: "{{ db_settings.instance_name|upper }}"
Features: "{{ sql_setup_components }}"
InstallSharedDir: "{{ db_settings.instance_dir_drive }}Program Files\\Microsoft SQL Server"
InstallSharedWOWDir: "{{ db_settings.instance_dir_drive }}Program Files (x86)\\Microsoft SQL Server"
InstanceDir: "{{ db_settings.instance_dir_drive }}SQL"
SQLCollation: 'Latin1_General_CI_AS'
SQLSvcAccount_username: "{{ ad_settings_domain }}\\{{ db_settings.sqlsvcaccount }}$"
SQLSvcAccount_password: '***'
AgtSvcAccount_username: "{{ ad_settings_domain }}\\{{ db_settings.agtsvcaccount }}$"
AgtSvcAccount_password: '***'
SQLSysAdminAccounts: "{{ ad_settings_domain|upper }}\\SQL Administrators"
InstallSQLDataDir: "{{db_settings.data_dir_drive}}SQL\\{{ db_settings.instance_name|upper }}\\MSSQL\\Data"
SQLUserDBDir: "{{db_settings.data_dir_drive}}SQL\\{{ db_settings.instance_name|upper }}\\MSSQL\\Data"
SQLUserDBLogDir: "{{db_settings.log_dir_drive }}SQL\\{{ db_settings.instance_name|upper }}\\MSSQL\\Log"
SQLTempDBDir: "{{db_settings.temp_dir_drive}}SQL\\{{ db_settings.instance_name|upper }}\\MSSQL\\Data\\Tempdb"
SQLTempDBLogDir: "{{db_settings.temp_dir_drive}}SQL\\{{ db_settings.instance_name|upper }}\\MSSQL\\Log\\Tempdb"
SQLBackupDir: "{{db_settings.backup_dir_drive}}SQL\\{{ db_settings.instance_name|upper }}\\MSSQL\\Backup"
PsDscRunAsCredential_username: "{{ad_settings_adminuser}}@{{ad_settings_domain}}"
PsDscRunAsCredential_password: "{{ad_settings_adminpassword}}"
tags:
- install_db
Use the win_psmodule first to install the required DSC modules on the server first:
- name: install DSC Modules
win_psmodule:
name: "{{ item }}"
state: present
with_items:
- "PSDesiredStateConfiguration"
- "SqlServerDsc"
- "ServerManager"
- "StorageDsc"
- "ComputerManagementDsc"
Another big advantage of DSC is that you can run it as a different user, you could've spotted "PsDscRunAsCredential_username" and "PsDscRunAsCredential_password" directives in the example above. It especially helps when some tasks have to be run from the Domain Administrator account.
DIY modules
Didn't find an appropriate DSC module? Not a big deal, there are two possible workarounds (apart from the obvious - using win_shell ansible module):
Step One: Use DSC resource "Script":
#Part of MSSQL setup playbook. Here we test if created service accounts
#have already been added to the server from AD
#if not, we reboot the servers first.
- name: Configure and Test the gMSA account
win_dsc:
resource_name: Script
GetScript: "@{ Result = '{{ item }}'| Use-ServiceAccount -Test }"
TestScript: "(iwr https://raw.githubusercontent.com/beatcracker/Powershell-Misc/master/Use-ServiceAccount.ps1 -UseBasicParsing).Content | iex; '{{ item }}'| Use-ServiceAccount -Test"
SetScript: '$global:DSCMachineStatus = 1'
PsDscRunAsCredential_username: "{{ad_settings_adminuser}}@{{ad_settings_domain}}"
PsDscRunAsCredential_password: "{{ad_settings_adminpassword}}"
with_items:
- "{{ db_settings.sqlsvcaccount }}"
- "{{ db_settings.agtsvcaccount }}"
register: TestADServiceAccount_result
Step Two: Ansible is an imperative tool, so we have to make sure that any task can be executed multiple times providing us with the same result. Play with "GetScrypt", "TestScrypt" and "SetScrypt" fields according to your needs. Having it played right should allow you to be able to identify the current state of the resource, change it if required and pass if not.
Step Three: Create a module yourself. It's not that hard, the only difference with making a module for Linux is you need to write code on PowerShell. We encountered a problem where there was no module KDS Root Key creation which is required for MSSQL installation. So we created it. Look here for more details regarding win module development.
Conclusion
Providing the key aspects related to Ansible usage with Windows. Having Windows servers configured for Ansible we can manage them nearly the same way as we manage Linux servers using Ansible. By using the approaches described in this article we successfully manage Windows servers for jobs like:
- MSSQL AG cluster installation and update.
- IIS configuration.
- Windows services failover cluster configuration.
- A/B deployment.
Discover more about Altenar and harness its growing and expanding expertise by contacting the award-winning team today!