Node Environment
Management Node
- • System Version: Rocky Linux release 8.10 (Green Obsidian), minimal installation
- • Python Environment: python3.12
- • Ansible-core Version: 2.15.13
For demonstration purposes, we are installing
<span>ansible-core</span>(which includes only the basic modules). For normal use, you can install<span>ansible</span>(which includes some extended modules) for easier usage.This installation is done via
<span>pip</span>.
Managed Node
- • System Version: Rocky Linux release 8.10 (Green Obsidian), minimal installation
- • Python Environment: python3.6
The addresses of the three nodes are: <span>192.168.221.142</span>, <span>192.168.221.143</span>, <span>192.168.221.144</span>.
Ansible has requirements for the Python version. If the Ansible version is too high and the Python version on the managed node is too low, there may be cases of module execution failure.
https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html#ansible-core-target-node-python-support
Configuring the Basic Environment
# Install Python and two commonly used basic packages
[root@ansible-controller ~]# dnf install -y vim bash-completion python3.12 python3.12-pip sshpass
# Upgrade pip
[root@ansible-controller ~]# python3.12 -m pip install --upgrade pip
Installing Ansible
# Check available Ansible-core versions
[root@ansible-controller ~]# python3.12 -m pip index versions ansible-core
ansible-core (2.18.5)
Available versions: 2.18.5, 2.18.4, 2.18.3, 2.18.2, 2.18.1, 2.18.0, 2.17.11, 2.17.10, 2.17.9, 2.17.8, 2.17.7, 2.17.6, 2.17.5, 2.17.4, 2.17.3, 2.17.2, 2.17.1, 2.17.0, 2.16.14, 2.16.13, 2.16.12, 2.16.11, 2.16.10, 2.16.9, 2.16.8, 2.16.7, 2.16.6, 2.16.5, 2.16.4, 2.16.3, 2.16.2, 2.16.1, 2.16.0, 2.15.13, 2.15.12, 2.15.11, 2.15.10, 2.15.9, 2.15.8, 2.15.7, 2.15.6, 2.15.5, 2.15.4, 2.15.3, 2.15.2, 2.15.1, 2.15.0, 2.14.18, 2.14.17, 2.14.16, 2.14.15, 2.14.14, 2.14.13, 2.14.12, 2.14.11, 2.14.10, 2.14.9, 2.14.8, 2.14.7, 2.14.6, 2.14.5, 2.14.4, 2.14.3, 2.14.2, 2.14.1, 2.14.0, 2.13.13, 2.13.12, 2.13.11, 2.13.10, 2.13.9, 2.13.8, 2.13.7, 2.13.6, 2.13.5, 2.13.4, 2.13.3, 2.13.2, 2.13.1, 2.13.0, 2.12.10, 2.12.9, 2.12.8, 2.12.7, 2.12.6, 2.12.5, 2.12.4, 2.12.3, 2.12.2, 2.12.1, 2.12.0, 2.11.12, 2.11.11, 2.11.10, 2.11.9, 2.11.8, 2.11.7, 2.11.6, 2.11.5, 2.11.4, 2.11.3, 2.11.2, 2.11.1, 2.11.0
# Install Ansible-core
[root@ansible-controller ~]# python3.12 -m pip install ansible-core==2.15.13 argcomplete
...output omitted...
# Configure command auto-completion
[root@ansible-controller ansible]# activate-global-python-argcomplete --user
Adding shellcode to /root/.zshenv...
Added.
Adding shellcode to /root/.bash_completion...
Added.
Please restart your shell or source the installed file to activate it.
[root@ansible-controller ~]# exit
logout
# Reopen a command line
# Check the installed Ansible
[root@ansible-controller ~]# ansible --version
ansible [core 2.15.13]
config file = /root/ansible_init/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.12/site-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.12.8 (main, Dec 12 2024, 16:30:29) [GCC 8.5.0 20210514 (Red Hat 8.5.0-22)] (/usr/bin/python3.12)
jinja version = 3.1.6
libyaml = True
# You can see that there are only 70 basic modules
[root@ansible-controller ~]# ansible-doc -l | wc -l
74
Configuring the Ansible Initialization Environment
This section mainly configures the managed nodes to support running Ansible modules, primarily by installing Python and creating Ansible connection accounts.
Initializing the Managed Node Module Execution Environment
Create a temporary environment to install Python on the managed nodes and create Ansible connection accounts.
# Create ansible main directory
[root@ansible-controller ~]# mkdir ansible_init
[root@ansible-controller ~]# cd ansible_init/
# Create configuration file
[root@ansible-controller ansible_init]# cat ansible.cfg
[defaults]
inventory=./inventory
become=false
host_key_checking=False
# Configure the host inventory for initialization
[root@ansible-controller ansible_init]# cat inventory
master1 ansible_ssh_host=192.168.221.142 HOSTNAME=master1.example.com
worker1 ansible_ssh_host=192.168.221.143 HOSTNAME=worker1.example.com
worker2 ansible_ssh_host=192.168.221.144 HOSTNAME=worker2.example.com
[all:vars]
ansible_ssh_user=root
ansible_ssh_password=redhat
# Test
[root@ansible-controller ansible_init]# ansible all -m raw -a id
worker1 | CHANGED | rc=0 >>
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Shared connection to 192.168.221.143 closed.
master1 | CHANGED | rc=0 >>
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Shared connection to 192.168.221.142 closed.
worker2 | CHANGED | rc=0 >>
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Shared connection to 192.168.221.144 closed.
# Install python3
[root@ansible-controller ansible_init]# ansible all -m raw -a 'dnf install -y python3'
...output omitted...
# Test module
[root@ansible-controller ansible_init]# ansible all -m ping
...output omitted...
Adding Ansible Management User
# Add Ansible user
[root@ansible-controller ~]# openssl passwd -6 -salt randomsalt redhat
$6$randomsalt$nyZMZWZT9mAsW3O4fAAX66nk/RagLSUr4vq921cGE/hsqskAzcOJvaF4uvHUFxYnT1vBB9tGbR7UhEAhuVWu/0
[root@ansible-controller ansible_init]# ansible all -m user \
-a 'name=ansible uid=10000 groups=wheel append=true password=$6$randomsalt$nyZMZWZT9mAsW3O4fAAX66nk/RagLSUr4vq921cGE/hsqskAzcOJvaF4uvHUFxYnT1vBB9tGbR7UhEAhuVWu/0'
...output omitted...
Configuring the Ansible Automation Environment
This section describes the steps to configure the managed nodes after they have the Ansible module execution environment.
Creating Automation Environment Related Configurations
Create a new environment directory for host management.
# Create a new Ansible directory and enter it
[root@ansible-controller ansible_init]# cd
[root@ansible-controller ~]# mkdir ansible
[root@ansible-controller ~]# cd ansible
# Create Ansible configuration file
[root@ansible-controller ansible]# ansible-config init --disabled -t all > ansible.cfg
# Modify the configuration file as needed
[root@ansible-controller ansible]# grep -Ev "^#|^$|^;" ansible.cfg
[defaults]
fact_caching=jsonfile
fact_caching_connection=./cache
inventory=./inventory
remote_user=ansible
host_key_checking=False
[privilege_escalation]
become=True
become_ask_pass=True
become_method=sudo
become_user=root
[persistent_connection]
[connection]
[colors]
[selinux]
[diff]
[galaxy]
[inventory]
[netconf_connection]
[paramiko_connection]
[jinja2]
[tags]
[runas_become_plugin]
[su_become_plugin]
[sudo_become_plugin]
[callback_tree]
[ssh_connection]
[winrm]
[inventory_plugins]
[inventory_plugin_script]
[inventory_plugin_yaml]
[url_lookup]
[powershell]
[vars_host_group_vars]
# Prepare host inventory
[root@ansible-controller ansible]# cat inventory
master1 ansible_ssh_host=192.168.221.142 HOSTNAME=master1.example.com
worker1 ansible_ssh_host=192.168.221.143 HOSTNAME=worker1.example.com
worker2 ansible_ssh_host=192.168.221.144 HOSTNAME=worker2.example.com
[all:vars]
ansible_ssh_user=ansible
ansible_ssh_password=redhat
Preparing an Initialization Configuration Playbook
- • You can use
<span>ansible-doc -l</span>command to see which modules are available - • You can use
<span>ansible-doc <module_name></span>to view module documentation, and search for module usage templates by entering<span>/EXAMPLE</span>.
For example, searching for template content using the <span>ansible.builtin.dnf</span> module:
EXAMPLES:
- name: Install the latest version of Apache
ansible.builtin.dnf:
name: httpd
state: latest
- name: Install Apache >= 2.4
ansible.builtin.dnf:
name: httpd >= 2.4
state: present
- name: Install the latest version of Apache and MariaDB
ansible.builtin.dnf:
name:
- httpd
- mariadb-server
state: latest
- name: Remove the Apache package
ansible.builtin.dnf:
name: httpd
state: absent
- name: Install the latest version of Apache from the testing repo
ansible.builtin.dnf:
name: httpd
enablerepo: testing
state: present
- name: Upgrade all packages
ansible.builtin.dnf:
name: "*"
state: latest
Playbook content:
[root@ansible-controller ansible]# cat init.yaml
---
- name: Initial configuration
hosts: all
tasks:
- name: Installing System Packages
ansible.builtin.dnf:
name:
- vim
- bash-completion
- python3-pip
- epel-release
state: present
- name: Upgrading system packages
ansible.builtin.dnf:
name: '*'
state: latest
- name: Set hostname
ansible.builtin.hostname:
name: "{{ HOSTNAME }}"
- name: Set up SSH to disable root login
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regex: "^#?PermitRootLogin"
line: "PermitRootLogin no"
notify: restart sshd
handlers:
- name: Restart SSH Service
ansible.builtin.systemd:
name: sshd
state: restarted
listen: restart sshd
...output omitted...
This Playbook does four things:
- • Installs some basic software packages
- • Upgrades all system packages
- • Sets the hostname
- • Disables root login via SSH
Executing Automation Tasks
[root@ansible-controller ansible]# ansible-playbook init.yaml
BECOME password:
...output omitted...
# Check configuration
[root@ansible-controller ansible]# ansible all -m command -a "dnf upgrade"
BECOME password:
worker2 | CHANGED | rc=0 >>
Last metadata expiration check: 3:12:13 ago on Tue 20 May 2025 07:36:03 PM CST.
Dependencies resolved.
Nothing to do.
Complete!
worker1 | CHANGED | rc=0 >>
Last metadata expiration check: 3:10:43 ago on Tue 20 May 2025 07:37:33 PM CST.
Dependencies resolved.
Nothing to do.
Complete!
master1 | CHANGED | rc=0 >>
Last metadata expiration check: 0:18:26 ago on Tue 20 May 2025 10:29:50 PM CST.
Dependencies resolved.
Nothing to do.
Complete!
[root@ansible-controller ansible]# ansible all -m command -a 'grep -E "^#?PermitRootLogin no" /etc/ssh/sshd_config'
BECOME password:
master1 | CHANGED | rc=0 >>
PermitRootLogin no
worker1 | CHANGED | rc=0 >>
PermitRootLogin no
worker2 | CHANGED | rc=0 >>
PermitRootLogin no
[root@ansible-controller ansible]# ansible all -m command -a 'hostname'
BECOME password:
worker1 | CHANGED | rc=0 >>
worker1.example.com
worker2 | CHANGED | rc=0 >>
worker2.example.com
master1 | CHANGED | rc=0 >>
master1.example.com
Using Collections to Extend Ansible’s Functionality
Installing Collections
<span>ansible-core</span> only includes some core modules, and sometimes additional modules are needed to extend Ansible’s functionality. By installing collections, you can expand Ansible’s capabilities, for example, <span>ansible.posix.selinux</span> is used to manage SELinux.
# Download collection
[root@ansible-controller ansible]# ansible-galaxy collection install ansible.posix
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/ansible-posix-2.0.0.tar.gz to /root/.ansible/tmp/ansible-local-111554mi8iz9y/tmpjgw4dlat/ansible-posix-2.0.0-fc5mr3e5
Installing 'ansible.posix:2.0.0' to '/root/.ansible/collections/ansible_collections/ansible/posix'
ansible.posix:2.0.0 was installed successfully
Using Newly Installed Modules
# Check newly installed modules
[root@ansible-controller ansible]# ansible-doc -l | grep ansible.posix
ansible.posix.acl Set and retrieve file ACL informatio...
ansible.posix.at Schedule the execution of a command ...
ansible.posix.authorized_key Adds or removes an SSH authorized ke...
ansible.posix.firewalld Manage arbitrary ports/services with...
ansible.posix.firewalld_info Gather information about firewalld
ansible.posix.mount Control active and configured mount ...
ansible.posix.patch Apply patch files using the GNU patc...
ansible.posix.rhel_facts Facts module to set or override RHEL...
ansible.posix.rhel_rpm_ostree Ensure packages exist in a RHEL for ...
ansible.posix.rpm_ostree_upgrade Manage rpm-ostree upgrade transactio...
ansible.posix.seboolean Toggles SELinux booleans
ansible.posix.selinux Change policy and state of SELinux
ansible.posix.synchronize A wrapper around rsync to make commo...
ansible.posix.sysctl Manage entries in sysctl.conf
# View ansible.posix.selinux module documentation
[root@ansible-controller ansible]# ansible-doc ansible.posix.selinux
# Enter /EXAMPLE to search for usage templates
# Prepare a YAML file to configure SELinux based on the template
---
- name: Disable SELinux
hosts: all
gather_facts: false
tasks:
- name: Disable SELinux
ansible.posix.selinux:
policy: targeted
state: permissive
# Execute Playbook
[root@ansible-controller ansible]# ansible-playbook selinux.yml
# Verify
[root@ansible-controller ansible]# ansible all -m command -a 'grep SELINUX= /etc/selinux/config'
BECOME password:
master1 | CHANGED | rc=0 >>
# SELINUX= can take one of these three values:
SELINUX=permissive
worker1 | CHANGED | rc=0 >>
# SELINUX= can take one of these three values:
SELINUX=permissive
worker2 | CHANGED | rc=0 >>
# SELINUX= can take one of these three values:
SELINUX=permissive
If an error occurs during execution,
<span>The error was: ModuleNotFoundError: No module named 'selinux'</span>, it indicates that the managed host does not have the<span>python3-libselinux</span>package installed.
Rebooting Nodes and Verifying Functionality
[root@ansible-controller ansible]# ansible all -m reboot
BECOME password:
worker1 | CHANGED => {
"changed": true,
"elapsed": 14,
"rebooted": true
}
worker2 | CHANGED => {
"changed": true,
"elapsed": 14,
"rebooted": true
}
master1 | CHANGED => {
"changed": true,
"elapsed": 15,
"rebooted": true
}
[root@ansible-controller ansible]# ssh [email protected]
[email protected]'s password:
Permission denied, please try again.
[email protected]'s password:
[root@ansible-controller ansible]# ssh [email protected]
[email protected]'s password:
Last login: Wed May 21 01:32:06 2025 from 192.168.221.141
[ansible@master1 ~]$
[ansible@master1 ~]$ hostname
master1.example.com
[ansible@master1 ~]$ sudo dnf upgrade
[sudo] password for ansible:
Last metadata expiration check: 0:19:34 ago on Wed 21 May 2025 01:14:37 AM CST.
Dependencies resolved.
Nothing to do.
Complete!