Contents

Braindump: Ansible

Run arbitrary commands

ansible-doc ping

ansible localhost -m ping

ansible localhost -m shell -a "cat ./a-file"

ansible localhost -m uri -a 'url=https://google.com return_content=true'

ansible all --list-hosts

Simple localhost playbook

ansible-playbook -e "my_var=123 other_var=345" ./deploy.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
---
- hosts: localhost
  vars_prompt:
    - name: 'env'
      prompt: 'Environment: nonprod | prod'
      private: no
  vars_files:
    - './vars/env-all.yaml'
    - './vars/env-{{ env }}-{{ instance }}.yaml'

  vars:
  tasks:
    - name: 'Assume to k8s'
      command: |
                aws eks update-kubeconfig --region ap-southeast-2 --profile {{ eks_profile }} --name {{ eks_cluster }}
    - name: 'Deploy {{ namespace }} ingress'
      command: |
                kubectl --namespace={{ namespace }} apply -f -
      args:
        stdin: '{{ lookup("template", "./manifests/my-deployment.yaml.j2") }}'

Default

1
2
# Can prevent exceptions
{{ item.val | default(100) }}

Loops

Avoid with_* keywords

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
- name: basic loop
    debug:
      msg: "{{ item }}"
    loop:
    - a
    - b
- name: hello
    debug:
        msg: "{{ item.val }}"
    loop:
    - val: a
    - val: b
    
# When clause is processed separately for each
- name: hello
    debug:
      msg: "{{ item.val }}"
    when: item.val > 10
    loop:
    - val: 10
    - val: 29
    -
# Dict loop
- hosts: localhost
  vars:
    my_dict:
      a: true
  tasks:
    - name: hello
      debug:
        msg: "{{ item }}"
      loop: "{{ my_dict | dict2items }}"

# Loop over commands and throw if nonzero rc
  tasks:
    - shell: "echo {{ item }}"
      loop:
        - "one"
        - "two"
      register: echo

    - name: Fail if return code is not 0
      fail:
        msg: "The command ({{ item.cmd }}) did not have a 0 return code"
      when: item.rc != 0
      loop: "{{ echo.results }}"

# Do until
- shell: /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10

Error handling

1
2
3
4
5
6
7
name: Perform possible failing process
block:
  - ...task
rescue:
  - ...task
always:
  - ...task

Task args

1
2
3
4
5
6
- tasks: 
  name: me
  changed_when: 'changed' in me.stdout
  failed_when: 'failed' in me.stderr
  when: true

Handlers

Basic direct handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
tasks:
  - name: write config
    template:
      src: template.j2
      dest: /etc/file.foo
    notify:
      - handler name

handlers: 
  - name: handler name
    service: nginx
    state: restarted

Publish subscribe as a handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tasks:
  - name:
    notify: "restart everything"

handlers:
  - name: a consumer
    ...
    listen: "restart everything"
  - name: another consumer
    ...
    listen: "restart everything"

Debugging

Set ANSIBLE_STRATEGY=debug to run a playbook and debug on error

Debugger can be used on any block with a name

1
2
3
- name: Execute a command
  command: false
  debugger: on_failed # or on_skipped 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
p task
p task.args
p task_vars["my-var"]
p host
p result._result

task.args['my-arg'] = 'new val'
redo

task_vars["my-vars"] = 'new val'
update_task
redo

continue

quit

Setting an environment variable

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  vars:
    proxy_env:
      http_proxy: http://proxy.example.com:8080

  tasks:

    - name: Install cobbler
      package:
        name: cobbler
        state: present
      environment: "{{ proxy_env }}"

Testing

Wait for a response from a url

1
2
3
4
  - wait_for:
      host: "{{ inventory_hostname }}"
      port: 22
    delegate_to: localhost

Curl and check a webpage

1
2
3
4
5
6
  - action: uri url=https://google.com return_content=yes
    register: webpage

  - fail:
      msg: "google is down"
    when: 'google' not in webpage.content

Check results of a shell command

1
2
3
4
5
6
7
   - shell: ls
     register: cmd_result

   - assert:
       that:
         - "'not ready' not in cmd_result.stderr"
         - "'gizmo enabled' in cmd_result.stdout"

Check for existence of an unmanaged file

1
2
3
4
5
6
7
   - stat:
       path: /path/to/something
     register: p

   - assert:
       that:
         - p.stat.exists and p.stat.isdir

Ansible config

  • ANSIBLE_CONFIG (environment variable)
  • ansible.cfg (per directory)
  • ~/.ansible.cfg (home directory)
  • /etc/ansible/ansible.cfg (global)

Ansible galaxy

ansible-galaxy install -r requirements.yml ansible-galaxy search elasticsearch --author geerlingguy ansible-galaxy info username.role_name

requirements.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# from galaxy
- src: yatesr.timezone

# from GitHub
- src: https://github.com/bennojoy/nginx

# from GitHub, overriding the name and specifying a specific tag
- src: https://github.com/bennojoy/nginx
  version: master
  name: nginx_role

# from a webserver, where the role is packaged in a tar.gz
- src: https://some.webserver.example.com/files/master.tar.gz
  name: http-role

# from Bitbucket
- src: git+http://bitbucket.org/willthames/git-ansible-galaxy
  version: v1.4

# from Bitbucket, alternative syntax and caveats
- src: http://bitbucket.org/willthames/hg-ansible-galaxy
  scm: hg

# from GitLab or other git-based scm
- src: git@gitlab.company.com:mygroup/ansible-base.git
  scm: git
  version: "0.1"  # quoted, so YAML doesn't parse this as a floating-point value