Auto-Install your Workstation’s OS

Posted on Oct 26, 2019 by VI

Somethings about Debian we operators love is the possibility to automate the installation process with a preseed file.
Another thing we love is Ansible, since it makes our lives much easier.

Usually we use these tools to automate the setup of our server systems. But since servers are just computers, why not apply these tools to the computers we like to spend so much time in front of.

This post is a guide demonstrating a method how to build your own custom autoinstaller.

The Debian Installer

If you’re not familiar with automated Debian installations, here is a little recap about preseeding the Debian installer.

The Preseed File

If you install a Debian system by hand, the installer will ask you a series of questions about your installation. A preseed file is basically a text file containing the answers to these questions. To write a preseed file, download the example file and edit it to your needs.
There are three ways to use a preseed file during the installation process: * Including the file in the installation media * Including the file in the installers initrd * Downloading the file from a http-server

Building your own

Testing the Preseed File

Before building an iso image you might like to test and modify your preseed file. To do so I’d recommend to setup a small http-server serving the preseed.cfg your machine.
With that in place you can setup a virtual machine, attach the Debian-stabele netinstall CD and choose Advanced OptionsAutomated Installation. The installer will prompt you for the url to fetch the preseed file from.
This way you can modify the file and re-run the installation easily.

After the Installation

Usually, you will perform several tasks after a fresh installation. This may include system settings, copying dotfiles and installing software. Also, if you are using Debian on a Laptop/Workstation you may want to upgrade the system from stable to unstable.
Ansible is the appropriate tool for these postinstall tasks. Since you have just setup a clean debian VM, you can start a ssh-server in it and use Ansible on the host system to setup your machine.

Building the Installation Media

The ultimate goal of this guide ia a iso-image you can dd on a usb-stick and impress your friends how smooth and easy your debian installation goes.

Extracting the Installer’s Filesystem

First you need to unpack the netinstaller’s iso:

mkdir isofs
bsdtar -C isofs -xf debian-netinstall.iso

Preparing the playbooks

When upgrading the OS with Ansible, most tasks after the OS-upgrades will fail due to changes in the python libraries Ansible uses. Therefore the postinstall playbook must be split into two separate ones. One for the OS upgrade and one for all other tasks.
Since these playbooks will be run on the freshly installed machine itself, change the host directives to localhost.

To give you an example, here are the playbooks I used:

- name: Setup a working Workstation
  hosts: localhost
    - name: Check for upgrade state
        path: /.testing
      register: upgrade_testing
    - name: change sources.list to testing
        dest: /etc/apt/sources.list
        content: |
          deb testing main non-free contrib
          #deb-src testing main non-free contrib
          deb testing-updates main contrib non-free
          #deb-src testing-updates main contrib non-free
      when: not upgrade_testing.stat.exists
    - name: update OS to testing
        update_cache: yes
        upgrade: dist
      when: not upgrade_testing.stat.exists
    - name: save upgrade-state
        dest: /.testing
        content: " "
    - name: change sources.list to sid
        dest: /etc/apt/sources.list
        content: |
          deb sid main non-free contrib
          #deb-src sid main non-free contrib
    - name: update OS to sid
        update_cache: yes
        upgrade: dist
- name: Setup a working Workstation
  hosts: localhost
    - name: create getty-override folder
        path: /etc/systemd/system/getty@tty1.service.d
        state: directory
    - name: Setup autologin
        dest: /etc/systemd/system/getty@tty1.service.d/override.conf
        content: |
          ExecStart=-/sbin/agetty --autologin vincent --noclear %I $TERM
    - name: install some software
          - neovim
          - git
          - zsh
          - firefox
          - thunderbird
          - awesome
          - awesome-extra
          - redshift-gtk
          - caffeine
          - keepassx
          - exa
          - fzf
          - ripgrep
          - pdfgrep
          - zathura
          - zathura-pdf-poppler
          - okular
          - asciidoctor
          - pandoc
          - network-manager
          - network-manager-gnome
          - cbatticon
          - pasystray
          - pulseaudio
          - xorg
          - xss-lock
          - i3lock
          - lxterminal
        state: present
    - name: create xorg.conf.d
        path: /etc/X11/xorg.conf.d
        state: directory
    - name: Setup Keyboard
        dest: /etc/X11/xorg.conf.d/00-keyboard.conf
        content: |
          Section "InputClass"
                  Identifier "system-keyboard"
                  MatchIsKeyboard "on"
                  Option "XkbLayout" "us"
                  Option "XkbModel" "pc104"
                  Option "XkbVariant" "altgr-intl"
                  Option "XkbOptions" "caps:swapescape"
    - name: check for zsh installation
        path: /usr/bin/zsh
      register: zsh
    - name: change user shell to zsh
        name: vincent
        shell: /usr/bin/zsh
      when: zsh.stat.exists

Deploying Files

Create a folder rootfs that includes the file-structure you want to be present on the freshly installed system. This might for example look like this:

├── home
│   └── vincent
│       ├── .config
│       ├── .gitconfig
│       ├── .oh-my-zsh
│       ├── .vim
│       ├── .vimrc
│       ├── .xinitrc
│       ├── .zprofile
│       └── .zshrc
└── root
    ├── postinstall2.yml
    └── postinstall.yml

Now let’s create a .tar archive of this folder structure and add it to the installation media:

cp -r rootfs rootfs.tmp
sudo chown -R root:root rootfs.tmp
sudo chmod 700 rootfs/root
sudo tar -C rootfs.tmp -cf postinstall.tar ./
sudo mv postinstall.tar isofs/
sudo rm -r rootfs.tmp

The preseed.cfg

Some modifications have to be made to your preseed.cfg.
First, you need to add ansible to the packages to be installed:

# Individual additional packages to install
d-i pkgsel/include string ansible

Secondly, you need to add a preseed/late_command directive that unpacks the tar archive:

d-i preseed/late_command string tar -C /target -xf /cdrom/postinstall.tar; \
in-target chown -R vincent:vincent /home/vincent;

Now the preseed-file has to be added to the installer’s initrd:

chmod +w -R isofs/install.amd
gunzip isofs/install.amd/initrd.gz
echo preseed.cfg | cpio -H newc -o -A -F isofs/install.amd/initrd
gzip isofs/install.amd/initrd
chmod -R -w isofs/install.amd

Generating a new Iso Image

After altering the content of the Image we need to re-calculate the checksums:

chmod +w isofs/md5sum.txt
cd isofs
md5sum `find -follow -type f` > md5sum.txt
chmod -w md5sum.txt
cd ..

Now we can generate a .iso image:

sudo genisoimage -quiet -V "preseed-autoinstall" -J -R -r -l -cache-inodes \
-c isolinux/  -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 \
-boot-info-table -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot \
-o preseed-autoinstall.iso isofs

isohybrid --uefi preseed-autoinstall.iso

Et violá, we have a .iso image!


Now, dd it on a flashdrive, boot your workstation from it and let the autoinstaller do it’s magic.
After the first boot simply run the two playbooks:

sudo ansible-playbook /root/postinstall.yml
sudo ansible-playbook /root/postinstall2.yml

Congratulations, you are good to go.

Tags: linuxdebianansible