AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Satellite and Foreman Integration: Complete Automation Guide

By Luca Berton · Published 2024-01-01 · Category: installation

Integrate Ansible with Red Hat Satellite and Foreman for enterprise server lifecycle management.

Red Hat Satellite (and its upstream project Foreman) provides enterprise-grade server lifecycle management — provisioning, patching, content management, and compliance. Integrating Ansible with Satellite/Foreman creates a powerful automation platform that combines Satellite's inventory and content management with Ansible's configuration automation. This guide covers the complete integration with production-ready examples.

Architecture Overview

Satellite/Foreman serves as the central management platform while Ansible provides the execution engine:

┌──────────────────────────┐
│   Red Hat Satellite 6.x   │
│   (or Foreman + Katello) │
│                          │
│  ┌─────────────────────┐ │
│  │ Content Management  │ │    ┌─────────────────────┐
│  │ - Repos & Products  │ │    │   Ansible Controller  │
│  │ - Content Views     │ │◄──►│   (AAP / AWX)        │
│  │ - Lifecycle Envs    │ │    │                       │
│  ├─────────────────────┤ │    │  - Satellite Inventory│
│  │ Host Management     │ │    │  - Playbooks          │
│  │ - Provisioning      │ │    │  - Roles & Collections│
│  │ - Host Groups       │ │    └─────────────────────┘
│  │ - Parameters        │ │              │
│  ├─────────────────────┤ │              ▼
│  │ Ansible Integration │ │    ┌─────────────────────┐
│  │ - Roles Assignment  │ │    │   Managed Hosts       │
│  │ - Reports Callback  │ │    │   (RHEL / CentOS)     │
│  └─────────────────────┘ │    └─────────────────────┘
└──────────────────────────┘

Key Collections

# Install required collections
ansible-galaxy collection install redhat.satellite
ansible-galaxy collection install theforeman.foreman

# Verify installation ansible-galaxy collection list | grep -E 'satellite|foreman'

See also: Ansible Patch Management: Automated OS Patching Across Linux and Windows Enterprise Fleets

Authentication Setup

# group_vars/all.yml - Satellite connection vars
satellite_url: "https://satellite.example.com"
satellite_username: "admin"
satellite_password: "{{ vault_satellite_password }}"
satellite_organization: "MyOrg"
satellite_location: "Default Location"
satellite_validate_certs: true

Content Management Automation

Create Products and Repositories

---
- name: Configure Satellite content
  hosts: localhost
  vars_files:
    - vault.yml
  module_defaults:
    group/redhat.satellite.foreman:
      server_url: "{{ satellite_url }}"
      username: "{{ satellite_username }}"
      password: "{{ satellite_password }}"
      validate_certs: "{{ satellite_validate_certs }}"
  tasks:
    - name: Create custom product
      redhat.satellite.product:
        name: "Internal Applications"
        organization: "{{ satellite_organization }}"
        description: "Internal application packages"
        state: present

- name: Create YUM repository redhat.satellite.repository: name: "Internal Apps - RHEL 9" product: "Internal Applications" organization: "{{ satellite_organization }}" content_type: yum url: "https://repo.internal.example.com/rhel9/apps/" download_policy: on_demand mirror_on_sync: true state: present

- name: Sync repository redhat.satellite.repository_sync: product: "Internal Applications" repository: "Internal Apps - RHEL 9" organization: "{{ satellite_organization }}"

Content Views and Lifecycle Environments

---
- name: Manage content views and lifecycle
  hosts: localhost
  module_defaults:
    group/redhat.satellite.foreman:
      server_url: "{{ satellite_url }}"
      username: "{{ satellite_username }}"
      password: "{{ satellite_password }}"
      validate_certs: "{{ satellite_validate_certs }}"
  tasks:
    - name: Create lifecycle environments
      redhat.satellite.lifecycle_environment:
        name: "{{ item.name }}"
        prior: "{{ item.prior }}"
        organization: "{{ satellite_organization }}"
        description: "{{ item.description }}"
        state: present
      loop:
        - { name: "Development", prior: "Library", description: "Development environment" }
        - { name: "Testing", prior: "Development", description: "QA and testing" }
        - { name: "Staging", prior: "Testing", description: "Pre-production staging" }
        - { name: "Production", prior: "Staging", description: "Production environment" }

- name: Create content view redhat.satellite.content_view: name: "RHEL9-BaseOS-AppStream" organization: "{{ satellite_organization }}" 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" - name: "Internal Apps - RHEL 9" product: "Internal Applications" state: present

- name: Publish content view redhat.satellite.content_view_version: content_view: "RHEL9-BaseOS-AppStream" organization: "{{ satellite_organization }}" description: "Monthly patch release {{ ansible_date_time.date }}" lifecycle_environments: - Development

- name: Promote content view to Testing redhat.satellite.content_view_version: content_view: "RHEL9-BaseOS-AppStream" organization: "{{ satellite_organization }}" current_lifecycle_environment: Development lifecycle_environments: - Testing

See also: RHSB-2024–001 Leaky Vessels — runc — (CVE-2024–21626)

Host Provisioning

Host Groups and Configuration

---
- name: Configure host groups
  hosts: localhost
  module_defaults:
    group/redhat.satellite.foreman:
      server_url: "{{ satellite_url }}"
      username: "{{ satellite_username }}"
      password: "{{ satellite_password }}"
      validate_certs: "{{ satellite_validate_certs }}"
  tasks:
    - name: Create host group for web servers
      redhat.satellite.hostgroup:
        name: "RHEL9/WebServers"
        organization: "{{ satellite_organization }}"
        locations:
          - "{{ satellite_location }}"
        lifecycle_environment: "Production"
        content_view: "RHEL9-BaseOS-AppStream"
        content_source: "satellite.example.com"
        compute_resource: "VMware vCenter"
        compute_profile: "Medium - 4CPU 8GB"
        domain: "example.com"
        subnet: "Production-VLAN100"
        architecture: "x86_64"
        operatingsystem: "RHEL 9.4"
        ptable: "Kickstart default"
        root_pass: "{{ vault_root_password }}"
        parameters:
          - name: "app_role"
            value: "webserver"
          - name: "monitoring"
            value: "enabled"
        state: present

- name: Provision new host redhat.satellite.host: name: "web-prod-05.example.com" organization: "{{ satellite_organization }}" location: "{{ satellite_location }}" hostgroup: "RHEL9/WebServers" build: true managed: true compute_resource: "VMware vCenter" compute_attributes: cpus: 4 memory_mb: 8192 cluster: "Production-Cluster" volumes_attributes: 0: size_gb: 50 datastore: "SAN-Prod" state: present

Satellite as Dynamic Inventory

Configure Satellite Inventory Plugin

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

# Group hosts by these Satellite attributes group_prefix: satellite_ want_hostcollections: true want_params: true want_facts: true

# Organize hosts by lifecycle environment keyed_groups: - key: foreman_lifecycle_environment prefix: lifecycle separator: "_" - key: foreman_content_view prefix: cv separator: "_" - key: foreman_hostgroup_title | replace("/", "_") prefix: hostgroup separator: "_" - key: foreman_location prefix: location separator: "_" - key: foreman_params.app_role | default('unassigned') prefix: role separator: "_"

# Only include managed hosts filters: - managed = true

Usage:

# List inventory from Satellite
ansible-inventory -i satellite_inventory.yml --list

# Run playbook against Satellite-managed hosts ansible-playbook -i satellite_inventory.yml site.yml

See also: A Preview of Ansible Journey in 2024

Patch Management

Automated Patching Workflow

---
- name: Satellite-driven patch management
  hosts: all
  become: true
  serial: "25%"
  vars:
    satellite_url: "https://satellite.example.com"
    satellite_username: "admin"
    satellite_password: "{{ vault_satellite_password }}"
    pre_patch_snapshot: true
    reboot_after_patch: true
    patch_timeout: 3600
  tasks:
    - name: Pre-patch health check
      block:
        - name: Check disk space
          ansible.builtin.command:
            cmd: df -h / --output=pcent
          register: disk_check
          changed_when: false

- name: Fail if disk full ansible.builtin.fail: msg: "Insufficient disk space: {{ disk_check.stdout_lines[-1] | trim }}" when: (disk_check.stdout_lines[-1] | trim | replace('%','') | int) > 90

- name: Create pre-patch snapshot (if VMware) community.vmware.vmware_guest_snapshot: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" name: "{{ inventory_hostname }}" snapshot_name: "pre-patch-{{ ansible_date_time.date }}" description: "Snapshot before patching" state: present delegate_to: localhost when: pre_patch_snapshot | bool

- name: Apply security errata ansible.builtin.dnf: name: "*" security: true state: latest update_cache: true register: patch_result timeout: "{{ patch_timeout }}"

- name: Display patched packages ansible.builtin.debug: msg: "Updated {{ patch_result.results | length }} packages" when: patch_result.changed

- name: Check if reboot required ansible.builtin.command: cmd: needs-restarting -r register: reboot_check changed_when: false failed_when: false

- name: Reboot if required ansible.builtin.reboot: reboot_timeout: 600 msg: "Rebooting for kernel/system updates" when: - reboot_after_patch | bool - reboot_check.rc != 0

- name: Post-patch verification block: - name: Verify critical services ansible.builtin.systemd: name: "{{ item }}" state: started loop: "{{ critical_services | default(['sshd', 'chronyd']) }}"

- name: Report patch status to Satellite ansible.builtin.uri: url: "{{ satellite_url }}/api/v2/hosts/{{ ansible_fqdn }}/host_parameters" method: POST user: "{{ satellite_username }}" password: "{{ satellite_password }}" validate_certs: true body_format: json body: parameter: name: "last_patched" value: "{{ ansible_date_time.iso8601 }}" status_code: [200, 201, 422] delegate_to: localhost

Errata Management

---
- name: Check and apply specific errata
  hosts: all
  become: true
  tasks:
    - name: List applicable errata
      ansible.builtin.command:
        cmd: dnf updateinfo list --security
      register: errata_list
      changed_when: false

- name: Parse critical errata ansible.builtin.set_fact: critical_errata: "{{ errata_list.stdout_lines | select('search', 'Critical') | list }}" important_errata: "{{ errata_list.stdout_lines | select('search', 'Important') | list }}"

- name: Report errata counts ansible.builtin.debug: msg: | Critical: {{ critical_errata | length }} Important: {{ important_errata | length }} Total security: {{ errata_list.stdout_lines | length }}

- name: Apply critical errata only ansible.builtin.dnf: name: "*" security: true bugfix: false state: latest when: critical_errata | length > 0

Ansible Roles in Satellite

Assign Ansible Roles to Host Groups

---
- name: Configure Ansible roles in Satellite
  hosts: localhost
  module_defaults:
    group/redhat.satellite.foreman:
      server_url: "{{ satellite_url }}"
      username: "{{ satellite_username }}"
      password: "{{ satellite_password }}"
      validate_certs: "{{ satellite_validate_certs }}"
  tasks:
    - name: Import Ansible roles into Satellite
      redhat.satellite.ansible_roles:
        organization: "{{ satellite_organization }}"
        state: present
      register: roles_import

- name: Assign roles to host group ansible.builtin.uri: url: "{{ satellite_url }}/api/v2/hostgroups/{{ hostgroup_id }}/assign_ansible_roles" method: POST user: "{{ satellite_username }}" password: "{{ satellite_password }}" validate_certs: true body_format: json body: ansible_role_ids: "{{ role_ids }}" status_code: [200, 204]

Satellite API Automation

Bulk Operations via API

---
- name: Satellite API bulk operations
  hosts: localhost
  vars:
    satellite_api: "{{ satellite_url }}/api/v2"
  tasks:
    - name: Get all hosts needing patches
      ansible.builtin.uri:
        url: "{{ satellite_api }}/hosts?search=applicable_errata%3E0&per_page=100"
        user: "{{ satellite_username }}"
        password: "{{ satellite_password }}"
        validate_certs: true
      register: hosts_needing_patches

- name: Display hosts needing patches ansible.builtin.debug: msg: "{{ item.name }}: {{ item.content_facet_attributes.errata_counts.total }} errata" loop: "{{ hosts_needing_patches.json.results }}" loop_control: label: "{{ item.name }}"

- name: Get content view versions ansible.builtin.uri: url: "{{ satellite_api }}/content_views?organization_id=1" user: "{{ satellite_username }}" password: "{{ satellite_password }}" validate_certs: true register: content_views

- name: Generate patch compliance report ansible.builtin.template: src: patch-report.html.j2 dest: "/tmp/patch-report-{{ ansible_date_time.date }}.html" vars: hosts: "{{ hosts_needing_patches.json.results }}" report_date: "{{ ansible_date_time.iso8601 }}"

Foreman-Specific Integration

For environments using Foreman (upstream) instead of Satellite:

---
- name: Foreman provisioning
  hosts: localhost
  collections:
    - theforeman.foreman
  module_defaults:
    group/theforeman.foreman.foreman:
      server_url: "https://foreman.example.com"
      username: "admin"
      password: "{{ vault_foreman_password }}"
      validate_certs: true
  tasks:
    - name: Create compute profile
      theforeman.foreman.compute_profile:
        name: "Standard Web Server"
        compute_attributes:
          - compute_resource: "Libvirt"
            vm_attrs:
              cpus: 2
              memory: 4294967296
              volumes_attributes:
                0:
                  pool_name: default
                  capacity: 50G
                  format_type: qcow2
        state: present

- name: Create Foreman host theforeman.foreman.host: name: "web-01.example.com" hostgroup: "WebServers" build: true state: present

Best Practices

1. Use Content Views for Consistency

Always pin content to specific versions via Content Views rather than syncing directly to latest. This ensures all environments get identical packages.

2. Lifecycle Promotion Pipeline

Library → Development → Testing → Staging → Production

Never skip environments. Use Ansible to automate the promotion after test validation.

3. Satellite Callback Plugin

Enable the Satellite callback plugin in ansible.cfg to report playbook results back to Satellite:

[defaults]
callback_whitelist = redhat.satellite.foreman

[callback_foreman] url = https://satellite.example.com ssl_cert = /etc/foreman-proxy/ssl_cert.pem ssl_key = /etc/foreman-proxy/ssl_key.pem verify_certs = /etc/foreman-proxy/ssl_ca.pem

4. Separate Credentials

# Use Ansible Vault for Satellite credentials
ansible-vault create group_vars/all/vault.yml

# Or use environment variables satellite_password: "{{ lookup('env', 'SATELLITE_PASSWORD') }}"

Frequently Asked Questions

What is the difference between Red Hat Satellite and Foreman?

Red Hat Satellite is the commercial, supported product built on top of Foreman (provisioning), Katello (content management), and Candlepin (subscription management). Foreman is the open-source upstream project. Both integrate with Ansible using similar collections.

How do I use Satellite as a dynamic inventory for Ansible?

Install the redhat.satellite collection, create a satellite_inventory.yml file with the redhat.satellite.foreman plugin configuration, and pass it as your inventory source: ansible-playbook -i satellite_inventory.yml playbook.yml.

Can I run Ansible playbooks directly from Satellite?

Yes. Satellite 6.x includes Remote Execution (REX) which can run Ansible roles and playbooks on managed hosts. You can assign Ansible roles to host groups and trigger runs from the Satellite UI or API.

How does Satellite patching work with Ansible?

Satellite manages content (repositories, errata, content views) while Ansible executes the actual patching. Ansible reads applicable errata from Satellite's API, applies patches via dnf/yum modules, and reports results back via the callback plugin.

What collections do I need for Satellite automation?

Use redhat.satellite for Red Hat Satellite or theforeman.foreman for Foreman. Both provide modules for managing hosts, content views, lifecycle environments, repositories, and host groups.

Related Articles

Ansible Automation Platform RBACAnsible Patch ManagementAnsible Compliance Automation CIS STIG PCI DSSAnsible VMware vSphere Automation

Conclusion

Integrating Ansible with Red Hat Satellite or Foreman creates a comprehensive server lifecycle management platform. Satellite handles content management, host provisioning, and subscription tracking while Ansible provides flexible, code-driven configuration automation. Together, they give enterprises a complete solution for managing thousands of servers with consistent patching, provisioning, and compliance — all automated and auditable.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home