Ansible

@slueg

Advances

  • do once, run everywhere
  • same state everywhere
  • review changes

Ansible is...

idempotent

Denoting an element of a set which is unchanged in value when multiplied or otherwise operated on by itself.

defines the state,

NOT the operation

no need to care about the previous state

(well... theoretically)

Ansible consists of...

Playbooks

Roles

Tasks

Modules

Playbooks

Roles

Tasks

Modules

Programs

Class

Functions

Operations

~

Playbook

  • define which hosts to run on
  • define which roles, tasks other playbooks to run

Playbook - example

- hosts: cambuildr-servers
  roles:
    - basic-server-setup
    - setup-cambuildr
    - setup-filebeat
    - setup-watchman

Role

  • includes one or more tasks
  • independent of the host systems
  • can have:
    • dependencies (other roles)
    • default variables
    • templates
    • etc.

Role - example

common/               # this hierarchy represents a "role"
        tasks/            #
            main.yml      #  <-- tasks file can include smaller files if warranted
        handlers/         #
            main.yml      #  <-- handlers file
        templates/        #  <-- files for use with the template resource
            ntp.conf.j2   #  <------- templates end in .j2
        files/            #
            bar.txt       #  <-- files for use with the copy resource
            foo.sh        #  <-- script files for use with the script resource
        vars/             #
            main.yml      #  <-- variables associated with this role
        defaults/         #
            main.yml      #  <-- default lower priority variables for this role
        meta/             #
            main.yml      #  <-- role dependencies

Tasks

  • uses one module
  • can be called for multiple items
  • ifs possible

Task - example 1

- name: create cambuildr app and varys subfolders
  file:
    path: "{{ item }}"
    state: directory
    owner: "{{ cambuildr_user }}"
    group: "{{ cambuildr_group }}"
  with_items:
    - "{{ cambuildr_app_dir }}/storage"
    - "{{ cambuildr_app_dir }}/releases"
    - "{{ cambuildr_app_dir }}/filesystem/private"
    - "{{ cambuildr_app_dir }}/filesystem/public"
    - "{{ cambuildr_varys_dir }}/releases"
    - "{{ cambuildr_generic_theme_dir }}/releases"

Task - example 2

- name: Enable apache2 module <headers>
  apache2_module:
    state: present
    name: headers
  notify:
    - restart apache2

and of...

Handlers

  • only called if the state is changed
  • only called once at the end
  • called in the order they are written
  • used to restart services, etc.

Handlers - example

- name: Disable apache2 docs configuration for Apache 2.4 on Ubuntu 14.04
  command: a2disconf apache2-doc
  args:
    removes: /etc/apache2/conf-enabled/apache2-doc.conf
  when: ansible_distribution_major_version == "14"
  notify:
    - restart apache2
- name: restart apache2
  service:
    name: apache2
    state: restarted

task:

handler:

called at the end?
I need them NOW!

flush to the rescue

- name: flush to the rescue
  meta: flush_handlers

not to forget the...

Templates

  • define templates that can use variables
  • useful for configuration files
  • and generally all files that are touched by ansible

Templates - example

- name: set sshd_config file
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config
    backup: yes
    owner: root
    group: root
    mode: 0644
    validate: '/usr/sbin/sshd -T -f %s'
  notify:
    - restart sshd

task:

Templates - example

###############################################################################
#
#   {{ ansible_managed }}
#
#==============================================================================

# What ports, IPs and protocols we listen for
Port 22
# Use these options to restrict which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
{% if ansible_distribution_major_version == "14" %}
HostKey /etc/ssh/ssh_host_ed25519_key
{% endif %}
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes

# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
{% if ansible_distribution_major_version == "12" %}
ServerKeyBits 768
{% else %}
ServerKeyBits 1024
{% endif %}

sshd_config.j2:

hosts & variables

hosts file

  • define servers with ip, hostname, etc.
  • order them in groups (prod, staging, db, etc.)

variables

  • server & group variables
  • default variables for roles
  • define variables in task
  • use ansible facts
  • and many more type of vars ...

variable precedence

(the last listed variables winning prioritization)

  • role defaults [1]
  • inventory vars [2]
  • inventory group_vars
  • inventory host_vars
  • playbook group_vars
  • playbook host_vars
  • host facts
  • play vars
  • play vars_prompt
  • play vars_files
  • registered vars
  • set_facts
  • role and include vars
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • extra vars (always win precedence)

host/group vars

  • you can define variables for each host or group
  • defined in inventory
  • this variables override defaults and vars in roles

host vars

---

cambuildr_base_dir: XXX
cambuildr_user: XXX
cambuildr_group: XXX

db_username: XXX
db_password: XXX

mail_server_active: XXX


watchman_active: true
watchman_db_password: XXX
inventories/hosteurope-server/host-vars/myHostName:

facts

  • Ansible gathers facts everytime it runs and provides them for you
  • see all facts for a host:
ansible -m setup myHostName

use variables

- name: set bash prompt
  lineinfile:
    dest: ~/.bashrc
    line: 'export PS1="\[\e[1;37;41m\] {{ inventory_hostname }} (\u) \[\e[0m\]:\[\e[0;34m\]\w\[\e[0m\] ⚡️  "'
    state: present

use a fact:

use variables - complex

---

# Stores the packages depending on the ubuntu version (12 or 14) with their state (absent or present)
basic_apt_packages_for_ubuntu:
  "14":
    - name: curl
      state: present

    - name: php5-curl
      state: present

    - name: php5-json
      state: present

    - name: php5-gd
      state: present

    - name: php5-readline
      state: present

    - name: php5-mysqlnd
      state: present

  "12":
    - name: curl
      state: present

    - name: php5-curl
      state: present

    - name: php5-json
      state: present

    - name: php5-gd
      state: present

    - name: php5-mysql
      state: present

define default:

use variables - complex

- name: install basic packages
  apt:
    name: "{{ item.name }}"
    state: "{{ item.state }}"
  with_items: "{{ basic_apt_packages_for_ubuntu[ansible_distribution_major_version] }}"

use defined variable, fact and loop:

top secret variables

  • how to store ssh-private-keys?
  • answer: ansible vault

Ansible Vault

Ansible Vault

  • specify ansible vault password in vault_password_file
  • tell ansible about the password file in ansible.cfg

need help?

so... how to start?

Installation

⚡️ ~   $ brew install ansible

setup:

⚡️ ~   $ ansible --version
ansible 2.2.0.0
  config file = 
  configured module search path = Default w/o overrides

validate and check version:

run

⚡️ ~   $ ansible -m setup myHostName

⚡️ ~   $ ansible -m ping all

run a single module (facts and ping):

⚡️ ~   $ ansible-playbook my-playbook.yml

run a playbook:

further reading

Ansible

By Christoph Schleifer