Skip to main content

Windows Server with Vagrant, WSL and Ansible

· 8 min read

Hello again, this post will explain how to create a Windows Server virtual machine with Vagrant, WSL and Ansible. I assume that you already saw my previous Windows Server post where I explain how to create a Windows Server vm, so go check that out if you didn't :)

Before we start, Plugins!

Installation

Windows

So, to put in action some plugins into our Vagrantfile we need to installed them directly to our terminal.

To do so, open an Powershell and install the following plugins (note that this plugin is for Windows OS):

vagrant plugin install vagrant-reload

This will allow us to reload our vm during the creation of the vm.

Others OS

Now, we want to use windows features but are using a Linux or MacOS.

For that, we need to install the following plugins in our Linux or MacOS machine:

vagrant plugin install vagrant-reload
vagrant plugin install winrm
vagrant plugin install winrm-fs
vagrant plugin install winrm-elevated

This will allow us to reload our vm and to use windows features with vagrant;

Now that we've have our plugins installed lets continue;

WSL

What's WSL and what it does? WSL stands for Windows Subsystem for Linux and, by installing it on our Windows Server, it will allow us, basically, to have Linux on a Windows terminal.

Meaning that you can, for example, install a tool in your Windows vm using Linux commands like wsl sudo apt ansible -y (this command will install Ansible in your Windows virtual machine).

But to use WSL we need to install it! And to install it we're going to use Vagrant. Lets take a look at our Vagrantfile:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

config.vm.box = "mcree/win2019"
config.vm.box_version = "1.0.1584095692"

config.vm.provider "virtualbox" do |v|
v.name = "Windows Server 2019"
v.memory = "2048"
end

config.vm.provision "shell", inline: <<-SHELL

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

SHELL

end

If this vagrantfile looks familiar it should! This is the vagrantfile from our previous post where we define our machine configuration and installed Chocolatey.

Now what we want to do is install WSL and to do so we need to add the installation command into our Shell provisioner.

So, copy paste this to the shell terminal in your vagranfile:

Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -NoRestart -WarningAction SilentlyContinue

So, our vagrantfile should look like this for now:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

config.vm.box = "mcree/win2019"
config.vm.box_version = "1.0.1584095692"

config.vm.provider "virtualbox" do |v|
v.name = "Windows Server 2019"
v.memory = "2048"
end

config.vm.provision "shell", inline: <<-SHELL

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -NoRestart -WarningAction SilentlyContinue

SHELL

end

So, we now have WSL installed in our Windows Server vm! This is great and we now have the freedom to use it at will!

Since we installed Chocoaltey and WSL we want to be sure that the machine takes in to account the modifications applied.

For that we will use a provisioner (shell) reload:

  config.vm.provision :reload

We will add this after our shell provisioner just like this:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

config.vm.box = "mcree/win2019"
config.vm.box_version = "1.0.1584095692"

config.vm.provider "virtualbox" do |v|
v.name = "Windows Server 2019"
v.memory = "2048"
end

config.vm.provision "shell", inline: <<-SHELL

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -NoRestart -WarningAction SilentlyContinue

SHELL

# trigger reload
config.vm.provision :reload

end

Now that our provisioner is reloaded we can install Kali Linux (Linux distribution base on Debian).

For that we're going to open another termninal and use chocolatey to install it.

Right after installing kali linux we're going to reload our provisioner once again.

This will give us certitude that everything was restarted including the virtual machine itself!

So, lets see what we need to add:

  # execute code after reload
config.vm.provision "shell", inline: <<-SHELL
choco install wsl-kalilinux -y
SHELL

# trigger reload
config.vm.provision :reload

And now that everything is fresh and clean lets open another shell terminal and start using WSL!

To start using WSL and see if it works lets add a shell with a simple package update:

wsl sudo apt update

Pretty easy right? So, lets see how we can use it effectively.

To try it out lets install some tools that we're going to need.

So lets install Ansible and unzip package:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

config.vm.box = "mcree/win2019"
config.vm.box_version = "1.0.1584095692"

config.vm.provider "virtualbox" do |v|
v.name = "Windows Server 2019"
v.memory = "2048"
end

config.vm.provision "shell", inline: <<-SHELL

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -NoRestart -WarningAction SilentlyContinue

SHELL

# trigger reload
config.vm.provision :reload

# execute code after reload
config.vm.provision "shell", inline: <<-SHELL
wsl sudo apt update
wsl sudo apt install -y ansible
wsl sudo apt install -y unzip
SHELL
end

Why do we need to install Ansible and Unzip might you ask? Well, we'll need to unzip some files so that's why we need Unzip.

For Ansible it requires a little bit more explaination, come with me :)

Ansible

Ansible is an automation engine, that works with .yml files called playbooks, that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other IT needs.

It uses no agents and no additional custom security infrastructure, so it's easy to deploy - and most importantly, it uses a very simple language (YAML, in the form of Ansible Playbooks) that allow you to describe your automation jobs in a way that approaches plain English.

We also have a hosts (also known as Inventory) file. That's where we're going to add our target machines that we wish to run the playbooks.

Go check the link for a more deep and detailed information about Ansible and what it does

So, to use Ansible we need a working playbook, hosts and a bunch of other stuff! And for that we're going to use wget to donwload a zip file containing all of this (our unzip package will come in handy :D )

Just copy paste this to your provisioner:

    wsl wget https://github.com/j0rdan-m/vagrant_wsl_ansible/archive/master.zip

This will download the zip file.

But we need to unzip it, so:

    wsl unzip master.zip

Thanks to this work we now have working playbooks available to us.

But before we check them out lets configure our hosts file. We will just add the localhost machine into the hosts file with an user, password, connection method and port configuration.

If everything is correct this should work perfectly with your playbooks

    wsl sudo bash -c "echo [self] >> /etc/ansible/hosts"
wsl sudo bash -c "echo 127.0.0.1 >> /etc/ansible/hosts"
wsl sudo bash -c "echo [self:vars] >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_port=5985 >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_connection=winrm >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_winrm_transport=basic >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_user=vagrant >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_password=vagrant >> /etc/ansible/hosts"

And, just to be sure that everything is correct, lets ping our vm:

    wsl ansible self -m win_ping

Now that the hosts file is correct we can advance to our playbooks!

We have 3 to be exact! One to install IIS, one to install some tools (Git, VSCode and SumatraPDF) and the last one that creates a service user and gives him the rights to the correct folders.

To use them we need to use ansible-playbook command just like this:

    wsl ansible-playbook vagrant_wsl_ansible-master/tools.yml
wsl ansible-playbook vagrant_wsl_ansible-master/iis.yml
wsl ansible-playbook vagrant_wsl_ansible-master/users.yml

This will launch each playbook in the order presented.

So, our playbook should look something like this:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

config.vm.box = "mcree/win2019"
config.vm.box_version = "1.0.1584095692"

config.vm.provider "virtualbox" do |v|
v.name = "Windows Server 2019"
v.memory = "2048"
end

config.vm.provision "shell", inline: <<-SHELL

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -NoRestart -WarningAction SilentlyContinue

SHELL

# trigger reload
config.vm.provision :reload

# execute code after reload
config.vm.provision "shell", inline: <<-SHELL
choco install wsl-kalilinux -y
SHELL

# trigger reload
config.vm.provision :reload

# execute code after reload
config.vm.provision "shell", inline: <<-SHELL
wsl sudo apt update
wsl sudo apt install -y ansible
wsl sudo apt install -y unzip
wsl wget https://github.com/j0rdan-m/vagrant_wsl_ansible/archive/master.zip
wsl unzip master.zip
wsl sudo bash -c "echo [self] >> /etc/ansible/hosts"
wsl sudo bash -c "echo 127.0.0.1 >> /etc/ansible/hosts"
wsl sudo bash -c "echo [self:vars] >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_port=5985 >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_connection=winrm >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_winrm_transport=basic >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_user=vagrant >> /etc/ansible/hosts"
wsl sudo bash -c "echo ansible_password=vagrant >> /etc/ansible/hosts"
wsl ansible self -m win_ping
wsl ansible-playbook vagrant_wsl_ansible-master/tools.yml
wsl ansible-playbook vagrant_wsl_ansible-master/iis.yml
wsl ansible-playbook vagrant_wsl_ansible-master/users.yml

SHELL

end

We now have a fully working Windows Server virtual machine with Ansible and a bunch of other cool stuff in it!

Hope that this was helpfull and make sure to check the other posts ;)