Skip to content

Instantly share code, notes, and snippets.

Last active December 16, 2022 11:34
What would you like to do?
Running Ansible on Windows
@echo off
cygwin-shim.bat /bin/ansible-galaxy %*
@echo off
cygwin-shim.bat /bin/ansible-playbook %*
@echo off
set COMMAND=%1
REM If you used the standard Cygwin installer this will be C:\cygwin
set CYGWIN=%USERPROFILE%\.babun\cygwin
REM You can switch this to work with bash with %CYGWIN%\bin\bash.exe
set SH=%CYGWIN%\bin\zsh.exe
if not exist "%SH%" (
echo cygwin's sh.exe not found. Did you delete %CYGWIN% ?
exit /b 255
"%SH%" -c "[[ -x "%COMMAND%" ]]"
if not errorlevel 0 (
echo %COMMAND% not found. Did you uninstall it ?
exit /b 255
"%SH%" -c "%*"
Copy link

Hi Michael

Great blog post, thank you! I've successfully used it to get Vagrant AWS with Ansible provisioning running from my Windows 8.1 machine.

I had two further issues, which I think are to do with working with the AWS plugin: I found that I was getting ssh group-readable or world-readable and thus insecure key file permission errors and then no hosts matched inventory file errors.

It seemed to be impossible to chmod the keyfile in its /cygdrive location to remove group or world permissions using babun (even when trying to manipulate the underlying windows permissions using icacls); it only worked when the keyfile was located in my cygwin home folder and accessed via ~.

Therefore, I found I had to modify the ansible-playbook parameters (generated by vagrant) using sed:

REM The first sed command looks for a full file path such as
REM     --inventory-file=C:/Users/name/project/.vagrant/provisioners/ansible/inventory
REM and replaces it with a relative path (based on splitting at the '.')
REM     --inventory-file=.vagrant/provisioners/ansible/inventory

REM The second sed command looks for a full file path like
REM     --private-key=C:/Users/name/keys/keyfile.pem
REM and replaces it with the same named file in your cygwin home dir
REM     --private-key=~/keyfile.pem
REM copy the pem file there (~) and chmod 400 it

"%SH%" -c "/bin/ansible-playbook `echo %* | sed -E 's/(--inventory-file=)[^.]*/\1/' | sed -E 's/(--private-key=)[^ ]*(\/.+ )/\1~\2/'`"

I hope this helps anyone else who has similar problems!

Thanks again for getting me to that point!


Copy link

@woodcoder Unfortunately, your solution did not work for me but I was able to come up with a complicated workaround involving sshpass, disabling host checking, copying private_key to ~/private_key, setting permissions on it, and then running my Ansible command manually instead of "vagrant provision" once the VM was already up.

There really should be a better solution to handling this on Windows. Maybe I missed the boat and just disabling host checking would have been enough but what a nightmare!

Copy link

kitsunde commented Feb 2, 2015

Are you able to get zsh to run in a full environment? When I execute it the whole environment is from windows. It's quite troublesome because it's picking up ssh from windows which wouldn't work with ansible under cygwin, it's also counting ~ as the windows user and not /home/kitsunde among other things.

Copy link

kitsunde commented Feb 2, 2015

Okay so I got it to work.

Ansible complains that it can't find the control_path.

I wanted to add ansible.cfg to cygwin ~, but because of my PATH issues I added it to the same folder as the Vagrant file.

control_path = /tmp/ansible-ssh-%%h-%%p-%%r

Invalid private key file permissions

As mentioned above, it's not possible to use the SSH key as-is because the file permissions under windows are fixed. Instead I'm copying the file and setting the file permissions:

"%SH%" --login -c "cp `echo %* | sed -E 's/.*\-\-private\-key=([^ ]*).*/\1/'` ~/private_key"
"%SH%" --login -c "chmod 600 ~/private_key"

Cygwin doesn't support descriptor passing. SSH

Fixed by disabling ControlMaster:

export ANSIBLE_SSH_ARGS='-o ControlMaster=no'

Get Ansible to use cygwin SSH

I couldn't simply pass --login to ansible-playbook because it seemed to break the relative path to my playbook, instead I have to just add /usr/bin to the bath.

export PATH=/usr/bin:$PATH

"File not found" on executing a shared drive executable.

I have a few executables that can't be run with windows CRLF endings. In my repo I configured unix line endings .gitattributes with:

# Auto detect text files and perform LF normalization
* text eol=lf

Final version of ansible-playbook.bat

@echo off

set CYGWIN=%USERPROFILE%\.babun\cygwin
set SH=%CYGWIN%\bin\zsh.exe

"%SH%" --login -c "export HOME=/home/%USERNAME% && cp `echo %* | sed -E 's/.*\-\-private\-key=([^ ]*).*/\1/'` ~/private_key"
"%SH%" --login -c "chmod 600 ~/private_key"

"%SH%" -c "export ANSIBLE_SSH_ARGS='-o ControlMaster=no' && export HOME=/home/%USERNAME% && export PATH=/usr/bin:$PATH && /bin/ansible-playbook `echo %* | sed -E 's#(.*\-\-private-key=)[^ ]*(.*)#\1~/private_key\2#'`"

Copy link

@CelC thanks for sharing -- really useful!

I tried to follow suit, by putting the cp/chmod in the batch file, but it doesn't work for me. For some reason the zsh login shell ends up mounting C:\HashiCorp\Vagrant\embedded as the root /of the new shell and so I get /usr/bin/chmod: cannot access '/home/username/private_key': No such file or directory issues. I can't see why that's happened, I can only think it's something to do with running the vagrant context (or something to do with babun/vagrant path ordering).

Also, in running the batch file on a new install on a colleagues machine, we ran into issues with the sed backreferences failing. I suspect it's to do with a version bump somewhere, but anyway, I ended up with the following, which works again:

"%SH%" -c "/bin/ansible-playbook $(echo %* | sed -r 's/(--inventory-file=)[^.]*/\\\\1/' | sed -r 's/(--private-key=)[^ ]*(\/.+ )/\\\\1~\\\\2/')"

Copy link

@woodcoder, this post really helped me out and I ended up handling the private_keys somewhat differently:

Seems to work for me just fine - I'm not sure why I have to cd to specific directory to chmod/setfacl the private_key, but it does work on my Windows machine. Hope it works for you.

@echo off

REM This is the default Babun install location.
set CYGWIN=%USERPROFILE%\.babun\cygwin

REM This is the default Babun shell, changed to bash if you need to
set SH=%CYGWIN%\bin\zsh.exe

REM You'll need to adjust the sed command below in order for this shim to work for your installation.  
REM Below the Babun install directory was C:\Users\Admin\.babun\ for the "Admin" user; if your user is FooUsername, change "Admin" to "FooUsername" in the sed commands

REM Setup a temp file where the default Vagrant private_key path is stored $HOME; if there's a previous entry in there, let's clear it
"%SH%" -c "cd $HOME && touch .babun_shim_vars && sed -i '/.*VAGRANT_PRIV_KEY_DIR.*/d' .babun_shim_vars"

REM Put the private_key path directory into the temp vars file we just setup: $HOME/.babun_shim_vars
"%SH%" -c "VAGRANT_PRIV_KEY_DIR=`echo %* | sed '0,/C:\/Users\/Admin\/\.babun\/cygwin/s///' | awk '{print $1}' | sed '0,/--private-key=/s///' | sed '0,/private_key/s///'` && echo export VAGRANT_PRIV_KEY_DIRECTORY=$VAGRANT_PRIV_KEY_DIR >> $HOME/.babun_shim_vars"

REM Change directory to where the private key is stored and set permissions
"%SH%" -c "source $HOME/.babun_shim_vars && cd $VAGRANT_PRIV_KEY_DIRECTORY && setfacl -s user::r--,group::---,other::--- private_key && ls -l private_key && pwd"

REM Run the playbook commands, except change the private_key location to the Cygwin path, instead of using the Windows file location reference.
"%SH%" -c "export ANSIBLE_SSH_ARGS='-o ControlMaster=no' && $HOME/ansible/bin/ansible-playbook `echo %* | sed '0,/C:\/Users\/Admin\/\.babun\/cygwin/s///'`"

Copy link

dannyim commented Jul 21, 2015

Thanks everyone, this post really helped me out.

The private key configuration got moved inside the auto-generated inventory file in vagrant 1.7.3.
Here is an updated version that works for me, I've only tested this on vagrant 1.7.4:

@echo off

REM If you used the stand Cygwin installer this will be C:\cygwin
set CYGWIN=%USERPROFILE%\.babun\cygwin

REM You can switch this to work with bash with %CYGWIN%\bin\bash.exe
set SH=%CYGWIN%\bin\zsh.exe

REM extract the location of the inventory file into an env variable.  Next, update the contents of the inventory file with the path to the private key file, relative to the cygwin root directory.  extract the private key path and change the permissions of the private key.

"%SH%" -c "export INV_FILE=`echo %* | sed -E 's/.*--inventory-file=([^ ]*).*/\1\/vagrant_ansible_inventory/'` && cat $INV_FILE | sed -E 's/(ansible_ssh_private_key_file=).*(\/home\/'$USER')([^ ]*)/\1\2\3/' > new_file; cp new_file $INV_FILE; rm new_file && export PRIV_KEY=`cat $INV_FILE | tr '\n' ' ' | sed -E 's/.*ansible_ssh_private_key_file=([^ ]*)/\1/'` && chmod 600 $PRIV_KEY; ls -la $PRIV_KEY"

"%SH%" -c "export ANSIBLE_SSH_ARGS='-o ControlMaster=no' && ansible-playbook %*"

Copy link

rivaros commented Jul 31, 2015

Here is a working solution for Vagrant 1.7.3+ on Windows, supports multiple hosts in inventory with multiple keys.

Copy link

Problem of repeated project root path with ansible/vars/app with ansible-playbook.bat

Copied ansible-playbook.bat from

I got the following command expression, it's executable with vagrant/Windows/babun:

"d:\yushen\bin\.babun\cygwin\bin\bash.exe" -c "/bin/ansible-playbook --user=vagrant --connection=ssh --timeout=30 --limit=all --inventory-file=D:/yushen/dev/clojure/cas-reports/ansible/dev D:/yushen/dev/clojure/cas-reports/ansible/provision.yml"
ERROR: file could not read: /cygdrive/d/yushen/dev/clojure/cas-reports/D:/yushen/dev/clojure/cas-reports/ansible/vars/app

The error above seems that in the path for ansible/vars/app the path for my project d:/yushen/dev/clojure/cas-reports was repeated in front of it as /cygdrive/d/yushen/dev/clojure/cas-reports (typical cygwin expression!)

I examed the ansible/provision.yml but could not find any clue. Please help to give me some pointer. Thanks,

Here is the content of ansible/provision.yml portion related to app:

- hosts: app
  sudo: true
    - vars/app

  - name: Create the project directory.
    file: state=directory path={{project_root}}

  - name: Install required system packages.
    apt: pkg={{item}} state=installed update-cache=yes
    with_items: "{{system_packages}}"

  - name: Get leiningen
    get_url: url= dest="{{lein_dir}}"

  - name: Leiningen permissions
    file: path={{lein_dir}} mode=0755

  - name: Download project dependencies
    shell: su - vagrant -c 'cd /var/projects/{{project_name}}; lein deps'

  - name: Upload nREPL upstart config file
    copy: src=files/nrepl-upstart.conf dest=/etc/init/nrepl.conf

  - name: Start nREPL server
    service: name=nrepl state=started

  - name: Make app log directory
    file: state=directory path=/var/log/{{project_name}} owner=vagrant group=vagrant

Copy link

where should i place the ansible-playbook.bat file?

Copy link

pwyoung commented May 7, 2020

FWIW, I use this sort of command since I want to mimic logging in.

c:\cygwin64\bin\bash.exe -l -c "echo 'the above is from my login script'"
JAVA_HOME is C:\Java\jdk1.8.0_241
the above is from my login script

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment