I recently got a new Macbook Pro laptop, and used Ansible for most of the setup. In my last post, I covered essential Ansible usage. This post will walk through an example laptop configuration.
Grabbing a Snapshot of the Ansible Config
If the Ansible playbook is in a remote git repo, it can just be installed with:
$ git clone ssh://DOMAIN/PATH_TO_REPO/
Depending on where the git repo is hosted, this may require logging in. Running git for the first time may automatically download the Command Line Tools. If not, they will be downloaded within the next few steps.
Bootstrapping Ansible
The example repo has a couple scripts in the bin/
directory. In particular, bin/bootstrap_osx
will install homebrew and then download Ansible.
The recommended install method involves curling directly to Ruby. My bootstrap script downloads the installer, ensures that it downloaded completely, and then prompts whether to run it or not (giving a chance to audit the installer).
If necessary, installing homebrew will also install the Xcode Command Line Tools package, which provides a C compiler and other important dependencies.
Public Keys
Since Ansible is typically ssh-ing into remote computers, it’s best to use public keys and ssh-admin, rather than entering passwords all the time. One of the first tasks in my ‘common’ role is to install SSH public keys with the authorized_key module if not already present.
To create an SSH key for the new laptop, run:
$ ssh-keygen
It will create private and public keys. By default, the public key will be in ~/.ssh/id_rsa.pub
. If the Ansible playbook involves signing in to remote servers, it will be much less trouble to add the key to their authorized keys.
To add the key to the SSH agent on OSX, use ssh-add KEY_FILE
. (Linux and BSD users will probably need to run something like eval $(ssh-agent -s)
to start and/or reconnect to their SSH agent.)
The authorized_key
module can do this:
- name: install SSH key (LAPTOP NAME)
sudo: no
authorized_key:
key: "ssh-rsa ... [email protected]"
user: '{{ansible_user_id}}'
state: present
The password will need to be typed during the first connection, but the playbook’s remaining tasks can use it.
The example repo’s bin/setup_ssh_key
script will run ssh-keygen
to generate a new key (if not present), and then add it to the Ansible playbook as described above.
Running the Playbook
Then, just run the playbook. Either specify a hosts file (via symlink or
-i
, for “inventory”), or edit the default one to include “localhost”. (The included script, bin/link_paths
, will create these symlinks.)
ansible-playbook will need to be able to log in to the target computer. Either configure SSH so that ssh localhost
works without a password prompt, or provide the -c local
option to explicitly run the playbook locally.
# -K: prompt for sudo password; -c local: use local connection
# if the playbook has more than one host, use -l HOSTNAME to limit
$ ansible-playbook -K -c local playbookname.yml
and let it run. Since the playbook is installing stuff, it will probably take some time downloading packages the first time.
If the git_homedir
role is used, the variables in group_vars/all
that define the git repository host and path will need to be configured.
Recovering from Mistakes Along the Way
Playbooks may need a few minor adjustments along the way. (Mine error’d out because gdb isn’t in base homebrew anymore.) After making updates, the playbook can be resumed with ansible-playbook --start TASK_NAME
. This is also useful while developing new playbooks.
The -K option prompts for a sudo password upfront, and catches most things, but sometimes commands require password input along the way. For example, the playbook may need to log in to a remote git repository. If this times out and the playbook fails, --start
can resume there, prompt immediately, and continue.
The homebrew and homebrew_cask Modules
Many packages can be installed via either the homebrew or homebrew_cask modules. To search homebrew, use brewformulas. For homebrew_cask, use caskroom.
To install packages via homebrew:
- name: install stuff
homebrew:
name: "{{item}}"
state: present
with_items:
- cowsay
- fuzzy-find
- tmux
For homebrew_cask, use:
- name: install casks
homebrew_cask:
name: "{{item}}"
state: present
with_items:
- arduino
- flux
- mou
...
Other Setup
Many other OSX preferences can be set via the defaults
command, and I found several useful settings options in Mathias Bynens’s dotfiles repo.
Individual shell commands can be run like this:
- name: automatically hide and show the Dock
sudo: yes
command: defaults write com.apple.dock autohide -bool true
- name: significantly speed up expose (skip animation)
sudo: yes
command: defaults write com.apple.dock expose-animation-duration -int 0
notify:
- restart the dock
The defaults
command seems to be idempotent, so writing settings with it multiple times should be fine.
My home directory is already tracked in git and set up via Ansible.
After the playbook finishes, my laptop is up to date. Down the line, if I make changes via the Ansible playbook, I’ll know that I can reproduce my setup.