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.
──────────────────────────────────────────────────────────
2. Set a Fully Qualified Hostname
Step 2.1 — Set the system hostname:
Code: Select all
hostnamectl set-hostname foreman.example.comStep 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- 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.
Code: Select all
hostname -f
ping -c1 $(hostname -f)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.
Code: Select all
reboot──────────────────────────────────────────────────────────
4. Enable the Required Repositories
Step 4.1 — Enable EPEL:
Code: Select all
dnf -y install epel-releaseStep 4.2 — Enable the CRB repository:
Code: Select all
dnf config-manager --set-enabled crbTip: 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.rpmNote: 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.rpmStep 4.5 — Refresh repository metadata:
Code: Select all
dnf -y makecache──────────────────────────────────────────────────────────
5. Install the Foreman Installer
Step 5.1 — Install the installer package:
Code: Select all
dnf -y install foreman-installer──────────────────────────────────────────────────────────
6. Run the Installer
Step 6.1 — Execute the installer with default settings:
Code: Select all
foreman-installer- 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.
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- 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.
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-pagerStep 8.2 — Run Foreman's own health check (optional):
Code: Select all
foreman-maintain health checkStep 8.3 — Retrieve the admin password if you missed it:
Code: Select all
grep "Initial credentials" /var/log/foreman-installer/foreman-installer.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).
──────────────────────────────────────────────────────────
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