Ansible configuration of Linux servers and desktops

Ansible is used for configuration of Linux servers and desktops.

Getting started with Ansible

Tutorials on Ansible:

Further documentation:

There is an Ansible_github repository.

Inventory

Ansible works against multiple systems in your infrastructure at the same time. It does this by selecting portions of systems listed in Ansible’s inventory_file, which defaults to being saved in the location:

/etc/ansible/hosts

You can specify a different inventory file using the -i <path> option on the command line.

Modules

Ansible Vault

Ansible Vault is a feature of Ansible that allows you to keep sensitive data such as passwords or keys in encrypted files, rather than as plaintext in playbooks or roles. These vault files can then be distributed or placed in source control.

To enable this feature, a command line tool:

ansible-vault

is used to edit files, and a command line flag (–ask-vault-pass, –vault-password-file or –vault-id) is used. Alternately, you may specify the location of a password file or command Ansible to always prompt for the password in your ansible.cfg file. These options require no command line flag usage.

Playbooks

Roles

Galaxy

Galaxy provides pre-packaged units of work known to Ansible as roles.

Some useful Galaxy packages include:

  • linux-system-roles/network This role enables users to configure network on target machines. Install by:

    ansible-galaxy install linux-system-roles.network
    

To upgrade a version from Galaxy, you have to install it with the --force option:

ansible-galaxy install --force <galaxy-role-name>

List installed Galaxy packages:

ansible-galaxy list

Callbacks

Callback plugins enable adding new behaviors to Ansible when responding to events. For example, the skippy plugin will make Ansible screen output that ignores skipped status.

You must whitelist any plugins in ansible.cfg, for example:

stdout_callback = skippy
callback_whitelist = skippy

Network Automation with Ansible

Ansible’s simple automation framework means that previously isolated network administrators can finally speak the same language of automation as the rest of the IT organization, extending the capabilities of Ansible to include native support for both legacy and open network infrastructure devices. Network devices and systems can now be included in an organization’s overall automation strategy for a holistic approach to application workload management.

Product specific Ansible documentation:

Setting up client hosts

SSH authorized keys

Password-less login from the Ansible server requires SSH authorized keys. Initially you must set up SSH keys on all client hosts as root:

mkdir $HOME/.ssh
restorecon -R -v $HOME/.ssh
scp <ansible-server>:.ssh/id_ecdsa.pub .
cat id_ecdsa.pub >> $HOME/.ssh/authorized_keys
rm -f id_ecdsa.pub

Test the Password-less login from the server:

server# ssh <client> date

Setting up the Ansible server

Configuration file

The Ansible configuration_file is /etc/ansible/ansible.cfg.

For local logging to a file uncomment this line:

log_path=/var/log/ansible.log

and create the file:

touch /var/log/ansible.log

Inventory: Hosts and Groups

Ansible works against multiple systems in your infrastructure at the same time. It does this by selecting portions of systems listed in Ansible’s Inventory, which defaults to being saved in the location /etc/ansible/hosts.

Add Ansible client hosts to the file /etc/ansible/hosts, for example:

[camd-desktops]
dirac.fysik.dtu.dk

Inventory: host-specific files

Sometimes some files with host-specific contents/data must be copied to the remote host. Unfortunately, Ansible doesn’t have any obvious way to copy host-specific files.

A solution exists, see Where should I be organizing host-specific files/templates?:

In the top-level directory (same level as playbooks) I have a files folder. In the files folder there is a folder for every host with it’s own files where the folder’s name is the same as the host name in inventory:

.
├── files
│   ├── common
│   ├── myhost1
│   ├── myhost2

Now in any role you can access the files with files modules relatively:

- name: Copy any host based file
  copy:
    src={{ inventory_hostname }}/file1
    dest= /tmp

Explanation:

The magic variable inventory_hostname is to get the host. Any file module (as for example copy) looks up the files directory in the respective role directory and the files directory in the same level as the calling playbook. Of course same applies to templates (but if you have different templates for the same role you should reconsider your design)

Basic Ansible tests

Make the recommended tests:

ansible all -m ping
ansible all -a "/bin/echo hello"

Ansible facts

To print all facts gathered use the setup module:

ansible XXX.fysik.dtu.dk -m setup

Playbook examples

To limit the playbook to one host only use the -l option:

ansible-playbook <playbook>.yml -l hostname

Yum install

Playbook task:

tasks:
- name: Install the latest version of EPEL repository
  yum:
    name: epel-release
    state: latest
- name: Install popular packages from the EPEL repository
  yum:
    name: Lmod,git-all,python34-pip,python2-pip
    state: latest

Create an empty file

See How to create an empty file with Ansible?. It is better to use the copy module:

- name: Create file if it does not exist
copy:
  content: ""
  dest: <file>
  force: no
  owner: root
  group: root
  mode: 0644

in stead of the standard touch module which actually modifies the timestamp.

Playbook error handlers

Sometimes you want to ignore the changed status of a task. Use the Playbook_error_handlers for Overriding The Changed Result:

# this will never report 'changed' status
- shell: wall 'beep'
  changed_when: False

Loops in Ansible

Ansible offers two keywords for creating loops: loop and with_<lookup>, see the loops page. Ansible added loop in version 2.5. It is not yet a full replacement for with_<lookup>, but we recommend it for most use cases.

Examples of loops:

disks:
  - /dev/sdb
  - /dev/sdc

- name: Create a new GPT primary partition for LVM
  parted:
    device: "{{ item }}"
    number: "{{ partition }}"
    label: gpt
    flags: [ lvm ]
   state: present
  loop: "{{ disks }}"

Nested loops are also possible (although difficult to write). See these examples:

Using filters to manipulate data

In Ansible functions are called filters and are used for transforming data inside a template expression. Ansible supports all filters provided by Jinja2 and also ships its own filters.

Filters let you transform JSON data into YAML data, split a URL to extract the hostname, get the SHA1 hash of a string, add or multiply integers, and much more. See the Ansible filters page and the blog post https://cloudaffaire.com/functions-in-ansible/

Getting an overview of available filters is surprisingly difficult! The Jinja template page contains a comprehensive list of builtin-filters.

Some example of useful filters include:

int() length() string()

and may be used, for example, as:

# Count the physical volumes in the disks array
- debug:
    msg: "{{ 'Number of disk volumes is ' + disks|length|string + ' on disks ' + disks|string }}"

Lookup plugins

Lookup plugins allow Ansible to access data from outside sources. This can include reading the filesystem in addition to contacting external datastores and services. Like all templating, these plugins are evaluated on the Ansible control machine, not on the target/remote.

The data returned by a lookup plugin is made available using the standard templating system in Ansible, and are typically used to load variables or templates with information from those systems.

Lookup s are an Ansible-specific extension to the Jinja2 templating language.

List all lookup plugins by:

ansible-doc -t lookup -l
ansible-doc -t lookup <plugin name>