Chapter 2. Implementing Ansible Playbook
ONLY FOR SELF STUDY, NO COMMERCIAL USAGE!!!
Building inventory
- An inventory defines a collection of hosts that Ansible manages.
- hosts can be assigned to groups
- group can contain child groups
- hosts can be members of multiple groups
- inventory can set variable to select a range of hosts/groups
- static host inventory(INI-style or YAML)
- Dynamic host inventory(Ansible plug-in)
Static Inventory
- txt file that support a number of different formats, including INI-style(most common) or YAML
- hostname cannot have space, like
[New York]
would not work, correct should be[Newyork]
-
A list of hostnames or IP addresses of managed hosts, each on a single line:
web1.example.com web2.example.com db1.example.com db2.example.com 192.0.2.42
-
Organize managed hosts into host groups.
In the following example, the host inventory defines two host groups:webservers
anddb-servers
.[webservers] web1.example.com web2.example.com 192.0.2.42 [db-servers] db1.example.com db2.example.com
-
Hosts can be in multiple groups. (role of the host, its physical location, whether it is in production or not, and so on).
[webservers] web1.example.com web2.example.com 192.0.2.42 [db-servers] db1.example.com db2.example.com [east-datacenter] web1.example.com db1.example.com [production] web1.example.com web2.example.com db1.example.com db2.example.com [development] 192.0.2.42
-
Nested Groups
- creating a host group name with the
:children
suffix. The following example creates a new group callednorth-america
, which includes all hosts from theusa
andcanada
groups. - A group can have both managed hosts and child groups as members. For example, in the previous inventory you could add a
[north-america]
section that has its own list of managed hosts. That list of hosts would be merged with the additional hosts that thenorth-america
group inherits from its child groups.
[usa] washington1.example.com washington2.example.com [canada] ontario01.example.com ontario02.example.com [north-america:children] canada usa [north-america] washington03.example.com
- creating a host group name with the
Simplifying Host Specifications with Ranges
Ranges have the following syntax:
[START:END]
Ranges match all values from START to END, inclusively. Consider the following examples:
192.168.[4:7].[0:255]
matches all IPv4 addresses in the 192.168.4.0/22 network (192.168.4.0 through 192.168.7.255).server[01:20].example.com
matches all hosts namedserver01.example.com
throughserver20.example.com
.[a:c].dns.example.com
matches hosts nameda.dns.example.com
,b.dns.example.com
, andc.dns.example.com
.2001:db8::[a:f]
matches all IPv6 addresses from 2001:db8::a through 2001:db8::f.
NTOE: If leading zeros are included in numeric ranges, they are used in the pattern. The second example above does not match server1.example.com
but does match server07.example.com
.
Verifying the Inventory
ansible-navigator inventory
command to query an inventory file.
[student@workstation ~]$ ansible-navigator inventory --help
Usage: ansible-navigator inventory [options]
inventory: Explore an inventory
Options (global):
-h --help Show this help message and exit
--version Show the application version and exit
--rad --ansible-runner-artifact-dir The directory path to store artifacts generated by ansible-runner
--rac --ansible-runner-rotate-artifacts-count Keep ansible-runner artifact directories, for last n runs, if set to 0
artifact directories won't be deleted
--rt --ansible-runner-timeout The timeout value after which ansible-runner will forcefully stop the
execution
--cdcp --collection-doc-cache-path The path to collection doc cache (default:
/home/student/.cache/ansible-navigator/collection_doc_cache.db)
--ce --container-engine Specify the container engine (auto=podman then docker)
(auto|podman|docker) (default: auto)
--co --container-options Extra parameters passed to the container engine command
--dc --display-color Enable the use of color for mode interactive and stdout (true|false)
(default: true)
--ecmd --editor-command Specify the editor command (default: vi +{line_number} {filename})
--econ --editor-console Specify if the editor is console based (true|false) (default: true)
--ee --execution-environment Enable or disable the use of an execution environment (true|false)
(default: true)
--eei --execution-environment-image Specify the name of the execution environment image (default:
registry.redhat.io/ansible-automation-platform-22/ee-supported-
rhel8:latest)
--eev --execution-environment-volume-mounts Specify volume to be bind mounted within an execution environment
(--eev /home/user/test:/home/user/test:Z)
--la --log-append Specify if log messages should be appended to an existing log file,
otherwise a new log file will be created per session (true|false)
(default: true)
--lf --log-file Specify the full path for the ansible-navigator log file (default:
/home/student/ansible-navigator.log)
--ll --log-level Specify the ansible-navigator log level
(debug|info|warning|error|critical) (default: warning)
-m --mode Specify the user-interface mode (stdout|interactive) (default:
interactive)
--osc4 --osc4 Enable or disable terminal color changing support with OSC 4
(true|false) (default: true)
--penv --pass-environment-variable Specify an existing environment variable to be passed through to and
set within the execution environment (--penv MY_VAR)
--pa --pull-arguments Specify any additional parameters that should be added to the pull
command when pulling an execution environment from a container
registry. e.g. --pa='--tls-verify=false'
--pp --pull-policy Specify the image pull policy always:Always pull the image,
missing:Pull if not locally available, never:Never pull the image,
tag:if the image tag is 'latest', always pull the image, otherwise
pull if not locally available (always|missing|never|tag) (default:
tag)
--senv --set-environment-variable Specify an environment variable and a value to be set within the
execution environment (--senv MY_VAR=42)
--tz --time-zone Specify the IANA time zone to use or 'local' to use the system time
zone (default: utc)
Options (inventory subcommand):
--hi --help-inventory Help options for ansible-inventory command in stdout mode (true|false)
-i --inventory Specify an inventory file path or comma separated host list
--ic --inventory-column Specify a host attribute to show in the inventory view
Note: With '--mode stdout', 'ansible-navigator inventory' additionally supports the same parameters as the 'ansible-
inventory' command. For more information about these, try 'ansible-navigator inventory --help-inventory --mode stdout'
Check local ‘inventory’ file:
[user@controlnode ~]$ ansible-navigator inventory -i inventory
# The examples assume that a file named `inventory` exists in the current directory and that the file uses ranges to simplify the `[usa]` and `[canada]` group definitions
[student@workstation playbook-inventory]$ cat inventory
[Webserver]
servera.lab.example.com
serverb.lab.example.com
serverc.lab.example.com
serverd.lab.example.com
[Raleigh]
servera.lab.example.com
serverb.lab.example.com
[Mountainview]
serverc.lab.example.com
[London]
serverd.lab.example.com
[Development]
servera.lab.example.com
[Testing]
serverb.lab.example.com
[Production]
serverc.lab.example.com
serverd.lab.example.com
[US:children]
Raleigh
Mountainview
Verify if the host in the inventory:
[user@controlnode ~]$ ansible-navigator inventory -i inventory -m stdout --host servera.lab.example.com
Lists all hosts in the inventory:
[user@controlnode ~]$ ansible-navigator inventory -i inventory -m stdout --list
[student@workstation playbook-inventory]$ ansible-navigator inventory -i inventory -m stdout --list
{
"Development": {
"hosts": [
"servera.lab.example.com"
]
},
"London": {
"hosts": [
"serverd.lab.example.com"
]
},
"Mountainview": {
"hosts": [
"serverc.lab.example.com"
]
},
"Production": {
"hosts": [
"serverc.lab.example.com",
"serverd.lab.example.com"
]
},
"Raleigh": {
"hosts": [
"servera.lab.example.com",
"serverb.lab.example.com"
]
},
"Testing": {
"hosts": [
"serverb.lab.example.com"
]
},
"US": {
"children": [
"Mountainview",
"Raleigh"
]
},
"Webserver": {
"hosts": [
"servera.lab.example.com",
"serverb.lab.example.com",
"serverc.lab.example.com",
"serverd.lab.example.com"
]
},
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"Development",
"London",
"Production",
"Testing",
"US",
"Webserver",
"ungrouped"
]
}
}
The following command lists all hosts in a group.
[user@controlnode ~]$ ansible-navigator inventory -i inventory -m stdout --graph US
[student@workstation playbook-inventory]$ ansible-navigator inventory -i inventory -m stdout --graph US
@US:
|--@Mountainview:
| |--serverc.lab.example.com
|--@Raleigh:
| |--servera.lab.example.com
| |--serverb.lab.example.com
[student@workstation playbook-inventory]$ ansible-navigator inventory -i inventory -m stdout --graph all
@all:
|--@Development:
| |--servera.lab.example.com
|--@London:
| |--serverd.lab.example.com
|--@Production:
| |--serverc.lab.example.com
| |--serverd.lab.example.com
|--@Testing:
| |--serverb.lab.example.com
|--@US:
| |--@Mountainview:
| | |--serverc.lab.example.com
| |--@Raleigh:
| | |--servera.lab.example.com
| | |--serverb.lab.example.com
|--@Webserver:
| |--servera.lab.example.com
| |--serverb.lab.example.com
| |--serverc.lab.example.com
| |--serverd.lab.example.com
|--@ungrouped:
Run the ansible-navigator inventory
command to interactively browse inventory hosts and groups:
[user@controlnode ~]$ ansible-navigator inventory -i inventory
Title Description
0│Browse groups Explore each inventory group and group members members
1│Browse hosts Explore the inventory with a list of all hosts
Type `:0` to select "Browse Groups":
Type `:1` to select "Browse Hosts"
Press the ESC key to exit the Groups menu.
Ensure that host groups do not use the same names as hosts in the inventory or you will get a warning when runs commands.
Overriding the Location of the Inventory
The /etc/ansible/hosts
file is considered the system’s default static inventory file. However, normal practice is not to use that file but to specify a different location for your inventory files as following:
ansible-navigator --inventory <pathname>
ansible-navigator -i <pathname>
You can also define a different default location for the inventory file in your Ansible configuration file.
Dynamic Inventories
By using Ansible plug-ins, Ansible inventory information can also be dynamically generated, using information provided by external databases.
For example, a dynamic inventory program could contact your Red Hat Satellite server or Amazon EC2 account, and use information stored there to construct an Ansible inventory. It can populate the inventory with up-to-date information provided by the service as new hosts are added, and old hosts are removed.
REFERENCES How to build your inventory: Ansible Documentation
Managing Ansible Configuration Files
Ansible configuration file
You can create and edit two files in each of your Ansible project directories that configure the behavior of Ansible and the ansible-navigator
command.
ansible.cfg
, which configures the behavior of several Ansible tools.ansible-navigator.yml
, which changes default options for theansible-navigator
command.
Ansible setting
use the following two sections:
[defaults]
, which sets defaults for Ansible operation[privilege_escalation]
, which configures how Ansible performs privilege escalation on managed hosts
The following is a sample ansible.cfg
file:
[defaults]
inventory = ./inventory
remote_user = user
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
inventroy | The inventory parameter specifies the path to a static inventory file, or to a directory containing multiple static inventory files and dynamic inventory scripts. |
---|---|
remote_user | The remote_user parameter specifies the username that Ansible uses to connect to the managed hosts. In a container-based automation execution environment run by ansible-navigator , this defaults to root . |
ask_pass | The ask_pass parameter specifies whether to prompt for an SSH password. Defaults to false . Set this parameter to true for password-based SSH authentication, or to false for SSH public key authentication. If you use the ansible-navigator command, then setting this parameter to true requires disabling playbook artifacts and using the standard output mode.ssh-keygen to create a public key and then ssh-copy-id user@host.example.com to copy the public key to the managed host host.example.com |
become | The become parameter specifies whether to automatically switch users on the managed host (typically to root ) after connecting. Defaults to false . Although you can enable privilege escalation for all plays by setting this parameter to true , you might decide to only enable privilege escalation on the plays, blocks, or tasks that require elevated privileges. |
become_method | The become_method parameter specifies how to switch users. Defaults to sudo , although other methods, such as su , are available. config file: /etc/sudoers or create file under folder/etc/sudoers.d , command: visudo ## Allows people in group wheel to run all commands %wheel ALL=(ALL) ALL ## Same thing without a password # %wheel ALL=(ALL) NOPASSWD: ALL |
become_user | The become_user parameter specifies which user to switch to on the managed host. Defaults to root . |
become_ask_pass | The become_ask_pass parameter specifies whether to prompt for a password for the become_method parameter. Defaults to false . If you use the ansible-navigator command, then setting this parameter to true requires disabling playbook artifacts and using the standard output mode. |
NOTE:
-
The
ansible-playbook
command can also use a.ansible.cfg
file in your home directory or the/etc/ansible/ansible.cfg
file, but these local files on your system are not used by theansible-navigator
command. -
If the managed hosts would not have SSH key-based authentication configured yet, you would have to run like following:
ansible-navigator run --ask-pass -pae false -m stdout <ping-internetweb.yml>
the command prompts to authenticate as the remote user.
Determining Current config
ansible-navigator config
command run from a /home/student/project/
directory that contains an ansible.cfg
file:
Name Default Source Current
...output omitted...
44│Default ask pass True default False
45│Default ask vault pass True default False
46│Default become False /home/student/project/ansible.cfg True
47│Default become ask pass False /home/student/project/ansible.cfg True
...output omitted...
50│Default become method False /home/student/project/ansible.cfg sudo
51│Default become user False /home/student/project/ansible.cfg root
...output omitted...
In the preceding example, each line describes an Ansible configuration parameter.
- The
True
value in theDefault
column for theDefault ask pass
andDefault ask vault pass
parameters means that the parameters are using their default values. The current value for both parameters isFalse
and the values are displayed in theCurrent
column. - The
Default become
andDefault become ask pass
parameters have been manually configured toTrue
in the/home/student/project/ansible.cfg
configuration file. TheDefault
column isFalse
for these two parameters. TheSource
column provides the path to the configuration file which defines these parameters, and theCurrent
column shows that the value for these two parameters isTrue
. - The
Default become method
parameter has the current value ofsudo
, and theDefault become user
parameter has the current value ofroot
.
Ansible-navigator setting
Creating a configuration file (or settings file) for ansible-navigator
to override the default values of its configuration settings. The settings file can be in JSON (.json
) or YAML (.yml
or .yaml
) format.
Automation content navigator looks for a settings file in the following order and uses the first file that it finds:
- If the
ANSIBLE_NAVIGATOR_CONFIG
environment variable is set, then use the configuration file at the location it specifies. - An
ansible-navigator.yml
file in your current Ansible project directory. - A
.ansible-navigator.yml
file in your home directory (notice that the file name contains a “dot” at the start of the name).
Just like the Ansible configuration file, each project can have its own automation content navigator settings file.
The following ansible-navigator.yml
file configures some common settings:
---
ansible-navigator:
execution-environment:
image: utility.lab.example.com/ee-supported-rhel8:latest
pull:
policy: missing
playbook-artifact:
enable: false
mode: stdout
execution-environment | The execution-environment section configures settings for the automation execution environment that the ansible-navigator command uses. |
---|---|
image | The image key defines the container image name to use for the automation execution environment. |
policy | The policy key nested below the pull section states to only pull the container image if it does not already exist on the local machine. |
playbook-artifact | The playbook-artifact section configures settings for the JSON files that Ansible generates every time you run a playbook. Each generated JSON file records information about a specific playbook run. You can use these files to review the results of a playbook run or to troubleshoot playbook issues. |
enable | The enable key nested below the playbook-artifact section disables generating playbook artifacts when using the ansible-navigator run command. Playbook artifacts must be disabled when you require a prompt for a password when running a playbook. You can temporarily override this setting from the command line with the --pae option. |
mode | The mode key defines the output mode for the ansible-navigator command. The value for this key should be set to either interactive (the default) or stdout . You can temporarily override this setting from the command line with the -m option. |
Additional Ref:
Developing Advanced Automation with Red Hat Ansible Automation Platform (DO374). Refer to https://ansible.readthedocs.io/projects/navigator/settings/ for more documentation on the settings that you can use in this file.
Configuration File Comments
Both the ansible.cfg
file and ansible-navigator.yml
support the number sign (#) at the start of a line as a comment character.
In addition, the ansible.cfg
file supports the semicolon (😉 as a comment character. The semicolon character comments out everything to the right of it on the line.
Writing and Running Ansible Playbooks
Format of Ansible Playbooks
-
The power of Ansible is that you can use playbooks to run multiple, complex tasks against a set of targeted hosts in an easily repeatable manner.
-
A task is the application of a module to perform a specific unit of work. A play is a sequence of tasks to be applied, in order, to one or more hosts selected from your inventory. A playbook is a text file containing a list of one or more plays to run in a specific order.
-
The following example contains one play with a single task.
--- - name: Configure important user consistently hosts: servera.lab.example.com tasks: - name: Newbie exists with UID 4000 ansible.builtin.user: name: newbie uid: 4000 state: present
-
A playbook is a text file written in YAML format(
.yml file
) -
Uses indentation with space characters to indicate the structure of its data (Do not use
TAB
characters). YAML does not place strict requirements on how many spaces are used for the indentation, but two basic rules apply:-
Data elements at the same level in the hierarchy (such as items in the same list) must have the same indentation.
-
Items that are children of another item must be indented more than their parents.
-
-
You can also add blank lines for readability.
-
For
vi
text editor to easily edit your playbooks. add the following line to your$HOME/.vimrc
file, and whenvi
detects that you are editing a YAML file, it performs a 2-space indentation when you press the Tab key and automatically indents subsequent lines.autocmd FileType yaml setlocal ai ts=2 sw=2 et
-
-
Rules of playbooks
-
Usually start with three dashes(—), might end with three dots(…) but often being omitted.
-
The play itself is a collection of key-value pairs. Keys in the same play should have the same indentation. The following example shows a YAML snippet with three keys. The first two keys have simple values. The third has a list of three items as a value. The Ansible always run plays in order.
name: just an example hosts: webservers tasks: - name: Web server is enabled ansible.builtin.service: name: httpd enabled: true - name: NTP server is enabled ansible.builtin.service: name: chronyd enabled: true - name: Postfix is enabled ansible.builtin.service: name: postfix enabled: true
-
-
Finding Modules for tasks
- The
ansible-core
package provides a single Ansible Content Collection namedansible.builtin
. These modules are always available to you. Visit https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ for a list of modules contained in theansible.builtin
collection. ansible-navigator collections
command show Red Hat Ansible Automation Platform 2.2,ee-supported-rhel8
, includes a number of other Ansible Content Collections.- Other ansible contents:(installed in the
collections
directory of your Ansible project, no formal support by RH)- The automation hub offered through the Red Hat Hybrid Cloud Console at
https://console.redhat.com/ansible/automation-hub
- A private automation hub managed by your organization
- The community’s Ansible Galaxy website at https://galaxy.ansible.com
- The automation hub offered through the Red Hat Hybrid Cloud Console at
- The
Running Playbooks
-
ansible-navigator run
-
The following example shows the contents of a simple playbook, and then the result of running it.
[user@controlnode playdemo]$ cat webserver.yml --- - name: Play to set up web server hosts: servera.lab.example.com tasks: - name: Latest httpd version installed ansible.builtin.dnf: name: httpd state: latest ...output omitted... [user@controlnode playdemo]$ ansible-navigator run \ > -m stdout webserver.yml PLAY [Play to set up web server] ************************************************ TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [Latest httpd version installed] ****************************************** changed: [servera.lab.example.com] PLAY RECAP ********************************************************************* servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The value of the
name
key for each play and task is displayed when the playbook is run. (TheGathering Facts
task is a special task that theansible.builtin.setup
module usually runs automatically at the start of a play.You should also see that the
Latest httpd version installed
task ischanged
forservera.lab.example.com
. This means that the task changed something on that host to ensure that its specification was met. In this case, it means that thehttpd
package was not previously installed or was not the latest version.In general, tasks in Ansible Playbooks are idempotent, and it is safe to run a playbook multiple times. If the targeted managed hosts are already in the correct state, no changes should be made. For example, assume that the playbook from the previous example is run again:
[user@controlnode playdemo]$ ansible-navigator run \ > -m stdout webserver.yml PLAY [Play to set up web server] ************************************************ TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [Latest httpd version installed] ****************************************** ok: [servera.lab.example.com] PLAY RECAP ********************************************************************* servera.lab.example.com : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This time, all tasks passed with status
ok
and no changes were reported.
Increasing Output Verbosity
The default output does not provide detailed task execution information. The -v
option provides additional information, with up to four levels.
Table 2.2. Configuring the Output Verbosity of Playbook Execution
Option | Description |
---|---|
-v | Displays task results. |
-vv | Displays task results and task configuration. |
-vvv | Displays extra information about connections to managed hosts. |
-vvvv | Adds extra verbosity options to the connection plug-ins, including users being used on the managed hosts to execute scripts, and what scripts have been executed. |
Syntax Verification
ansible-navigator run --syntax-check
command to validate the syntax of a playbook. The following example shows the successful syntax validation of a playbook.
[user@controlnode playdemo]$ ansible-navigator run \
> -m stdout webserver.yml --syntax-check
playbook: /home/user/playdemo/webserver.yml
When syntax validation fails, a syntax error is reported. The output also includes the approximate location of the syntax issue in the playbook. The following example shows the failed syntax validation of a playbook where the space separator is missing after the name
attribute for the play.
[user@controlnode playdemo]$ ansible-navigator run \
> -m stdout webserver.yml --syntax-check
ERROR! Syntax Error while loading YAML.
mapping values are not allowed in this context
The error appears to have been in ...output omitted... line 3, column 8, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name:Play to set up web server
hosts: servera.lab.example.com
^ here
Executing a Dry Run
ansible-navigator run --check
option to run a playbook in check mode, which performs a “dry run” of the playbook. it does not make any actual changes to managed hosts. (Sometimes the Dry run report a failure but actually Run would finished OK, eg. the Dru Run can not start a service so the later service check step failed but the Real Run would not have the problem.)
In this case, the dry run reports that the task would make a change on the managed host for the latest httpd version to be installed.
[user@controlnode playdemo]$ ansible-navigator run \
> -m stdout webserver.yml --check
PLAY [Play to set up web server] ***********************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Latest httpd version installed] ******************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
REFERENCES
Intro to playbooks — Ansible Documentation
Working with playbooks — Ansible Documentation
Validating tasks: check mode and diff mode — Ansible Documentation
Implementing Multiple Plays
Writing a playbook that contains multiple plays is very straightforward. Each play in the playbook is written as a top-level list item in the playbook. Each play is a list item containing the usual play keywords.
Remote Users in Plays
-
Ansible determines which user account to use when connecting to a managed host based on the following list, selecting the first username it finds in this order:
-
The
ansible_user
variable set for the host or group, if set. -
The
remote_user
from the current play, if set.remote_user: remoteuser
-
The
remote_user
from theansible.cfg
configuration file, if set.
-
If no value has been set for any of the preceding settings, and you are running playbooks by using ansible-navigator
with an execution environment, Ansible uses root
. (If you are using ansible-playbook
, Ansible uses the name of the user that ran the command.)
Privilege Escalation in Plays
-
Use the
become
Boolean keyword to enable(yes/true) or disable(no/false) privilege escalation for an individual play or task. -
use the
become_method
keyword in the play to specify the privilege escalation method to use for that play -
use the
become_user
keyword in the play to define the user account to use for privilege escalation in that specific play. -
The following example demonstrates some of these keywords in a play:
- name: /etc/hosts is up-to-date hosts: datacenter-west remote_user: automation become: true become_method: sudo become_user: root tasks: - name: server.example.com in /etc/hosts ansible.builtin.lineinfile: path: /etc/hosts line: '192.0.2.42 server.example.com server' state: present
Selecting Modules
Table 2.3. Ansible Modules
Category | Modules |
---|---|
Files | ansible.builtin.copy : Copy a local file to the managed host.ansible.builtin.file : Set permissions and other properties of files.ansible.builtin.lineinfile : Ensure a particular line is or is not in a file.ansible.posix.synchronize : Synchronize content using rsync . |
Software | ansible.builtin.package : Manage packages using the automatically detected package manager native to the operating system.ansible.builtin.dnf : Manage packages using the DNF package manager.ansible.builtin.apt : Manage packages using the APT package manager.ansible.builtin.pip : Manage Python packages from PyPI. |
System | ansible.posix.firewalld : Manage arbitrary ports and services using firewalld .ansible.builtin.reboot : Reboot a machine.ansible.builtin.service : Manage services.ansible.builtin.user : Add, remove, and manage user accounts. |
Net Tools | ansible.builtin.get_url : Download files over HTTP, HTTPS, or FTP.ansible.builtin.uri : Interact with web services. |
Module Documentation
- run the
ansible-navigator doc -l
command to displays a list of module short names and a synopsis of their functions. - run
ansible-navigator doc *
module_name*
command to display detailed documentation for a module. (interactive mode and can use-m stdout
to show pages) ansible-navigator collections
to use interactive mode to browse moduleansible-navigator -s doc *
module_name*
to show summary of a module
[user@controlnode ~]$ ansible-navigator doc -l
add_host Add a host (and alt...
amazon.aws.aws_az_facts Gather information ...
amazon.aws.aws_az_info Gather information ...
amazon.aws.aws_caller_info Get information abo...
amazon.aws.aws_s3 manage objects in S...
...output omitted...
vyos.vyos.vyos_user Manage the collecti...
vyos.vyos.vyos_vlan Manage VLANs on VyO...
wait_for Waits for a conditi...
wait_for_connection Waits until remote ...
yum Manages packages wi...
yum_repository Add or remove YUM r...
Example
---
- name: Enable intranet services
hosts: servera.lab.example.com
become: true
tasks:
- name: Latest version of httpd and firewalld installed
ansible.builtin.dnf:
name:
- httpd
- firewalld
state: latest
- name: Test html page is installed
ansible.builtin.copy:
content: "Welcome to the example.com intranet!\n"
dest: /var/www/html/index.html
- name: Firewall enabled and running
ansible.builtin.service:
name: firewalld
enabled: true
state: started
- name: Firewall permits access to httpd service
ansible.posix.firewalld:
service: http
permanent: true
state: enabled
immediate: true
- name: Web server enabled and running
ansible.builtin.service:
name: httpd
enabled: true
state: started
- name: Test intranet web server
hosts: workstation.lab.example.com
become: false
tasks:
- name: Connect to intranet web server
ansible.builtin.uri:
url: http://servera.lab.example.com
return_content: true
status_code: 200
-
The directory has already been populated with an
ansible.cfg
configuration file and an inventory file namedinventory
. The managed host,servera.lab.example.com
, is already defined in this inventory file. -
Create a playbook named
/home/student/playbook-multi/intranet.yml
and add the lines needed to start the first play. It should target the managed hostservera.lab.example.com
and enable privilege escalation. -
As the first task in the first play, define a task that ensures that the
httpd
andfirewalld
packages are up-to-date. -
Add a task to the first play’s list that ensures that the correct content is in the
/var/www/html/index.html
file. -
Define two more tasks in the play to ensure that the
firewalld
service is running and starts on boot, and allows connections to thehttpd
service. -
Add a final task to the first play that ensures that the
httpd
service is running and starts at boot. -
In the
/home/student/playbook-multi/intranet.yml
file, define a second play that targetsworkstation.lab.example.com
and tests the intranet web server. By testing from theworkstation.lab.example.com
machine, you can verify that theservera.lab.example.com
machine allows external web requests through the firewall. This play does not require privilege escalation. -
Add a single task to the second play, and use the
ansible.builtin.uri
module to request content fromhttp://servera.lab.example.com
. The task should verify a return HTTP status code of200
. Configure the task to place the returned content in the task results variable.Run the playbook using the
ansible-navigator run
command.[student@workstation playbook-multi]$ ansible-navigator run \ > -m stdout intranet.yml PLAY [Enable intranet services] ************************************************ TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [Latest version of httpd and firewalld installed] ************************* changed: [servera.lab.example.com] TASK [Test html page is installed] ********************************************* changed: [servera.lab.example.com] TASK [Firewall enabled and running] ******************************************** changed: [servera.lab.example.com] TASK [Firewall permits access to httpd service] ******************************** changed: [servera.lab.example.com] TASK [Web server enabled and running] ****************************************** changed: [servera.lab.example.com] PLAY [Test intranet web server] ************************************************ TASK [Gathering Facts] ********************************************************* ok: [workstation.lab.example.com] TASK [Connect to intranet web server] ****************************************** ok: [workstation.lab.example.com] PLAY RECAP ********************************************************************* servera.lab.example.com : ok=6 changed=5 unreachable=0 failed=0 ... workstation.lab.example.com : ok=2 changed=0 unreachable=0 failed=0 ...
Use the
curl
command to verify that an HTTP GET request tohttp://servera.lab.example.com
provides the correct content.[student@workstation playbook-multi]$ curl http://servera.lab.example.com Welcome to the example.com intranet!
Running Arbitrary Commands on Managed Hosts
The ansible.builtin.command
module is the simplest of these commands. Its cmd
argument specifies the command that you want to run.
The following example task runs /opt/bin/makedb.sh
on managed hosts.
- name: Run the /opt/bin/makedb.sh command
ansible.builtin.command:
cmd: /opt/bin/makedb.sh
Unlike most modules, ansible.builtin.command
is not idempotent. Every time the task is specified in a play, it runs and it reports that it changed something on the managed host, even if nothing needed to be changed.
You can try to make the task safer by configuring it only to run based on the existence of a file. The creates
option causes the task to run only if a file is missing; the assumption is that if the task runs, it creates that file. The removes
option causes the task to run only if a file is present; the assumption is that if the task runs, it removes that file.
For example, the following task only runs if /opt/db/database.db
is not present:
- name: Initialize the database
ansible.builtin.command:
cmd: /opt/bin/makedb.sh
creates: /opt/db/database.db
The ansible.builtin.command
module cannot access shell environment variables or perform shell operations such as input/output redirection or pipelines. When you need to perform shell processing, you can use the ansible.builtin.shell
module. Like the ansible.builtin.command
module, you pass the commands to be executed as arguments to the module.
Both ansible.builtin.command
and ansible.builtin.shell
modules require a working Python installation on the managed host. A third module, ansible.builtin.raw
, can run commands directly using the remote shell, bypassing the module subsystem. This is useful when you are managing systems that cannot have Python installed (for example, a network router). It can also be used to install Python on a managed host.
IMPORTANT NOTE:
When possible, try to avoid the ansible.builtin.command
, ansible.builtin.shell
, and ansible.builtin.raw
modules in playbooks, even though they might seem simple to use. Because these run arbitrary commands on the managed hosts, it is very easy to write non-idempotent playbooks with these modules.
If you must use them, it is probably best to use the ansible.builtin.command
module first, resorting to ansible.builtin.shell
or ansible.builtin.raw
only if you need their special features.
As another example, the following task using the ansible.builtin.shell
module is not idempotent. Every time the play is run, it rewrites /etc/resolv.conf
even if it already consists of the line nameserver 192.0.2.1
. A task that is not idempotent displays the changed
status every time the task is run.
- name: Non-idempotent approach with shell module
ansible.builtin.shell:
cmd: echo "nameserver 192.0.2.1" > /etc/resolv.conf
You can create idempotent tasks in several ways using the ansible.builtin.shell
module, and sometimes making those changes and using ansible.builtin.shell
is the best approach. But in this case, a better solution would be to use ansible-navigator doc
to discover the ansible.builtin.copy
module and use that to get the desired effect.
The following example does not rewrite the /etc/resolv.conf
file if it already consists of the correct content:
- name: Idempotent approach with copy module
ansible.builtin.copy:
dest: /etc/resolv.conf
content: "nameserver 192.0.2.1\n"
The ansible.builtin.copy
module tests to see if the state has already been met, and if so, it makes no changes. The ansible.builtin.shell
module allows a lot of flexibility, but also requires more attention to ensure that it runs with idempotency.
You can run idempotent playbooks repeatedly to ensure systems are in a particular state without disrupting those systems if they already are.
YAML Syntax
The last part of this section investigates some variations of YAML or Ansible Playbook syntax that you might encounter.
YAML Comments
Comments can also be used to aid readability. In YAML, everything to the right of the number sign (#) is a comment. If there is content to the left of the comment, precede the hash with a space.
# This is a YAML comment
some data # This is also a YAML comment
YAML Strings
Strings in YAML do not normally need to be put in quotation marks even if the string contains no spaces. You can enclose strings in either double or single quotation marks.
this is a string
'this is another string'
"this is yet another a string"
You can write multiline strings in either of two ways. You can use the vertical bar (|) character to denote that newline characters within the string are to be preserved. (keep the original format of sentence, not single sentence once folded.)
include_newlines: |
Example Company
123 Main Street
Atlanta, GA 30303
You can also write multiline strings using the greater-than (>) character to indicate that newline characters are to be converted to spaces and that leading white spaces in the lines are to be removed. This method is often used to break long strings at space characters so that they can span multiple lines for better readability. (break down a single long sentence into pieces)
fold_newlines: >
This is an example
of a long string,
that will become
a single sentence once folded.
YAML Dictionaries
You have seen collections of key-value pairs written as an indented block, as follows:
name: svcrole
svcservice: httpd
svcport: 80
Dictionaries can also be written in an inline block format enclosed in braces, as follows:
{name: svcrole, svcservice: httpd, svcport: 80}
Avoid the inline block format because it is harder to read. However, there is at least one situation in which it is more commonly used. The use of roles is discussed later in this course. When a playbook includes a list of roles, it is more common to use this syntax to make it easier to distinguish roles included in a play from the variables being passed to a role.
YAML Lists
You have also seen lists written with the normal single-dash syntax:
hosts:
- servera
- serverb
- serverc
Lists also have an inline format enclosed in square braces, as follows:
hosts: [servera, serverb, serverc]
You should avoid this syntax because it is usually harder to read.
REFERENCES
Intro to playbooks — Ansible Documentation
Working with playbooks — Ansible Documentation
Module Maintenance & Support — Ansible Documentation
Adding modules and plugins locally — Ansible Documentation
YAML Syntax — Ansible Documentation
Ansible.Builtin — Ansible Documentation
Chapter 2 TEST
The /home/student/playbook-review
working directory has been created on the workstation
machine for the Ansible project. The directory has already been populated with an ansible.cfg
configuration file and an inventory
file. The managed host, serverb.lab.example.com
, is already defined in this inventory file.
Instructions
-
Change into the
/home/student/playbook-review
directory and create a new playbook calledinternet.yml
. Add the necessary entries to start a first play namedEnable internet services
and specify its intended managed host,serverb.lab.example.com
. Add the necessary entry to enable privilege escalation, and one to start a task list. -
Add the necessary entries to the
/home/student/playbook-review/internet.yml
file to define a task that installs the latest versions of thefirewalld
,httpd
,mariadb-server
,php
, andphp-mysqlnd
packages. Indent the beginning of the entry four spaces. -
Add the necessary entries to the
/home/student/playbook-review/internet.yml
file to define the firewall configuration tasks. They should ensure that thefirewalld
service is enabled and running, and that access is allowed to thehttp
service. Indent the beginning of these entries four spaces. -
Add the necessary entries to ensure the
httpd
andmariadb
services are enabled and running. Indent the beginning of these entries four spaces. -
Add the necessary entry that uses the
ansible.builtin.copy
module to copy the/home/student/playbook-review/index.php
file to the/var/www/html/
directory on the managed host. Ensure the file mode is set to 0644. Indent the beginning of these entries four spaces. -
Define another play in the
/home/student/playbook-review/internet.yml
file for a task to be performed on the control node. This play tests access to the web server that should be running on theserverb.lab.example.com
managed host. This play does not require privilege escalation, and runs on theworkstation.lab.example.com
managed host. -
Add the necessary entry that tests the web service running on
serverb
from the control node using theansible.builtin.uri
module. Look for a return status code of200
. Indent the beginning of the entry four spaces.internet.yml:
--- - name: Enable internet services hosts: serverb.lab.example.com become: true tasks: - name: Install latest packages for APPs ansible.builtin.dnf: name: - firewalld - httpd - mariadb-server - php - php-mysqlnd state: latest - name: Firewall configuration tasks(running) ansible.builtin.service: name: firewalld state: started enabled: true - name: Enable access to httpd ansible.posix.firewalld: service: http permanent: true state: enabled immediate: true - name: Ensure httpd running ansible.builtin.service: name: httpd state: started enabled: true - name: Ensure mariadb running ansible.builtin.service: name: mariadb state: started enabled: true - name: Copy index.html to /var/www/html folder ansible.builtin.copy: src: /home/student/playbook-review/index.php dest: /var/www/html mode: 0644 - name: Access the internet from workstation to serverb hosts: workstation become: false tasks: - name: Connect to serverb web ansible.builtin.uri: url: http://serverb.lab.example.com return_content: true status_code: 200
-
Validate the syntax of the
internet.yml
playbook.[student@workstation playbook-review]$ ansible-navigator run \ > -m stdout internet.yml --syntax-check playbook: /home/student/playbook-review/internet.yml
-
Use the
ansible-navigator run
command to run the playbook. Read through the generated output to ensure that all tasks completed successfully.[student@workstation playbook-review]$ ansible-navigator run -m stdout internet.yml PLAY [Enable internet services] ************************************************ TASK [Gathering Facts] ********************************************************* ok: [serverb.lab.example.com] TASK [Install latest packages for APPs] **************************************** ok: [serverb.lab.example.com] TASK [Firewall configuration tasks(running)] *********************************** ok: [serverb.lab.example.com] TASK [Enable access to httpd] ************************************************** ok: [serverb.lab.example.com] TASK [Ensure httpd running] **************************************************** changed: [serverb.lab.example.com] TASK [Ensure mariadb running] ************************************************** changed: [serverb.lab.example.com] TASK [Copy index.html to /var/www/html folder] ********************************* changed: [serverb.lab.example.com] PLAY [Access the internet from workstation to serverb] ************************* TASK [Gathering Facts] ********************************************************* ok: [workstation] TASK [Connect to serverb web] ************************************************** ok: [workstation] PLAY RECAP ********************************************************************* serverb.lab.example.com : ok=7 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 workstation : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
TO BE CONTINUED…