Automating Debian Linux installation

Maros Kukan
12 min readJul 31, 2023

Foreword

“You can’t build a great building on a weak foundation.”
— Gordon B. Hinckley

In the previous article titled Automating Red Hat Enterprise Linux installation, we reviewed and demonstrated the process for automating the Linux installation using kickstart file. We also took some time to look at the Anaconda installer itself and how we can customized it to fit our needs.

In this one, we will look at automating the installation process for another very popular and well established Linux distribution.

Debian Linux was initially released on September 15, 1993. It was created by Ian Murdock and is one of the earliest and most influential Linux distributions in the open-source software community.

💡Tip: I highly recommend you follow along and practice as you learn to make the most of your time and retain knowledge effectively.

Environment Setup

We first describe our starting point. To make the most of this article, you need to have hypervisor software and a web server installed.

Environment Setup

First, using a Hypervisor for practicing the automation of any OS installation has numerous advantages. These include virtual machine isolation, rapid deployment, and multiple concurrent dev-test environments.

Hypervisors facilitate testing across various OS versions and distributions without the need for multiple physical machines. They offer a safe learning environment for experimentation with new scripts and tools.

Web server will be required for hosting the installation answer file, also known as preseed. We will address this requirement with Python3’s http.server module.

💡Tip: A detailed guide how to setup development environment on Windows (including Hyper-V and Python3) can be found in Increase Your Output: Essential Tools Every Developer Needs!.

To better organize our work let’s open a PowerShell window and create a new directory in the home folder and also download the required installation media for Debian.

# Create new project directory
New-Item -ItemType Directory -Force -Path "$HOME\projects\debian"

# Move to this directory
cd $HOME\projects\debian

# Download the installation media
Invoke-WebRequest -Uri "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso" `
-OutFile debian-12.1.0-amd64-netinst.iso

# Download the checksum file
Invoke-WebRequest -Uri "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA512SUMS" `
-OutFile SHA512SUMS

# Display the expected SHA512 sum
Select-String -Pattern "debian-12" -Path SHA512SUMS

# Calculate the actual SHA512 sum
Get-FileHash -Algorithm SHA512 -Path debian-12.1.0-amd64-netinst.iso

Download the custom PowerShell wrapper scripts for Virtual Machine creation and deletion. It will streamline the VM lifecycle.

# Download and store VM Creation Script
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/maroskukan/hypervisor-cookbook/main/hyperv/scripts/vm_create.ps1" `
-OutFile "vm_create.ps1"

# Download and store VM Deletion Script
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/maroskukan/hypervisor-cookbook/main/hyperv/scripts/vm_delete.ps1" `
-OutFile "vm_delete.ps1"

That concludes the environment setup. In the next section, we will dissect the installation’s answer file.

Preseed

A preseed also called an answer file, is a configuration file used in the Debian Linux distribution to automate the installation process. It serves as a blueprint that provides answers to the installation prompts, automating the entire installation procedure, including:

  • Localization
  • Network configuration
  • Package selection
  • Disk partitioning
  • User account creation

And many more…

By using a preseed file, we can perform unattended installations, eliminating the need for manual intervention during the setup.

To use a preseed file, we must create a customized configuration that aligns with the specific requirements for our deployment. This file can be placed on an accessible server, such as a web server or a local network repository.

By appending additional kernel arguments during the installation process, we can point the installer to the location of the preseed file. This allows it to fetch the configuration and proceed with the automated installation based on the preconfigured settings.

It is also possible to include preseed file in the installation ISO file itself. This approach eliminates the need for a web server which hosts these configuration files. or even a DHCP service, however it requires a custom ISO for each node or group of nodes.

💡Tip: Preseed file is not a shell script, it does not support conditional logic (if-then-else). However, a clever use of early_command and late_ command sections can be used to produce single file for group of nodes.

This can be combined with custom ISO GRUB2 bootloader menu entry which can be invoked automatically without any interaction.

We will cover both use cases in later sections.

Preseed Customization

As described in previous section, preseed offers a huge number of customization options. It would be quite impractical to start from an empty file, and write everything by ourself.

As starting point I recommend looking at the reference example-preseed.txt file provided by Debian developers. It is heavily commented and provides a huge number of example for each section.

I have used it as well to construct the preseed files for both scenarios — single node as well as cluster automation.

Kernel Arguments

Kernel arguments, also known as boot parameters or command-line options, are parameters passed to the Linux kernel during the boot process. These arguments allow you to customize various aspects of the kernel’s behavior and can be particularly useful during Debian Linux installation customization.

Here are some common use cases and examples of kernel arguments used in this context:

  1. Hardware Configuration: Kernel arguments can be used to configure hardware settings during installation e.g. apci=off or nomodeset.
  2. Storage Configuration: You can use kernel arguments to configure storage-related settings during installation e.g. disk=sda or root=/dev/sda1.
  3. Network Configuration: Kernel arguments can be used to configure network-related settings during installation e.g. hostname=debian12.
  4. Preseed File: During Debian automated installations using Preseed files, you can use kernel arguments to point to the location of the Preseed file e.g. preseed/url=http://medium.mshome.net/preseed.cfg.
  5. Boot process Control: You can use kernel arguments to stop boot process at initramfs e.g. break=<run-time>.

💡Tip: In order to view the kernel arguments that were passed at boot time you can examine the content of /proc/cmdline.

Kernel Arguments Customization

To use these kernel arguments during Debian Linux installation customization, you typically need to access the bootloader’s menu (e.g., GRUB) and append the desired arguments to the kernel line. This can usually be done by pressing a specific key (e.g. e) when the boot menu appears, allowing you to edit the kernel parameters temporarily for that boot session.

Customizing the kernel arguments during installation can significantly improve the installation process, especially in scenarios where specific hardware configurations, networking setups, or automated installations are required.

We will use the one-time approach for automating a single node deployment in next section.

Single Node Automation

Now that we are familiar how preseed customization and kernel arguments work, it is time to get our hands dirty by performing a single node deployment.

Single Node Automation

From Hyper-V node start a web server using python3’s http.server module along with the project path that will server as web root.

# Start a simple web server
python -m http.server --directory $HOME/projects/debian

From same host provision a new virtual machine debian12. Using the wrapper script saves us from clicking around in Hyper-V Manager UI.

# Download the boilerplace preseed file
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/maroskukan/linux-cookbook/main/support/preseed/node-preseed.cfg" `
-OutFile "node-preseed.cfg"

# From Hyper-V Host, in project directory execute the script
.\vm_create.ps1 -Name debian12 `
-Size S `
-IsoPath $HOME/projects/debian\debian-12.1.0-amd64-netinst.iso

Once the Installer menu loads, hit c to invoke grub command line and using linux command specify path to kernel and additional kernel parameters. You also need to define the location of Initial RAM disk using the initrd command. Once ready type boot and enter.

linux /install.amd/vmlinuz auto=true preseed/url=http://medium.mshome.net:8000/node-preseed.cfg priority=critical ---
initrd /install.amd/initrd.gz
boot

💡Tip: In a Microsoft Hyper-V environment a virtual machine can resolve it’s default gateway address by using a combination of hostname (in my case medium) and default domain - mshome.net.

Therefore full preseed path would be $preseed_url = "http://" + $(hostname) + ".mshome.net" + ":8000/preseed_node.cfg”.

The installer will now proceed with any requiring any user intervention. In the meantime, there are several interesting log file which we can view from another console which can be accessed with ALT + F2.

Then we can follow the main log using tail -f .

# Follow the main installation log
tail -f /var/log/syslog

💡Tip: I have not found a way how to start a proper SSH server in the installation environment (BusyBox). However I have noticed that Netcat is available and can be used to attach a TCP port to shell.

nc -l -p 5000 -e /bin/sh &

Once the installation completes and the virtual machine reboots.

Verification

Verify from the Hyper-V host that the SSH service is running.

# From Hyper-V Host, verify that the VM listens on TCP/22 (SSH)
Test-NetConnection -ComputerName "debian12.mshome.net" `
-Port 22 `
| Select-Object -Property `
@{Name="ComputerName"; Expression={$_.ComputerName}}, `
@{Name="RemoteAddress"; Expression={$_.RemoteAddress.IPAddressToString}}, `
@{Name="TcpTestSucceeded"; Expression={$_.TcpTestSucceeded}} `
| ConvertTo-Json

An example of happy output would look like the following.

{
"ComputerName": "debian12.mshome.net",
"RemoteAddress": "172.20.52.144",
"TcpTestSucceeded": true
}

In summary, we deployed Debian Linux automatically.

Cleanup

When done exploring the environment, we can clean up by removing the debian12 virtual machine.

# From Hyper-V Host, delete the virtual machine
.\vm_delete.ps1 -name debian12

⚠️Warning: I encourage you to keep this virtual machine around until you finish the exercise where we deploy a three node cluster with single pressed in next section.

Debian Installation Media

The Debian installation media refers to the physical or digital media used to boot and initiate the installation process for the Debian operating system. The installation media provides the necessary files, tools, and packages to start the installation and configure the Debian system on a target computer.

The GRUB2 (Grand Unified Bootloader version 2) bootloader is used to handle the boot process of the installation environment. GRUB2 is responsible for loading the Linux kernel and initializing the Debian installer, allowing users to start the installation process and configure the system.

You may observed that when we loaded the installation media the bootloader is waiting for our input. There is automatic timer for selecting the default entry. For true unattended installation we need to add a timer and some default kernel arguments.

💡Tip: We could include the preseed file directly in the ISO further decreasing our dependency for an external web server.

Installation Media Customization

The general approach for customizing an installation media can be summarized in the following key steps:

  1. Download and unpack the upstream installation source ISO image
  2. Perform necessary file changes (e.g., boot loader entries, default values, timers, etc.)
  3. Repack the files to create a new custom ISO image

The devel is in the details, as the process for repacking the Debian installation CD is not the same as for Red Hat Enterprise Linux.

When working with new distribution, it is sometimes worthwhile to refer to the upstream documentation. Again kudos to Debian devs. Their docs are very usable.

Cluster Automation

In this section, we are going to describe and implement a solution for deploying multiple nodes with single ISO image and preseed file.

Cluster Automation

Deployment Part 1 — Custom ISO

In the first portion of this solution, we focus on custom installation ISO generation.

We start by logging in to the existing debian12 virtual machine which we created in previous section. Then we download and then verify the integrity of the Debian netinst ISO image.

💡Tip: The debian12 VM is running SSH server by default. You can connect to it from Hyper-V host using ssh ansible@debian12.mshome.net with password of ansible.

# Download the Debian netist ISO image
wget -O debian-12.1.0-amd64-netinst.iso \
https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso

# Download the SHA512 checksum file
wget -O SHA512SUMS \
https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA512SUMS

# Verify the ISO image integrity
sha512sum -c SHA512SUMS --ignore-missing

debian-12.1.0-amd64-netinst.iso: OK

Next, we need to extract the MBR template (first 432 bytes) from the original ISO.

At this point we will also create a temporary directory and extract the contents from the original ISO file. This is the place where we will do our modifications.

# Extract MBR template
dd if=$HOME/debian-12.1.0-amd64-netinst.iso bs=1 count=432 of=isohdpfx.bin

# Create a temporary directory
tmpdir=$(mktemp -d)

# Extract the ISO
xorriso -osirrox on -indev $HOME/debian-12.1.0-amd64-netinst.iso \
-extract / $tmpdir

Next, we update the GRUB2 configuration file for EUFI-based systems.

# Add 5s timeout
sudo sed -i '/^play/ a set timeout=5' $tmpdir/boot/grub/grub.cfg

# Update default menu entry
sudo sed -i '/^set timeout/ a set default="2>5"' $tmpdir/boot/grub/grub.cfg

# Append kernel parameters
sudo sed -i '77s,^.*$, linux /install.amd/vmlinuz auto=true priority=critical vga=788 preseed/url=http://medium.mshome.net:8000/cluster-preseed.cfg --- quiet,' $tmpdir/boot/grub/grub.cfg

💡Tip: Unfortunately, for the kernel parameters I had to fall down to line number matching to replace the kernel arguments. In real world you would likely keep these files in version control.

Next, we update the ISOLINUX configuration file for BIOS-based systems.

# Add timeout from 0 (disabled) to 5s
sudo sed -i 's/timeout 0/timeout 50/' $tmpdir/isolinux/isolinux.cfg

# Select Automated install as default
sudo sed -i '/menu label \^Automated install/ a\ menu default' $tmpdir/isolinux/adtxt.cfg

# Append kernel parameters
sudo sed -i 's#append auto=true priority=critical vga=788 initrd=/install\.amd/initrd\.gz --- quiet#append auto=true priority=critical vga=788 initrd=/install.amd/initrd.gz preseed/url=http://medium.mshome.net:8000/cluster-preseed.cfg --- quiet#' $tmpdir/isolinux/adtxt.cfg

💡Tip: More information about the repack process can be found at RepackBootableISO and ModifyCD page.

After these file changes it is time to generate a new ISO image.

# Install tools required to generate ISO image
apt install -y xorriso

# Create ISO image
xorriso -as mkisofs \
-r -V 'Debian 12.1.0 amd64 n' \
-J -J -joliet-long -cache-inodes \
-isohybrid-mbr isohdpfx.bin \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-boot-load-size 4 -boot-info-table -no-emul-boot \
-eltorito-alt-boot \
-e boot/grub/efi.img \
-no-emul-boot -isohybrid-gpt-basdat -isohybrid-apm-hfsplus \
-o /tmp/debian-12.1.0-amd64-cluster.iso \
$tmpdir

Before we we can remove the temporary directory, we need to fix the file permissions.

# Add user-writable permissions for files
find $tmpdir -type d -exec chmod u+w '{}' ';'

# Remove the temporary directory
rm -rf $tmpdir

Finally, from the Hyper-V we download the newly generated ISO image.

# Download the custom ISO from debian12 guest
scp ansible@debian12.mshome.net:/tmp/debian-12.1.0-amd64-cluster.iso .

Deployment Part 2 — Custom Preseed

From Hyper-V Host open PowerShell and start a web server. This is required to host the cluster-preseed.cfg which we soon prepare.

python -m http.server --directory $HOME/projects/debian

Serving HTTP on :: port 8000 (http://[::]:8000/) ...

From same host provision a new virtual machine debian12.

# From Hyper-V Host, in project directory execute the script
$vmNames = "node1", "node2", "node3"

# Create three new VMs
foreach ($vmName in $vmNames) {
.\vm_create.ps1 -name $vmName `
-size XS `
-isoPath "debian-12.1.0-amd64-cluster.iso" `
-state create
}

Below is an example output from the script above.

{
"ElementName": "node1",
"BIOSSerialNumber": "8669-1645-1055-9872-8113-0647-92"
}
{
"ElementName": "node2",
"BIOSSerialNumber": "9814-0909-0086-2094-5925-4183-47"
}
{
"ElementName": "node3",
"BIOSSerialNumber": "9007-2555-4883-9065-1728-2512-44"
}

Next, download and update the cluster-preseed.cfg file. We need update the placeholders with values from the above output.


# Download the boilerplace preseed file
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/maroskukan/linux-cookbook/main/support/preseed/cluster-preseed.cfg" `
-OutFile "cluster-preseed.cfg"

# Update the serial numbers for the three VMs
$replacements = @(
@{ OldValue = "VM1_SN_PLACEHOLDER"; NewValue = "8669-1645-1055-9872-8113-0647-92" },
@{ OldValue = "VM2_SN_PLACEHOLDER"; NewValue = "9814-0909-0086-2094-5925-4183-47" },
@{ OldValue = "VM3_SN_PLACEHOLDER"; NewValue = "9007-2555-4883-9065-1728-2512-44" }
)

$file = "cluster-preseed.cfg"

(Get-Content -Path $file) | ForEach-Object {
$line = $_
foreach ($replacement in $replacements) {
$line = $line -replace $replacement.OldValue, $replacement.NewValue
}
$line
} | Set-Content -Path $file

⚠️Warning: Be mindful of line ending, make sure that is set to LF instead of CRLF.

Finally, from Hyper-V host start all three nodes.

foreach ($vmName in $vmNames) {
Start-VM $vmName
}

From the console where you started the web server you should see how each node grabs the preseed file. This is a good indication that ISO customization worked as expected.

::ffff:172.17.67.164 - - [28/Jul/2023 02:50:25] "GET /cluster-preseed.cfg HTTP/1.1" 200 -
::ffff:172.17.76.244 - - [28/Jul/2023 02:50:25] "GET /cluster-preseed.cfg HTTP/1.1" 200 -
::ffff:172.17.75.100 - - [28/Jul/2023 02:50:27] "GET /cluster-preseed.cfg HTTP/1.1" 200 -

Verification

After a few minutes, perform a simple connection test targeting SSH.

# From Hyper-V Host, verify if VMs listen on TCP/22 (SSH)
foreach ($vmName in $vmNames) {
Test-NetConnection -ComputerName "$vmName.mshome.net" `
-Port 22 `
| Select-Object -Property `
@{Name="ComputerName"; Expression={$_.ComputerName}}, `
@{Name="RemoteAddress"; Expression={$_.RemoteAddress.IPAddressToString}}, `
@{Name="TcpTestSucceeded"; Expression={$_.TcpTestSucceeded}} `
| ConvertTo-Json
}

An example of happy output would look like the following:

{
"ComputerName": "node1.mshome.net",
"RemoteAddress": "172.17.67.164",
"TcpTestSucceeded": true
}
{
"ComputerName": "node2.mshome.net",
"RemoteAddress": "172.17.76.244",
"TcpTestSucceeded": true
}
{
"ComputerName": "node3.mshome.net",
"RemoteAddress": "172.17.76.244",
"TcpTestSucceeded": true
}

Cleanup

When done exploring each node, we can clean up by removing the three virtual machines.

foreach ($vmName in $vmNames) {
.\vm_delete.ps1 -name $vmName
}

Closing thoughts

To sum up, the automation of Debian Linux installation not only saves time but also increases consistency and enhances productivity for system administrators. I enjoyed writing this article. I hope you learned something new and valuable performing the practical examples. I would love to hear about your experience in the comment section below.

--

--