Automate Windows VM Creation and Configuration in vSphere Using Packer, Terraform and Ansible (Part 3 of 3)
This is the final entry in the series. In this post, I want to show how Ansible can be used to automate Windows VM provisioning. As always all the scripts and configurations are available at my GitHub repository.
Provisioning with Ansible
Actually, Ansible was not my first choice when it came to VM provisioning. I've spent a lot of time with Chef at first because it was used in this repo which I took as a starting point. It almost worked for me but I've encountered a problem with a domain joining process. In order to join a domain the DNS server setting of the joining machine should be pointing to the domain controller. When the DNS server setting changed, the Chef client on that machine stopped resolving the Chef server and was unable to continue operation. Of course, I could fix it by adding necessary DNS entry on the server beforehand. Instead, I decided to try out Ansible which cannot run into such problems due to its agentless design.
Edit inventory.yml and group_vars/all.yml according to your environment
I use Ansible Vault to store my credentials in group_vars/all.yml in encrypted form. To create your own encrypted passwords issue
ansible-vaultencrypt_string<string_to_encrypt>
command for each password you want to encrypt. It will ask you for the new vault password. Put that password in .vault_pass file in Ansible working directory.
The install ad task installs the AD DS role on the server, creates a new forest and promotes the server to a domain controller. The reboot server reboots the server only if the status of the previous task returned "changed".
win_reboot module doesn't have any reliable way to tell if the system is ready for management after the reboot. When Windows is rebooted after becoming a domain controller it takes a substantial amount of time to finish all the related tasks. To address this issue I specify the post_reboot_delay parameter. You may have to adjust it depending on your system's performance. Please refer to official module documentation for further details.
RDP is enabled on PDC by roles/common/tasks/main.yml which calls roles/common/tasks/enable_rdp.yml.
This task is applied to all machines, so it is omitted hereafter.
-name:Windows | Check for xRemoteDesktopAdmin Powershell modulewin_psmodule:name:xRemoteDesktopAdminstate:present-name:Windows | Enable Remote Desktopwin_dsc:resource_name:xRemoteDesktopAdminEnsure:presentUserAuthentication:Secure-name:Windows | Check for xNetworking Powershell modulewin_psmodule:name:xNetworkingstate:present-name:Firewall | Allow RDP through Firewallwin_dsc:resource_name:xFirewallName:"AdministratoraccessforRDP(TCP-In)"Ensure:presentEnabled:TrueProfile:"Domain"Direction:"Inbound"Localport:"3389"Protocol:"TCP"Description:"OpensthelistenerportforRDP"
Replica domain controller (RDC) is configured by roles/replica_domain_controller/tasks/main.ymlscript.
----name:change DNS serverwhen:not ansible_windows_domain_memberwin_dns_client:adapter_names:'*'ipv4_addresses:"{{groups['primary_domain_controller'][0]}}"-name:join domainwin_domain_membership:dns_domain_name:"{{domain}}"domain_admin_user:"{{domain_admin}}"domain_admin_password:"{{domain_admin_password}}"state:domainregister:domain_joined-name:reboot after domain joinwin_reboot:when:domain_joined.reboot_required-name:Wait for system to become reachable over WinRMwait_for_connection:timeout:900-name:install adwin_domain_controller:dns_domain_name:"{{domain}}"domain_admin_user:"{{domain_admin}}"domain_admin_password:"{{domain_admin_password}}"safe_mode_password:"{{domain_safemode_password}}"state:domain_controllerregister:ad-name:reboot serverwin_reboot:msg:"InstallingAD.Rebooting..."pre_reboot_delay:15when:ad.changed
First DNS server is changed to point to the PDC. Then the server joins the domain and reboots. After that AD role is installed and server reboots.
The file server is configured by roles/fileserver/tasks/main.yml.
----name:change DNS serverwin_dns_client:adapter_names:'*'ipv4_addresses:-"{{groups['primary_domain_controller'][0]}}"-"{{groups['replica_domain_controller'][0]}}"-name:join domainwin_domain_membership:dns_domain_name:"{{domain}}"domain_admin_user:"{{domain_admin}}"domain_admin_password:"{{domain_admin_password}}"state:domainregister:domain_joined-name:reboot after domain joinwin_reboot:when:domain_joined.reboot_required
It repeats the steps taken with SDC except install ad task.
Here is the sample output of ansible-playbook winlab.yml:
As you can see not everything went smooth at first. I guess there were some issues with Internet connectivity when Ansible tried to install xRemoteDesktopAdmin module. Fortunately, it succeeded on the second try. And here is what I like about Ansible: it didn't try to install AD during the second run, because it was already there. This idempotency feature is very handy because it allows you to run playbooks against the same hosts over and over again and not to worry about making any changes to already configured resources.
Conclusion
Now you have a fully functional basic Windows domain setup which you can use to prepare for MCSA exams or to build custom PoC setups.