Installing Foreman 3.18 on AlmaLinux 9.x — Manual Step-by-Step Guide

Ansible, Terraform, OpenTofu, Pulumi, infrastructure provisioning, configuration management, and repeatable deployments.
Post Reply
Murali Krishna
Posts: 35
Joined: Wed Jun 10, 2026 8:34 am

Installing Foreman 3.18 on AlmaLinux 9.x — Manual Step-by-Step Guide

Post by Murali Krishna »

Installing Foreman 3.18 on AlmaLinux 9.x — Manual Step-by-Step Guide

Tested on AlmaLinux 9.x (also valid for RHEL 9 / Rocky 9). Foreman release: 3.18 (official repositories). Estimated time: 20–30 minutes.

Foreman is an open-source tool for provisioning, configuring, and managing the full lifecycle of physical and virtual servers. This guide walks through a clean manual installation on AlmaLinux 9 using the official Foreman repositories, explaining what every command does so you understand the system you are building — not just copy/paste it.

──────────────────────────────────────────────────────────

1. Prerequisites
  • A fresh AlmaLinux 9.x server (minimal install is fine), 64-bit (x86_64).
  • Minimum 4 GB RAM (8 GB recommended) and 2 vCPUs.
  • At least 20 GB free disk space.
  • root access (or a sudo-enabled user).
  • A resolvable Fully Qualified Domain Name (FQDN), e.g. foreman.example.com.
  • Outbound internet access to the package repositories.
Warning: Install Foreman on a freshly provisioned host. The installer reconfigures Apache, PostgreSQL, and several other components, so it should not share a server with other production services.

──────────────────────────────────────────────────────────

2. Set a Fully Qualified Hostname

Step 2.1 — Set the system hostname:

Code: Select all

hostnamectl set-hostname foreman.example.com
This permanently sets the server's hostname. Foreman uses the FQDN to generate SSL certificates and the web URL, so it must be a proper name.domain.tld form. Replace foreman.example.com with your real name.

Step 2.2 — Map the FQDN to the server's real IP in /etc/hosts:

Code: Select all

echo "$(hostname -I | awk '{print $1}') foreman.example.com foreman" >> /etc/hosts
Breaking this line down:
  • hostname -I prints the host's IP address(es).
  • awk '{print $1}' keeps only the first (primary) IP.
  • The whole line appends "IP FQDN shortname" to /etc/hosts so the name resolves locally.
Step 2.3 — Verify name resolution:

Code: Select all

hostname -f
ping -c1 $(hostname -f)
hostname -f must return the full FQDN, and the ping must resolve to the server's real IP address.

Important: If the FQDN resolves to 127.0.0.1 or 127.0.1.1, the installer will fail. The /etc/hosts entry above prevents this.

──────────────────────────────────────────────────────────

3. Update the Operating System

Step 3.1 — Apply all pending updates:

Code: Select all

dnf -y upgrade --refresh
  • dnf upgrade updates every installed package to the latest version.
  • -y auto-confirms the transaction (no interactive prompt).
  • --refresh forces fresh repository metadata before upgrading.
Step 3.2 — Reboot if the kernel was updated:

Code: Select all

reboot
Note: A reboot is only required if a new kernel or glibc was installed. Reconnect via SSH afterwards and continue.

──────────────────────────────────────────────────────────

4. Enable the Required Repositories

Step 4.1 — Enable EPEL:

Code: Select all

dnf -y install epel-release
EPEL (Extra Packages for Enterprise Linux) provides supplementary dependencies that several Foreman packages rely on.

Step 4.2 — Enable the CRB repository:

Code: Select all

dnf config-manager --set-enabled crb
CRB (CodeReady Builder, formerly "PowerTools") ships development and build-time libraries that some Foreman/Puppet dependencies need. On AlmaLinux 9 it is disabled by default.

Tip: If the command above reports that "crb" is unknown, try powertools instead — the name differs across EL9 rebuilds.

Step 4.3 — Enable the Puppet 8 repository:

Code: Select all

dnf -y install https://yum.puppet.com/puppet8-release-el-9.noarch.rpm
This installs the release RPM that adds Puppet's official EL9 repository. Foreman's default installer scenario configures a Puppet server, which needs Puppet 7 or newer.

Note: Puppet integration is optional in modern Foreman. If you do not want a Puppet server you can skip this step and later run the installer with Puppet disabled, but the default scenario expects it.

Step 4.4 — Enable the Foreman 3.18 repository:

Code: Select all

dnf -y install https://yum.theforeman.org/releases/3.18/el9/x86_64/foreman-release.rpm
This adds the official Foreman 3.18 repository for EL9. Version 3.18 is the current stable release; you can substitute another release number in the URL if you need a specific version.

Step 4.5 — Refresh repository metadata:

Code: Select all

dnf -y makecache
Builds the local metadata cache so the newly added repositories are immediately usable.

──────────────────────────────────────────────────────────

5. Install the Foreman Installer

Step 5.1 — Install the installer package:

Code: Select all

dnf -y install foreman-installer
foreman-installer is a Puppet-based wrapper that pulls in the Foreman core packages and provides a single, supported command to configure the entire stack. Installing it also resolves the Foreman, Smart Proxy, and database dependencies.

──────────────────────────────────────────────────────────

6. Run the Installer

Step 6.1 — Execute the installer with default settings:

Code: Select all

foreman-installer
Running it with no arguments performs a complete, non-interactive default installation. In a single run it will:
  • Install and configure a local PostgreSQL database automatically.
  • Configure the Apache web server with Puma as the Foreman application server.
  • Set up the Puppet server and the Smart Proxy (foreman-proxy).
  • Generate SSL certificates based on your FQDN.
  • Apply the correct SELinux contexts (Foreman fully supports SELinux in enforcing mode).
  • Print the web URL and the initial admin credentials at the end.
Tip: The full installer log — including the initial admin password — is saved to /var/log/foreman-installer/foreman-installer.log. The installer is idempotent, so it is safe to re-run if it is interrupted.

Note (optional plugins): To enable plugins during installation, add flags such as --enable-foreman-plugin-ansible or --enable-foreman-plugin-remote-execution. List all available options with foreman-installer --full-help.

──────────────────────────────────────────────────────────

7. Configure the Firewall

Step 7.1 — Open the required ports:

Code: Select all

firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=8140/tcp
firewall-cmd --permanent --add-port=8443/tcp
firewall-cmd --reload
What each port is for:
  • 80/tcp — HTTP (redirects to HTTPS).
  • 443/tcp — HTTPS, the Foreman web interface.
  • 8140/tcp — Puppet server communication.
  • 8443/tcp — Smart Proxy (foreman-proxy) over HTTPS.
--permanent writes the rule to disk; --reload applies the permanent rules to the running firewall.

Note: Leave firewalld and SELinux enabled. Foreman is designed to run with SELinux in enforcing mode — disabling it is unnecessary and reduces security.

──────────────────────────────────────────────────────────

8. Verify the Installation

Step 8.1 — Confirm the core services are running:

Code: Select all

systemctl status httpd foreman foreman-proxy postgresql --no-pager
All four services should report active (running).

Step 8.2 — Run Foreman's own health check (optional):

Code: Select all

foreman-maintain health check
Note: foreman-maintain is provided by the rubygem-foreman_maintain package and is a quick way to validate the deployment.

Step 8.3 — Retrieve the admin password if you missed it:

Code: Select all

grep "Initial credentials" /var/log/foreman-installer/foreman-installer.log
This shows the auto-generated admin username and password from the install log.

──────────────────────────────────────────────────────────

9. Access Foreman

Open a browser and go to:

Code: Select all

https://foreman.example.com
  • Username: admin
  • Password: the value printed at the end of the installer run (see Step 8.3).
Tip: The certificate is self-signed by default, so your browser will warn you on first connection. Accept the exception, or replace it later with a CA-signed certificate.

──────────────────────────────────────────────────────────

10. Common AlmaLinux 9 Pitfalls
  • FQDN resolves to loopback — the installer aborts if hostname -f returns 127.0.0.x. Fix the /etc/hosts entry (Step 2.2).
  • Missing CRB repository — dependency resolution errors usually mean CRB is not enabled (Step 4.2).
  • SHA1 crypto policy — EL9 disables SHA1 by default; if an external repo signed with SHA1 fails, prefer the official sources used here rather than weakening the system crypto policy.
  • Low memory — under 4 GB RAM the Puma workers may fail to start. Increase RAM and re-run the installer.
──────────────────────────────────────────────────────────

Conclusion

You now have a fully working Foreman 3.18 server on AlmaLinux 9, with PostgreSQL, a Puppet server, and a Smart Proxy configured automatically and SELinux left in enforcing mode. From here you can add Smart Proxies, enable plugins (Ansible, Remote Execution, OpenSCAP, and others), and begin registering hosts.

One click Install script

Code: Select all

#!/usr/bin/env bash
###############################################################################
# install_foreman.sh
#
# Automated, idempotent Foreman installation for AlmaLinux 9 / RHEL 9 (EL9).
#
# What it does:
#   - Sets the hostname to foreman.example.com (and fixes /etc/hosts so the
#     FQDN resolves to a real, non-loopback IP — required by the installer)
#   - Updates the operating system
#   - Enables EPEL, Puppet 8, and the official Foreman 3.18 repositories
#   - Installs foreman-installer
#   - Configures SELinux (enforcing) and opens the required firewall ports
#   - Runs foreman-installer, enabling the full "Popular Plugins" set
#     (Ansible, Remote Execution, Discovery, OpenSCAP, Salt, Bootdisk,
#     Templates, Hooks, Default Host Group, Azure, KubeVirt, Virt-who, Tasks,
#     Graphite, PuppetDB, ...) plus extra widely-used plugins. The installer
#     itself configures PostgreSQL automatically and sets up the Puppet server,
#     Smart Proxy, Apache/Puma, and the Foreman SELinux policy.
#   - Optionally installs Katello (own scenario, off by default) and the
#     Foreman Maintain CLI + theforeman Ansible collections (not plugins)
#   - Enables and starts all required services
#   - Verifies installation status
#   - Prints the Foreman web URL and admin credentials
#
# Plugin safety: every plugin flag is validated against the installer's own
# --help output before use, so an unsupported/renamed flag is skipped (logged)
# rather than aborting the whole installation.
#
# Target   : AlmaLinux 9 / RHEL 9, x86_64
# Foreman  : 3.18 (official theforeman.org repositories)
# Log file : /var/log/foreman-install.log
#
# NOTE: Foreman requires a minimum of 4 GB RAM and should be installed on a
#       freshly provisioned host, as the installer reconfigures several system
#       components.
###############################################################################

# -e  : exit on any unhandled command failure
# -u  : treat unset variables as errors
# -o pipefail : a pipeline fails if any element fails (not just the last)
set -euo pipefail

# ---------------------------------------------------------------------------
# Configuration variables
# ---------------------------------------------------------------------------
readonly FQDN="foreman.example.com"
readonly SHORT_HOSTNAME="${FQDN%%.*}"
readonly FOREMAN_VERSION="3.18"
readonly PUPPET_RELEASE_RPM="https://yum.puppet.com/puppet8-release-el-9.noarch.rpm"
readonly FOREMAN_RELEASE_RPM="https://yum.theforeman.org/releases/${FOREMAN_VERSION}/el9/x86_64/foreman-release.rpm"
readonly LOG_FILE="/var/log/foreman-install.log"
readonly INSTALLER_LOG="/var/log/foreman-installer/foreman-installer.log"

# Katello is NOT a plugin (see section 7b). It needs its own scenario + repo.
# Leave this false unless you specifically want full content management.
readonly ENABLE_KATELLO="false"
readonly KATELLO_VERSION="4.20"   # Foreman 3.18 pairs with Katello 4.20 (3.16->4.18, +1 minor each)

# Firewall ports. Base Foreman + common provisioning-plugin needs:
#   80/tcp        - HTTP  (redirects to HTTPS)
#   443/tcp       - HTTPS (Foreman web UI)
#   8140/tcp      - Puppet server
#   8443/tcp      - Smart Proxy (foreman-proxy) HTTPS
#   9090/tcp      - Smart Proxy HTTP (used by several plugins/Katello content)
#   5910-5930/tcp - VNC/SPICE provisioning consoles
# Optional (uncomment if you enable these services):
#   53/tcp 53/udp - DNS  | 67/udp 68/udp - DHCP | 69/udp - TFTP (Discovery/PXE)
readonly FIREWALL_PORTS=("80/tcp" "443/tcp" "8140/tcp" "8443/tcp" "9090/tcp" "5910-5930/tcp")

# ---------------------------------------------------------------------------
# Logging setup
# ---------------------------------------------------------------------------
# Must be root before we can write to /var/log or reconfigure the system.
if [[ "${EUID}" -ne 0 ]]; then
    echo "ERROR: This script must be run as root (or via sudo)." >&2
    exit 1
fi

# Send all stdout/stderr to both the console and the log file.
touch "${LOG_FILE}"
exec > >(tee -a "${LOG_FILE}") 2>&1

# Timestamped logging helper for human-readable progress markers.
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ===> $*"
}

log "Starting Foreman ${FOREMAN_VERSION} installation on $(hostname). Logging to ${LOG_FILE}"

# ---------------------------------------------------------------------------
# Pre-flight checks
# ---------------------------------------------------------------------------
# Confirm we are on Enterprise Linux 9 (AlmaLinux 9 / RHEL 9 / Rocky 9, etc.).
if [[ -r /etc/os-release ]]; then
    # shellcheck disable=SC1091
    source /etc/os-release
    EL_MAJOR="${VERSION_ID%%.*}"
    log "Detected OS: ${PRETTY_NAME:-unknown}"
    if [[ "${EL_MAJOR}" != "9" ]]; then
        log "WARNING: This script targets EL9. Detected major version '${EL_MAJOR}'. Continuing anyway."
    fi
else
    log "WARNING: /etc/os-release not found; cannot verify OS version."
fi

# Architecture sanity check (official Foreman EL9 repo is x86_64 only).
ARCH="$(uname -m)"
if [[ "${ARCH}" != "x86_64" ]]; then
    log "WARNING: Architecture is '${ARCH}'. Official Foreman EL9 packages are x86_64."
fi

# Memory advisory (Foreman recommends >= 4 GB RAM).
MEM_MB="$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo)"
if [[ "${MEM_MB}" -lt 3800 ]]; then
    log "WARNING: Only ${MEM_MB} MB RAM detected. Foreman recommends at least 4 GB."
fi

# ---------------------------------------------------------------------------
# 1. Set hostname and ensure the FQDN resolves to a real (non-loopback) IP
# ---------------------------------------------------------------------------
# The installer fails if 'hostname -f' resolves to 127.0.x.x, so we map the
# primary IP to the FQDN in /etc/hosts. Idempotent: the entry is only added
# once, and hostnamectl is naturally idempotent.
log "Setting hostname to ${FQDN}"
hostnamectl set-hostname "${FQDN}"

PRIMARY_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
if [[ -n "${PRIMARY_IP}" ]]; then
    if ! grep -qE "[[:space:]]${FQDN}([[:space:]]|$)" /etc/hosts; then
        log "Adding /etc/hosts entry: ${PRIMARY_IP} ${FQDN} ${SHORT_HOSTNAME}"
        echo "${PRIMARY_IP} ${FQDN} ${SHORT_HOSTNAME}" >> /etc/hosts
    else
        log "/etc/hosts already contains an entry for ${FQDN}; skipping."
    fi
else
    log "WARNING: Could not determine a primary IP address; verify /etc/hosts manually."
fi

# ---------------------------------------------------------------------------
# 2. Update the operating system
# ---------------------------------------------------------------------------
log "Updating the operating system (dnf -y upgrade)"
dnf -y upgrade --refresh

# ---------------------------------------------------------------------------
# 3. Install EPEL and required repositories
# ---------------------------------------------------------------------------
# EPEL provides supplementary dependencies; dnf install of an already-present
# release package is a no-op, so each of these steps is idempotent.
log "Enabling EPEL repository"
dnf -y install epel-release

# Some Foreman dependencies live in CodeReady Builder / PowerTools (CRB).
log "Enabling CRB (CodeReady Builder) repository"
dnf config-manager --set-enabled crb 2>/dev/null || \
    dnf config-manager --set-enabled powertools 2>/dev/null || \
    log "NOTE: CRB/PowerTools not toggled (may already be enabled or named differently)."

log "Enabling Puppet 8 repository"
dnf -y install "${PUPPET_RELEASE_RPM}"

log "Enabling Foreman ${FOREMAN_VERSION} repository"
dnf -y install "${FOREMAN_RELEASE_RPM}"

# Refresh metadata so the new repos are usable.
dnf -y makecache

# ---------------------------------------------------------------------------
# 4. Install foreman-installer (pulls in Foreman packages as dependencies)
# ---------------------------------------------------------------------------
log "Installing foreman-installer"
dnf -y install foreman-installer

# ---------------------------------------------------------------------------
# 5. Configure SELinux (enforcing)
# ---------------------------------------------------------------------------
# AlmaLinux 9 ships with SELinux enforcing by default. Foreman fully supports
# enforcing mode, and foreman-installer applies the correct policy and file
# contexts (via the foreman-selinux package). We make sure it is enforcing.
log "Ensuring SELinux is set to enforcing"
if command -v getenforce >/dev/null 2>&1; then
    CURRENT_SELINUX="$(getenforce)"
    if [[ "${CURRENT_SELINUX}" != "Enforcing" ]]; then
        setenforce 1 || log "WARNING: setenforce failed (SELinux may be disabled in the kernel)."
    fi
    # Persist the setting across reboots.
    if [[ -f /etc/selinux/config ]]; then
        sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
    fi
    log "SELinux mode is now: $(getenforce)"
else
    log "WARNING: getenforce not available; skipping SELinux configuration."
fi

# ---------------------------------------------------------------------------
# 6. Open required firewall ports
# ---------------------------------------------------------------------------
# Ensure firewalld is installed and running, then add each port permanently.
# firewall-cmd is idempotent: re-adding an existing port just warns.
log "Configuring firewall ports"
if ! rpm -q firewalld >/dev/null 2>&1; then
    dnf -y install firewalld
fi
systemctl enable --now firewalld

for port in "${FIREWALL_PORTS[@]}"; do
    log "Opening firewall port ${port}"
    firewall-cmd --permanent --add-port="${port}" >/dev/null || true
done

# Apply the permanent rules to the running firewall.
firewall-cmd --reload >/dev/null
log "Active firewall ports: $(firewall-cmd --list-ports)"

# ---------------------------------------------------------------------------
# 7. Build the plugin list and run foreman-installer
# ---------------------------------------------------------------------------
# Plugins are enabled by passing --enable-foreman-plugin-<name> (plus the
# matching --enable-foreman-proxy-plugin-<name> / --enable-foreman-cli-<name>)
# flags to the installer. These flags are saved in the installer answers file,
# so the chosen set persists on every future run -> idempotent.
#
# IMPORTANT: plugin flag names vary slightly between Foreman versions, and a
# single unknown flag makes the WHOLE installer abort. To be both complete and
# robust we declare every plugin we want, then keep only the flags this
# installer build actually advertises in its --help output. Anything
# unsupported/renamed is logged and skipped instead of breaking the run.
log "Querying installer for the plugin flags supported by Foreman ${FOREMAN_VERSION}"
INSTALLER_HELP="$(foreman-installer --full-help 2>/dev/null || foreman-installer --help 2>/dev/null || true)"

# Every plugin from the "Popular Plugins" set + extra widely-used community
# plugins. Where a flag name differs between versions, both variants are listed
# and the unsupported one is filtered out below.
DESIRED_FLAGS=(
    # --- Ansible: config management + Ansible-based remote execution ---
    --enable-foreman-plugin-ansible
    --enable-foreman-proxy-plugin-ansible
    --enable-foreman-cli-ansible
    # --- Remote Execution: SSH-based job runner ---
    --enable-foreman-plugin-remote-execution
    --enable-foreman-proxy-plugin-remote-execution-ssh
    --enable-foreman-proxy-plugin-remote-execution-script
    --enable-foreman-cli-remote-execution
    # --- Discovery: bare-metal auto-discovery (MaaS) ---
    --enable-foreman-plugin-discovery
    --enable-foreman-proxy-plugin-discovery
    --enable-foreman-cli-discovery
    # --- OpenSCAP: security compliance scanning ---
    --enable-foreman-plugin-openscap
    --enable-foreman-proxy-plugin-openscap
    --enable-foreman-cli-openscap
    # --- Salt: SaltStack config management integration ---
    --enable-foreman-plugin-salt
    --enable-foreman-proxy-plugin-salt
    # --- Bootdisk: iPXE boot images for provisioning ---
    --enable-foreman-plugin-bootdisk
    # --- Templates: sync provisioning templates from Git ---
    --enable-foreman-plugin-templates
    --enable-foreman-cli-templates
    # --- Tasks: Dynflow task management (core dep; kept for completeness) ---
    --enable-foreman-plugin-tasks
    --enable-foreman-cli-tasks
    # --- Hooks: run scripts on Foreman model events ---
    --enable-foreman-plugin-hooks
    # --- Default Host Group: auto-assign new hosts to a hostgroup ---
    --enable-foreman-plugin-default-hostgroup
    # --- Azure: Azure Resource Manager compute resource ---
    --enable-foreman-plugin-azure
    --enable-foreman-cli-azure
    # --- KubeVirt: KubeVirt / OpenShift Virtualization compute resource ---
    --enable-foreman-plugin-kubevirt
    --enable-foreman-cli-kubevirt
    # --- Virt-who Configure: manage virt-who for subscription reporting ---
    --enable-foreman-plugin-virt-who-configure
    # --- Graphite: export Foreman host metrics to Graphite ---
    --enable-foreman-plugin-graphite
    # --- PuppetDB: correlate Foreman hosts with PuppetDB data ---
    --enable-foreman-plugin-puppetdb
    # --- Webhooks: HTTP callbacks on events (CLI side) ---
    --enable-foreman-cli-webhooks
    # ---- Extra popular community plugins ----
    --enable-foreman-plugin-expire-hosts   # auto-expire / decommission hosts
    --enable-foreman-plugin-column-view    # customizable host-list columns
    --enable-foreman-plugin-leapp          # in-place EL major-version upgrades
    --enable-foreman-plugin-rh-cloud       # Red Hat Insights integration
    --enable-foreman-plugin-statistics     # extra dashboard statistics
    --enable-foreman-plugin-setup          # guided setup helper
)

# Keep only the flags this installer build actually supports.
INSTALL_FLAGS=()
for flag in "${DESIRED_FLAGS[@]}"; do
    opt="${flag#--enable-}"          # e.g. "foreman-plugin-ansible"
    if grep -qE -- "enable-${opt}([[:space:]]|$)" <<<"${INSTALLER_HELP}"; then
        INSTALL_FLAGS+=("${flag}")
    else
        log "SKIP (not supported by Foreman ${FOREMAN_VERSION}): ${flag}"
    fi
done

# Extra parameters for plugins that need them, added only when the matching
# plugin flag survived the support check above.
EXTRA_PARAMS=()
if printf '%s\n' "${INSTALL_FLAGS[@]}" | grep -q -- '--enable-foreman-proxy-plugin-remote-execution-ssh'; then
    # Have the Smart Proxy generate/install its SSH key for remote execution.
    EXTRA_PARAMS+=(--foreman-proxy-plugin-remote-execution-ssh-install-key true)
fi
if printf '%s\n' "${INSTALL_FLAGS[@]}" | grep -q -- '--enable-foreman-plugin-openscap'; then
    # OpenSCAP needs Remote Execution (enabled above) and can deploy its client
    # via both the Puppet module and the Ansible role.
    EXTRA_PARAMS+=(--foreman-proxy-plugin-openscap-puppet-module true)
    EXTRA_PARAMS+=(--foreman-proxy-plugin-openscap-ansible-module true)
fi

log "Enabling ${#INSTALL_FLAGS[@]} plugin flag(s) during installation"

# The installer is non-interactive. With default settings it ALSO installs and
# configures a local PostgreSQL database, Apache/Puma, the Puppet server and
# Smart Proxy, and generates a random initial admin password (in its own log).
# Running it again is safe/idempotent - Puppet converges to the same state.
log "Running foreman-installer (with all plugins this can take 10+ minutes)..."
set +e
foreman-installer "${INSTALL_FLAGS[@]}" "${EXTRA_PARAMS[@]}"
INSTALLER_RC=$?
set -e

if [[ "${INSTALLER_RC}" -ne 0 ]]; then
    log "ERROR: foreman-installer exited with code ${INSTALLER_RC}. See ${INSTALLER_LOG}"
    exit "${INSTALLER_RC}"
fi
log "foreman-installer completed successfully."

# ---------------------------------------------------------------------------
# 7b. Katello (OPTIONAL - disabled by default)
# ---------------------------------------------------------------------------
# Katello is NOT a normal plugin: it is a large content-management add-on with
# its own repository and a dedicated installer scenario. It cannot be enabled
# with --enable-foreman-plugin-katello on the default scenario, and adding it
# on top of an existing plain-Foreman install is unsupported. Set
# ENABLE_KATELLO="true" at the top to install it via its own scenario instead.
if [[ "${ENABLE_KATELLO}" == "true" ]]; then
    log "Installing Katello ${KATELLO_VERSION} (separate scenario + repository)"
    dnf -y install "https://yum.theforeman.org/katello/${KATELLO_VERSION}/katello/el9/x86_64/katello-repos-latest.rpm"
    dnf -y install katello
    set +e
    foreman-installer --scenario katello "${INSTALL_FLAGS[@]}" "${EXTRA_PARAMS[@]}"
    KATELLO_RC=$?
    set -e
    if [[ "${KATELLO_RC}" -ne 0 ]]; then
        log "ERROR: Katello installer exited with code ${KATELLO_RC}. See ${INSTALLER_LOG}"
        exit "${KATELLO_RC}"
    fi
    log "Katello installation completed."
fi

# ---------------------------------------------------------------------------
# 7c. Companion tooling (these are NOT installer plugins)
# ---------------------------------------------------------------------------
# These items appear next to the plugins but install differently:
#   - Foreman Maintain : maintenance/health CLI shipped as an RPM
#   - Foreman Ansible Modules / Operations Collection : Ansible *collections*
#     used to MANAGE Foreman from a control node, installed via ansible-galaxy
log "Installing the Foreman Maintain CLI"
dnf -y install rubygem-foreman_maintain 2>/dev/null \
    || dnf -y install foreman-maintain 2>/dev/null \
    || log "NOTE: foreman-maintain package not found in enabled repos; skipping."

log "Installing Foreman Ansible collections (theforeman.foreman / theforeman.operations)"
if command -v ansible-galaxy >/dev/null 2>&1; then
    ansible-galaxy collection install theforeman.foreman theforeman.operations \
        || log "NOTE: ansible-galaxy collection install failed (Galaxy/network access?)."
else
    log "NOTE: ansible-galaxy not installed; skipping the theforeman.* collections."
fi

# ---------------------------------------------------------------------------
# 8. Enable and start all required services
# ---------------------------------------------------------------------------
# The installer enables these, but we explicitly ensure they are enabled and
# running so the script is self-contained and idempotent.
log "Enabling and starting Foreman-related services"
SERVICES=("postgresql" "httpd" "foreman" "foreman-proxy" "puppetserver")
for svc in "${SERVICES[@]}"; do
    if systemctl list-unit-files | grep -q "^${svc}.service"; then
        systemctl enable --now "${svc}" >/dev/null 2>&1 || \
            log "WARNING: could not enable/start ${svc} (it may be managed differently)."
    fi
done

# ---------------------------------------------------------------------------
# 9. Verify installation status
# ---------------------------------------------------------------------------
log "Verifying service status"
ALL_OK=true
for svc in "${SERVICES[@]}"; do
    if systemctl list-unit-files | grep -q "^${svc}.service"; then
        if systemctl is-active --quiet "${svc}"; then
            log "  [OK]   ${svc} is active"
        else
            log "  [FAIL] ${svc} is NOT active"
            ALL_OK=false
        fi
    fi
done

# Probe the web UI to confirm Foreman responds (self-signed cert -> use -k).
log "Probing the Foreman web UI"
HTTP_CODE="$(curl -kso /dev/null -w '%{http_code}' "https://${FQDN}/" || echo "000")"
if [[ "${HTTP_CODE}" =~ ^(200|301|302)$ ]]; then
    log "  [OK]   Web UI responded with HTTP ${HTTP_CODE}"
else
    log "  [WARN] Web UI returned HTTP ${HTTP_CODE} (it may still be starting up)"
    ALL_OK=false
fi

# ---------------------------------------------------------------------------
# 10. Print the Foreman web URL and admin credentials
# ---------------------------------------------------------------------------
echo
echo "==============================================================================="
echo " Foreman installation summary"
echo "==============================================================================="
echo " Web URL : https://${FQDN}"
echo

# On first install, foreman-installer prints the initial credentials. Extract
# them from the installer log. On re-runs the password is not reprinted, so we
# provide the reset command as a fallback.
if [[ -f "${INSTALLER_LOG}" ]] && grep -q "Initial credentials" "${INSTALLER_LOG}"; then
    grep "Initial credentials" "${INSTALLER_LOG}" | tail -n 1 | sed 's/^/ Admin   : /'
else
    echo " Admin   : initial password not found in the installer log (likely a re-run)."
    echo "           Reset it with:"
    echo "             foreman-rake permissions:reset"
fi

echo
echo " Installer log : ${INSTALLER_LOG}"
echo " Script log    : ${LOG_FILE}"
echo "==============================================================================="

if [[ "${ALL_OK}" == "true" ]]; then
    log "Installation completed successfully."
    exit 0
else
    log "Installation finished with warnings. Review the log files above."
    exit 1
fi

Questions or problems? Reply below with your installer log output and we'll help you troubleshoot.
Post Reply