Ansible

Ansible is a configuration management system written in Python (Private) and maintained by Red Hat.

Concepts

  • Inventory describes the systems we want to manage and groups them for easier management.
    • Static inventory, as an INI file (Private). Servers are grouped using headings, with nested groups denoted using colons (:) in section headings. Groups can be members of other groups.
    • Dynamic inventory, using configuration stores like Cobbler.
  • Tasks perform one-off operations, useful for exploration and quick configuration changes.
  • Modules perform actions, generally idempotently.
  • Tasks are instances of Modules, with a configuration.
  • Playbooks are user-configured sets of Tasks.
  • Facts provide data to be used for targeting.

Components

  • Galaxy is a package manager for Ansible modules.
  • Tower provides a compliance/reporting dashboard.

Design

Ansible is agent-less by default, using SSH to administrate systems remotely.

Installation

For Linux systems, Ansible is distributed as a single ansible package.

Configuration

Ansible searches for its con figuration file in this order:

  1. The ANSIBLE_CONFIG environment variable overrides the search behaviour.
  2. ./ansible.cfg in the current working directory.
  3. ~/.ansible.cfg in your home directory.
  4. As a last resort, /etc/ansible/ansible.cfg.

By default, the configuration is structured as follows:

  • /etc/ansible/:
    • ansible.cfg:
      • [defaults]
        • inventory
        • remote_user
        • remote_ports
        • ask_pass
      • [privilege_escalation]
        • become
        • become_user
        • become_method
        • become_ask_pass

Usage

Inventory

When expressing inventory as a static file, the inventory file looks something like this:

[us-east-1:compute]
app[0:99].compute.us-east-1.example.com

[us-east-1]
us-east-1:compute

ansible-inventory can be used to examine the inventory's content.

Dynamic

Dynamic inventory is provided by inventory scripts, executable programs which generate the inventory contents automatically. They might be used together with a CMDB or asset register. They can be written in any programming language, and just need to be marked as executable and able to output a JSON structure like the following:

{
    "all": {
        "children": ["x"]
    }
}

Running ad hoc tasks

Execute ad hoc tasks:

ansible host-pattern -m module [-a args] [-i inventory]

Common modules

Common modules (list more with ansible-doc -l):

  • copy copies a local file to the managed host.
  • file sets content and properties of files.
  • lineinfile ensures a specific line exists within a file.
  • ping performs a basic connectivity test.
  • synchronize copies files and directories using rsync.

Templating

The template module allows templating files using the Jinja template engine (Private):

- name: Generate the nginx server {}
  template:
      src: nginx.server.j2
      dest: /etc/nginx/conf.d/server.conf
      owner: root
      group: root
      mode: "0600"

Useful variables:

Lookup plugins allow templating using data from outside of Ansible. There are two functions for interacting with them:

  • lookup(plugin, object) yields a comma-delimited string of items; and
  • query(plugin, object) yields a list of items.

Some common lookup plugins:

  • file reads file content.
  • lines gets content of a file as a list of lines.
  • template renders a template and yields output.
  • url fetches content of a web page or API query.

Details of others are available in the documentation:

ansible-doc -t lookup

Host patterns

Host patterns denote which systems upon which a task should be executed.

  • all specifies all hosts.
  • Host and group names.

Playbooks

Playbooks define automated tasks for Ansible to execute. They describe the desired state of the environment using YAML (*.yml). Ordering is top to bottom, in the definition order.

They're made up of the following files:

  • ansible.cfg configures Ansible.
  • inventory defines the hosts and groups.
  • group_vars/
    • all/
      • vars.yml defines variables applied to all deployments.
    • <environment>/
      • vars.yml defines variables applied to just the <environment> environment.
  • host_vars/
  • requirements.yml lists dependent roles
  • site.yml defines the primary Playbook

The structure:

---
- name: The name of the Playbook
  hosts: webservers
  vars:
      key: value
  vars_files:
    - vars/webservers.yaml
  tasks:
    - name: The name of the job
        <thing>:
            property: value

Playbooks can be executed as follows:

ansible-playbook some-playbook

To perform a dry-run, specify the -C (--check) switch.

Variables

Variables can be declared at multiple scopes, with those at the bottom taking precedence:

  1. CLI arguments
  2. Role defaults
  3. Inventory file or script group variables
  4. Inventory group_vars/all
  5. Playbook group_vars/all
  6. Inventory group_vars/*
  7. Playbook group_vars/*
  8. Inventory file or script host variables
  9. Inventory host_vars/*
  10. Playbook host_vars/*
  11. Host facts (including cached set_facts)
  12. Play vars
  13. Play vars_prompt
  14. Play vars_files
  15. Role variables (defined in role's /vars/main.yml)
  16. Block variables
  17. Task variables
  18. include_vars
  19. set_facts and register variables
  20. Role (and include_role) parameters
  21. include_* parameters
  22. CLI --extra-vars

Vault

Vault encrypts secrets stored in Playbooks for safe storage in source code management systems. It's managed using the ansible-vault CLI:

  • create
  • view
  • edit
  • encrypt
  • decrypt
  • rekey to change a key.

Encryption keys are specified using the --vault-id parameter, and are named in the form label@source. prompt is a commonly used source, causing Ansible to prompt the user for the encryption passphrase.

In task definitions, include_vars can be used to load variables.

Loops

Loops allow reducing duplication, and altering the structure of a playbook based upon variable contents.

- name: loops
  hosts: all
  tasks:
    - name: list
        debug:
            msg: "{{ item }}"
        loop:
          - a
          - b
    - name: dict
        debug:
            msg: "({{ item.x }}, {{ item.y }})"
        with_dict:
          - { x: 1, y: 1 }
          - { x: 2, y: 2 }
    - name: list from variable
        debug:
            msg: "{{ item }}"
        loop: "{{ some_variable }}"

Registers

Tasks can include a register key, making its execution result available as a variable for later conditional evaluation.

Conditionals

Conditionals can be used to constrain execution of a module based on variables. They're specified using the when key on a task. Conditions:

  • bool_value and not bool_value
  • var is defined and var is not defined
  • x == y and x != y
  • x < y, x <= y, x >= y, x > y
  • value in set
  • and and or can be used to combine conditions

When when is specified as a list, all conditions in the list must evaluate to true (they're grouped by and). Parentheses can be used for grouping conditions.

ansible_facts is a dictionary of values about the host which may be useful here:

Handlers

Handlers provide a publisher-subscriber model for delaying or re-applying modules based on changes happening elsewhere. A notifying task can specify a notify key, which will cause all tasks with the same value in their listen key to re-run.

Note that handlers will run most once after all notifications to the handler in the Play have completed.

Blocks

Blocks allow grouping logically related tasks together, and allow for a try-except-finally approach to error handling:

tasks:
  - name: Do a bunch of things
      block:
        - name: Step one
            debug:
                msg: Step one
        - name: Step two
            debug:
                msg: Step two
      rescue:
        - name: Rescue
            debug:
                msg: Failed
      always:
        - name: Always
            debug:
                msg: Complete

Roles

Roles package tasks in a form allowing reuse with a different set of variables, allowing Playbooks to scale to larger projects through composition of smaller, focused modules.

ansible-galaxy init some-role creates a structure like the following:

  • some-role/ is the root of the role.
    • defaults/ contains default values, intended to be overridden in Plays.
      • main.yml
    • files/
    • handlers/
      • main.yml
    • meta/ contains authorship, licensing and dependency metadata about the role.
      • main.yml
    • README.md
    • tasks/ is similar to the tasks section of a Play.
      • main.yml
    • templates/
    • tests/
      • inventory
      • test.yml
    • vars/ contains values not intended for overriding in Plays.
      • main.yml

Roles are consumed in the roles section of a Playbook:

---
- name: Some Playbook
  hosts: all
  roles:
    - some-role

Backlinks