Ansible-playbooks-deepdive


#1

Introduction to Playbooks:
Ansible playbook is series of adhoc commands. we are able to execute multiple tasks in a playbook. which is different from ad-hoc commands, by using ad-hoc commands we execute only task at a time.
Sample-Playbok:
---
- hosts: localhost
gather_facts: no
become: yes
tasks:
- name: install apache latest version
yum: name=httpd state=latest
async: 300
poll: 5
- hosts: webserver
become: yes
tasks:
- name: install postgresql is at the latest version
yum: name=postgresql state=latest
- name: start postgresql is at the latest version
service:
name: postgresql
state: started
Ansible uses a combination of a hosts file and a group_vars directory to pull variables per host group and run Ansible plays/tasks against hosts. group_vars/all is used to set variables that will be used for every host that Ansible is ran against.
A hosts file consists of host groups and hosts within those groups. A super-set of hosts can be built from other host groups using the children operator. These files can be found within a Ansible repository or in /etc/ansible/hosts.

Gather_facts: gather_facts about the remote user. This module takes care of executing the configured facts modules, * This module is automatically called by playbooks to gather useful variables about remote hosts that can be used in playbooks.

  • It can also be executed directly by /usr/bin/ansible to check what variables are available to a host.
  • Ansible provides many facts about the system, automatically.

become: Ansible allows you to ‘become’ another user, different from the user that logged into the machine (remote user). This is done using existing privilege escalation tools such as sudo, su, pfexec, doas, pbrun, dzdo, ksu, runas, machinectl and others.

Tasks: Each play contains a list of tasks. Tasks are executed in order, one at a time, against all machines matched by the host pattern, before moving on to the next task. It is important to understand that, within a play, all hosts are going to get the same task directives. It is the purpose of a play to map a selection of hosts to tasks.
When running the playbook, which runs top to bottom, hosts with failed tasks are taken out of the rotation for the entire playbook. If things fail, simply correct the playbook file and rerun.The goal of each task is to execute a module, with very specific arguments. Variables can be used in arguments to modules.

Inventory: Inventories are how ansible can locate and run against multiple systems. inventory is a list of hosts. The default inventory file location is etc/ansible/hosts, but it is configurable. Inventories may be formatted as an INI file or as a yaml file. file formats are interchangable. It is also possible to define group of hosts, host or group level variables and group of groups within the inventory. there are a number of variables that may be used in the inventory to control how ansible connects to and interact with target hosts.
Inventory is of two types. they are
a. Static inventory
b. Dynamic inventory
a. Static inventory: it is in INI or .yaml format. these are maintained by manually. Easy to manage for static configuration.
b. Dynamic Inventory: We recommend plugins over scripts for dynamic inventory. You can write your own plugin to connect additional dynamic inventory sources. this is in executable format ( like python, bash scripts etc). Scripts returns JSON containing inventory information.

 #!/usr/bin/env python

'''
Example custom dynamic inventory script for Ansible, in Python.
'''

import os
import sys
import argparse

try:
    import json
except ImportError:
    import simplejson as json

class ExampleInventory(object):

    def __init__(self):
        self.inventory = {}
        self.read_cli_args()

        # Called with `--list`.
        if self.args.list:
            self.inventory = self.example_inventory()
        # Called with `--host [hostname]`.
        elif self.args.host:
            # Not implemented, since we return _meta info `--list`.
            self.inventory = self.empty_inventory()
        # If no groups or vars are present, return an empty inventory.
        else:
            self.inventory = self.empty_inventory()

        print json.dumps(self.inventory);

    # Example inventory for testing.
    def example_inventory(self):
        return {
            'group': {
                'hosts': ['192.168.28.71', '192.168.28.72'],
                'vars': {
                    'ansible_ssh_user': 'vagrant',
                    'ansible_ssh_private_key_file':
                        '~/.vagrant.d/insecure_private_key',
                    'example_variable': 'value'
                }
            },
            '_meta': {
                'hostvars': {
                    '192.168.28.71': {
                        'host_specific_var': 'foo'
                    },
                    '192.168.28.72': {
                        'host_specific_var': 'bar'
                    }
                }
            }
        }

    # Empty inventory for testing.
    def empty_inventory(self):
        return {'_meta': {'hostvars': {}}}

    # Read the command line args passed to the script.
    def read_cli_args(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--list', action = 'store_true')
        parser.add_argument('--host', action = 'store')
        self.args = parser.parse_args()

# Get the inventory.
ExampleInventory()

 # ansible -i  inv.ssh localhost -m ping

Play: Play is a set of instructions expressed in in YAML. It targets a host or group of hosts. It provides a series of tasks that are excluded in in sequence. the goal of play is to map agroup of hosts to well defined role. Plays are kept in files known as playbooks. which may contain one or more plays.
Variables: Ansible uses variables to help deal with differences between systems.
To understand variables you’ll also want to read Conditionals and Loops. Useful things like the group_by module and the when conditional can also be used with variables, and to help manage differences between systems.
Use of variables:

  • customize configuration of variables

  • Hold return values of commands

  • Ansible has many default variables for controlling ansible behaviour.

  • Variable names build be letters, numbers and underscores

  • variables should always start with a letter

  • Variables can bescoped by a group, host or with in a playbook

  • Variables may used to store
    a simple text or numeric value
    simple lists
    python style Dictionaries

  • A dictionary is a list of key-value pair

  • variables may be defined in a no of ways
    [quote=“Raju.B, post:1, topic:207, full:true”]
    Introduction to Playbooks:
    Ansible playbook is series of adhoc commands. we are able to execute multiple tasks in a playbook. which is different from ad-hoc commands, by using ad-hoc commands we execute only task at a time.
    Sample-Playbok:

    - hosts: localhost
    gather_facts: no
    become: yes
    tasks:
    - name: install apache latest version
    yum: name=httpd state=latest
    async: 300
    poll: 5
    - hosts: webserver
    become: yes
    tasks:
    - name: install postgresql is at the latest version
    yum: name=postgresql state=latest
    - name: start postgresql is at the latest version
    service:
    name: postgresql
    state: started
    Ansible uses a combination of a hosts file and a group_vars directory to pull variables per host group and run Ansible plays/tasks against hosts. group_vars/all is used to set variables that will be used for every host that Ansible is ran against.
    A hosts file consists of host groups and hosts within those groups. A super-set of hosts can be built from other host groups using the children operator. These files can be found within a Ansible repository or in /etc/ansible/hosts.

Gather_facts: gather_facts about the remote user. This module takes care of executing the configured facts modules, * This module is automatically called by playbooks to gather useful variables about remote hosts that can be used in playbooks.

  • It can also be executed directly by /usr/bin/ansible to check what variables are available to a host.
  • Ansible provides many facts about the system, automatically.

become: Ansible allows you to ‘become’ another user, different from the user that logged into the machine (remote user). This is done using existing privilege escalation tools such as sudo, su, pfexec, doas, pbrun, dzdo, ksu, runas, machinectl and others.

Tasks: Each play contains a list of tasks. Tasks are executed in order, one at a time, against all machines matched by the host pattern, before moving on to the next task. It is important to understand that, within a play, all hosts are going to get the same task directives. It is the purpose of a play to map a selection of hosts to tasks.
When running the playbook, which runs top to bottom, hosts with failed tasks are taken out of the rotation for the entire playbook. If things fail, simply correct the playbook file and rerun.The goal of each task is to execute a module, with very specific arguments. Variables can be used in arguments to modules.

Inventory: Inventories are how ansible can locate and run against multiple systems. inventory is a list of hosts. The default inventory file location is etc/ansible/hosts, but it is configurable. Inventories may be formatted as an INI file or as a yaml file. file formats are interchangable. It is also possible to define group of hosts, host or group level variables and group of groups within the inventory. there are a number of variables that may be used in the inventory to control how ansible connects to and interact with target hosts.
Inventory is of two types. they are
a. Static inventory
b. Dynamic inventory
a. Static inventory: it is in INI or .yaml format. these are maintained by manually. Easy to manage for static configuration.
b. Dynamic Inventory: We recommend plugins over scripts for dynamic inventory. You can write your own plugin to connect additional dynamic inventory sources. this is in executable format ( like python, bash scripts etc). Scripts returns JSON containing inventory information.

 #!/usr/bin/env python

'''
Example custom dynamic inventory script for Ansible, in Python.
'''

import os
import sys
import argparse

try:
    import json
except ImportError:
    import simplejson as json

class ExampleInventory(object):

    def __init__(self):
        self.inventory = {}
        self.read_cli_args()

        # Called with `--list`.
        if self.args.list:
            self.inventory = self.example_inventory()
        # Called with `--host [hostname]`.
        elif self.args.host:
            # Not implemented, since we return _meta info `--list`.
            self.inventory = self.empty_inventory()
        # If no groups or vars are present, return an empty inventory.
        else:
            self.inventory = self.empty_inventory()

        print json.dumps(self.inventory);

    # Example inventory for testing.
    def example_inventory(self):
        return {
            'group': {
                'hosts': ['192.168.28.71', '192.168.28.72'],
                'vars': {
                    'ansible_ssh_user': 'vagrant',
                    'ansible_ssh_private_key_file':
                        '~/.vagrant.d/insecure_private_key',
                    'example_variable': 'value'
                }
            },
            '_meta': {
                'hostvars': {
                    '192.168.28.71': {
                        'host_specific_var': 'foo'
                    },
                    '192.168.28.72': {
                        'host_specific_var': 'bar'
                    }
                }
            }
        }

    # Empty inventory for testing.
    def empty_inventory(self):
        return {'_meta': {'hostvars': {}}}

    # Read the command line args passed to the script.
    def read_cli_args(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--list', action = 'store_true')
        parser.add_argument('--host', action = 'store')
        self.args = parser.parse_args()

# Get the inventory.
ExampleInventory()

 # ansible -i  inv.ssh localhost -m ping

Play: Play is a set of instructions expressed in in YAML. It targets a host or group of hosts. It provides a series of tasks that are excluded in in sequence. the goal of play is to map agroup of hosts to well defined role. Plays are kept in files known as playbooks. which may contain one or more plays.
Variables: Ansible uses variables to help deal with differences between systems.
To understand variables you’ll also want to read Conditionals and Loops. Useful things like the group_by module and the when conditional can also be used with variables, and to help manage differences between systems.
Use of variables:

  • customize configuration of variables
  • Hold return values of commands
  • Ansible has many default variables for controlling ansible behaviour.
  • Variable names build be letters, numbers and underscores
  • variables should always start with a letter
  • Variables can bescoped by a group, host or with in a playbook
  • Variables may used to store
    a simple text or numeric value
    simple lists
    python style dictionaries
  • A dictionary is a list of key-value pairs
  • Variables may be defined in a no of ways
    a. via command line argument
    b. within a variable file, playbook
    c. with in a inventory file
  • It is a good practice to wrap variable names or statements containing variable names in weak quotes.
  • The register module is used to store task output in a dictionary variable.

Ex:1 Vars_play.yml
---
- hosts: localhost
tasks:
- name: create working directory
file:
name: "{{ working_dir }}"
state: directory
- name: write service list
lineinfile:
path: "{{ working_dir }}/services.txt"
create: yes
line: "{{ service_list }}"
- name: view share property
command: ls -la "{{ share_paths['nfs'] }}"
register: cmd_output
- name: write command module output to file
copy:
content: "{{ cmd_output }}"
dest: "{{ working_dir }}/cmd_output.txt"

Ex:2 vars.yml

working_dir: /home/ansadm/working
service_list:
- httpd
- nfs
- mariadb
share_paths:
nfs: /mnt/nfs
cifs: /mnt/cifs
iscsi: /mnt/iscsi

run as a ansible-playbook vars_play.yml
and run as a
ansible-playbook vars_play.yml -e @vars.yml

Here -e tells about the to pass a variable from command line to a playbook.

TEMPLATES:
Ansible templates provides a simple means of maintaining various configuration files that may be customized on deployment. templates gives the ability to provide a skeltel file that can be dynamically completed using variables. the most common template use case is configuration file management. Templates are generally used by providing a template file on the ansible control node, and then using the template module with in your playbook to deploy the file to a target server or group. Templates are processed using the jinja2 template language.

  • Templates uses template module
  • Template files are essentially text files that have variable referenceces.
  • They tend to be identified by using the file extension .j2
    Ex: Template.yml
    ---
- hosts: localhost
  gather_facts: no
 become: yes
 vars: 
 code_name: whisky
 version: 4.2
 tasks:
 - name: deploy config file
 template:
   src: config.j2
   dest: /opt/config

vim config.j2

     name={{ code_name }}
     version={{ version }}

Run as a ansible-playbook -i inv Template.yml

Validate: The validate command to run before copying into place. The path to the file to validate is passed in a via ‘%s’ which must be present. The command is passed securely so shell features like expansion and pipes won’t work.

FACTS:

  • Facts are information discovered by Ansible about a target system.
  • There are two ways factors are collected
    a. using setup module with an adhoc command: ansible all -m setup
    b. facts are gathered by default when a playbook is executed.
  • facts gathering in playbooks may be disabled using the gather_facts attribute.
  • The keyword gather-facts may be set in a playbooks to change facts gathering behaviour.
  • It is possible to print ansible facts in files using variables.
  • facts may be filtered using the setup module adhoc by passing a value for the filter
    parameter.
  • It is possible to use {{ ansible_facts }} for conditional plays based on facts.

ansible -i inv localhost -m setup -a filter=*pv4*
This command is used to display the facts and these are represents in variable format.

Ex: Facts.yml
---
- hosts: remote
tasks:
- name: create a file
lineinfile:
path: /home/ansadm/hostname
create: yes
line: "{{ ansible_hostname }}"
- name: access magic variable
lineinfile:
path: /home/ansadm/hostname
line: "{{ hostvars[' locahost'] ['ansible_deault_ipv4']['address'] }}"

Run as a ansible-playbook -i inv facts.yml

CONDITIONALS:

  • Ansible playbooks are capable of making actions conditionals
  • The when keyword is used to test a condition with in aplaybook
  • Jinja2 expressions are used for conditonals evaluation.
  • It is possible to use module output conditionally.
    stat: Returns the information about the target file.

Ex: cond.yml
---
- hosts: localhost
vars:
target_file: /home/ansadm/hostname
tasks:
- name: gather file Distribution
stat:
path: "{{ target_file }}"
register: hostname
- name: Rename hostname when found
command: mv "{{ target_file }}" /home/ansadm/net-info
when: hostname.stat.exists

Run as ansible-playbook -i inv cond.yml

LOOPS:
Sometimes you want to repeat a task multiple times. In computer programming, this is called a loop. Common Ansible loops include changing ownership on several files and/or directories with the file module, creating multiple users with the user module.
Ansible offers two keywords for creating loops:
a. loop
b. with_lookup

  • The loop keyword is equivalent to with_list
  • The loop keyword maybe used to express a repeated action more concisely
  • loop may also operate operate with a list variable.
  • It is also possible to combine loops and conditions.

Ex: loop.yml

---
- hosts: localhost
become: yes
tasks:
- name: install software
yum:
name: "{{ item }}"
state: absent
loop:
- elinks
- nmap-ncat
- bind-utils
Run as ansible-playbook loop.yml

Ex:loop2.yml

---
- hosts: localhost
become: yes
vars_files:
- vars.yml
tasks:
- name: check services
service:
name: "{{ item }}"
state: started
loop: "{{ service_list }}"