Skip to content

Instantly share code, notes, and snippets.

@jondkelley
Last active October 20, 2022 16:56
Show Gist options
  • Save jondkelley/07906fb0843ddf218e02625163b322f4 to your computer and use it in GitHub Desktop.
Save jondkelley/07906fb0843ddf218e02625163b322f4 to your computer and use it in GitHub Desktop.
Linux Awesome Gists: Ansible Best Practices

Ansible Playbook Design & Best Practices

The complexity you might consider dictates the style of your environments as you see the examples 'good' 'better' and 'best' below. The best is not always better if it's a waste of time after all.

General best-advice writing root playbooks

Tips

  • Do use hosts: limiters to the least destructive inclusion group for added safety whenever possible.
  • Do use extended block notation if you need additional levels of control around playbook / role execution.

Public Examples

---
- hosts: localhost
  remote_user: root
  become: true
  roles:
    - role: ansible-nginx-oss

Good

---
- name: configure webservers
  remote_user: root
  become: true
  become_method: sudo
  hosts: webservers
  roles:
    - { role: nginx }
    - { role: nginx_myconfigs, http_port: '80', max_clients: '200 }

Better

Simply define multiple blocks of below for different roles if more isolation is desired. Allows use of conditional logic and add many features at the root before calling a role.

---
- name: configure webservers
  hosts: webservers
  remote_user: root
  become: true
  become_method: sudo
  vars:
    http_port: 80
    max_clients: 200
  when: blah is defined
  failed_when: condition is defined
  tasks:
   - name: display all internal ansible variables when -vvvv supplied
    debug:
      var: hostvars[inventory_hostname]
      verbosity: 4
    when: print_debug_vars is defined and print_debug_vars == "True"
  - name: install nginx
    include_role:
      name: geerlingguy.ansible-role-nginx
      tasks_from: main
    when: myconfig_webserver_settings is defined
    failed_when: condition2 is defined
  - name: configure nginx
    include_role:
      name: nginx_myconfigs
      tasks_from: main
    when: myconfig_webserver_settings is defined
    failed_when: condition2 is defined
  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted

Best (for extremists)

If you have really large projects, you can link other playbooks in a chained manner.

If you need to run a lot of processes in a linked manner, you can use this example. The limitation is you cannot run this inside of an elaborate block like the example above.

It's best practice to name playbooks accordingly so you can determine a "linked playbook" like below from an "executive" playbook that actually calls the roles.

This would be useful when installing a set of extremely complex dependecies. You could imagine a playbook called host_billing_webserver.yml, here is a an elaborate example:

---
- name: install ldap auth for activedirectory
  import_playbook: linux_authentication.yml

- name: setup datadog, zabbix agent, newrelic (perhaps based on tags within?)
  import_playbook: linux_setup_monitoring.yml

- name: sets up nginx base install
  import_playbook: linux_nginx_base.yml

- name: sets up nginx proxy config for billing app python backend
  import_playbook: linux_nginx_proxy_billing_app.yml

- name: installs python billing app and supervisord or systemctl configs
  import_playbook: linux_python_billing_app.yml

- name: installs redis on local node for some form of caching
  import_playbook: linux_redis.yml

- name: configure resolv.conf with internal dns resolvers
  import_playbook: linux_resolv_conf.yml

- name: harden the system for PCI DSS compliance
  import_playbook: linux_pci_dss_hardening.yml

- name: configure the firewall for strict rules (inherited as host_vars)
  import_playbook: linux_firewalld.yml
Note

Ansible 2.3 and below requires this method of including other playbooks. This may work in newer versions but will raise a deprecated warning.

---
- include: linux_authentication.yml
- include: linux_setup_monitoring.yml
- include: linux_nginx_proxy_billing_app.yml
- include: linux_python_billing_app.yml
- include: linux_memcached.yml
- include: linux_resolv_conf.yml
- include: linux_pci_dss_hardening.yml
- include: linux_firewalld.yml

Appendex 1, Proposed Directory Structure per example

As your site complexity changes, your organization of playbooks might as well. Extending information into filenames and doing includes to other files becomes documentation from a file explorer level, which is really useful just as classes and modules are in programming languages. [java and .net developers know what i mean here]

***Scenario: You have a billing API, billing web frontend and a retail API and retail web frontend, this comes with the requirements as applications do. Add requirements for monitoring, repositories, databases, and handle different approaches by scale.

Good

Minimal verbosity in file structure.

roles/geeringuy.nginx/..
roles/geeringuy.redis/..
roles/geeringuy.rabbitmq/..
roles/setup_billing_api/..
roles/setup_billing_webapp/..
roles/setup_billing_datastores/..
roles/setup_retail_api/..
roles/setup_retail_webapp/..
roles/setup_retail_datastores/..
playbooks/host_billing_webserver.yml
playbooks/host_billing_api.yml
playbooks/host_billing_datastores.yml
playbooks/host_retail_webserver.yml
playbooks/host_retail_api.yml
playbooks/host_retail_datastores.yml

Better

Medium verbosity in file structure

roles/geeringuy.nginx/..
roles/geeringuy.redis/..
roles/geeringuy.rabbitmq/..
roles/setup_billing_api/..
roles/setup_billing_webapp/..
roles/setup_billing_datastores/..
roles/setup_retail_api/..
roles/setup_retail_webapp/..
roles/setup_retail_datastores/..
playbooks/host_webservers.yml
playbooks/host_apis.yml
playbooks/host_datastores.yml

Best

Super structured file layout

Extended organization layout for a complex scaled infrastructure enviroment.

roles/geeringuy.nginx/..
roles/geeringuy.redis/..
roles/geeringuy.rabbitmq/..
roles/geeringuy.mysql/..
roles/host_retail_api/..
roles/host_retail_webserver/..
roles/host_retail_redis/..
roles/host_retail_amqp/..
roles/host_retail_database/..
roles/linux_authentication/..
roles/linux_memcached/..
roles/linux_resolv_conf/..
roles/linux_pci_dss_hardening/..
roles/linux_resolv_conf/..
roles/host_billing_database/..
roles/linux_nginx_proxy_billing_app/..
roles/linux_python_billing_app/..
roles/host_billing_haproxy/..
roles/linux_firewalld/..
roles/linux_repo_epel/..
roles/linux_repo_ius/..
playbooks/host_retail_api.yml
playbooks/host_retail_webserver.yml
playbooks/host_retail_redis.yml
playbooks/host_retail_amqp.yml
playbooks/host_retail_database.yml
playbooks/host_billing_webserver.yml
playbooks/host_billing_api.yml
playbooks/host_billing_haproxy.yml
playbooks/host_billing_database.yml
playbooks/linux_authentication.yml
playbooks/linux_setup_monitoring.yml
playbooks/linux_nginx_proxy_billing_app.yml
playbooks/linux_python_billing_app.yml
playbooks/linux_memcached.yml
playbooks/linux_resolv_conf.yml
playbooks/linux_pci_dss_hardening.yml
playbooks/linux_firewalld.yml
playbooks/linux_epel.yml
playbooks/linux_repo_epel.yml
playbooks/linux_repo_ius.yml

Ansible Role Definition Best Practices

  • how to form roles structure (templates, defaults, tasks, handlers, files, templates)
  • how to construct role tasks, when to include additional files
  • how to detect between OS versions or distributions
  • how to check for conditions and fail in main.yml
  • how to include variables but you should use role defaults or inventory variables when possible
  • how to define tags (single, multiple) for sections in main.yml
  • how to name variables for standardiation
  • how to add and trigger handlers
  • how to reference jinja templates

Static Inventory Best Practices

  • how to create long structure with group_vars, host_vars (why this is good idea) (for instance mkdir -p example, then explain)
  • how to create simple host structure single file (how to inject extra vars) (notes about dns)
  • how to create children groups with multiple hosts
  • how to add group vars
  • how to add host vars
  • how to call ansible-playbook example

Ansible Execution Shortcut Snippets

-u user, -b --become-user=root --become-method=sudo -K

-u user, -b --become-user=root --become-method=su -K

-e EXTRA_VARS, --extra-vars=EXTRA_VARS

--list-tags, --list-hosts, --list-tasks

-i INVENTORY, --inventory=

--flush-cache

-D, --diff

-f FORKS, --forks=FORKS

  • getting around host key errors (ansible.cfg)

Mastering Ansible Jinja2 Templates by Example (Copy PAsta)

Build custom lookup function in python

Todo

General jinja foo

  • Extra reading

** https://docs.ansible.com/ansible/2.6/user_guide/playbooks_templating.html

{% for id in range(201,221) %}  
192.168.0.{{ id }} client{{ "%02d"|format(id-200) }}.vpn  
{% endfor %}

Add a list of all nodes to a comme delimited list. nodes=(list)

{% for node in groups['nodes'] %}nodes={{ node }}:5672{% if not loop.last %},{% endif %}{% endfor %}

Use python string functions like join

{{ groups["nodes"] | join(",") }}

Use python string functions lower

{{ groups["nodes"] | lower }}
{{ groups["nodes"] | upper }}
{{ groups["nodes"].lower() }}
{{ groups["nodes"].upper() }}
{% filter upper %}
uppercase
{% endfilter %}

Convert items to yaml/json

{{ some_variable | to_json }}
{{ some_variable | to_yaml }}

for for indentions

{{ some_variable | to_nice_json }}
{{ some_variable | to_nice_yaml }}

for importing

{{ some_variable | from_json }}
{{ some_variable | from_yaml }}

Test ip addressses

{{ myvar | ipv4 }}
{{ myvar | ipv6 }}
{{ myvar | ipaddress }}

Hashes

{{ 'test1'|hash('sha1') }}


{{ some_variable | default(5) }}
{{ 'test2'|checksum }}

{{ 'passwordsaresecret'|password_hash('sha512') }} {{ 'secretpassword'|password_hash('sha256', 'mysecretsalt') }} {{ 'secretpassword'|password_hash('sha512', 65534|random(seed=inventory_hostname)|string) }}

Get directory from path

{{ path | dirname }}
{{ path | win_dirname }}
{{ path | expanduser }} (To expand a path containing a tilde (~) character (new in version 1.5):)

Base64 strings

{{ encoded | b64decode }}
{{ decoded | b64encode }}
example
tasks:
  - shell: cat /some/path/to/file.json
    register: result

  - set_fact:
      myvar: "{{ result.stdout | from_json }}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment