Test Environment for Ansible on a Windows System Without Linux Subsystem Support

Some weeks ago, I gave a workshop about Ansible and I was asked how to set up a local test environment for Ansible. Requirement is that this test environment can run on a Windows 10 without a Linux subsystem. I don't why, but it was not my first customer where Windows 10 could not be used with Linux subsystem (Don't ask me about reasons). So I recommend following set up.

Idea

Ansible itself requires a Linux-based system as the control machine (so-called control node). The machine that is provisioned (so-called managed node) can be both, Linux- or Windows-based. In my following example, the manage node is Linux-based.   So when I have to develop on a Windows machine, I install two Linux-based virtual machines. One for calling the Ansible's playbooks and a second one for testing the provisioning. I set up the virtual machines with VirtualBox and Vagrant. Vagrant allows me to share the playbooks easily between host and virtual machines. That means, I can develop the playbook on the Windows system and the virtual machines can run headless. The next section shows you how to set up this tool chain.

Tool Chain Setup

At first, install VirtualBox and Vagrant on your machine. I additionally use Babun, a windows shell based on Cygwin and oh-my-zsh, for a better shell experience on Windows, but this isn't necessary. Then, go to the directory (let's called it ansible-workspace), where your Ansible's playbooks are located. Create there a Vagrant configuration file with the command vagrant init:

 1$ cd ansible-workspace
 2$ vagrant init
 3$ tree  
 4.
 5├── deploy-app.yml
 6├── host_vars
 7│   └── ubuntu-server
 8├── inventories
 9│   ├── production
10│   └── test
11├── roles
12│   ├── deploy-on-tomcat
13│   │   ├── defaults
14│   │   │   └── main.yml
15│   │   └── tasks
16│   │       ├── cleanup-webapp.yml
17│   │       ├── deploy-webapp.yml
18│   │       ├── main.yml
19│   │       ├── start-tomcat.yml
20│   │       └── stop-tomcat.yml
21└── Vagrantfile

Now, we have to choose a so-called Vagrant Box on Vagrant Cloud. A box is the package format for a Vagrant environment. It depends on the provider and the operating system that you choose to use. In our case, it is a VirtualBox VM image based on a minimal Ubuntu 18.04 system (box name is bento/ubuntu-18.04 ). In our case, we have to configure two boxes in our Vagrantfile. Both are based on bento/ubuntu-18.04.

1Vagrant.configure("2") do |config|  
2  config.vm.define "managed-node" do |ubuntu|
3    ubuntu.vm.box = "bento/ubuntu-18.04"
4  end
5
6  config.vm.define "control-node" do |vbox|
7     vbox.vm.box = "bento/ubuntu-18.04"
8  end
9end

The next step is to ensure that Python is installed on the managed node and this node is available via an IP address. For that, we use the shell provisioning, and we configure a private network in the Vagrantfile:

 1# ...  
 2  config.vm.define "managed-node" do |ubuntu|
 3    ubuntu.vm.box = "bento/ubuntu-18.04"
 4    ubuntu.vm.network "private_network", ip: "192.168.33.11"
 5
 6    ubuntu.vm.provision "shell", inline: <<-SHELL
 7      sudo apt-get update
 8      sudo apt-get install python -y
 9    SHELL
10  end
11# ...

In the control node, we have to ensure that Ansible is installed and that a SSH connection from control node to our managed node via SSH-key is possible. For that agian, we use the shell provisioning in the Vagrantfile:

 1 # ...
 2  config.vm.define "controll-node" do |vbox|
 3     vbox.vm.box = "bento/ubuntu-18.04"
 4
 5     vbox.vm.provision "shell", inline: <<-SHELL
 6       sudo apt-get update -y
 7       sudo apt-get install -y software-properties-common
 8       sudo apt-add-repository ppa:ansible/ansible
 9       sudo apt-get update -y
10       sudo apt-get install -y ansible
11     SHELL
12
13     vbox.vm.provision "shell", privileged: false, inline: <<-SHELL
14       ssh-keygen -t rsa -q -f "/home/vagrant/.ssh/id_rsa" -N ""
15       ssh-keygen -R 192.168.33.11
16       ssh-keyscan -t rsa -H 192.168.33.11 >> /home/vagrant/.ssh/known_hosts
17       sshpass -p 'vagrant' ssh-copy-id -i /home/vagrant/.ssh/id_rsa.pub vagrant@192.168.33.11
18     SHELL
19  end
20# ...

On Github's Gist you can find the whole Vagrantfile.

The last step is to configure your Ansible project, so that the playbooks can run against the managed node. Therefore, you create a new inventory file called local-test with managed node's IP address:

1$ cat inventories/local-test
2[application_server] # name of your group that is mention in your playbook
3192.168.33.11

Workflow

After setting up the tool chain let's have a look how to work with it. I write my Ansible playbook on the Windows system and run them from the control node against managed node. First at all, we have to start our Vagrant boxes.

1$ cd ansible-workspace
2$ vagrant up

When both Vagrant boxes are ready to use, we can jump into control node box with:

1$ vagrant ssh control-node

You can find the Ansible playbooks inside the box in the folder /vagrant .  In this folder run Ansible:

1$ cd /vagrant
2$ ansible-playbook -i inventories/local-test -u vagrant deploy-app.yml

It is important that you use the local-test inventory.

Outlook

Some Docker fans would prefer a container instead of a virtual machine. But remember, Docker runs on Windows in a virtual machine, therefore, I don't see a benefit for using Docker instead of a virtual machine. But of course with Windows 10 native container support a setup with Docker is a good alternative if Ansible doesn't run on the Linux subsystem.

Do you another idea or approach? Let me know and write a comment.

  1. VirtualBox
  2. Vagrant
  3. Whole Vagrantfile on GitHub.