Recently i was asked to help someone run vagrant ansible combination on windows. Its a fun experiment coz ansible never claimed to support windows as control device and the solution [partial at this point] is a series of workaround and gotcha’s that i have listed so far.
I want to make sure i don’t lose this experiment hence documenting it out in a quick blog post
So lets get down to it.
1. For the setup what i have is a Local User with limited privileges lets call him “Anant Shrivastava”, and a local admin lets call him anant.
2. We have installed vagrant from https://vagrantup.com
3. We have a full installation of cygwin and will be using cygwin bash for this experiment.
4. Ansible is installed on system using the cygwin admin console and pip2 install ansible
Now the fun begins.
Sample Vagrantfile in use
# -*- mode: ruby -*-
# vi: set ft=ruby :
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox'
Vagrant.configure("2") do |config|
config.vm.box = "tamer_debian8"
config.vm.network "private_network", type: "dhcp"
config.vm.provider "virtualbox" do |vb|
vb.gui = true
end
config.ssh.username = "android"
config.ssh.password = "tamer"
config.vm.provision "ansible" do | ansible|
ansible.playbook = "provisioning/tamerbook.yml"
ansible.verbose = "v"
ansible.compatibility_mode="2.0"
end
end
Sample playbook provisioning/tamerbook.yml
---
- hosts: all
become: false
roles:
- essentials
And a essentials role file “provisining/roles/essentials/tasks/main.yml”
---
- name: install packages essential for ansible+vagrant stuff
become: yes
become_method: sudo
apt:
name: "{{ packages }}"
state: present
update_cache: yes
vars:
packages:
- python-apt
- python-pycurl
- name: update the system
become: yes
become_method: sudo
apt:
update_cache: yes
- name: upgrade the system
become: yes
become_method: sudo
apt:
upgrade: full
And a simple ansible.cfg
[defaults]
become_ask_pass=False
remote_user=android
Now the moment of truth
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'tamer_debian10'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: TEST_BOX_default_1556175812838_75445
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
default: Adapter 2: hostonly
==> default: Forwarding ports...
default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
default: SSH username: android
default: SSH auth method: password
default:
default: Inserting generated public key within guest...
default: Removing insecure key from the guest if it's present...
default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
default: The guest additions on this VM do not match the installed version of
default: VirtualBox! In most cases this is fine, but in rare cases it can
default: prevent things such as shared folders from working properly. If you see
default: shared folder errors, please make sure the guest additions within the
default: virtual machine match the version of VirtualBox you have installed on
default: your host and reload your VM.
default:
default: Guest Additions Version: 6.0.4
default: VirtualBox Version: 5.2
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
default: /vagrant => C:/Work/TEST_BOX
==> default: Running provisioner: ansible...
Windows is not officially supported for the Ansible Control Machine.
Please check https://docs.ansible.com/intro_installation.html#control-machine-requirements
default: Running ansible-playbook...
PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit="default" --inventory-file=C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
The Ansible software could not be found! Please verify
that Ansible is correctly installed on your host system.
If you haven't installed Ansible yet, please install Ansible
on your host system. Vagrant can't do this for you in a safe and
automated way.
Please check https://docs.ansible.com for more information.
Interesting so it seems ansible is not detected. however
$ ansible --version
ansible 2.7.10
config file = /cygdrive/c/Work/TEST_BOX/ansible.cfg
configured module search path = [u'/home/Anant Shrivastava/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.14 (default, Oct 31 2017, 21:12:13) [GCC 6.4.0]
So ansible is installed lets see what is the problem.
We will take out the provisioning command shown in the above debug output of vagrant
PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit="default" --inventory-file=C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
and lets try running it directly that might give us an idea of what’s going on.
$ PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit="default" --inventory-file=C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
Using /cygdrive/c/Work/TEST_BOX/ansible.cfg as config file
/cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory did not meet host_list requirements, check plugin documentation if this is unexpected
/cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory did not meet script requirements, check plugin documentation if this is unexpected
/cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory did not meet yaml requirements, check plugin documentation if this is unexpected
/cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory did not meet ini requirements, check plugin documentation if this is unexpected
/cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory did not meet auto requirements, check plugin documentation if this is unexpected
[WARNING]: Unable to parse /cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: Could not match supplied host pattern, ignoring: default
PLAY [all] ****************************************************************************************************************************************************skipping: no hosts matched
PLAY RECAP ****************************************************************************************************************************************************
Bunch of different errors lets try ot address them one by one.
We get a whole bunch of errors of simmilar lines.
/cygdrive/c/Work/TEST_BOX/C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory did not meet yaml requirements, check plugin documentation if this is unexpected
error with entries like yaml/ini/auto/script/host_list further digging to it seems to suggest they are more of warning and can be ignored but if you want to get rid of them use following in your ansible.cfg you need to disable inventory plugins which are not in use. Refer https://docs.ansible.com/ansible/2.7/plugins/inventory.html for list of inventory plugin’s in use. However the fun part is all the inventory plugin’s are giving error so what could be the problem.
Interestingly the mix of windows and unix system is visible here for the first time. the inventory file is marked as C:/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory
but the actual file path for cygwin is /cygdrive/c/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory/
Lets see if this changes anything
$ PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit="default" --inventory-file=/cygdrive/c/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
Using /cygdrive/c/Work/TEST_BOX/ansible.cfg as config file
/cygdrive/c/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory did not meet host_list requirements, check plugin documentation if this is unexpected
PLAY [all] ****************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: command-line line 0: garbage at end of line; \"Shrivastava/.ansible/cp/9d1067cbae\".", "unreachable": true}
to retry, use: --limit @/cygdrive/c/Work/TEST_BOX/provisioning/tamerbook.retry
PLAY RECAP ****************************************************************************************************************************************************default : ok=0 changed=0 unreachable=1 failed=0
Some improvment so looks like inventory path was another problem. Lets try to fix that at vagrantfile level.
config.vm.provision "ansible" do | ansible|
ansible.playbook = "provisioning/tamerbook.yml"
ansible.verbose = "v"
ansible.compatibility_mode="2.0"
ansible.inventory_path=".vagrant/provisioners/ansible/inventory"
end
And now lets try to run it again
$ vagrant destroy -f && vagrant up
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'tamer_debian10'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: TEST_BOX_default_1556178393801_90881
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
default: Adapter 2: hostonly
==> default: Forwarding ports...
default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
default: SSH username: android
default: SSH auth method: password
default:
default: Inserting generated public key within guest...
default: Removing insecure key from the guest if it's present...
default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
default: The guest additions on this VM do not match the installed version of
default: VirtualBox! In most cases this is fine, but in rare cases it can
default: prevent things such as shared folders from working properly. If you see
default: shared folder errors, please make sure the guest additions within the
default: virtual machine match the version of VirtualBox you have installed on
default: your host and reload your VM.
default:
default: Guest Additions Version: 6.0.4
default: VirtualBox Version: 5.2
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
default: /vagrant => C:/Work/TEST_BOX
==> default: Running provisioner: ansible...
Windows is not officially supported for the Ansible Control Machine.
Please check https://docs.ansible.com/intro_installation.html#control-machine-requirements
default: Running ansible-playbook...
PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile=C:/Work/TEST_BOX/.vagrant/machines/default/virtualbox/private_key -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --extra-vars=ansible_user\=\'android\' --limit="default" --inventory-file=.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
The Ansible software could not be found! Please verify
that Ansible is correctly installed on your host system.
If you haven't installed Ansible yet, please install Ansible
on your host system. Vagrant can't do this for you in a safe and
automated way.
Please check https://docs.ansible.com for more information.
Looks like we are back to ground zero. So one problem that seems to be confirmed is that vagrant is not able to find ansible this could be due to the convergence of vagrant being installed on windows and ansible used from cygwin. However lets try running ansible directly to see if that works.
$ PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile=C:/Work/TEST_BOX/.vagrant/machines/default/virtualbox/private_key -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --extra-vars=ansible_user\=\'android\' --limit="default" --inventory-file=.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
Using /cygdrive/c/Work/TEST_BOX/ansible.cfg as config file
/cygdrive/c/Work/TEST_BOX/.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory did not meet host_list requirements, check plugin documentation if this is unexpected
PLAY [all] ****************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: command-line line 0: garbage at end of line; \"Shrivastava/.ansible/cp/9d1067cbae\".", "unreachable": true}
to retry, use: --limit @/cygdrive/c/Work/TEST_BOX/provisioning/tamerbook.retry
PLAY RECAP ****************************************************************************************************************************************************default : ok=0 changed=0 unreachable=1 failed=0
Anant Shrivastava@DESKTOP-IEBEI0J /cygdrive/c/Work/TEST_BOX
A warning is present about inventory file not meeting host_list requirements lets get rid of it as we are using ini style so lets add this in ansible.cfg to disable other inventory plugins.
[inventory]
enable_plugins = ini
now the next error is Failed to connect to the host via ssh: command-line line 0: garbage at end of line; \"Shrivastava/.ansible/cp/9d1067cbae\
If you are keen eyes you would have spotted the error message being path issue “Shrivastava” is second part of the user name and seems like somewhere in path there is a double quote missing. However i am not good with ruby to spot where the bug is in the code however i went for a alternative approach which was to figure out what this is. First searches kept me to ansible temp location changes and hence i was fiddling with them to no concrete results references blog.davesnigier.com/changing-ansible-temporary-directory and a bunch of other simmilar posts. however then i realized my error folder location is not .ansible/tmp
but .ansible/cp
This could be something else. more searchs led me to control_path_dir an option burried deep in https://docs.ansible.com/ansible/2.4/intro_configuration.html#control-path-dir however this is a specific config for ssh connections hence the ansible.cfg not just needed a new line but a new section also.
[ssh_connection]
control_path_dir=/cygdrive/c/Work/TEST_BOX/.ansible/cp
This path needs to be full path as far as i can see. Next task would be to see if i can get it to do a relative path. However with this in place we can achieve our goal by following these two steps.
1. vagrant up
2. copy ansible command from debug output and fire it manually.
Step 1
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'tamer_debian10'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: TEST_BOX_default_1556180158430_17098
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
default: Adapter 2: hostonly
==> default: Forwarding ports...
default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
default: SSH username: android
default: SSH auth method: password
default:
default: Inserting generated public key within guest...
default: Removing insecure key from the guest if it's present...
default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
default: The guest additions on this VM do not match the installed version of
default: VirtualBox! In most cases this is fine, but in rare cases it can
default: prevent things such as shared folders from working properly. If you see
default: shared folder errors, please make sure the guest additions within the
default: virtual machine match the version of VirtualBox you have installed on
default: your host and reload your VM.
default:
default: Guest Additions Version: 6.0.4
default: VirtualBox Version: 5.2
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
default: /vagrant => C:/Work/TEST_BOX
default: /tmp/vagrant-cache => C:/Users/Anant Shrivastava/.vagrant.d/cache/tamer_debian10
==> default: Configuring cache buckets...
==> default: Running provisioner: ansible...
Windows is not officially supported for the Ansible Control Machine.
Please check https://docs.ansible.com/intro_installation.html#control-machine-requirements
default: Running ansible-playbook...
PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile=C:/Work/TEST_BOX/.vagrant/machines/default/virtualbox/private_key -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --extra-vars=ansible_user\=\'android\' --limit="default" --inventory-file=.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
The Ansible software could not be found! Please verify
that Ansible is correctly installed on your host system.
If you haven't installed Ansible yet, please install Ansible
on your host system. Vagrant can't do this for you in a safe and
automated way.
Please check https://docs.ansible.com for more information.
Step 2
$ PYTHONUNBUFFERED=1 ANSIBLE_NOCOLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile=C:/Work/TEST_BOX/.vagrant/machines/default/virtualbox/private_key -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --extra-vars=ansible_user\=\'android\' --limit="default" --inventory-file=.vagrant/provisioners/ansible/inventory -v provisioning/tamerbook.yml
Using /cygdrive/c/Work/TEST_BOX/ansible.cfg as config file
PLAY [all] ****************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************ok: [default]
TASK [essentials : install packages essential for ansible+vagrant stuff] **************************************************************************************changed: [default] => {"cache_update_time": 1556180456, "cache_updated": false, "changed": true, "stderr": "", "stderr_lines": [], "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nThe following additional packages will be installed:\n ca-certificates iso-codes libcurl3-gnutls libnghttp2-14 libpsl5\n libpython-stdlib libpython2-stdlib
[..snip...]
"Processing triggers for ca-certificates (20190110) ...", "Updating certificates in /etc/ssl/certs...", "0 added, 0 removed; done.", "Running hooks in /etc/ca-certificates/update.d...", "done."]}
TASK [essentials : update the system] *************************************************************************************************************************ok: [default] => {"cache_update_time": 1556180456, "cache_updated": false, "changed": false}
TASK [essentials : upgrade the system] ************************************************************************************************************************ [WARNING]: Could not find aptitude. Using apt-get instead.
changed: [default] => {"changed": true, "msg": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nCalculating upgrade...\nThe following NEW packages will be installed:\n anacron\nThe following packages will be upgraded:\n bash binutils binutils-common binutils-x86-64-linux-gnu libbinutils\n libdns-export1104 libisc-export1100 libssl1.1 openssh-client openssh-server\n openssh-sftp-server pinentry-curses task-english task-laptop task-
[..snip....]
"Processing triggers for systemd (241-3) ...", "Processing triggers for dbus (1.12.12-1) ...", "Processing triggers for libc-bin (2.28-8) ..."]}
PLAY RECAP ****************************************************************************************************************************************************default : ok=4 changed=2 unreachable=0 failed=0
Hope this helps someone struggling with vagrant ansible setup on windows. I know its not a full solution but it should be a good enough start point for others more capable them me to figure out what’s wrong and work on increasing the support.
Alterantively a much easier option could be to leverage ansible_local which would be a much smoother experiance as ansible will be executing on the guest itself. this process is outlined here