Automatically build Raspberry PIs using Ansible

I started to build Raspberry PI images automatically using packer-builder-arm. This combined with Ansible is very reproducible tooling to automatically build images for only slightly different RaspberryPIs.

I need to automatically add a SSH key for the pi user, a wpa_supplicant config and a Tinc setup with the private key for exactly this PI.

But first I want to see if everything is good without flashing the PI every time. This can be done by mounting the image. To find out which sector is the start sector we can use fdisk like this:

$ fdisk -l raspios-arm.img

Disk raspios-arm.img: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x8acef004

Device           Boot  Start     End Sectors  Size Id Type
raspios-arm.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
raspios-arm.img2      532480 8388607 7856128  3.7G 83 Linux

The start sector of the root partition is 532480 multiplied with the sector size of 512 bytes. So to mount the root partition we can run this:

mkdir -p root
sudo mount raspios-arm.img -o loop,offset=$(( 512 * 532480 )) root/

But now to the Ansible setup. I use this to setup Rasperry PIs and add then the actual sensor and scripts manually. The important parts for me are Wifi, SSH and (Tinc) VPN, so that I can login when they are deployed. To store this setup in a public repository, for others to see, I use Ansible Vault a lot.

The ssh playbook adds the ssh key and activates the service. Nobody needs to know the key I use for my PIs so the key is stored encrypted.


- name: ssh keys
    user: pi
    exclusive: true
    key: !vault |
- name: Enable ssh service
    name: ssh
    enabled: true

Creating the "key" entry could be done like this:

cat ~/.ssh/ | ansible-vault encrypt_string --vault-password-file ansible/.vault_pass.txt --stdin-name 'key'

The other setups for WiFi and Tinc are similar. Tinc needs a few files to be generated so I used templates for them with a few variables from Ansible Vault. Differences between the PIs are stored in a variables file for each PI. Additionally a common.yaml with the variables that are shared.

My current setup is in a public repository and I will add more of my PIs in the following weeks.