Using Ansible to synchronize your development machines - Part 1

Recently I've found myself with several machines (home, client1, client2, etc) and got tired of setting up my bash environment, themes, and libs to run my development environments. To combat this, I thought I would give automating some things a try using a few of my favorite tools.


Ansible is an automation tool used to provision machines to be configured and ran in a very specific and reproducible way.

You might do things like:

  • Configure a web server to run your application.
  • Setup user accounts, folders, and permissions on a remote host.
  • Deploy your application.

While containerization (Docker) has eliminated the need for Ansible for a lot of my use-cases, it's still a great tool if you ever find yourself wanting to run configuration/setup on a bunch of remote machines.

Think of it like declarative bash, configured in yaml.

A note on Ansible nomenclature

When working with Ansible you'll see things like role, playbook, group_vars, and such. A deep explanation of what those are and how to use them is outside the scope of this post, but you can think of it like this:

  • a role is a collection of tasks and vars that produces a desired state.
  • the inventory is a list of all hosts that Ansible can communicate with.
  • a playbook will apply roles to your hosts defined in your inventory.

The Setup

Check out my initial setup here.

At this stage in the repo, it has the following features:

  • An init script that will install local dependencies (Brew, Ansible, Python)
  • An Ansible role that sets up gitlab-runner

It starts here with the Makefile


	@sh ./scripts/

	ansible-galaxy install -r requirements.yml

provision: install-galaxy
	ansible-playbook mac.yml $(PLAYBOOK_ARGS)

	ansible-lint mac.yml

	ansible-playbook mac.yml --syntax-check

.PHONY: all install provision lint syntax

(Make is a great tool to use for running cross-platform tasks).


Running this will run the init script found in the scripts directory. This will setup your baseline dependencies; Homebrew, Python (for Ansible), and Ansible itself.

# /bin/bash
set -e

DIR="$(dirname "$(which "$0")")"

source $DIR/

# NOTE: this hasn't been done yet on a fresh machine, so use at your
# own caution. Will update when this is run with a fresh OS install.
# It also will only be tested on the latest version of Mac OSx, so
# don't expect any support for the older OS distros.

echo "\nChecking xcode and Homebrew..."

# Update xcode
if ! xcode-select -p; then
    echo "\n   Installing xcode CLI tools"
    xcode-select --install

# Homebrew
if ! command_exists brew; then
    echo "\n   Installing Homebrew"
    /usr/bin/ruby -e "$(curl -fsSL"

# export PATH=/usr/local/bin:/usr/local/sbin:$PATH

brew update > /dev/null
brew doctor > /dev/null

echo "\nChecking Brew and Brewfile..."
brew --version
brew bundle

echo "\nChecking Python..."
python3 --version

echo "\nInstalling Ansible..."
pip3 install -r requirements.txt > /dev/null

echo "\nChecking Ansible..."
if ! command_exists ansible; then
    echo "\n ERR: Ansible not installed."
    exit 1

ansible --version

echo "\nDONE"

make provision

This will run Ansible itself, configuring your machine.

ansible-playbook mac.yml


While I build this out I want to make sure I have some constraints in place to prevent creeping scope:

  • Keep as few development dependencies on the host machine as possible.
  • Keep the number of manual steps within reason.
  • Don't go down authentication rabbit holes; e.g you will have to login yourself.


  • Entire bash environment with dependencies in one click.
  • Document any manual step and make it obvious when you must execute them.

Next Steps

The things I want to get setup next are:

  • Pull my dot files from my dot file repo.
  • Setup iTerm and my themes.
  • Setup nvm, rbenv, php, and virtualenv.
  • Setup my applications (VSCode, 1password, ¬†etc).