Warning
bootstrap_init has moved — 2026-06-08
The bootstrap_init role now has a dedicated home at
rhis-builder-bootstrap-init.
All new deployments and active development should use the new repository.
This repository is retained for users of the baremetal_init role only.
The bootstrap_init role remains here during the transition period to avoid
breaking existing users, but it will not receive enhancements here.
See Migration below.
Generates OEMDRV kickstart files to bootstrap RHEL 9 baremetal and virtual hosts for the rhis-builder family of projects.
Kickstart files are delivered either directly to a USB-mounted OEMDRV volume (lab method) or as ISO9660 images for iDRAC, iLO, or Redfish virtual media (datacenter method).
Supported host roles: provisioner, idm, satellite, kvm
This repository is Phase 1 in the RHIS infrastructure lifecycle:
┌─────────────────────────────────────────────────────────────────────────┐
│ rhis-builder-inventory │
│ Generate deployment configs (inventory, group_vars, host_vars, vault) │
└─────────────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ rhis-builder-baremetal-init ← THIS REPO │
│ Generate OEMDRV kickstart files for each baremetal host │
└─────────────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Baremetal Install │
│ Anaconda reads ks.cfg from OEMDRV → unattended RHEL 9 install │
└─────────────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ rhis-provisioner container │
│ Configure IdM (Phase 2) → Satellite (Phase 3) → AAP (Phase 4) │
└─────────────────────────────────────────────────────────────────────────┘
Note: All unified deployment configurations — group_vars, host_vars, inventory, and vault templates — are managed through rhis-builder-inventory. Start there before running this repository.
| Role | Status |
|---|---|
bootstrap_init |
Moved — canonical home is rhis-builder-bootstrap-init |
baremetal_init |
Deprecated — kept for backwards compatibility only |
Do not use baremetal_init for new environments. New deployments should clone
rhis-builder-bootstrap-init
directly. The bootstrap_init role is retained here only during the user migration period.
If you are currently using the baremetal_init role:
- Review
bootstrap_init_vars.SAMPLE.yml— the variable format has changed significantly.bootstrap_inituses abootstrap_init_hostslist with per-host entries and thebootstrap_init_prefix on all role-level variables. - Clone rhis-builder-bootstrap-init instead of this repository.
- Update your host variable files to the
bootstrap_init_hostsformat. - Update any scripts that reference this repo path to point to
rhis-builder-bootstrap-init.
If you are already using the bootstrap_init role from this repository:
- Clone rhis-builder-bootstrap-init.
- Copy your
group_vars/provisioner/variable files to the new clone. - Update any scripts (e.g.
build_highside_isos.sh) that reference this repo path to point torhis-builder-bootstrap-initinstead. - The variable format is identical — no changes to your host definitions are required.
The provisioner is the RHEL 9 system you run this playbook from. It can be the system you are also bootstrapping (if you are rebuilding it), a laptop, or an existing management node.
- RHEL 9 with
ansible-core2.14 or later community.generalcollection (installed viarequirements.yml)- Root or sudo access to mount the OEMDRV volume
- Network connectivity to the internet (for CDN registration in the kickstart)
Install the required collection:
ansible-galaxy collection install -r requirements.yml- Active Red Hat subscription
- CDN organization ID
- Activation key configured for RHEL 9 minimal install
An SSH key pair for the automation user (ansiblerunner by convention). The
public key will be embedded in the kickstart so the provisioner can reach the
bootstrapped hosts immediately after install.
Sensitive values (passwords, keys, org credentials) must be stored in an Ansible Vault encrypted file. See Vault variables below.
Use rhis-builder-inventory to generate a consistent set of group_vars, host_vars, inventory, and vault templates for your deployment domain.
Do not clone rhis-builder-inventory directly. Cloning leaves the upstream remote in place and risks pushing your environment-specific configuration back to the template repository. Instead, download the archive and initialise a fresh repository pointed at your own remote:
wget https://github.com/parmstro/rhis-builder-inventory/archive/refs/heads/main.zip
unzip main.zip
mv rhis-builder-inventory-main rhis-builder-inventory
cd rhis-builder-inventory
git init -b main
git add --all
git commit -m "Initial commit"
# Point origin at your own repository — not the upstream template
git remote add origin https://github.com/<your_org_or_login>/rhis-builder-inventory.git
git remote -v
git push -u origin mainCopy and edit the base variables file for your domain:
cp inventory_basevars.yml your-domain_inventory_basevars.ymlEdit your-domain_inventory_basevars.yml to set your domain name, network
ranges, timezone, and the number of hosts of each type:
global_domain_name: "your.domain"
default_network: "192.168.0.0"
default_network_prefix: "22"
default_network_mask: "255.255.252.0"
rhis_timezone: "America/New_York"
rhis_locale: "en"
rhis_aap_release_version: "2.6"
rhis_system_count:
satellite: 1
idm: 2
aapcontroller: 1
aaphub: 1
capsule: 1
quadlet: 2Generate the deployment:
./inventory_update.sh -b your-domain_inventory_basevars.ymlThis creates deployments/your.domain/ containing:
deployments/your.domain/
├── group_vars/ # Group-level Ansible variables
├── host_vars/ # Per-host variable files
├── inventory/ # Ansible inventory
├── templates/ # Jinja2 configuration templates
├── vault_SAMPLES/ # Vault variable templates (copy and encrypt)
└── vars/ # Additional variable files
Copy the vault sample and populate it with your secrets:
cp deployments/your.domain/vault_SAMPLES/rhis_builder_vault_SAMPLE.yml.j2 \
/path/to/secure/vault/rhis_builder_vault.ymlAt minimum, populate the following variables for this repository:
encrypted_root_pass_vault: # Generated with openssl passwd -6 (see below)
encrypted_grub_pass_vault: # Generated with grub2-mkpasswd-pbkdf2 (see below)
encrypted_user_pass_vault: # Generated with openssl passwd -6
user_sudoer_policy_vault: # e.g. "ansiblerunner ALL=(ALL) NOPASSWD: ALL"
ssh_pub_key_vault: # Contents of ~/.ssh/id_*.pub
cdn_organization_vault: # Your Red Hat CDN organization ID
cdn_activation_key_vault: # Your activation key nameEncrypt the vault file:
ansible-vault encrypt /path/to/secure/vault/rhis_builder_vault.ymlNote: The variables listed above cover kickstart generation only. The full RHIS deployment (IdM, Satellite, AAP) requires many additional vault variables. See the
README.mdin rhis-builder-inventory for the complete vault variable reference and thevault_SAMPLES/directory in your generated deployment for the full template.
Security note: Keep vault files outside of your project directory. Ensure
.gitignoreexcludes any vault or credential files before committing. Consider enabling secret scanning on your repository.
Clone this repository and set up your host configuration files under
group_vars/provisioner/. The .gitignore excludes these files from commits.
git clone https://github.com/parmstro/rhis-builder-baremetal-init.git
cd rhis-builder-baremetal-init
mkdir -p group_vars/provisionerCopy the sample and create one file per host group (or combine all hosts in one file):
cp bootstrap_init_vars.SAMPLE.yml group_vars/provisioner/your_init_vars.ymlEdit the file to define your hosts. Each entry in bootstrap_init_hosts generates
one kickstart file. See Variable reference for all fields.
Run the playbook, specifying which host list to use via bootstrap_init_hosts:
ansible-playbook -i inventory --limit provisioner main.yml \
-e "vault_path=/path/to/secure/vault/rhis_builder_vault.yml" \
-e "bootstrap_init_hosts={{ satellite_bootstrap_init_hosts }}" \
--ask-vault-passThe bootstrap_init_hosts extra variable selects a named list from your
group_vars/provisioner/ file (e.g. satellite_bootstrap_init_hosts,
idm_bootstrap_init_hosts). Run the playbook once per host group, or pass
a combined list if generating all kickstarts in one pass.
Prepare the install media:
- Download the RHEL 9 bootable DVD ISO from access.redhat.com.
- Write the ISO to a USB drive and verify it is bootable.
- Format a second USB drive with an
xfsorext4filesystem and label it OEMDRV. Mount it at the path configured inbootstrap_init_oem_dir(default:/mnt/OEMDRV). - Run the playbook (Step 4). The
ks.cfgfile is written directly to the OEMDRV volume. - Unmount the OEMDRV drive.
Install the node:
- Insert both USB drives into the target baremetal system.
- Power on and boot from the RHEL 9 installer USB.
- Anaconda detects the OEMDRV drive and reads
ks.cfgautomatically. - The system installs and reboots unattended. Remove the USB drives before the second boot, or the system will re-read the kickstart.
Repeat for each target host.
This method attaches both the RHEL 9 installer ISO and an OEMDRV ISO as virtual DVD drives via iDRAC, iLO, Redfish, or your hypervisor's virtual media facility.
Set generate_oemdrv_iso: true in your host entry:
bootstrap_init_hosts:
- hostname: "satellite1"
...
generate_oemdrv_iso: trueRun the playbook. The role writes ks.cfg to bootstrap_init_oem_dir and then
generates an ISO9660 image at:
bootstrap_init_iso_dir/<rhis_role>_<hostname>.<domain>.oemdrv.iso
Attach both the RHEL 9 DVD ISO and this OEMDRV ISO as virtual media to the target system and boot. Anaconda will detect both drives and automate the install.
The exact procedure for attaching virtual media depends on your hardware vendor. See your BMC documentation for details.
Once all baremetal nodes are installed and reachable over SSH, proceed to the rhis-provisioner container to configure IdM, Satellite, and AAP.
When inventory_update.sh generated your deployment it also created two
container launch scripts in the rhis-builder-inventory root directory:
your.domain.24.sh # AAP 2.4 — being phased out, use only if required
your.domain.25.sh # AAP 2.5 and later — recommended
These scripts are pre-configured with all the correct deployment paths and
call run_container.sh internally — you do not need to pass any arguments:
cd rhis-builder-inventory
./your.domain.25.shWhy two scripts? The ansible.controller collection introduced a breaking
API change in AAP 2.5 due to significant architectural changes in that release.
The two scripts launch different container images that bundle the correct
collection version for each AAP generation. The containers are otherwise
identical.
Recommendation: Use the
.25.shscript. AAP 2.4 support is being phased out and the 2.4-specific script will be removed in a future release of rhis-builder-inventory, after which a single launch script will be generated.
The container mounts your deployment configuration and executes the RHIS provisioning playbooks in sequence: IdM → Satellite → AAP.
These are set once per playbook run, either in group_vars/provisioner/ or
passed as extra variables.
| Variable | Default | Description |
|---|---|---|
bootstrap_init_ks_path |
ks.cfg |
Filename written to the OEMDRV volume |
bootstrap_init_oem_dir |
/mnt/OEMDRV |
Path to the mounted OEMDRV volume |
bootstrap_init_iso_dir |
/mnt/OEMDRV/ISO |
Directory for generated OEMDRV ISO files |
bootstrap_init_hosts |
[] |
List of host definitions (required) |
Each entry in bootstrap_init_hosts defines one system.
| Variable | Required | Description |
|---|---|---|
rhis_role |
Yes | One of: provisioner, idm, satellite, kvm |
hostname |
Yes | Short hostname (no domain) |
domain |
Yes | DNS domain name |
| Variable | Required | Description |
|---|---|---|
mac |
Yes | MAC address of the primary NIC (xx:xx:xx:xx:xx:xx) |
ipv4_address |
Yes | Static IPv4 address |
ipv4_netmask |
Yes | Dotted-decimal subnet mask (used by kickstart network directive) |
ipv4_prefix |
Yes | CIDR prefix length integer (used by NetworkManager keyfile) |
ipv4_gateway |
Yes | Default gateway |
name_server1 |
Yes | Primary DNS server |
name_server2 |
Yes | Secondary DNS server |
Network configuration is written as a NetworkManager keyfile in %post with
MAC-based interface matching. Legacy ifcfg profiles are removed during install.
| Variable | Required | Description |
|---|---|---|
boot_disk |
Yes | Disk path for /boot and /boot/efi (e.g. /dev/nvme0n1) |
root_disk |
Yes | Disk path for the root volume group (often same as boot_disk) |
additional_disks |
No | List of additional disk paths to extend the root VG |
filesystem |
No | xfs (default) or ext4. Use ext4 for KVM hosts if online resize is needed |
Both boot_disk and root_disk are fully initialized by clearpart. If they
are the same disk, it is targeted once. This satisfies the IdM and Satellite
installation criteria that require a fully dedicated system.
The compliance layout below meets CIS Level 2, DISA-STIG, and PCI-DSS
partition requirements. Satellite requires a large lv_var — set to 1 to
instruct anaconda to grow it into all remaining space.
| Variable | Recommended (IdM/KVM) | Recommended (Satellite) | Description |
|---|---|---|---|
boot_mb |
1024 |
1024 |
/boot partition |
boot_efi_mb |
2048 |
2048 |
/boot/efi partition |
lv_root_mb |
65536 |
65536 |
/ logical volume |
lv_home_mb |
20480 |
20480 |
/home logical volume |
lv_tmp_mb |
6144 |
6144 |
/tmp logical volume |
lv_var_tmp_mb |
6144 |
6144 |
/var/tmp logical volume |
lv_var_log_mb |
6144 |
6144 |
/var/log logical volume |
lv_var_log_audit_mb |
6144 |
6144 |
/var/log/audit logical volume |
lv_var_mb |
1 |
1 |
/var — set to 1 to grow into remaining space |
A 256 GiB minimum drive is recommended for IdM and KVM. Satellite should have a 1 TiB or larger drive; downloading all standard RHEL repositories requires approximately 900 GiB.
RHIS fully supports disconnected environments using Satellite's content
export/import workflow. Storage sizing for /var must account for the
additional space that the export and transfer process requires.
Connected Satellite (exporting content)
The connected Satellite needs at least 2x the space required for the
exported content set in /var — 1x for the live Satellite content and 1x
for the export archive. For a full Library export covering RHEL 8, 9, and 10
content, a 2 TiB /var is recommended.
Disconnected Satellite (importing content)
The space required on the disconnected host depends on how the transfer media is presented:
| Import method | /var required |
Notes |
|---|---|---|
| Mount transfer drive, import directly | 2x export size | 1x tar extraction + 1x final content (tar files remain on the transfer drive) |
| Copy from transfer drive then import | 3x export size | 1x tar files + 1x tar extraction + 1x final content |
The copy-then-import method requires the most headroom because all three
artifacts — the tar archives, the extracted content, and the final imported
content — coexist on /var simultaneously during the import process. When
mounting the transfer drive directly, the tar files never land on the
disconnected host's /var, reducing the requirement by one full copy.
When sizing the disconnected Satellite, set lv_var_mb: 1 as usual to
consume all remaining disk space, and provision the drive accordingly before
running the kickstart.
| Variable | Required | Description |
|---|---|---|
username |
Yes | Automation user created during install |
user_enc_pass |
Yes | Encrypted password (vault reference) |
user_sudoer_policy |
Yes | Sudoers policy line (vault reference) |
ssh_pub_key |
Yes | SSH public key string for the user (vault reference) |
| Variable | Required | Description |
|---|---|---|
root_enc_pass |
Yes | Encrypted root password (vault reference) |
grub_enc_pass |
No | Encrypted GRUB2 bootloader password (vault reference) |
org |
Yes | Red Hat CDN organization ID (vault reference) |
activation_key |
Yes | CDN activation key name (vault reference) |
The node is registered to the CDN during kickstart %post to pull the minimal
package set. IdM nodes are later re-registered to Satellite by rhis-builder-idm.
| Variable | Default | Description |
|---|---|---|
generate_oemdrv_iso |
false |
When true, generates an ISO9660 image instead of writing directly to the OEMDRV volume |
Generate encrypted values with the following commands:
openssl passwd -6openssl passwd -6 generates a SHA-512 hashed password ($6$...). Run it
without arguments and enter the password at the prompt to avoid the plaintext
appearing in your shell history. openssl is included by default on RHEL and
Fedora — no additional packages are required on the provisioner or your
workstation.
grub2-mkpasswd-pbkdf2Copy the full grub.pbkdf2.sha512.… string into your vault file.
encrypted_root_pass_vault: "$6$rounds=..."
encrypted_grub_pass_vault: "grub.pbkdf2.sha512.10000...."
encrypted_user_pass_vault: "$6$rounds=..."
user_sudoer_policy_vault: "ansiblerunner ALL=(ALL) NOPASSWD: ALL"
ssh_pub_key_vault: "ssh-ed25519 AAAA... ansiblerunner@provisioner"
cdn_organization_vault: "12345678"
cdn_activation_key_vault: "rhis-bootstrap"The project is validated against the ansible-lint production profile:
ansible-lintThe configuration is in .ansible-lint and .yamllint. The baremetal_init
role is excluded from linting. The bootstrap_init role must pass with 0
failures before merging.
Install ansible-lint under the same Python version as your system ansible-core:
python3.12 -m pip install --user ansible-lintFork it. Clone it. Configure it. Change it. Test it. Commit it. Create a PR.
If you have comments, tips, or suggestions, PRs are greatly appreciated. Let's share the wealth!
ENJOY!