These days, almost all my installations are done using Ansible or Chef and tested using test-kitchen or molecule. However good end-to-end tests benefit from being able to start with a fresh build of your base OS system. Having to restore sdcards when I want fresh Pi, makes this something of a chore.

Obviously virtualization/containerization makes this easy for x86_64 systems. However raspberry pis are on the Arm architecture, and emulating this on an x86_64 based linux desktop is not that straightforward. So an alternative solution is to create a restore partition which can be used factory reset the Pi back to its original state. We can then use this as the provisioner during setup in molecule or test-kitchen.

Or it can just be used to reset the pi when you have messed it up!!

This article was inspired by this blog post, which provided the main idea. Other steps culled from stack exchange and the raspberry pi forums.

The main requirement for this project is to be able to reset the rpi over ssh with a single command. It is mostly useful for development and testing where you are not keeping persistent data or configurations on the pi. Be warned, don’t use this if you intend the Pi to be used as a production system, it will blat the pi and the sdcard.

If you are not interested in the details, then I will start with a short demo on remotely reseting, and the second section on creating the recovery image.

I have a script create-factory-reset which modifies the base image to add the tools for resetting. It modifies the raspbian image to produce a new image which you use to flash to your rpi. once installed, it works like this.

What it does…?

Login to the machine over ssh, as as root, issue the reset command;

$ ssh pi@raspberrypi.local

pi@raspberrypi:~ $ sudo su -
root@raspberrypi:~# /boot/factory_reset --reset
factory restore script
resetting
rebooting...
Connection to raspberrypi.local closed by remote host.
Connection to raspberrypi.local closed.

The Pi will reset itself…

My helpful screenshot

And if you wait 10 minutes or so… It will replace the / file system with a pristine copy of raspbian (and enabled ssh access at the same time), and you will be able to login again to fresh Raspberry Pi over ssh;

$ ssh pi@raspberrypi.local
Warning: Permanently added 'raspberrypi.local,192.168.0.23' (ECDSA) to the list of known hosts.
pi@raspberrypi.locals password:
Linux raspberrypi 4.9.80+ #1098 Fri Mar 9 18:51:28 GMT 2018 armv6l

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

pi@raspberrypi:~ $

How to do this…

Follow these steps;

Checkout the repo with the script.

The script to create the factory-reset partition is included as part of an ansible role which I use to setup my rpis. For the factory reset mode, the scripts are in the ~/files directory of that repo.

First clone the repo

$ cd ~/git
$ git clone https://github.com/limepepper/ansible-role-raspberrypi
Cloning into 'ansible-role-raspberrypi'...
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.

Switch to the files directory

$ cd ansible-role-raspberrypi/files
$ ls -1
create-factory-reset
init_resize2.sh
notes-on-creating-recovery.txt
display_funcs
revision_data.json

Download raspian stretch lite, and unzip it into the files directory

$ wget -q https://downloads.raspberrypi.org/raspbian_lite_latest
raspbian_lite_latest     16%[=====>    ]  58.31M  9.05MB/s    eta 26s

$ unzip raspbian_lite_latest
Archive:  raspbian_lite_latest
  inflating: 2018-03-13-raspbian-stretch-lite.img

Verify loopback is clear of other devices

Currently the script will detach any loopback devices, so make sure you don’t have anything on /dev/loop0 etc.

$ losetup -a -v

should return nothing…

Run the setup script to generate the img file;

$ sudo bash ./create-factory-reset

PARTUUID generated is c385fba9
detach any existing loop devices
make a copy of the pristine image for use as the live rootfs
...
copying the recovery image to the recovery /opt dir for restoring
511+1 records in
511+1 records out
2147479552 bytes (2.1 GB, 2.0 GiB) copied, 8.44098 s, 254 MB/s

detach loop devices

This process creates an img file;

2018-03-13-raspbian-stretch-lite.restore.img

Write the restore img to an sdcard.

The recovery partition is currently 4G so you probably want an sdcard at least 32GB, though you might get away with 16GB if you aren’t planning to install much else.

First determine which device is your sdcard. Generally I check dmesg immediately after I plug the card. The output will contain the identifier for the new device. ;

$ dmesg

[473478.615634] sd 7:0:0:1: Attached scsi generic sg4 type 0
[473480.141565] sd 7:0:0:1: [sdd] 60751872 512-byte logical blocks: (31.1 GB/29.0 GiB)
[473480.142326] sd 7:0:0:1: [sdd] Write Protect is off
[473480.142331] sd 7:0:0:1: [sdd] Mode Sense: 2f 00 00 00
[473480.143278] sd 7:0:0:1: [sdd] Write cache: disabled, read cache: enabled
[473480.148969]  sdd: sdd1 sdd2 sdd3
[473480.151080] sd 7:0:0:1: [sdd] Attached SCSI removable disk

You can double check that the device you found corresponds to the size you expect for the sdcard;

$ sudo fdisk -lu /dev/sdd
Disk /dev/sdd: 29 GiB, 31104958464 bytes, 60751872 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: 0x2228528e

Device     Boot   Start      End Sectors  Size Id Type
/dev/sdd1          8192    93802   85611 41.8M  c W95 FAT32 (LBA)
/dev/sdd2         98304  8486911 8388608    4G 83 Linux
/dev/sdd3       8486920 12681215 4194296    2G 83 Linux

WARNING!!! The next step will blat any data on the sdcard!!!!

So my card is at /dev/sdd. I write the restore img to that device;

$ sudo dd bs=4M \
      if=2018-03-13-raspbian-stretch-lite.restore.img \
      of=/dev/sdd \
      conv=fsync \
      status=progress
8388608000 bytes (8.4 GB, 7.8 GiB) copied, 134.145 s, 62.5 MB/s
2000+0 records in
2000+0 records out
8388608000 bytes (8.4 GB, 7.8 GiB) copied, 731.165 s, 11.5 MB/s      

Boot the Raspberry PI from the sdcard

Now when you boot from the sdcard image, you have additional commands available;

$ ssh pi@raspberrypi.local
Linux raspberrypi 4.9.80+ #1098 Fri Mar 9 18:51:28 GMT 2018 armv6l
ls -lah  /boot/factory_reset
-rwxr-xr-x 1 root root 786 Apr 15 00:36 /boot/factory_reset

WARNING!!! The next step will blat the rpi and any data on it!!!!

Factory reset the Raspberry Pi

use the --reset option to /boot/factory_reset to trigger restoration;

$ sudo /boot/factory_reset --reset
resetting
rebooting...
Connection to raspberrypi.local closed by remote host.
Connection to raspberrypi.local closed.

The pi will boot to the recovery partition and restore itself to factory condition;

My helpful screenshot

And now when the Pi reboots, it will boot to a fresh copy of Raspbian lite with everything restored to factory settings;

[~] $ ssh pi@raspberrypi.local

Warning: Permanently added 'raspberrypi.local,192.168.0.23' (ECDSA) to the list of known hosts.
pi@raspberrypi.locals password:
Linux raspberrypi 4.9.80+ #1098 Fri Mar 9 18:51:28 GMT 2018 armv6l

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

pi@raspberrypi:~ $

If you want to restore other distros, such as the full fat desktop, you will probably have to checkout the next part in this series. How to create the image