From my early days with Solaris Jumpstart to more modern tools like Ansible, I’ve always been big on configuration management. For a long time I only thought about CM in terms of server configuration. Even when I was running Linux as my primary operating system, I never really thought about using CM to keep it configured properly. It wasn’t until I switch over to MacOS about 5 years ago I started to think about using CM to configure my laptop. I was pretty big on Ansible at the time, so I started digging into it. After I came across a couple of Ansible roles by Jeff Geerling for installing Homebrew and App Store applications, I setup my own repository for configuring MacOS.

Creating the Playbook

The first step is to set up the Ansible playbook. Since I am running it on my local laptop, the connection needs to be set to local. I put all my variables in a file called config.yaml, and then define the two roles that I want to run.

---
- hosts: all
  connection: local

  vars_files:
    - config.yml

  roles:
    - geerlingguy.homebrew
    - geerlingguy.mas

You also need to set up a requirements.yml file to install the roles.

---
- name: geerlingguy.homebrew
- name: geerlingguy.mas

Finally, create an inventory file with the following:

[localhost]
127.0.0.1

Creating the config.yaml

In the config.yaml, you need to set up the following variables:

# Homebrew Taps to use
homebrew_taps:
  - homebrew/core
  - homebrew/cask

# Homebrew packeges to install
homebrew_installed_packages:
  - python@2
  - python3
  - mas

# Homebrew Casks Apps to install
homebrew_cask_apps:
  - firefox
  - alfred
  - iterm2
  
# Mac App Store Apps to install
mas_installed_apps:
  - { id: 441258766, name: "Magnet" }

The mas package is needed for installing the Mac App Store applications, and can be used to lookup the ID for an app.

% mas search magnet
   441258766  Magnet                                                  (2.4.5)
   716628309  An Alien with a Magnet HD                               (1.0.0)
  1032456933  Magnetola - Vintage Cassette Player with Sound Softener (1.0.8)
   446490677  Edit In The Magnetic Timeline                           (7.1)

First Run

When running the playbook for the first time on a fresh MacOS install, there are a couple of things that need to be done. First thing is to log in to the Mac App Store so that when the mas role runs it can install what is necessary. The other is to get a terminal and run the following commands:

xcode-select --install
sudo easy_install pip
sudo pip install ansible
git clone https://${YOUR_GIT_REPO}
cd ${YOUR_GIT_REPO}
ansible-galaxy install -r requirements.yml
# osascript needed to setup sandboxing for the terminal
osascript -e 'tell application "Finder"' -e 'set _b to bounds of window of desktop' -e 'end tell'
ansible-playbook playbook.yml -i inventory -K

The osascript command helps setup the correct permissions for the terminal to be able to run the Ansible automation in the ansible-playbook command.

Additional Configuration

In addition to Jeff Geerling’s roles, I created a role local to the playbook called local-config, which I use to do additional configuration to my Mac, including cloning and syncing my dotfiles, setting up Python, Ruby, and Node, and cleaning up and reordering my dock.

Now, when I need to reinstall or configure a new laptop, I can have everything back to the way that I want it in about an hour without having to download everything. One of these days, I need to figure out how to configure everything that gets installed (Dropbox, 1Password, etc).