Introduction

Red Hat Satellite (and its upstream project Foreman) is the enterprise platform for lifecycle management of RHEL hosts — provisioning, patching, content management, and compliance. The theforeman.foreman Ansible collection provides a dynamic inventory plugin and 70+ modules to automate every Satellite/Foreman operation. This guide covers inventory integration, host provisioning, content view management, patching workflows, and compliance reporting.

Prerequisites

# Install the collection
ansible-galaxy collection install theforeman.foreman

# Dependencies
pip install requests

Satellite Credentials

# group_vars/all/satellite.yml (vault-encrypted)
satellite_url: https://satellite.example.com
satellite_username: admin
satellite_password: "{{ vault_satellite_password }}"
satellite_organization: "My Organization"
satellite_location: "Default Location"

Dynamic Inventory from Satellite

Basic Configuration

# satellite_inventory.foreman.yml
plugin: theforeman.foreman.foreman
url: https://satellite.example.com
user: admin
password: "{{ lookup('env', 'SATELLITE_PASSWORD') }}"
validate_certs: true

# Group by Satellite attributes
group_prefix: satellite_
want_hostcollections: true
want_params: true

# Only active hosts
want_facts: true

# Use Satellite host groups as Ansible groups
hostgroup_separator: "_"
export SATELLITE_PASSWORD=secret
ansible-inventory -i satellite_inventory.foreman.yml --graph

Advanced Inventory

# satellite_inventory.foreman.yml
plugin: theforeman.foreman.foreman
url: https://satellite.example.com
user: admin
password: "{{ lookup('env', 'SATELLITE_PASSWORD') }}"
validate_certs: true

# Groups from Satellite
want_hostcollections: true
want_params: true
want_facts: true
want_content_facet_attributes: true
want_organization: true
want_location: true

# Map Satellite fields to Ansible vars
compose:
  ansible_host: ip

# Custom groups
groups:
  rhel9: "'RHEL 9' in content_facet_attributes.content_view_name | default('')"
  patching_window_1: "'patch-window-1' in hostcollection | default([])"

keyed_groups:
  - key: hostgroup_title | default('ungrouped')
    prefix: hg
  - key: lifecycle_environment_name | default('Library')
    prefix: env
  - key: content_view_name | default('Default')
    prefix: cv

Host Provisioning

Create Host

---
- name: Provision hosts via Satellite
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Create host in Satellite
      theforeman.foreman.host:
        server_url: "{{ satellite_url }}"
        username: "{{ satellite_username }}"
        password: "{{ satellite_password }}"
        validate_certs: true
        name: "{{ item.name }}"
        organization: "{{ satellite_organization }}"
        location: "{{ satellite_location }}"
        hostgroup: "RHEL9/WebServers"
        compute_resource: "VMware vCenter"
        compute_profile: "Medium"
        content_source: "{{ satellite_url | urlsplit('hostname') }}"
        lifecycle_environment: "Production"
        content_view: "RHEL9-WebServer"
        build: true
        state: present
      loop:
        - { name: "web01.example.com" }
        - { name: "web02.example.com" }
        - { name: "web03.example.com" }

Create Host Group

- name: Create host group
  theforeman.foreman.hostgroup:
    server_url: "{{ satellite_url }}"
    username: "{{ satellite_username }}"
    password: "{{ satellite_password }}"
    name: "RHEL9/WebServers"
    organization: "{{ satellite_organization }}"
    locations:
      - "{{ satellite_location }}"
    lifecycle_environment: "Production"
    content_view: "RHEL9-WebServer"
    puppet_proxy: ""
    domain: "example.com"
    subnet: "Production-10.0.1.0/24"
    architecture: "x86_64"
    operatingsystem: "RHEL 9.4"
    medium: "RHEL9"
    partition_table: "Kickstart default"
    root_pass: "{{ vault_root_password }}"
    state: present

Content View Management

Create Content View

- name: Create content view
  theforeman.foreman.content_view:
    server_url: "{{ satellite_url }}"
    username: "{{ satellite_username }}"
    password: "{{ satellite_password }}"
    organization: "{{ satellite_organization }}"
    name: "RHEL9-WebServer"
    repositories:
      - name: "Red Hat Enterprise Linux 9 for x86_64 - BaseOS RPMs 9"
        product: "Red Hat Enterprise Linux for x86_64"
      - name: "Red Hat Enterprise Linux 9 for x86_64 - AppStream RPMs 9"
        product: "Red Hat Enterprise Linux for x86_64"
    state: present

- name: Publish content view
  theforeman.foreman.content_view_version:
    server_url: "{{ satellite_url }}"
    username: "{{ satellite_username }}"
    password: "{{ satellite_password }}"
    organization: "{{ satellite_organization }}"
    content_view: "RHEL9-WebServer"
    lifecycle_environments:
      - Library
    state: present

Promote Content View

- name: Promote content view to Production
  theforeman.foreman.content_view_version:
    server_url: "{{ satellite_url }}"
    username: "{{ satellite_username }}"
    password: "{{ satellite_password }}"
    organization: "{{ satellite_organization }}"
    content_view: "RHEL9-WebServer"
    current_lifecycle_environment: "Testing"
    lifecycle_environments:
      - Production
    state: present

Patching Workflow

Scheduled Patching via Satellite

---
- name: Patch RHEL hosts via Satellite
  hosts: satellite_patching_window_1
  become: true
  serial: "25%"
  max_fail_percentage: 10
  tasks:
    - name: Pre-patch — check services
      ansible.builtin.service_facts:

    - name: Pre-patch — create snapshot
      theforeman.foreman.host_power:
        server_url: "{{ satellite_url }}"
        username: "{{ satellite_username }}"
        password: "{{ satellite_password }}"
        name: "{{ inventory_hostname }}"
        state: state  # Get current state
      delegate_to: localhost
      register: host_state

    - name: Install all available errata
      ansible.builtin.dnf:
        name: '*'
        state: latest
        update_cache: true
      register: patch_result

    - name: Reboot if kernel updated
      ansible.builtin.reboot:
        reboot_timeout: 600
      when: patch_result.changed and 'kernel' in (patch_result.results | default([]) | join(' '))

    - name: Post-patch — verify services
      ansible.builtin.service_facts:

    - name: Post-patch — verify key services running
      ansible.builtin.assert:
        that:
          - ansible_facts.services['sshd.service'].state == 'running'
        fail_msg: "Critical service not running after patching"

    - name: Update Satellite host facts
      theforeman.foreman.host:
        server_url: "{{ satellite_url }}"
        username: "{{ satellite_username }}"
        password: "{{ satellite_password }}"
        name: "{{ inventory_hostname }}"
        state: present
      delegate_to: localhost

Errata Management

- name: Apply specific errata
  theforeman.foreman.host_errata:
    server_url: "{{ satellite_url }}"
    username: "{{ satellite_username }}"
    password: "{{ satellite_password }}"
    host: "{{ inventory_hostname }}"
    errata:
      - "RHSA-2026:1234"
      - "RHSA-2026:5678"
    state: present
  delegate_to: localhost

Activation Keys

- name: Create activation key
  theforeman.foreman.activation_key:
    server_url: "{{ satellite_url }}"
    username: "{{ satellite_username }}"
    password: "{{ satellite_password }}"
    organization: "{{ satellite_organization }}"
    name: "RHEL9-WebServer-Prod"
    lifecycle_environment: "Production"
    content_view: "RHEL9-WebServer"
    auto_attach: true
    host_collections:
      - "WebServers"
    state: present

Compliance Reporting

- name: Generate compliance report
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Get all hosts with outdated packages
      theforeman.foreman.resource_info:
        server_url: "{{ satellite_url }}"
        username: "{{ satellite_username }}"
        password: "{{ satellite_password }}"
        resource: hosts
        search: "applicable_errata > 0"
      register: outdated_hosts

    - name: Display hosts needing patches
      ansible.builtin.debug:
        msg: "{{ item.name }}: {{ item.content_facet_attributes.errata_counts | default({}) }}"
      loop: "{{ outdated_hosts.resources }}"
      loop_control:
        label: "{{ item.name }}"

Troubleshooting

API Connection Failed

# Test API connectivity
curl -k -u admin:password https://satellite.example.com/api/v2/status

Inventory Empty

  • Verify hosts have Managed flag set in Satellite
  • Check organization/location permissions for the API user
  • Verify validate_certs matches your SSL setup

Slow Inventory

# Enable caching
plugin: theforeman.foreman.foreman
cache: true
cache_plugin: jsonfile
cache_connection: /tmp/satellite-cache
cache_timeout: 1800

Conclusion

Satellite/Foreman plus Ansible creates a complete RHEL lifecycle management platform. The dynamic inventory plugin groups hosts by hostgroup, lifecycle environment, content view, and host collections — ready for targeted patching, provisioning, and compliance workflows. Use theforeman.foreman modules to automate content view publishing and promotion, host provisioning via compute resources, and errata management across thousands of RHEL hosts.