Using Ansible to Bootstrap My Work Environment Part 2

Besides my standard day-to-day laptop running Ubuntu, I also have a Chromebook I picked up a while back. I like the light weight and the long battery life for traveling around. ChromeOS along covers a lot of what I do daily, but I do like to have a few additional linux tools to get things done so I use crouton.

With crouton there’s a few manual steps involved in preparing the Chromebook but they are well documented. You need to put it in developer mode, download the script and kick it off. It takes a good while to run on that relatively limited hardware but at the end you add your initial user account and have a mostly bare bones Ubuntu install depending on the options selected. Currently I do crouton -t xenial -r xiwi,lxde-desktop for my crouton setup. I can launch a full lxde desktop or run GUI apps in a window on the ChromeOS desktop that way. I also had to manually install and start sshd so Ansible running on my laptop could connect.

At this point I could start creating my ansible roles and playbooks. One role would be the crouton specific bits and then a separate role for common items. So here’s ~/source/ansible-mystuff/roles/crouton/tasks/main.yml:


- name: update /etc/rc.local
  become: yes
    src: rc.local
    dest: /etc/rc.local
    mode: 0755

This will be sourced automatically from the primary playbook in ~/source/ansible-mystuff/utilhost.yml when called via ansible-playbook:


- hosts: crouton
    - role: crouton
    - role: common

This is pretty small. The only real crouton-specific item provided via the role is that /etc/rc.local file which, because it has executable permissions, will be sourced and executed when the crouton chroot is started (eg with sudo enter-chroot from the Chrome developer shell accessed from Crosh) :

#!/bin/sh -e
# rc.local
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
# In order to enable or disable this script just change the execution
# bits.
# By default this script does nothing.

# for ssh
if [ ! -d /var/run/sshd ]; then
    mkdir /var/run/sshd
/sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT

# for vpnc
stop shill 
start shill BLACKLISTED_DEVICES=tun0
mkdir -p /run/resolvconf/interface
cp /etc/resolv.conf /run/resolvconf/resolv.conf
mv /etc/resolv.conf /run/resolvconf/interface/mlan0
ln -s /run/resolvconf/resolv.conf /etc/resolv.conf
resolvconf --enable-updates

exit 0

So this will add a few things, some of which are relevant to roles/common/tasks/main.yml but also start sshd. As is my habit I link to where I got info from for future reference in the comments (“wait, why did I put that there?“) In this case I’m working around the crouton chroot and inability to run services via systemd in that particular setup at least for now. The rest of the items are specific to vpnc which I use for some ad-hoc client vpn connections. ChromeOS doesn’t natively support ipsec VPNs but I can work around that with crouton.

It takes a bit of troubleshooting and research to build this up and get it right so it’s restartable. Ansible’s idempotent execution model helps here so you can run things repeatedly. For example I had to learn that I needed that mkdir -p /run/resolvconf/interface at chroot start via trial and error. But then I put it in the file and update my ansible task and capture that change via git. Now, when I replace that Chromebook at some point, I can just roll it out via Ansible and I won’t stumble across that same issue and have to research it anew or try and hunt through various note files where I may or may not have captured the detail. This way, it is captured both as documentation and it will get done.

If something changes in ChromeOS and this breaks, I’ll catch it, make the update, and push it out via Ansible rather than just fixing the change subject to future forgetfulness.

The other important bit is that this is testable. After I was confident I could do all the Ansible bits to the chroot, I complete wiped out the crouton chroot, built it from scratch (which does take a while) and pushed it all out in one pass with Ansible.

The rest of the action is in roles/common/tasks/main.yml which I’ll dive into in my next post

comments powered by Disqus