While the accepted answer is generally correct, and a good explanation of what's going on, I though I'd add some clarity that's missing and then show an actual working implementation variation I'm using which demonstrates some of this.
First, lets drive home the "variables" point. The %pre
and %post
blocks are simply wrappers around arbitrary code which has no meaning to the installer. It sees everything in those blocks as simply text that gets handed off to the --interpreter
. In fact, you could use %pre --interpreter=/bin/sh
and %post --interpreter=/usr/bin/python
, in which case variables in the two blocks would not even be syntactically compatible. So, variable "scope" is first local to a %pre/%post
block at the very least, then would be further restricted according to the "scope" rules of the language specified by the --interpreter
option.
Now, an important point the accepted answer didn't get into is the %pre
block gets executed in the install environment. The install environment is like a live-cd
- It's running a RAM resident filesystem.
- You only have access to the commands and utilities that are included on the installer filesystem.
- Devices and their names in the "install" environment may differ from the "installed" environment.
For example, this %pre --log /tmp/pre-install.log
may not do what you expect. If the install fails for some reason before the installer boots into the new install you could use the installer shell to cat
that file. But once the reboot happens, anything that was written to the temporary installer filesystem will be lost.
Also, this is technically incorrect:
One idea would be to use satellite files where to keep your data (like
you write a /tmp/file in your %pre and then get it back in your
%post... that you can do)
You could do that, but only if you write it to a file system that will persist across reboots, like a USB, iSCSI, NFS, CIFS, etc., mount.
With that in mind, follow the KISS (Keep It Simple...) principal when it comes to the %pre
selection. You may not have access to the more advanced commands/tools/languages; it will depend on what is, and is not, in the installer package and various distros and versions may change that over time.
Conversely, you pretty much have card blanche in the %post
block, since by that time you'll have access to everything that you specified in the %packages
block.
While you want to keep the %pre
block simple in terms of the tools and languages used, the more you do to configure the system using the installers commands, the less you'll be forced to "patch" the configuration in the %post
block.
Working Example - Variation
When provisioning new hosts I start by collecting and/or generating three things (for the purpose of this example); MAC Address, IP Address, Host Name. If I have a large block of those they usually go into a configuration spreadsheet. The next step is to create reservations in DHCP which connects the the baremetal or virtual MAC to it's assigned IP. The IP and hostname then go into the DNS forward and reverse lookup zones. You can use formulas in the spreadsheet to generate the DHCP and DNS entries so it becomes a cut and past exercise. Once DHCP and DNS are done you just network boot the box and the correct configuration just happens. I find a DHCP reservation to be a small price to pay for the consistency and efficiency of deploying and even redeploying server instances.
This is the kickstart file, which has been stripped down to little more than the relevant entries
#version=RHEL8
text
%include /tmp/network.ks
eula --agreed
reboot
skipx
%pre
#!/bin/bash
DEVICE=$(ip r|grep default|grep -oP '(?<=dev )\S*')
declare -A value
while IFS= read -r line; do
kvp=${line##DHCP4.OPTION\[*\]:*[ ][ ]}
value+=([${kvp% = *}]=${kvp#* = })
done <<< $(nmcli -f dhcp4 device show $DEVICE)
echo "network --device link --bootproto static --ip ${value[ip_address]} --netmask ${value[subnet_mask]} --gateway ${value[routers]} --hostname ${value[host_name]}.${value[domain_name]} --nameserver ${value[domain_name_servers]} --onboot yes --noipv6" > /tmp/network.ks
%end
%post --log=/root/install-post.log
cat << EOF >> /etc/hosts
$(hostname -i) $(hostname -s) $(hostname -f)
EOF
%end
Since I've already created a DHCP reservation for the "Static" configuration I wish to use, in the %pre
block I use nmcli
to get the relevant DHCP parameters and use them to dynamically generate the network...
command for the installer to use. That dynamically generated file is then included in the body of the kickstart script.
One thing that may seem a little confusing here is the use of $DEVICE
to query the dhcp4
parameters, yet I'm using network --device link ...
. The reason is the differences between the installers devices, which is what I need to query, and the name of the network device(s) in the installed system, which I am not forcing by specifying the "first active link", and allowing the installer to name devices as it sees fit. I've had trouble in the past by assuming the active name in the installers environment will be the same in the installed environment... and then things went sideways.
In the %post
block I do one thing I need that isn't done by the network...
command, add the IP and hostname to the hosts file. If all the prior steps have configured the system correctly by the time the %post
block executes you can grab whatever you need straight out of the running configuration... you don't need to persist any of the %pre
variables since the system will be running on them at this point.