linux_wiki:os_install_post_install

This is an old revision of the document!


OS Install: Post Install

General Information

After installing an OS via kickstart or a VM Template, there is typically additional standard configuration performed depending upon the environment.

This page demonstrates how to create VM templates and kickstarts that will auto-execute scripts one time for a system's first boot.

Checklist

  • Distro(s): Enterprise Linux 6/7
  • Other: NFS Server sharing a post install configuration script

Firstboot

  • The firstboot script is executed once.
  • It is baked into the system via a VM template or kickstart.
  • It stays generic and calls other external scripts on remote admin systems to do the actual post install configuration.
  • It also reboots the system and sends an email once it has completed

This script is meant to run once and then disable itself. It calls other post install script(s) to do the actual work.

/root/scripts/firstboot.sh

#!/bin/bash
# Name: firstboot.sh
# Description: Auto runs a post install script 1 time
 
#### Customize These Variables ####
nfs_server="10.1.2.3"
nfs_server_share="${nfs_server}:/scripts"
nfs_client_mountpoint="/mnt"
post_install_script="${nfs_client_mountpoint}/scripts/postinstall.sh"
post_install_log="/root/postinstall.log"
 
# Write a successful run file
firstboot_ran_file="/root/.firstboot-ran"
 
# System Admins Group Email
system_admins_email='sysadmins@example.com'
 
# Reboot delay in minutes
reboot_delay="1"
#### End of Customize Variables ####
 
#==============================
# Functions; Main Starts After
#==============================
function check_os_type
{
## Gather Distro and Major Version
  if [ -f /etc/system-release-cpe ];then
    distro=$(awk -F: '{printf "%s", $3}' /etc/system-release-cpe)
    major_version=$(awk -F: '{printf "%d", $5}' /etc/system-release-cpe)
  elif [ -f /etc/redhat-release ];then
    distro=$(awk '{printf "%s", $1}' /etc/redhat-release)
    major_version=$(awk -F. '{print $1}' /etc/redhat-release | awk '{printf "%d", $3}')
  fi
}
 
#====================
# Main Program
#====================
 
echo -e "================================================"
echo -e "####========== Firstboot Script ============####"
echo -e "================================================"
 
# Check to see if script has been run before
if [[ -f ${firstboot_ran_file} ]]; then
  echo -e "\nfirstboot>> Error; First boot ran file detected at: ${firstboot_ran_file}!"
  echo -e "firstboot>> Will NOT execute this script. If you really want to execute, remove that file."
  exit 1
fi
 
# Discover OS Type
check_os_type
 
# Start rpcbind service
if [[ ${major_version} == "7" ]]; then
  systemctl start rpcbind
else
  service rpcbind start
fi
 
# Try to reach the NFS server 3 times
for index in 1 2 3; do
  ping -c 1 ${nfs_server} &> /dev/null
 
  if [[ $? -eq 0 ]]; then
    # Successful ping, exit loop
    break
  else
    # Unsuccessful; wait 10 seconds and try again
    echo -e "firstboot>> Could not reach ${nfs_server}. Sleeping for 10 seconds and will try again."
    sleep 10
  fi
done
 
# Mount script location
echo -e "\nfirstboot>> Mounting script location..."
mount -t nfs ${nfs_server_share} ${nfs_client_mountpoint}
 
# Execute post install script
echo -e "\nfirstboot>> Executing post install script..."
${post_install_script}
 
if [[ $? -eq 0 ]]; then
  echo -e "\nfirstboot>> Post install script successful."
else
  echo -e "\nfirstboot>> Error; Post install script exited with error(s)! Quiting..."
  exit 1
fi
 
#==============================================
# Call other post install scripts/actions here
#==============================================
 
# Unmount nfs share
echo -e "\nfirstboot>> Unmounting nfs share.."
umount ${nfs_client_mountpoint}
 
#### Safeguards to prevent firstboot.sh from running more than once ####
# Create firstboot-ran file
echo -e "\nfirstboot>> Creating firstboot ran file..."
echo -e "firstboot>> firstboot.sh was run on $(date)." > ${firstboot_ran_file}
chown -v root:root ${firstboot_ran_file}
chmod -v 400 ${firstboot_ran_file}
 
# Make script not executable
echo -e "\nfirstboot>> Removing execute permissions on this script..."
chown -v root:root ${0}
chmod -v 400 ${0}
 
# Disable auto execution
if [[ ${major_version} == "7" ]]; then
  systemctl disable firstboot.service
else
  sed -i '/firstboot.sh/d' /etc/rc.d/rc.local
fi
#### End of Safeguards ####
 
# Email notification of completion
echo -e "\nfirstboot>> E-mailing notification that the script completed..."
echo -e "The firstboot script process has completed for: '$(hostname)' on $(date).\n\nThe following actions have successfully run:\n1) Post install script (System updates, General system configuration)\n2) Other scripts\n\nThe system ($(hostname)) will reboot in ${reboot_delay} minute(s).\n\n--- Post Install Errors and Warnings ---\n$(grep ERROR ${post_install_log})\n$(grep WARNING ${post_install_log})" | /bin/mail -s "Firstboot Complete: $(hostname)" ${system_admins_email}
 
# Allow some time for the email to be sent
sleep 5
 
# Reboot system
shutdown -r +${reboot_delay} "firstboot>> Post install script(s) complete. System will be rebooted."

Firstboot will get executed on CentOS 7 via a custom systemd service unit.

Create the following service unit file: /etc/systemd/system/firstboot.service

[Unit]
Description=Auto-execute post install scripts
After=network.target
 
[Service]
ExecStart=/root/scripts/firstboot.sh
 
[Install]
WantedBy=multi-user.target

CentOS 6 will make use of rc.local to execute the script.

Append to: /etc/rc.d/rc.local

/root/scripts/firstboot.sh

Auto Setup

Now that we have a firstboot script and method of executing on boot(CentOS 7 service or CentOS 6 rc.local), the combination of the two can be added to VM templates or kickstarts for unattended execution.


The modifications for auto execution need to be done on a new template that is a modification of your base VM template.

Warning: Do not delete your base template after you have created an auto setup version! If you ever want to update your auto setup template, you will need to deploy from the base template, make changes, and create a new auto setup version.

  • Deploy a new VM from your base template (Create a base template here)
  • Make the following modifications to the new system.
    • CentOS 6
      ## VM deployed from the base template ##
       
      ## Create a script directory for root
      mkdir /root/scripts
       
      ## Mount NFS Server and Copy firstboot.sh to the VM
      mount -t nfs <nfs-server>:/scripts /mnt
      cp -v /mnt/firstboot.sh /root/scripts/
      chown -Rv root:root /root/scripts
      chmod -Rv 700 /root/scripts
       
      ## Create line in rc.local to auto execute firstboot script
      echo "/root/scripts/firstboot.sh" >> /etc/rc.d/rc.local
       
      ## Unmount NFS server
      umount /mnt
    • CentOS 7
      ## VM deployed from the base template ##
       
      ## Create a script directory for root
      mkdir /root/scripts
       
      ## Mount NFS Server and Copy firstboot.sh to the VM
      mount -t nfs <nfs-server>:/scripts /mnt
      cp -v /mnt/firstboot.sh /root/scripts/
      chown -Rv root:root /root/scripts
      chmod -Rv 700 /root/scripts
       
      ## Copy firstboot.service unit to the VM
      cp -v /mnt/firstboot.service /etc/systemd/system/
      chown -v root:root /etc/systemd/system/firstboot.service
      chmod -v 644 /etc/systemd/system/firstboot.service
      systemctl enable firstboot.service
       
      ## Unmount NFS server
      umount /mnt

Kickstart files require a post install section to be edited in order for the firstboot script to be placed on a new system.

  • Modify the “%post” section at the bottom to include the following:
    • CentOS 6
      %post --interpreter /bin/sh --log=root/ks-post.log
      (
       
      ## Start rpcbind for NFS
      service rpcbind start
       
      ## Mount NFS Server
      mount -vt nfs 10.1.2.3:/scripts /mnt
       
      ## Create root's scripts directory
      mkdir /root/scripts
       
      ## Copy the firstboot script to the new directory
      cp -v /mnt/firstboot.sh /root/scripts/
      chown -Rv root:root /root/scripts
      chmod -Rv 700 /root/scripts
       
      ## Create rc.local entry for auto execution on boot
      echo "/root/scripts/firstboot.sh" >> /etc/rc.d/rc.local
       
      ## Unmount NFS Server
      umount -v /mnt
      )
      %end
    • CentOS 7
      %post --interpreter /bin/sh --log=root/ks-post.log
      (
       
      ## Start rpcbind for NFS
      systemctl start rpcbind
       
      ## Mount NFS Server
      mount -vt nfs 10.1.2.3:/scripts /mnt
       
      ## Create root's scripts directory
      mkdir /root/scripts
       
      ## Copy the firstboot script to the new directory
      cp -v /mnt/firstboot.sh /root/scripts/
      chown -Rv root:root /root/scripts
      chmod -Rv 700 /root/scripts
       
      ## Copy the firstboot service for auto execution on boot
      cp -v /mnt/firstboot.service /etc/systemd/system/
      chown -v root:root /etc/systemd/system/firstboot.service
      chmod -v 644 /etc/systemd/system/firstboot.service
       
      ## Enable firstboot service
      systemctl enable firstboot.service
       
      ## Unmount NFS Server
      umount -v /mnt
      )
      %end

Post Install Script

  • The post install script is what gets called via the firstboot script.
  • This script does all the heavy lifting (system updates, configuration, etc).

Post Install Script: Parent

Post install script: Provide logging and error checking

postinstall.sh
#!/bin/bash
# Title: postinstall.sh
# Description: Wrapper script to start the postinstall_worker.sh script with logging.
# Last Updated: 2016-10-24
# Most Recent Changes:-Initial release
#######################################################################################
 
function print_usage
{
echo
echo " Usage: postinstall.sh [-y]"
echo
echo "   This script(${0}), provides logging for its worker script, worker_postinstall.sh"
echo
echo "   Recommended action"
echo "   1) Mount: mount -t nfs nfs-server:/admin /mnt"
echo "   2) Execute parent script: /mnt/deploy/postinstall.sh [-y]"
echo "      -y  => Yes, execute script without prompting."
echo
exit 1
}
 
#=====================================
# Get Script Arguments
#=====================================
# Reset POSIX variable in case it has been used previously in this shell
OPTIND=1
 
# By default, do not force run script. Prompt for running or not.
force_run_script="no"
 
while getopts "hy" opt; do
  case "${opt}" in
    h) # -h (help) argument
      print_usage
      exit 0
    ;;
    y) # -y (yes to running script) argument
      force_run_script="yes"
    ;;
    *) # invalid argument
      print_usage
      exit 0
    ;;
  esac
done
 
##====================
## Pre-req checks
##====================
 
## Ensure we are root ##
if [[ $(id --user) -ne 0 ]]; then
  echo ">>Error; this script must be run as root. Exiting..."
  exit 1
fi
 
##======================
## Set Script Variables
##======================
 
# Set base path from executed command (relative or full path works)
base_path="$(echo ${0} | sed 's/postinstall.sh//')"
 
# Set log file and script locations
postinstall_log="/root/postinstall.log"
postinstall_worker="worker_postinstall.sh"
 
##================
## Setup Logging
##================
echo -e ">>Logging output and errors to: ${postinstall_log}\n"
 
# Clear log and timestamp the beginning
cat /dev/null > ${postinstall_log}
echo -e "---- Log Started: $(date) ----\n" >> ${postinstall_log}
 
##=========================
## Execute External Scripts
##=========================
# Start script, pass base path argument
if [[ ${force_run_script} == "no" ]]; then
  ${base_path}${postinstall_worker} -d ${base_path} 2>&1 | tee -a ${postinstall_log}
elif [[ ${force_run_script} == "yes" ]]; then
  ${base_path}${postinstall_worker} -d ${base_path} -y 2>&1 | tee -a ${postinstall_log}
else
  echo -e ">>Error: Unknown value for force_run_script (${force_run_script}). Exiting..."
  exit 1
fi
 
##==========================
## Close Logs, Show Location
##==========================
# Ending timestamp
echo -e "\n---- Log Completed: $(date) ----" >> ${postinstall_log}
 
# Reminder of where the log file is at
echo -e "\n>>Logged output and errors were sent to: ${postinstall_log}\n"
echo -e "----> Remember to umount NFS before rebooting <----"

Post Install Script: Worker

Post install worker: Perform the actual installations/config work

worker_postinstall.sh
 

  • linux_wiki/os_install_post_install.1505272049.txt.gz
  • Last modified: 2019/05/25 23:50
  • (external edit)