A collection of notes on Ansible best practices…
Ansible playbooks are first parsed as YAML and then processed. This is important to keep in mind as errors may occur both during YAML parsing or during playbook execution.
Block quotes help avoid long lines.
The >-
operator converts line breaks to spaces.
The |-
operator keeps newlines.
# Need a better example... this should use lineinfile...
- name: Add envirnoment variables
blockinfile:
block: |-
MYAPP_DOMAIN=example.com
MYAPP_DIR=/opt/myapp
- name: Run Installer Script
command: >-
installer.sh
--config=example/config/path
--domain=app23.example.com
--enable-advanced-features
--admin-password={{ admin_password | quote }}
args:
creates: /etc/example/install.log
- name: Deployment for MyApp
k8s:
state: present
definition: "{{ myapp_deployment }}"
when: >-
myapp_install_k8s|bool or
myapp_install_openshift|bool
Levels of code quality:
-
Ansible code works once, cannot re-run
-
Check if code has run, avoids reconfiguring
-
Checks can reset, reconfigure
-
Reconfiguration properly indicates changes
Ansible may be pointed to an inventory directory and will load all inventories found within the inventory directory.
Hosts should normally be grouped to specify the purpose assigned to each host. Even if there is only one host in a group, use of groups still provides clarity.
This provides a simple way to separate environment specific variables from the common variables.
Separate environments by inventory.
Using group_vars
and host_vars
in a playbook requires strict rules of ansible group naming which are best avoided.
It is better practice to have variables for host and group names when a playbook needs different handling depending on the host or host group.
If sets of variables need to be set based on group, try using include_vars
instead using a check on the hosts groups (group_names
special variable).
- name: Include Database Host Vars when: myplaybook_database_group_name in group_names include_vars: dir: vars/database/
Start structuring your playbooks around use of Ansible roles early in your development process. Role directory structure provides a standard for tasks, files, templates, modules, etc.
Do not ignore_errors
.
Using ignore_errors
still reports an error and is often confusing.
Use failed_when
instead.
Using failed_when
allows you to give your own error conditions.
Using fail
:
- name: Check for valid url fail: msg: Invalid url: {{ myapp_url }} when: >- not myapp_url.startswith('https://') or not myapp_url is search('/myapp/')
Using assert
:
- name: Assert valid url assert: - myapp_url.startswith('https://') or - myapp_url is search('/myapp/')
-
Use sparingly
-
Use
quote
filter with variables -
Calling shell scripts from command?
-
Template shell script and copy to host
-
-
Don’t call
ansible
oransible-playbook
fromcommand
-
Use args:
-
chdir
Set working directory for command -
creates
Only run if file does not exist -
removes
Only run if file exists -
warn
Use to disable warnings when needed
-
Set vars on include_tasks
or include_role
instead.
Ansible provides a number of filters in addition to the core jinja2 filters.
The json_query
filter uses the powerful JMESPath query language:
Custom modules are faster than running multiple tasks and are often easier.
Python programming for modules is not as hard as you may think. To start,
you can add a library
folder to your module, then create your python code.
Should follow this document.
The name of the python file, can then be utilized within your ansible-playbook.
Ansible lets you use jinja2 templating anywhere. Ansible Jinja2 Templating.