### Ansible Playbook Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/CONTRIBUTE.adoc A minimal Ansible playbook demonstrating a simple task to print a message. This is used as an example within the Asciidoc guideline structure. ```yaml --- - name: a mini example of playbook hosts: all gather_facts: false become: false tasks: - name: say what we all think debug: msg: asciidoctor is {{ my_private_thoughts }} ---- ``` -------------------------------- ### Ansible README File Content Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Essential components for a role's README file to guide users. Includes examples, argument specifications, capabilities, idempotency, and rollback information. ```Markdown ## Role: foo ### Purpose This role automates the installation and configuration of the foo service. ### Example Playbook ```yaml - hosts: all roles: - role: foo foo_extra_packages: [ "extra_package_a", "extra_package_b" ] ``` ### Arguments * **foo_packages** (list, required): A list of packages required for the foo service. * **foo_extra_packages** (list, optional): A list of additional packages to install. ### Capabilities * Installs foo service * Configures foo service ### Unit of Automation Installation and configuration of the foo service. ### Outcome The foo service is installed and configured according to the provided variables. ### Rollback Capabilities This role does not provide automated rollback capabilities. ### Idempotent True ### Atomic False ``` -------------------------------- ### Python Type Hints with MyPy Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Illustrates the use of Python type hints for documenting variable types, supported in Python 3.5+. This example shows a simple function with type hints and mentions MyPy for static analysis. ```python def greeting(name: str) -> str: return 'Hello ' + name ``` -------------------------------- ### Asciidoc Guideline Template Source: https://github.com/redhat-cop/automation-good-practices/blob/main/CONTRIBUTE.adoc This snippet shows the structure of a single guideline in Asciidoc format, including a title, collapsible description with explanations, rationale, and examples. It also demonstrates embedding a YAML code block for a playbook example. ```asciidoc == Do this and do not do that is the guideline [%collapsible] ==== Explanations:: These are explanations Rationale:: This is the rationale Examples:: These are examples + .A mini playbook example [source,yaml] ---- --- - name: A mini example of playbook hosts: all gather_facts: false become: false tasks: - name: Say what we all think ansible.builtin.debug: msg: asciidoctor is {{ my_private_thoughts }} ---- +Even more examples... ==== ``` -------------------------------- ### Ansible RFC Addresses in Examples Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Using RFC-compliant IP addresses for examples in documentation to avoid conflicts with real network addresses. ```Ansible server: 192.0.2.1 network: 198.51.100.0/24 ``` -------------------------------- ### Ansible Inventory Directory Structure Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc This example demonstrates a comprehensive directory structure for an Ansible inventory. It includes files for dynamic inventory plugins and scripts, a static list of groups and hosts, and organized directories for group and host variables. ```tree inventory_example/ ├── dynamic_inventory_plugin.yml ├── dynamic_inventory_script.py ├── groups_and_hosts ├── group_vars/ │   ├── alephs/ │   │   └── capital_letter.yml │   ├── all/ │   │   └── ansible.yml │   ├── alphas/ │   │   ├── capital_letter.yml │   │   └── small_caps_letter.yml │   ├── betas/ │   │   └── capital_letter.yml │   ├── greek_letters/ │   │   └── small_caps_letter.yml │   └── hebrew_letters/ │   └── small_caps_letter.yml └── host_vars/ ├── host1.example.com/ │   └── ansible.yml ├── host2.example.com/ │   └── ansible.yml └── host3.example.com/ ├── ansible.yml └── capital_letter.yml ``` -------------------------------- ### Pytest Unit Testing Example for Ansible Modules Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Provides an example of using pytest for unit testing Ansible plugins, specifically demonstrating a test for the `split_pre_existing_dir` function. It includes parameterization and mocking. ```python from __future__ import (absolute_import, division, print_function) __metaclass__ = type import pytest from ansible.modules.copy import AnsibleModuleError, split_pre_existing_dir from ansible.module_utils.basic import AnsibleModule ONE_DIR_DATA = (('dir1', ('.', ['dir1']), ('dir1', []), ), ('dir1/', ('.', ['dir1']), ('dir1', []), ), ) @pytest.mark.parametrize('directory, expected', ((d[0], d[2]) for d in ONE_DIR_DATA)) def test_split_pre_existing_dir_one_level_exists(directory, expected, mocker): mocker.patch('os.path.exists', side_effect=[True, False, False]) split_pre_existing_dir(directory) == expected ``` -------------------------------- ### Ansible Role Argument Specification Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Provides an example of an `argument_specs.yml` file for Ansible roles, demonstrating how to define and validate arguments. This enhances role stability by ensuring correct input parameters. ```yaml argument_specs: main: short_description: Role description. options: string_arg1: description: string argument description. type: "str" default: "x" choices: ["x", "y"] dict_arg1: description: dict argument description. type: dict required: True options: key1: description: key1 description. type: int key2: description: key2 description. type: str key3: description: key3 description. type: dict ``` -------------------------------- ### Automation Structure - Concrete Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/structures/README.adoc A concrete example demonstrating the application of the automation structure, using a three-tier application (web-front-end, middleware, database) as a landscape. It details how types, functions (roles), and components (tasks) are defined for each tier. ```Ansible * as already written, a _landscape_ could be a three tier application with web-front-end, middleware and database. We would probably create a workflow to deploy this landscape at once. * our types would be relatively obvious here as we would have "web-front-end server", "middleware server" and "database server". Each type can be fully deployed by one and only one playbook (avoid having numbered playbooks and instructions on how to call them one after the other). * each server type is then made up of one or more _functions_, each implemented as a role. For example, the middleware server type could be made of a "virtual machine" (to create the virtual machine hosting the middleware server), a "base Linux OS" and a "JBoss application server" function. * and then the base OS role could be made of multiple components (DNS, NTP, SSH, etc), each represented by a separate `tasks/{component}.yml` file, included or imported from the `tasks/main.yml` file of the _function_-role. If a component becomes too big to fit within one task file, it might make sense that it gets its own component-role, included from the function-role. ``` -------------------------------- ### Ansible Groups and Hosts File Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc This example shows the content of the `groups_and_hosts` file, which defines the static list of hosts and their group memberships. It uses an INI format for readability and simplicity, avoiding variable definitions within this file. ```ini [all] host1.example.com host2.example.com host3.example.com [alephs] capital_letter.example.com [alphas] small_caps_letter.example.com [betas] capital_letter.example.com [greek_letters] small_caps_letter.example.com [hebrew_letters] small_caps_letter.example.com ``` -------------------------------- ### Ansible: Include Role Tasks and Install Packages Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Demonstrates how to include a specific task file from a role and install packages based on role-defined variables. This avoids maintaining separate package lists. ```yaml - hosts: all tasks: - name: Set platform/version specific variables include_role: name: my.fqcn.rolename tasks_from: set_vars.yml public: true - name: Install test packages package: name: "{{ __rolename_packages }}" state: present ``` -------------------------------- ### Ansible: Good Inventory Structure for Host Provisioning Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Presents an example of a well-structured Ansible inventory file ('groups_and_hosts') that follows best practices for host provisioning, organizing hosts and groups effectively. ```ini include::inventory_loop_hosts/inventory_good/groups_and_hosts[] ``` -------------------------------- ### Automation Structure Example Source: https://github.com/redhat-cop/automation-good-practices/blob/main/structures/README.adoc An example illustrating the hierarchical structure for organizing automation code, including landscapes, types, functions (roles), and components (tasks). This structure promotes reusability and maintainability. ```Ansible * a _landscape_ is anything you want to deploy at once, the underlay of your cloud, a three tiers application, a complete application cluster... This level is represented at best by a Controller/Tower/AWX workflow, potentially by a "playbook of playbooks", i.e. a playbook made of imported _type_ playbooks, as introduced next. * a _type_ must be defined such that each managed host has one and only one type, applicable using a unique playbook. * each type is then made of multiple _functions_, represented by roles, so that the same function used by the same _type_ can be re-used, written only once. * and finally _components_ are used to split a _function_ in maintainable bits. By default a component is a task file within the _function_-role, if the role becomes too big, there is a case for splitting the _function_ role into multiple _component_ roles. ``` -------------------------------- ### Ansible Playbook with Roles Source: https://github.com/redhat-cop/automation-good-practices/blob/main/playbooks/README.adoc An example of an Ansible playbook that solely consists of a list of roles. This structure promotes reusability and simplifies playbook management by delegating logic to roles. ```yaml --- - name: A playbook can solely be a list of roles hosts: all gather_facts: false become: false roles: - role1 - role2 - role3 ``` -------------------------------- ### Ansible: Platform Specific Tasks with Skip Option Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Similar to the previous example, but uses `skip: true` with `lookup('first_found')` to avoid requiring a default file if no specific match is found. ```yaml - name: Perform platform/version specific tasks include_tasks: "{{ lookup('first_found', __rolename_ff_params) }}" vars: __rolename_ff_params: files: - "{{ ansible_facts['distribution'] }}_{{ ansible_facts['distribution_version'] }}.yml" - "{{ ansible_facts['os_family'] }}.yml" paths: - "{{ role_path }}/tasks/setup" skip: true ``` -------------------------------- ### Zen of Ansible Principles Source: https://github.com/redhat-cop/automation-good-practices/blob/main/structures/README.adoc A set of guiding principles for Ansible automation, emphasizing clarity, conciseness, simplicity, and user experience. These principles serve as a reference for designing effective automation strategies. ```Ansible Ansible is not Python. YAML sucks for coding. Playbooks are not for programming. Ansible users are (most probably) not programmers. Clear is better than cluttered. Concise is better than verbose. Simple is better than complex. Readability counts. Helping users get things done matters most. User experience beats ideological purity. “Magic” conquers the manual. When giving users options, always use convention over configuration. Declarative is always better than imperative - most of the time. Focus avoids complexity. Complexity kills productivity. If the implementation is hard to explain, it's a bad idea. Every shell command and UI interaction is an opportunity to automate. Just because something works, doesn't mean it can't be improved. Friction should be eliminated whenever possible. Automation is a continuous journey that never ends. ``` -------------------------------- ### Ansible: Bad Host Variables for Provisioning Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Provides an example of host-specific variables (`provision.yml`) used in an anti-pattern for Ansible host provisioning, highlighting the duplication of information. ```yaml include::inventory_loop_hosts/inventory_bad/host_vars/manager_a/provision.yml[] ``` -------------------------------- ### Gather Minimum Ansible Facts Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc This task ensures that the minimum required Ansible facts are gathered for the role to function. It uses the `setup` module with `gather_subset: min` and is conditional on whether the necessary facts are already available. ```yaml - name: Ensure ansible_facts used by role setup: gather_subset: min when: not ansible_facts.keys() | list | intersect(__rolename_required_facts) == __rolename_required_facts ``` -------------------------------- ### Ansible: Bad Inventory Structure for Host Provisioning Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Shows an example of an Ansible inventory file ('groups_and_hosts') that represents an anti-pattern for host provisioning, where hosts are managed in a less organized manner. ```ini include::inventory_loop_hosts/inventory_bad/groups_and_hosts[] ``` -------------------------------- ### Ansible Slurp Module for File Content Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Provides an example of using the `slurp` module in Ansible to read file content, which is a more idempotent alternative to using the `command: cat` module. The decoded content can be registered and used in subsequent tasks. ```Ansible --- - name: Read file content idempotently ansible.builtin.slurp: src: /path/to/your/file register: file_content - name: Use file content debug: msg: "{{ file_content.content | b64decode }}" ``` -------------------------------- ### Ansible Inventory with Complex Host Variables Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc This example illustrates a more complex inventory structure where host variables are further organized into sub-directories. This is useful for managing numerous variables related to a specific topic, such as Satellite configuration. ```tree inventory_satellite/ ├── groups_and_hosts └── host_vars/ └── sat6.example.com/ ├── ansible.yml └── satellite/ ├── content_views.yml ├── hostgroups.yml └── locations.yml ``` -------------------------------- ### Dynamic Task Naming with Jinja2 in Ansible Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Demonstrates how to use Jinja2 templates at the end of task names to make them dynamic, improving log readability. It also shows an example of what not to do, where the template is not at the end of the string. ```yaml tasks: - name: Manage the disk device {{ storage_device_name }} some.module: device: "{{ storage_device_name }}" ``` ```yaml tasks: - name: Manage {{ storage_device_name }}, the disk device some.module: device: "{{ storage_device_name }}" ``` -------------------------------- ### Publish HTML Documentation Source: https://github.com/redhat-cop/automation-good-practices/blob/main/CONTRIBUTE.adoc Bash commands to publish the documentation to a website using Asciidoctor. It generates HTML files and copies images to the correct directory. ```bash asciidoctor -a toc=left -D docs -o index.html README.adoc asciidoctor -a toc=left -D docs CONTRIBUTE.adoc mkdir -p docs/images cp -v images/*.svg docs/images ``` -------------------------------- ### Scaffold New Ansible Plugins Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Utilize the `ansible.plugin_builder` tool to scaffold new Ansible plugins. This tool assists developers in setting up the basic structure and files required for a new plugin. ```bash ansible-plugin-builder --help ``` -------------------------------- ### Generate PDF Documentation Source: https://github.com/redhat-cop/automation-good-practices/blob/main/CONTRIBUTE.adoc Bash commands to generate PDF documentation using `asciidoctor-pdf`. It includes attributes for git date and hash, and generates separate PDFs for the main README and contribution files. ```bash asciidoctor-pdf \ --attribute=gitdate=$(git log -1 --date=short --pretty=format:%cd) \ --attribute=githash=$(git rev-parse --verify HEAD) \ --out-file Good_Practices_for_Ansible.pdf \ README.adoc asciidoctor-pdf \ --attribute=gitdate=$(git log -1 --date=short --pretty=format:%cd) \ --attribute=githash=$(git rev-parse --verify HEAD) \ --out-file Contributing-to-GPA.pdf \ CONTRIBUTE.adoc ``` -------------------------------- ### Ansible: Execution Time Comparison Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Compares the execution times of Ansible playbooks using both the 'bad' and 'good' host provisioning approaches, demonstrating the performance benefits of the recommended method. ```bash ANSIBLE_STDOUT_CALLBACK=profile_tasks \ ansible-playbook -i inventory_bad playbook_bad.yml Saturday 23 October 2021 13:11:45 +0200 (0:00:00.040) 0:00:00.040 ***** Saturday 23 October 2021 13:11:45 +0200 (0:00:00.858) 0:00:00.899 ***** =============================================================================== create some file to simulate an API call to provision a host ------------ 0.86s $ ANSIBLE_STDOUT_CALLBACK=profile_tasks \ ansible-playbook -i inventory_good playbook_good.yml Saturday 23 October 2021 13:11:55 +0200 (0:00:00.040) 0:00:00.040 ***** Saturday 23 October 2021 13:11:56 +0200 (0:00:00.569) 0:00:00.610 ***** =============================================================================== create some file to simulate an API call to provision a host ------------ 0.57s ``` -------------------------------- ### Ansible: Good Host Provisioning Playbook Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Illustrates the recommended Ansible approach for host provisioning, utilizing inventory groups and variables for efficient management. This method enables parallel execution and easier host limiting. ```yaml include::inventory_loop_hosts/playbook_good.yml[] ``` -------------------------------- ### Display Verbose Information in Ansible Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Leverage the `ansible.utils.display.Display` class to show information at different verbosity levels. Use `display.vvvv` for highly detailed output, typically used with `-vvvv` flag in Ansible commands. ```python from ansible.utils.display import Display display = Display() lookupfile = self.find_file_in_search_path(variables, 'files', term) display.vvvv("File lookup using {0} as file".format(lookupfile)) ``` -------------------------------- ### Ansible Role: Unified Entry Point Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Demonstrates a role designed as a unified entry point for executing multiple underlying automations. This approach abstracts implementation details, providing a stable interface for users. ```yaml - hosts: all gather_facts: false tasks: - name: Perform several actions include_role: mycollection.run vars: actions: - name: thing_1 vars: thing_1_var_1: 1 thing_1_var_2: 2 - name: thing_2 vars: thing_2_var_1: 1 thing_2_var_2: 2 ``` -------------------------------- ### Ansible Role Variable Naming Convention Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc To avoid naming collisions in Ansible, all default and argument variables for a role should be prefixed with the role name. For example, use `foo_packages` instead of `packages`. ```Ansible --- - name: Example Role hosts: all vars: myrole_packages: ["package1", "package2"] myrole_config_file: "/etc/myrole/config.yml" tasks: - name: Install packages ansible.builtin.package: name: "{{ myrole_packages }}" state: present - name: Configure myrole ansible.builtin.template: src: "templates/config.j2" dest: "{{ myrole_config_file }}" ``` -------------------------------- ### Ansible: Good Host Variables for Provisioning Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Demonstrates the recommended variable structure for Ansible host provisioning, showing how host and group variables (`provision.yml`, `provision.yml`) carry relevant information efficiently. ```yaml $ cat inventory_good/host_vars/host1/provision.yml provision_value: uno $ cat inventory_good/group_vars/managed_hosts_a/provision.yml manager_hostname: manager_a ``` -------------------------------- ### Python Docstrings with Sphinx (reST) Format Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Demonstrates the recommended Sphinx (reST) format for Python docstrings, including parameters, yields, raises, and return values. This adheres to PEP-257 and is preferred for Ansible development. ```python """[Summary] :param [ParamName]: [ParamDescription], defaults to [DefaultParamVal] :type [ParamName]: [ParamType](, optional) ... :raises [ErrorType]: [ErrorDescription] ... :return: [ReturnDescription] :rtype: [ReturnType] """ ``` -------------------------------- ### Ansible Inventories Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc Explains how to manage and structure Ansible inventory files, which define the hosts and groups that Ansible will manage, including static and dynamic inventory sources. ```Ansible include::inventories/README.adoc[leveloffset=1] ``` -------------------------------- ### Ansible Structures Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc This section explains the recommended structures for Ansible projects, detailing what to use for different purposes to ensure organization and maintainability. ```Ansible include::structures/README.adoc[leveloffset=1] ``` -------------------------------- ### Ansible Plugins Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc Covers the best practices for using and developing Ansible plugins, which extend Ansible's functionality, including modules, lookups, and connection plugins. ```Ansible include::plugins/README.adoc[leveloffset=1] ``` -------------------------------- ### Ansible: Use community.general.nmcli module Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Suggests utilizing the `community.general.nmcli` module for NetworkManager configurations to enhance code clarity and guarantee idempotence. ```ansible community.general.nmcli ``` -------------------------------- ### Ansible Collections Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc Details the use and best practices for Ansible Collections, which are the standard way to package and distribute Ansible content, including modules, plugins, and roles. ```Ansible include::collections/README.adoc[leveloffset=1] ``` -------------------------------- ### Ansible: Include Provider Specific Tasks Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Shows how to include tasks from a file that is dynamically determined by a variable, such as `storage_provider`. This supports different service providers. ```yaml - name: include the appropriate provider tasks include_tasks: "main_{{ storage_provider }}.yml" ``` -------------------------------- ### Ansible Module Utilities Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Move reusable functions and classes to a module_utils directory for Ansible modules or plugin_utils for other plugin types to improve code readability and maintainability. Import these utilities into your plugins. ```ansible module_utils/ my_validation_utils.py my_manipulation_utils.py plugins/ my_module.py ``` -------------------------------- ### Ansible Playbooks Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc Provides guidelines on writing effective Ansible Playbooks, which are the core of Ansible automation, defining tasks, hosts, and variables for orchestrating complex workflows. ```Ansible include::playbooks/README.adoc[leveloffset=1] ``` -------------------------------- ### Include Tasks for Setting Variables Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc This task serves as a placeholder to include the actual logic for setting platform-specific variables from a separate file (`tasks/set_vars.yml`). It's intended to be the first task in `tasks/main.yml`. ```yaml - name: Set platform/version specific variables include_tasks: tasks/set_vars.yml ``` -------------------------------- ### Ansible Inventory Plugins Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Ansible provides various inventory plugins to dynamically pull data from different sources, allowing for the combination of multiple data sources into a single, comprehensive inventory. These plugins help in modeling the environment to be automated with minimal maintenance effort. ```Ansible ansible-inventory --list -i ``` -------------------------------- ### Ansible: Template file naming convention Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Explains the convention of appending `.j2` to template file names when using the `ansible.builtin.template` module. This aids in identifying template files and enables syntax highlighting. ```ansible templates/example.conf.j2 ``` -------------------------------- ### Ansible Role: Direct Execution (Not Recommended) Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Illustrates a less desirable approach where users must be aware of individual role structures and execution order. Changes to underlying roles necessitate playbook modifications. ```yaml - hosts: all gather_facts: false tasks: - name: Do thing_1 include_role: name: mycollection.thing_1 tasks_from: thing_1.yaml vars: thing_1_var_1: 1 thing_1_var_2: 2 - name: Do thing_2 include_role: name: mycollection.thing_2 tasks_from: thing_2.yaml vars: thing_2_var_1: 1 thing_2_var_2: 2 ``` -------------------------------- ### Ansible Playbook Including Roles with Tags Source: https://github.com/redhat-cop/automation-good-practices/blob/main/playbooks/README.adoc Illustrates including roles dynamically within an Ansible playbook and applying tags to both the include task and the role's tasks. This provides granular control over role execution and tagging. ```yaml - name: a playbook can be a list of roles included with tags applied hosts: all gather_facts: false become: false tasks: - name: include role1 include_role: name: role1 apply: tags: - role1 - deploy tags: - role1 - deploy - name: include role2 include_role: name: role2 apply: tags: - role2 - deploy - configure tags: - role2 - deploy - configure - name: include role3 include_role: name: role3 apply: tags: - role3 - configure tags: - role3 - configure ``` -------------------------------- ### Ansible Coding Style Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc Details the recommended coding style for Ansible automation to ensure consistency, readability, and maintainability across projects. ```Ansible include::coding_style/README.adoc[leveloffset=1] ``` -------------------------------- ### Ansible Distribution-Specific Variable Files Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc To manage distribution and version-specific configurations, avoid testing for distribution and version directly in tasks. Instead, create separate variable files in the `vars/` directory for each supported distribution and version. ```Ansible --- # vars/Debian-11.yml nginx_package: "nginx" php_package: "php-fpm" --- # vars/RedHat-8.yml nginx_package: "nginx" php_package: "php-fpm" --- # playbook.yml - name: Deploy web server hosts: webservers vars_files: - "vars/{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml" tasks: - name: Install Nginx ansible.builtin.package: name: "{{ nginx_package }}" state: present - name: Install PHP-FPM ansible.builtin.package: name: "{{ php_package }}" state: present ``` -------------------------------- ### Ansible Role Variable Management for Distributions Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Illustrates how to manage role variables based on OS platform and version in Ansible. This allows for customization of settings like service names or configuration file paths, ensuring the role works across different distributions. ```Ansible --- - name: Set variables based on OS ansible.builtin.include_vars: file: "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml" name: "os_specific_vars" when: ansible_facts_gathered - name: Use OS specific variable ansible.builtin.debug: msg: "Service name is {{ os_specific_vars.service_name }}" ``` -------------------------------- ### Ansible: Use service module instead of upstart/systemd Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Promotes the use of the generic `service` module over specific modules like `upstart` or `systemd` to ensure playbooks run on a wider range of operating systems with minimal modifications. ```ansible service ``` -------------------------------- ### Ansible: Bad Host Provisioning Playbook Source: https://github.com/redhat-cop/automation-good-practices/blob/main/inventories/README.adoc Demonstrates an anti-pattern in Ansible where hosts are provisioned by looping through a list, lacking parallelism and limiting capabilities. This approach makes host management difficult and prone to duplication. ```yaml include::inventory_loop_hosts/playbook_bad.yml[] ``` -------------------------------- ### Ansible: Platform Specific Tasks with lookup('first_found') Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Uses `lookup('first_found')` to include platform-specific task files. It searches a list of files in order of specificity, falling back to a default. ```yaml - name: Perform platform/version specific tasks include_tasks: "{{ lookup('first_found', __rolename_ff_params) }}" vars: __rolename_ff_params: files: - "{{ ansible_facts['distribution'] }}_{{ ansible_facts['distribution_version'] }}.yml" - "{{ ansible_facts['distribution'] }}_{{ ansible_facts['distribution_major_version'] }}.yml" - "{{ ansible_facts['distribution'] }}.yml" - "{{ ansible_facts['os_family'] }}.yml" - "default.yml" paths: - "{{ role_path }}/tasks/setup" ``` -------------------------------- ### Ansible: Use community.general.ssh_config module Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Recommends using the `community.general.ssh_config` module for managing SSH configurations to ensure idempotence and improve code readability. ```ansible community.general.ssh_config ``` -------------------------------- ### Ansible Roles Source: https://github.com/redhat-cop/automation-good-practices/blob/main/README.adoc Covers the low-level aspects of Ansible code, focusing on roles as the recommended way to organize reusable automation components. It details tasks, variables, and other elements within roles. ```Ansible include::roles/README.adoc[leveloffset=1] ``` -------------------------------- ### Include Platform Specific Variables Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc This task dynamically includes variables based on the detected OS family, distribution, and version. It loops through a list of potential variable files, including them only if they exist, allowing for platform-specific configurations. ```yaml - name: Set platform/version specific variables include_vars: "{{ __rolename_vars_file }}" loop: - "{{ ansible_facts['os_family'] }}.yml" - "{{ ansible_facts['distribution'] }}.yml" - "{{ ansible_facts['distribution'] }}_{{ ansible_facts['distribution_major_version'] }}.yml" - "{{ ansible_facts['distribution'] }}_{{ ansible_facts['distribution_version'] }}.yml" vars: __rolename_vars_file: "{{ role_path }}/vars/{{ item }}" when: __rolename_vars_file is file ``` -------------------------------- ### Ansible: Use package module instead of yum/dnf Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Recommends using the `package` module instead of distribution-specific package managers like `yum` or `dnf` for greater cross-platform compatibility. ```ansible package ``` -------------------------------- ### Ansible: Avoid iterative package module calls Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Illustrates the inefficiency of calling the `package` module iteratively for each item in a list. It recommends using the module with a list of packages for better performance. ```ansible name: "{{ foo_packages }}" ``` -------------------------------- ### Ansible: Correct Task Argument Formatting Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Illustrates the preferred method of spelling out task arguments in YAML style, avoiding the 'key=value' format. This ensures consistency and adherence to YAML standards. ```yaml tasks: - name: Print a message ansible.builtin.debug: msg: This is how it's done. ``` ```yaml tasks: - name: Print a message ansible.builtin.debug: msg="This is the exact opposite of how it's done." ``` -------------------------------- ### Ansible Variable Management: Vars vs Defaults Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Guidelines for managing variables in Ansible roles. Use `vars/main.yml` for required variables and `defaults/main.yml` for optional variables with meaningful defaults. ```Ansible # vars/main.yml foo_packages: - package1 - package2 # defaults/main.yml foo_extra_packages: [] ``` -------------------------------- ### Ansible: Prefer Command Module Over Shell Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Advises using the 'command' module for executing commands unless shell functionality like piping is explicitly required. It also suggests using dedicated modules when available and ensuring idempotency. -------------------------------- ### Ansible Playbook Importing Roles with Tags Source: https://github.com/redhat-cop/automation-good-practices/blob/main/playbooks/README.adoc Demonstrates how to import roles within an Ansible playbook and apply tags for selective execution. This allows individual roles or functional groups to be triggered using specific tags. ```yaml --- - name: A playbook can be a list of roles imported with tags hosts: all gather_facts: false become: false tasks: - name: Import role1 ansible.builtin.import_role: name: role1 tags: - role1 - deploy - name: Import role2 ansible.builtin.import_role: name: role2 tags: - role2 - deploy - configure - name: Import role3 ansible.builtin.import_role: name: role3 tags: - role3 - configure ``` -------------------------------- ### Define Collection Variables in Role Defaults - YAML Source: https://github.com/redhat-cop/automation-good-practices/blob/main/collections/README.adoc Demonstrates how to define collection-wide variables in a role's defaults/main.yml file by referencing them. This approach ensures roles remain reusable while allowing for shared variable management. ```yaml # specific role variables alpha_job_name: 'some text' # collection wide variables alpha_controller_username: "{{ mycollection_controller_username }}" alpha_no_log: "{{ mycollection_no_log | default('true') }}" ``` ```yaml # specific role variables beta_job_name: 'some other text' # collection wide variables beta_controller_username: "{{ mycollection_controller_username }}" beta_no_log: "{{ mycollection_no_log | default('false') }}" ``` -------------------------------- ### Ansible: Use ini_file, blockinfile, xml modules Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Suggests using specific modules like `ini_file`, `blockinfile`, or `xml` for editing configuration files directly, as opposed to `lineinfile`, to maintain idempotence and code clarity. ```ansible ini_file ``` ```ansible blockinfile ``` ```ansible xml ``` -------------------------------- ### Ansible: Use template module for file modification Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Advises using the `template` module for modifying configuration files, especially when multiple lines or complex changes are involved. This ensures idempotence and readability. ```ansible template ``` -------------------------------- ### Ansible: Use List for Multiple Conditions in 'when' Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Demonstrates how to use a list for multiple conditions in Ansible's 'when' statement for better readability compared to a single string with 'and'. It also highlights the importance of using filters like '| bool' with bare variables. ```yaml when: - myvar is defined - myvar | bool ``` ```yaml when: myvar is defined and myvar | bool ``` -------------------------------- ### Jinja2: Single Space Around Variables Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Specifies the correct formatting for Jinja2 templates, requiring a single space between template markers and variable names, including when using filters. ```jinja {{ variable_name_here }} ``` ```jinja {{ variable_name | default('hiya, doc') }} ``` -------------------------------- ### Ansible: Use Bracket Notation for Value Retrieval Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Promotes the use of bracket notation (e.g., `item['key']`) over dot notation (e.g., `item.key`) for retrieving values from dictionaries or lists within Ansible playbooks. -------------------------------- ### Ansible: Use 'true' and 'false' for Boolean Values Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Recommends using the standard YAML boolean values 'true' and 'false' instead of Ansible-specific 'yes'/'no' or Python-style 'True'/'False'. This ensures compatibility with future YAML standards. -------------------------------- ### Exit Ansible Module with Failure Source: https://github.com/redhat-cop/automation-good-practices/blob/main/plugins/README.adoc Use the `module.fail_json` method to exit an Ansible module with a failure status. This method allows you to provide a detailed error message and include relevant data about the failure. ```python if checksum and checksum_src != checksum: module.fail_json( msg='Copied file does not match the expected checksum. Transfer failed.', checksum=checksum_src, expected_checksum=checksum ) ``` -------------------------------- ### Ansible YAML Indentation Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Adheres to a two-space indentation for YAML files and indents list contents beyond the list definition. This ensures proper structure and readability. ```yaml example_list: - example_element_1 - example_element_2 - example_element_3 - example_element_4 ``` -------------------------------- ### Ansible Role OS Default Provider Variable Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc To ensure consistent behavior across different OS versions, a role should set a variable or custom fact named `$ROLENAME_provider_os_default` with the appropriate default provider for the detected OS. This allows users to easily set the provider to the OS default using `{{ $ROLENAME_provider_os_default }}`. ```Ansible --- - name: Role setting OS default provider hosts: all vars: # Example: If OS is RHEL 8, chrony is the default chrony_provider_os_default: "chrony" # Example: If OS is Debian 11, systemd-timesyncd is the default timesyncd_provider_os_default: "systemd-timesyncd" tasks: - name: Set provider to OS default if not specified ansible.builtin.set_fact: ntp_provider_provider: "{{ ntp_provider_provider | default(ntp_provider_os_default) }}" when: ntp_provider_provider is not defined - name: Include provider-specific tasks ansible.builtin.include_tasks: file: "tasks/{{ ntp_provider_provider }}.yml" ``` -------------------------------- ### Ansible Role Check Mode with changed_when Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Demonstrates how to handle commands in Ansible's check mode by using the `changed_when:` attribute to accurately report changes without executing them. This is crucial for ensuring roles function correctly when `ansible_check_mode` is true. ```Ansible --- - name: Example task in check mode command: some_command --dry-run register: dry_run_result changed_when: "dry_run_result.rc != 0" when: ansible_check_mode ``` -------------------------------- ### Prefix Task Names in Ansible Sub-tasks Source: https://github.com/redhat-cop/automation-good-practices/blob/main/roles/README.adoc Explains the practice of prefixing task names in sub-task files within Ansible roles. This improves clarity and troubleshooting by indicating the origin of each task in the execution logs. ```yaml - name: sub | Some task description mytask: [...] ``` -------------------------------- ### Wrapping Long Jinja Expressions Source: https://github.com/redhat-cop/automation-good-practices/blob/main/coding_style/README.adoc Demonstrates how to wrap long Jinja expressions and assignments across multiple lines for improved readability in Ansible playbooks. It covers wrapping within expressions, assignments, and conditional statements. ```yaml - name: Wrap long Jinja expressions foo: "{{ a_very.long_variable.name | somefilter('with', 'many', 'arguments') | another_filter | list }}" when: a_very.long_variable.name | somefilter('with', 'many', 'arguments') | another_filter | list - name: Wrap when first line is already too long very_indented_foo: "{{ a_very.long_variable.name | somefilter('with', 'many', 'arguments') | another_filter | list }}" when: \ a_very.long_variable.name | somefilter('with', 'many', 'arguments') | another_filter | list | length > 0 - name: Set some test variables set_fact: my_very_long_variable_1: "{{ __pre_digest | filter1 }}" my_very_long_variable_2: "{{ __pre_digest | filter2 }}" vars: __pre_digest: "{{ a_very.long_variable.name | some_filter }}" - name: Use a very long URL uri: url: "https://{{ my_very_long_value_for_hostname }}\ {{ my_very_long_value_for_port }}\ {{ my_very_long_value_for_uri }}?\ {{ my_very_long_value_for_query }}" ```