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: microsoft.mecm and microsoft.scom: New Certified Ansible Collections for Windows Automation in AAP 2.7

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: RHSB-2024–001 Leaky Vessels — runc — (CVE-2024–21626)

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.

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