Bookmark this page

Chapter 3. Parameterizing Automation

SectionDefining Variables
SectionObjectives
SectionNaming Variables
SectionDefining Variables
SectionUsing Variables in Playbooks
SectionNote: A YAML/Jinja2 Quirk
SectionDiscovering Facts About Hosts
SectionMagic Variables
SectionIntrospection with Debug Module
SectionRegistering Variables
SectionRead the Manual on *OS_FACTS
SectionFacts Modules are Self-registering
Guided Exercise: Defining and Using Variables
Controlling Tasks with Loops and Conditions
Objectives
Simple Conditions
Compound when Statements
Programmatic Style
Ansible Style
Applying Tasks to Objects
Simple Loops
Nested Loops
Looping Over Dict With Conditions
Normal Task Processing
Ignoring Errors on a Task
Guided Exercise: Controlling Tasks with Loops and Conditions
Transforming Variable Data with Filters
Objectives
Filtering Variables and Data
Parsing Command Output
Running Tasks Conditionally by Testing Variables
Guided Exercise: Looping Over a Filtered List
Working with Roles
Objectives
Creating Reusable Playbooks
Organizing Playbooks with Roles
Installing Roles
Automating Role Installation
Initializing Roles Locally
Identifying Role Subdirectories
Identifying Your Role
Roles and Variables
Creating an Ansible Project
Incorporating Roles into Playbooks
Adapting Playbooks to Use Roles
An Example of Adapting a Playbook
Role Dependencies
Order of Execution
Guided Exercise: Creating and Using Roles
Customizing Data with Jinja2 Templates
Objectives
Ansible Variables Use Jinja2
The Playbook as Jinja2 Interface
Configuring Devices from Templates
Scope of Template Configuration
Configuring from a Template
Identifying Data for Static Host Names
Building a Template
Viewing the Results
Guided Exercise: Generating Config Statements with Jinja2
Lab: Parameterizing Automation

Abstract

Goal Perform complex tasks with loops, variables, conditions, roles, and templates.
Objectives
  • Define variables and use them in conditionals to control tasks.

  • Parameterize playbooks and deploy customized files using Jinja2 templates.

Sections
  • Defining Variables (and Guided Exercise)

  • Controlling Tasks with Loops and Conditions (and Guided Exercise)

  • Transforming Variable Data with Filters (and Guided Exercise)

  • Working with Roles (and Guided Exercise)

  • Customizing Data with Jinja2 Templates (and Guided Exercises)

Lab

Parameterizing Automation

Defining Variables

Objectives

After completing this section, you should be able to:

  • Manage variables in Ansible projects.

  • Explain variable precedence.

Naming Variables

Variable names must start with a letter and may only contain letters, numbers, and underscores.

The following are valid Ansible variable names:

  • network

  • Net7Work

  • net_work

The following are not valid Ansible variable names:

  • net-work

  • 7network

  • net@work

Defining Variables

Where and how variables are defined affects their scope and precedence.

DescriptionScope
Extra variables, set on the command line: -e name=valueglobal
"vars_files" variables, set in the vars_files block in the playplay
Play variables, set in the vars block in the playplay
Host facts, typically gathered when play startshost
Host variables, set in host_vars/host.ymlhost
Group variables, set in group_vars/group.ymlhost
Role variables, set in roles/rolename/defaults/main.ymlrole

This is a simplified view of the relative precedence of different kinds of variables. There are many more types. Extra variables override vars_files variables, which override play variables, which override host facts, and so forth.

Using Variables in Playbooks

Ansible variable substitution uses the Jinja2 templating system. Variable names enclosed in curly braces are replaced during evaluation with values.

---
- name: trace the path from source (remote host) to trace_me
  hosts: cs01
  gather_facts: no

  vars:
    trace_me: 172.25.250.254

  tasks:
    - name: trace the path from {{ inventory_hostname }} to {{ trace_me }}
      ios_command:
        commands:
          - traceroute {{ trace_me }}
      register: path

    - name: show the path from {{ inventory_hostname }} to {{ trace_me }}
      debug:
        msg: "{{ path }}"

Note: A YAML/Jinja2 Quirk

Ordinarily, YAML syntax does not care whether strings are quoted or not. The double braces used by Jinja2, though, are similar enough to a YAML construct for notating mappings to confuse the YAML parser when the variable is the first element of a value.

debug:
  msg: {{ output_from_show_command }}
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"

If the value starts with a brace, quote the string. Avoid unwarranted quoting, which could cause quotes to be interpreted as data.

Discovering Facts About Hosts

The connection modules used to connect to networking devices run Ansible's built-in, Python-based, fact-gathering system on the control node (localhost). You see that this is the case if you run an ad hoc command using the setup module: the data returned refers to the control node (localhost), not the network device.

[user@host ~]$ ansible -m setup network-device-host-inventory-identifier

When network devices are targeted by plays, turn off Ansible's built-in fact gathering. This disables fact gathering in a play:

- hosts: network-devices
  gather_facts: no

Facts are available for networking devices by way of platform specific *os_facts modules, such as ios_facts, iosxr_facts, nxos_facts, vyos_facts, junos_facts, and so forth.

Magic Variables

Ansible provides some predefined variables that are automatically set. Some of the better known magic variables are listed below. For the full list, see http://docs.ansible.com/ansible/playbooks_variables.html.

Variable nameDescription
hostvarsContains the variables for managed hosts, and can be used to get the values for another managed host's variables. It does not include the managed host's facts if they have not been gathered yet for that host.
group_namesLists all groups the current managed host is in.
groupsLists all groups and hosts in the inventory.
inventory_hostnameContains the host name for the current managed host as configured in the inventory. This may be different from the host name reported by facts for various reasons.

Introspection with Debug Module

The debug module prints messages while plays are executing. It can be useful for debugging variables or expressions.

- debug:
  msg: >
    "{{ansible_net_hostname}} is {{ansible_net_model}}
    running {{ansible_net_version}}"

The debug module could be used, for instance, to see values assumed by magic variables in a particular context in playbooks created for this purpose.

- debug:
  msg: "{{ role_names }} and {{ vars }}"

Registering Variables

The register keyword is used in the context of a playbook task. It saves the result of a task to a variable. Like facts, registered variables have host scope.

---
- name: >
    play for ios devices
    that uses commands
    to examine indicators
  hosts: ios
  gather_facts: no
  tasks:
    - name: look at indicators
      ios_command:
        commands:
          - sh ver | include uptime
          - sh ip domain
          - sh clock
          - sh ip name-server
          - sh proc mem | include Total
      register: results

- name: show results
  debug:
    var: results.stdout
PLAY [play for ios devices that uses commands to examine indicators] *******

TASK [look at indicators] **************************************************
ok: [cs01]

TASK [show results] ********************************************************
ok: [cs01] => {
                "results.stdout": [
                "cs01 uptime is 3 hours, 12 minutes",
                "lab.example.com",
                "*01:09:01.866 UTC Sun Jun 3 2018",
                "255.255.255.255",
                "Processor Pool Total: 2202704640 Used:   250994304 Free:
1951710336\n lsmpi_io Pool Total: 6295128 Used:   6294296 Free:
                832\n                             257260704 Total"
]
}

PLAY RECAP *****************************************************************!
cs01                             : ok=2  changed=0  unreachable=0  failed=0

Read the Manual on *OS_FACTS

You are probably already familiar with the CLI for a given network OS, but *os_facts modules provide easy access to important facts. This example is for the ios_facts module, where interesting facts show up listed under # hardware.

$ ansible-doc ios_facts | sed -n '/^# hardware/,/^$/p'
# hardware
ansible_net_filesystems:
  description: All file system names available on the device
  returned: when hardware is configured
  type: list
ansible_net_memfree_mb:
  description: The available free memory on the remote device in Mb
  returned: when hardware is configured
  type: int
ansible_net_memtotal_mb:
  description: The total memory on the remote device in Mb
  returned: when hardware is configured
  type: int

Facts Modules are Self-registering

The *os_facts modules handle variable registration for you. This example uses the hardware subset that you learned about from ansible-doc ios_facts.

---
- name: >
    play for ios devices
    that uses ios_facts
  hosts: ios
  gather_facts: no
  tasks:
  - name: look at some facts
    ios_facts:
      gather_subset:
        - hardware

  - name: show results
    debug:
      msg:
        - "filesystems: {{ ansible_net_filesystems }}"
        - "free mem MB: {{ ansible_net_memfree_mb }}"
        - "mem total MB: {{ ansible_net_memtotal_mb }}"
PLAY [play for ios devices that uses ios_facts] ****

TASK [look at some facts] **************************
ok: [cs01]

TASK [show results] ********************************
ok: [cs01] => {
    "msg": [
        "filesystems: [u'bootflash:']",
        "free mem MB: 1905270",
        "mem total MB: 2151078"
    ]
}

PLAY RECAP *****************************************
cs01      : ok=2 changed=0 unreachable=0 failed=0
Revision: do457-2.5-4693601