Skip to content

Instantly share code, notes, and snippets.

@ianballou
Last active January 20, 2026 22:36
Show Gist options
  • Select an option

  • Save ianballou/9e38948de7b4e9064e6777cacfccd9a4 to your computer and use it in GitHub Desktop.

Select an option

Save ianballou/9e38948de7b4e9064e6777cacfccd9a4 to your computer and use it in GitHub Desktop.
Default Foreman kickstart provisioning template for image mode machines
<%#
kind: provision
name: Kickstart Default bootc - Trimmed
model: ProvisioningTemplate
oses:
- AlmaLinux
- CentOS
- CentOS_Stream
- Fedora
- RedHat
- Rocky
-%>
<%
# Variable setup for post scripts and container URL
ostreecontainer = host_param('ostreecontainer')
-%>
# This kickstart file was rendered from the Foreman provisioning template "<%= @template_name %>".
lang <%= host_param('lang') || 'en_US.UTF-8' %>
selinux --<%= host_param('selinux-mode') || 'enforcing' %>
keyboard <%= host_param('keyboard') || 'us' %>
<%
# Network setup is essential for pulling the container and reporting to Foreman
@host.interfaces.reject{ |iface| iface.bmc? }.sort_by { |iface| (iface.bond? || iface.bridge?) ? 0 : iface.provision? ? 20 : 10 }.each do |iface|
-%>
<%= snippet(
'kickstart_network_interface',
variables: {
iface: iface,
host: @host,
static: @static,
static6: @static6
}
) -%>
<%
end
-%>
# --- Core Installation ---
# 1. Set the container image as the installation source
ostreecontainer --url <%= ostreecontainer %>
# 2. Set the root password to make the system login-able
rootpw --iscrypted <%= root_pass %>
# 3. Allow SSH for remote login
firewall --service=ssh
# --- Time ---
timezone --utc <%= host_param('time-zone') || 'UTC' %>
<% if host_param('ntp-pools') -%>
<% host_param('ntp-pools').each do |ntppool| -%>
timesource --ntp-pool <%= ntppool %>
<% end -%>
<% elsif host_param('ntp-server') -%>
timesource --ntp-server <%= host_param('ntp-server') %>
<% end -%>
# --- Bootloader and Partitioning ---
# This assumes you are assigning a partition table in Foreman.
# For a modern bootc install, you might replace the bootloader/diskLayout
# sections with: autopart --type=bootc
bootloader --location=mbr --append="<%= host_param('bootloader-append') || 'nofb quiet splash=quiet' %>" <%= grub_pass %>
<%= @host.diskLayout %>
# --- Finalize ---
text
skipx
reboot
# --- Post-Install Scripts ---
%post --nochroot
exec < /dev/tty3 > /dev/tty3
chvt 3
(
<% if host_param_false?('no-resolv-override') -%>
# Copy DNS configuration from installation environment
cp -va /etc/resolv.conf /mnt/sysimage/etc/resolv.conf
<% end -%>
chvt 1
) 2>&1 | tee /mnt/sysimage/root/install.postnochroot.log
%end
<%#
This section injects SSH keys for Foreman, registers the system,
and signals the build is done.
%>
%post
exec < /dev/tty3 > /dev/tty4
chvt 3
(
# DNS should already be configured from %post --nochroot copy
# But verify and recreate if missing (bootc containers may not have it)
if [ ! -f /etc/resolv.conf ] || [ ! -s /etc/resolv.conf ]; then
echo "WARNING: /etc/resolv.conf missing or empty in chroot, recreating from Foreman configuration"
cat > /etc/resolv.conf << 'RESOLV_EOF'
<% if @host.domain -%>
search <%= @host.domain.name %>
<% end -%>
<% [@host.subnet.dns_primary, @host.subnet.dns_secondary].compact.each do |nameserver| -%>
nameserver <%= nameserver %>
<% end -%>
RESOLV_EOF
fi
echo "=== DNS Configuration ==="
cat /etc/resolv.conf
echo "=== Testing DNS Resolution ==="
nslookup cdn.redhat.com || echo "WARNING: Cannot resolve cdn.redhat.com"
echo "=========================="
<%= snippet 'redhat_register' -%>
<%= snippet('remote_execution_ssh_keys') %>
touch /tmp/foreman_built
chvt 1
) 2>&1 | tee /root/install.post.log
%end
<%#
The last post section tells Foreman the build is complete.
%>
%post --erroronfail --log=/root/install-callhome.post.log
if test -f /tmp/foreman_built; then
echo "calling home: build is done!"
<%= indent(2, skip1: true, skip_content: 'EOF') { snippet('built', :variables => { :endpoint => 'built', :method => 'POST', :body_body_file => '/root/install.post.log' }) } -%>
else
echo "calling home: build failed!"
<%= indent(2, skip1: true, skip_content: 'EOF') { snippet('built', :variables => { :endpoint => 'failed', :method => 'POST', :body_body_file => '/root/install.post.log' }) } -%>
fi
sync
%end
@ianballou
Copy link
Author

Latest KS template with /etc/resolv in chroot from the Foreman host:

<%#
kind: provision
name: Kickstart Default bootc - Trimmed
model: ProvisioningTemplate
oses:
- AlmaLinux
- CentOS
- CentOS_Stream
- Fedora
- RedHat
- Rocky
-%>
<%
  # Variable setup for post scripts and container URL
  ostreecontainer = host_param('ostreecontainer')
-%>
# This kickstart file was rendered from the Foreman provisioning template "<%= @template_name %>".

lang <%= host_param('lang') || 'en_US.UTF-8' %>
selinux --<%= host_param('selinux-mode') || 'enforcing' %>
keyboard <%= host_param('keyboard') || 'us' %>

<%
# Network setup is essential for pulling the container and reporting to Foreman
@host.interfaces.reject{ |iface| iface.bmc? }.sort_by { |iface| (iface.bond? || iface.bridge?) ? 0 : iface.provision? ? 20 : 10 }.each do |iface|
-%>
<%= snippet(
      'kickstart_network_interface',
      variables: {
        iface: iface,
        host: @host,
        static: @static,
        static6: @static6
      }
    ) -%>
<%
end
-%>

# --- Core Installation ---
# 1. Set the container image as the installation source
ostreecontainer --url <%= ostreecontainer %>

# 2. Set the root password to make the system login-able
rootpw --iscrypted <%= root_pass %>

# 3. Allow SSH for remote login
firewall --service=ssh

# --- Time ---
timezone --utc <%= host_param('time-zone') || 'UTC' %>
<% if host_param('ntp-pools') -%>
<% host_param('ntp-pools').each do |ntppool| -%>
timesource --ntp-pool <%= ntppool %>
<% end -%>
<% elsif host_param('ntp-server') -%>
timesource --ntp-server <%= host_param('ntp-server') %>
<% end -%>

# --- Bootloader and Partitioning ---
# This assumes you are assigning a partition table in Foreman.
# For a modern bootc install, you might replace the bootloader/diskLayout
# sections with: autopart --type=bootc
bootloader --location=mbr --append="<%= host_param('bootloader-append') || 'nofb quiet splash=quiet' %>" <%= grub_pass %>
<%= @host.diskLayout %>

# --- Finalize ---
text
skipx
reboot

# --- Post-Install Scripts ---


%post --nochroot
exec < /dev/tty3 > /dev/tty3
chvt 3
(
<% if host_param_false?('no-resolv-override') -%>
# Copy DNS configuration from installation environment
cp -va /etc/resolv.conf /mnt/sysimage/etc/resolv.conf
<% end -%>

chvt 1
) 2>&1 | tee /mnt/sysimage/root/install.postnochroot.log
%end


<%#
  This section injects SSH keys for Foreman, registers the system,
  and signals the build is done.
%>
%post
exec < /dev/tty3 > /dev/tty4
chvt 3
(
# DNS should already be configured from %post --nochroot copy
# But verify and recreate if missing (bootc containers may not have it)
if [ ! -f /etc/resolv.conf ] || [ ! -s /etc/resolv.conf ]; then
  echo "WARNING: /etc/resolv.conf missing or empty in chroot, recreating from Foreman configuration"
  cat > /etc/resolv.conf << 'RESOLV_EOF'
<% if @host.domain -%>
search <%= @host.domain.name %>
<% end -%>
<% [@host.subnet.dns_primary, @host.subnet.dns_secondary].compact.each do |nameserver| -%>
nameserver <%= nameserver %>
<% end -%>
RESOLV_EOF
fi

echo "=== DNS Configuration ==="
cat /etc/resolv.conf
echo "=== Testing DNS Resolution ==="
nslookup cdn.redhat.com || echo "WARNING: Cannot resolve cdn.redhat.com"
echo "=========================="

<%= snippet 'redhat_register' -%>
<%= snippet('remote_execution_ssh_keys') %>
touch /tmp/foreman_built
chvt 1
) 2>&1 | tee /root/install.post.log
%end

<%#
The last post section tells Foreman the build is complete.
%>
%post --erroronfail --log=/root/install-callhome.post.log
if test -f /tmp/foreman_built; then
  echo "calling home: build is done!"
  <%= indent(2, skip1: true, skip_content: 'EOF') { snippet('built', :variables => { :endpoint => 'built', :method => 'POST', :body_body_file => '/root/install.post.log' }) } -%>
else
  echo "calling home: build failed!"
  <%= indent(2, skip1: true, skip_content: 'EOF') { snippet('built', :variables => { :endpoint => 'failed', :method => 'POST', :body_body_file => '/root/install.post.log' }) } -%>
fi
sync
%end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment