Low Orbit Flux Logo 2 F

Ansible Cheat Sheet

Purpose:

Install



sudo apt update && sudo apt install -y ansible
sudo dnf install ansible   # Fedora

git clone https://github.com/ansible/ansible.git
cd ./ansible
source ./hacking/env-setup

ansible myhost --become -k -K -m raw -a "yum install -y python3"    # hosts without python

Configure

In order:



ansible.cfg

[defaults]
inventory = ./hosts
host_key_checking = False
nocows = True

Show current effective config:



ansible-config list    #  Print all config options
ansible-config dump    #  Dump configuration
ansible-config view    #  View configuration file

Inventory



/etc/ansible/hosts


192.0.3.25
server1.lab.net
server2.lab.net


[webservers]
web1
web2
web3


[web:vars]
port=8080
x=50
datadir="/data"

[web:children]
dev
prod

[dev]
test1

[prod]
server1

Variable files/dirs for groups and hosts:

/etc/ansible/group_vars/all all
/etc/ansible/group_vars/web.yaml var file for web group
/etc/ansible/group_vars/db/data.yaml var file for db group
/etc/ansible/host_vars/host1.yaml vars for host1
/etc/ansible/host_vars/test1/data1.yaml file for host test1

Adhoc Runs



ansible all -m ping                                     # ping all hosts
ansible all -a "uname -a"                               # command module - default
ansible all -m ping -u user1  -bkK                      # sudo to root, ask pass
ansible web -m shell -a 'echo test > output.txt'        # webhosts, shell module
ansible web -m copy -a "src=/etc/hosts dest=/tmp/hosts" # copy module with args
ansible web -a "/sbin/reboot" -bkK -f 10 -u testuser    # 10 parallel forks
ansible all -m ansible.builtin.setup                    # see all facts

Playbooks



ansible-playbook test.yaml                  # run a playbook ( need SSH key )
ansible-playbook test.yaml -bkK             # prompt for SSH pass and sudo pass and become
ansible-playbook test.yaml --limit host1    # limit to specified host
ansible-playbook test.yml -f 10             # with more options ( 10 forks )
ansible-playbook test.yaml --check
ansible-playbook test.yaml --diff
ansible-playbook test.yaml --list-hosts
ansible-playbook test.yaml --list-tasks
ansible-playbook test.yaml --syntax-check

Playbook example:



web_deploy.yaml


- name: Update db servers
  hosts: databases
  remote_user: admin
  become: yes
  become_user: postgres
  vars:
    trigger_task: true
    supported_os:
      - RedHat
      - Fedora
  vars_files:
    - my_variables.yaml
    - db_params.yaml
  gather_facts: false

  tasks:

  - name: Ensure postgresql is at the latest version
    ansible.builtin.yum:
      name: postgresql
      state: latest

Privilege Escalation - Become

Commandline args:



-b  ( --become )       ( typcially sudo for root )
-k  ( --ask-pass )
-K  ( --ask-become-pass )   # prompt for become password
--become_user=postgres      # doesn't imply --become
--become-method=su          # sudo is default
-C  --check                 # check only
-u user1   # connect as a user
-f 10      # 10 parallel forks

Playbook options ( specify inside playbook, don’t need to but you can ):



become: yes            # enable become, doesn't imply prompting
become_user: postgres  # user to become, default is root, doesn't imply become
become_method: su      # alternate methods you could use
remote_user: admin     # user for initial SSH connection

ISSUE

ansible.cfg
allow_world_readable_tmpfiles

Vars

Vars on command line:



ansible-playbook release.yml -e "version=1.23.45 other_variable=foo"
ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'  # JSON for non-YAML
ansible-playbook release.yml --extra-vars "@some_file.json"           # JSON or YAML file
var1: !unsafe 'this variable has {{ characters that should not be treated as a jinja2 template'     # unsafe chars


Prompt for variable:



  vars_prompt:
    - name: ansible_password
      prompt: "Enter password"
      private: yes
    - name: ansible_b

Vars file:



---
somevar: somevalue
password: magic
a: 1
b: 2
c: 3
list1:
  - apple
  - banana
  - toast
var1: "test"

Register Variables:




    - name: Read file
      ansible.builtin.shell: cat /etc/passwd | head -n 4
      register: result

    - name: Print Var
      ansible.builtin.debug:
        var: result

    - name: Print Msg
      ansible.builtin.debug:
        msg: "This is it: {{ result }}"

    - name: Print stdout
      ansible.builtin.debug:
        var: result.stdout

    - name: Print stdout lines
      ansible.builtin.debug:
        var: item
      loop: "{{ result.stdout_lines }}"

    - name: Print result
      ansible.builtin.debug:
        var: result.rc

    - name: Check if string found
      ansible.builtin.debug:
        msg: "Found"
      when: result.stdout.find('root') != -1


List / Dictionary / Print / Loop

with_ can be used for looping
loop added in 2.5, not full replacement for with_, equivalent to with_list, best for simple loops



---
- name: A Test Playbook
  hosts: all
  vars:
    list1:
      - RedHat
      - Fedora
      - Debian
      - Ubuntu
    dict1:
      OS: Ubuntu
      IP: 192.168.3.2
      CPU: intel
      Mem: 32
  tasks:

    - name: Print List / Dict
      ansible.builtin.debug:
        msg: "{{ list1 }} {{ dict1 }} {{ list1[2] }} {{ dict1['OS'] }}"

    - name: Loop Over List
      ansible.builtin.debug:
        msg: "Value: {{ item }}"
      loop: "{{ list1 }}"

    - name: Loop Over Dictionary
      ansible.builtin.debug:
        msg: "Key: {{ item.key }} Value: {{ item.value }}"
      loop: "{{ dict1|dict2items }}"

    - name: Example
      ansible.builtin.debug:
        var: item
      loop:
        - test1
        - test2

    - name: Loop over list of
      user:
        name: "{{ item.name }}"
        state: present
        groups: "{{ item.groups }}"
      loop:
        - { name: 'testuser1', groups: 'wheel' }
        - { name: 'testuser2', groups: 'root' }

    ....
    loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"       # no
    ....
    with_fileglob: '*.txt'                                         # yes


Conditionals



tasks:
  - name: "shut down Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"

    when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
          (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"

  - command: /bin/false
    register: result
    ignore_errors: True

  - command: /bin/something
    when: result is failed       # or succeeded or skipped

Filters

Loads of useful Filters exist: Filters





tasks:
  - shell: cat /data/some_silly_config.yaml
    register: result
  - debug:
      msg: '{{ item }}'
    loop: '{{ result.stdout | from_yaml_all | list }}'

{{ dict | dict2items }}                                          # dictionary to list of key value pairs ( for looping )
{{ tags | items2dict }}                                          # reverse of dict2items, list of key value pairs back to dict



Lookups

Ways to use a lookup:

lookup(‘dict’, dict_variable, wantlist=True) return a list
query(‘dict’, dict_variable) return a list
q(‘dict’, dict_variable) short form of query
with_dict: for looping

Show a list of all lookups ( list plugins and filter for lookup ):



ansible-doc -t lookup -l

Lookup Examples




vars:
  motd_value: "{{ lookup('file', '/etc/motd') }}"
tasks:
  - debug:
      msg: "motd value is {{ motd_value }}"

  - debug:
      msg: "{{ lookup('fileglob', '/etc/*') }}"
  - debug:
      msg: "{{ lookup('fileglob', '/etc/*', wantlist=True) }}"
  - debug:
      msg: "{{ query('fileglob', '/etc/*') }}"
  - debug:
      msg: "TEST: {{ item }}"
    with_fileglob:
      - '/etc/*'

  - debug:
      msg: "TEST: {{ item }}"
    with_file:
      - '/etc/hosts'
      - '/etc/passwd'



Common Facts

Some common facts:



  tasks:
    - debug: var=ansible_facts['distribution']
    - debug: var=ansible_facts['distribution_major_version']
    - debug: var=ansible_facts['os_family']
    - debug: var=ansible_facts['all_ipv4_addresses']
    - debug: var=ansible_facts['default_ipv4']
    - debug: var=ansible_facts['env']
    - debug: var=ansible_facts['hostname']
    - debug: var=ansible_facts['interfaces']
    - debug: var=ansible_facts['kernel']


Templates



- name: Test Playbook
  hosts: all
  vars:
    color: blue
    size: 30
    food:
      - apple
      - pizza
      - rice
    data:
     - host: localhost
       IP: 127.0.0.1
     - host: silly-wombat1
       IP: 104.131.68.105

  tasks:
    - name: Template test
      template:
        src: deployments/test.conf.j2
        dest: /data/test.conf




deployments/test.conf.j2


This is a test template.
Selected color is {{ color }}
That's it.


{% if size > 25 %}
Large enough
 {% else %}
Too small
{% endif %}


Here is the list:
{% for f in food %}
{{ f }}
{% endfor %}

{% for x in data %}
{{ x['IP'] }} {{ x['host'] }}
{% endfor %}


Raw section to escape special characters:




\{% raw %\} ... \{% endraw %\}



Common Tasks

Command / Shell / Script / Raw



tasks:
  - name: enable selinux
    command: /sbin/setenforce 1

  - name: run this command and ignore the result
    shell: /usr/bin/somecommand
    ignore_errors: True

  - name: Run a script with arguments (free form)
    script: script1.sh -a 1234

  - name: Run a script with arguments (using 'cmd' parameter)
    script:
      cmd: /opt/app1/script1.sh -a 1234

  - name: Install Python on RHEL / Fedora
    ansible.builtin.raw: dnf install -y python3

File



  - name: Change file ownership, group and permissions
    ansible.builtin.file:
      path: /etc/foo.conf
      owner: foo
      group: foo
      mode: '0644'

  - name: Touch the same file, but add/remove some permissions
    ansible.builtin.file:
      path: /data/set1
      state: directory
      recurse: yes
      mode: u+rw,g-wx,o-rwx

  ...
  ...
    state: link
    state: absent

Copy



- name: Copy ansible inventory file to client
  copy:
    src=/etc/ansible/hosts
    dest=/etc/ansible/hosts
    owner=root
    group=root
    mode=0644

Synchronize

Sync from control host to remote host:



- name: Synchronization of src on the control machine to dest on the remote hosts
  ansible.posix.synchronize:
    src: some/relative/path
    dest: /some/absolute/path

...
...
    mode: pull                 # pull instead of default push
  delegate_to: delegate.host   # run it from another host


Replace




- name: Replace old hostname with new hostname (requires Ansible >= 2.4)
  ansible.builtin.replace:
    path: /etc/hosts
    regexp: '(\s+)old\.host\.name(\s+.*)?$'
    replace: '\1new.host.name\2'

- name: Replace between the expressions (requires Ansible >= 2.4)
  ansible.builtin.replace:
    path: /etc/hosts
    after: '(?m)^<VirtualHost [*]>'
    before: '</VirtualHost>'
    regexp: '^(.+)$'
    replace: '# \1'


Lineinfile

backup: true make a dated backup of file
insertafter: “regex insert after last line that matches this
insertbefore “regex insert before last line that matches this
firstmatch: true modify insertafter/insertbefore to match first line
regexp: “regex replace last line matched, or remove if “state: absent” ( use regex )
search_string: “string” replace last line matched, or remove if “state: absent” ( use literal )
line: “string” the actual string we want
state: absent remove line instead, default is present,



- name: Ensure SELinux is set to enforcing mode
  ansible.builtin.lineinfile:
    path: /etc/selinux/config
    regexp: '^SELINUX='
    line: SELINUX=enforcing

- name: Make sure group wheel is not in the sudoers configuration
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    state: absent
    regexp: '^%wheel'

- name: Ensure the default Apache port is 8080
  ansible.builtin.lineinfile:
    path: /etc/httpd/conf/httpd.conf
    regexp: '^Listen '
    insertafter: '^#Listen '
    line: Listen 8080


Packages and Services



- name: Update repo cache and install nginx package
  ansible.builtin.apt:
    name: nginx


- name: Install nginx package ( Ubuntu/Debian )
  ansible.builtin.apt:
    name: nginx
    update_cache: yes


- name: Remove the nginx package ( Red Hat )
  ansible.builtin.dnf:
    name: nginx
    state: absent



- name: Make sure a service is started, enabled, and not masked
  ansible.builtin.systemd_service:
    state: started
    name: nginx
    enabled: true
    masked: no

...
...
    state: stopped
    state: restarted
    state: reloaded

Users / Groups

User and group:




- name: Create a user
  ansible.builtin.user:
    name: jsmith
    password: {{ 'mypassword' | password_hash('sha512', 'mysecretsalt') }}

- name: Ensure group "docker" exists with correct gid
  ansible.builtin.group:
    name: docker
    state: present
    gid: 1750



SSH Keys

Managing up SSH keys is easy:




- name: Set authorized key taken from file
  ansible.posix.authorized_key:
    user: charlie
    state: present
    key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"