Ansible - kamialie/knowledge_corner GitHub Wiki
ansible-pull
gets the playbook from VCS and runs on the local system. Can be
implemented as decentralized management, where each "self-managed" node runs
the command on schedule.
$ ansible HOST_PATTERN -m MODULE [-a LIST_OF_ARGUMENTS] [-i INVENTORY]
$ ansible all -m ping
Some modules support --diff
and --check
flags. --diff
flags also shows the
differences being made in git diff
format. --check
runs the command in dry
mode, thus showing what will happen, if you actually run the command. Two flags
together would output complete info on changes that will apply.
Check connection to hosts:
$ ansible all -m ping -i inventory
Facts about host:
$ ansible -m setup localhost
File:
- copy - copy local file to remote
- file - set permissions and other properties
- lineinfile - ensure particular line is present or not
- synchonize - synchonize content using sync
System:
- firewalld - manage arbitrary ports and services
- reboot
- service - manage services
- user - add, remote, manage users
Net tools:
- get_url - download file over HTTP(S), FTP
- nmcli - manage networking
- uri - interact with web services and communicate with API
Command:
- command - runs commands directly
- shell - runs commands in shell
- raw - bypasses module subsystem (does not require Python installation on remote); use case - no Python, network devices
Debug module is useful for debugging variables and expressions. msg
argument
print cutomized message (can contain variables). var
argument is used to
print variable (no need to enclose in {{ }}). Mutually exclusive with
msg
. verbosity
argument is used to set on which verbocity level to run this
module. F.e. if set to 3, then will run, if playbook was executed with -vvv
parameter.
Assert module checks that given expressions are true with an optional custom
message. that
is a list of string conditions (same as when
argument for the
task). quite
boolean argument to avoid verbose output. success_msg
and
fail_msg
set custom message to output depending on result.
- assert:
that:
- my_param <= 10
- my_param > 1
fail_msg: "should be between 1 and 10"
success_msg: "my_param is valid"
Fail module forces playbook to fail. Used for testing conditionals. msg
argument is used to output message.
The order of places where Ansible looks for its configuration file:
-
ANSIBLE_CONFIG
environment variable, if set, is the path to configuration file -
./ansible.cfg
- in Ansible command's current working directory ~/.ansible.cfg
-
/etc/ansible/ansible.cfg
- default Ansible configuration file
Use ansible --version
or ansible-config --version
commands to see which
file is being used.
Two basic section: defaults
that sets defaults for Ansible operations and
privilege_escalation
, which configures how Ansible performs privilege
escalation.
Default settings are inventory file path, remote user to use, whether to ask password, etc.
Settings to control privilege escalation:
-
become
- control whether to use escalation automatically (default is no, can be overridden at various levels, play, task...) -
become_user
- what user to become (default is root) -
become_method
-sudo
by default, can usesu
and others -
become_ask_pass
- whether to ask for password (default is no)
Output variables that are changed with custom config file:
$ ansible-config dump --only-changed
Simple setup, file entries in host_vars
directory must match the host name in
inventory; var files are in yaml format with just key-value pairs:
project
|-- ansible.cfg
|-- host_vars
| |-- s1.example.com
| |-- s2.example.com
--- inventory
s1.example.com
# Comment specifying intention
---
var: value
# Run the playbook
$ ansible-playbook playbook.yml
# Syntax check
$ ansible-playbook --syntax-check playbook.yml
# Dry run
$ ansible-playbook --check playbook.yml
Add more verbose output with --verbose, -v
flag, repeat for more info.
While running plays on local machine add connection: local
to speed up the
process, as it avoids establishing regular remote connection.
To specify which tasks to run add tags block either on tasks or plays level.
--skip-tags
and --tags
command line options are available to specify desired
behavior.
It is also possible to specify which task to start from with --start-at-task
option, which takes entire task name (including white spaces) as a parameter.
Some modules support dry run; pass --check
or -C
option to see what Ansible
would do on normal run. Modules that do not support dry runs, may produce
unexpected results on dry-run. Set check_mode
to no
on those tasks to
disable check mode. Setting check_mode
to yes
would run task in dry-run
mode in normal run of playbook.
set_fact
module is used to create dynamic variables.
While using non-idempotent modules, like shell module, is it useful to register
the state to make later decisions. Fe, grep module can be used to capture
desired output and register its return status (need to set ignore_errors: true
not to stop at failed grep) that later modules can use.
Ansible facts are special variables that contain unique info on the host. By default, they are set my implied task at the beginning of a play - gathering facts. Can also collect facts at any time using setup. Facts are set as dictionary in ansible_facts variable.
Output info available on hosts:
$ ansible all -m debug -i inventory -a "var=hostvars['server1']"
Loop over list of items using item, loop keywords.
- name: Play
hosts: all
vars:
new_users:
- one
- two
- three
tasks:
- name: create users
user:
name: "{{ item }}"
state: present
loop: "{{ new_users }}"
It is also possible looping over list of dictionaries, using item and with_dict keywords.
- name: Play
hosts: all
tasks:
- name: create users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
with_dict:
- { name: "one", groups: "a" }
- { name: "two", groups: "a" }
- { name: "three", groups: "b" }
- { name: "four", groups: "b" }
Use conditionals to run or skip tasks. Can user both variables and facts.
Operators (f.e. >
, <
) to compare strings, numerical data, or Boolean are
available.
ansible_facts is a dictionary of facts about a host (gathered by gather facts). ansible_mounts fact is a list of dictionaries, each representing facts about one mounted file system. group_names is a list of all group names ansible is aware of.
Operation | Example |
---|---|
equal (string) | var == "string" |
equal (numeric) | var == 23 |
comparison | var > 23, var <= 10, var != 5 |
variable exists | var is defined |
variable does not exist | var is not defined |
Boolean is true (1, True, or yes) | var |
Boolean is false (0, False, no) | not var |
variable is present in an array | var in list |
or
and and
can be used to evaluate multiple conditions. when also
supports list of conditions, which is interpreted as and
that is all
conditions must be true.
# runs if run_task is true
when: run_task
# runs if run_task is present (use not defined for
when: run_task is defined
Blocks are clauses that logically group tasks as a unit.
- name: some play
hosts: all
tasks:
- name: some block
block:
- name: first task
module:
arg: value
- name: second task
module:
arg: value
when: var is defined
A block can have a set of tasks grouped in a rescue statement. Those tasks run only if a task in a block has failed. always statement groups tasks that always run (but are subject to conditional on the block).
tasks:
- name: some block
block:
- name: first task
module:
arg: value
- name: second task
module:
arg: value
rescue:
- name: third task
module:
arg: value
always:
- name: fourth task
module:
arg: value
Scope
- global - values set for all hosts; f.e. variables set in the job template
- host - value set for host or group; f.e variables set in the inventory or
host_vars
directory, gathered facts - play - values set in the context of a play; vars directive, include_vars tasks, and so on
Narrow scope overwrites wider scope. Variables defined in playbook are overwritten by variables defined on command line, with -e option.
Defining variables in a playbook
- vars top directive of a play
- vars_files top directive of a play - load variables from a list of files
Host and group variables can be defined in host_vars or group_vars sub-directories (in the same directory as inventory) that contain yaml files matching name of host or a group. Those have higher precedence that the ones defined in the inventory. Example:
ansible_user: devops
newfiles:
- /tmp/a.conf
- /tmp/b.conf
To access child values of a dictionary use square brackets and single quotes.
---
- name: Create user
hosts: all
become: true
vars:
username:
test:
uname: test
tasks:
- name: user task
user:
name: "{{ username['test']['uname'] }}"
state: present
register captures the output of a task and stores it in a variable for later use in the playbook.
inventory_hostname special variable.
Handlers are tasks that respond to a notification triggered by other tasks. Tasks notify handlers only when they change something on the system. Each handler has a unique (per play namespace) name and is triggered at the end of a block of tasks in a playbook (if more than one task notified a handler, it will still run only once). One task can notify multiple handlers. Handler run in the order they are defined in the file.
tasks:
- name: copy config file
template:
src: /some/path/file.template
dest: /other/path/file
notify:
- restart apache
handlers:
- name: restart apache
service:
name: httpd
state: restarted
{# COMMENT #}
syntax is used to add comments - those will not appear in the
final file.
for loop example:
{% for host in groups['all'] %}
{{ hostvars[host]['ansible_facts']['default_ipv4']['address'] }} {{ host_vars[host]['ansible_facts']['fqdn'] }}
{% endfor %}
if example:
{# Only included if finished is True #}
{% if finished %}
{{ result }}
{% endif %}
Lookup plugin is an Ansible extension of the Jinja2 templating language. They import and format data from external sources for use in variables and templates. Allows to use contents of a file as a value for a variable. Also allows to look up info from other sources and put them in template:
# List available plugins
$ ansible-doc -t lookup -l
# Info on file lookup plugin
$ ansible-doc -t lookup file
Lookup call:
-
lookup
- returns a string of comma separated items -
query
- returns yaml list of items
- name: Play
host: all
vars:
mxvar: "{{ query('dig', 'gmail.com', 'gtype=MX') }}"
tasks:
- name: List entries
dbg:
msg: MX record is: {{ item }}
loop: "{{ mxvar }}"
- name: Print the names of each account in /etc/passwd
dbg:
msg: A user is {{ item | regex_replace(':.*$') }}
loop: "{{ query('lines', 'cat /etc/passwd') }}"
# Initialize role by creating directory with prepared layout
$ ansible-galaxy init ROLE_NAME
role_name
|-- defaults
| |-- main.yml
|-- files
|-- handlers
| |-- main.yml
|-- meta
|-- README.md
|-- tasks
| |-- main.yml
|-- templates
|-- tests
| |-- intentory
| |-- test.yml
|-- vars
| |-- main.yml
Directory | Functions |
---|---|
defaults | contains default values for variables that are most likely to be overwritten by the user of the role |
files | static files referenced by role tasks |
handlers | handler definitions |
meta | program readable file with meta data about the role: author, description, licence, galaxy tags (so other ppl can find it), dependency list, etc |
tasks | where individual tasks go in the list form; main.yml must be present, while it can include other files too |
templates | Jinja2 templates referenced by tasks |
tests | contains inventory and test.yml to test the role |
vars | defines variables to be used internally by the role, have high precedence; not intended to be changed by the user |
Ansible roles are shared via Github repository, simply create and commit changes there. To import a role login to Ansible Galaxy via Github credentials and run the following command:
$ ansible-galaxy login
$ ansible-galaxy import USER_NAME REPO_NAME
Download Ansible role from Ansible Galaxy (default installation path is galaxy
global directory, ~/.ansible/roles
; use -p
option to specify custom path):
$ ansible-galaxy install USER_NAME.REPO_NAME
requirements.yml
file can be used to specify required roles for the playbook.
Roles are added in a list form.
# name is used to override the local name of the role
- src: geerlingguy.redis
version: "1.16.0"
name: new_name
# from Git repo specifying the version
# version can be branch or commit hash
- src: https:/some-site/repo.git
scm: git
version: master
name: new_name
Create necessary directory hierarchy:
$ sudo mkdir -p /etc/ansible/roles
Create Ansible configuration file, /etc/ansible/ansible.cfg
(looks like
Ansible is checking if it exists?):
[defaults]
roles_path=/etc/ansible/roles
Now can put full directory name, [USER].[REPO_NAME]
in playbook to reference
global roles directory contents.
Enables used to check and set values of variables, update module arguments, and re-run tasks.
Enable debugging with one of the following ways:
-
ansible.cfg
[defaults] enable_task_debugger = True
-
environment variable
ANSIBLE_ENABLE_TASK_DEBUGGER=True
Any failed of unreachable task will trigger the debugger interaction by
default. debugger
keyward can alter that behaviour and be used in any block
that utilises name
keyward, suck as play, role, task, or block. More specific
(task vs play level) setting takes precedence, f.e. if debugger is set to
never on play level and to on_failed on task level, it will trigger, if
that task fails.
- always
- never
- on_failed
- on_unreachable
- on_skipped
Debugger commands.
Command | Description |
---|---|
p(rint) task/task_vars/host/result | print values used to execute module |
task.args[key] = value | update module's variable |
task_vars[key] = value | update vars used in task |
u(date_task) | recreate the task from original structure with updated task_vars |
r(edo) | re-run the task |
c(ontinue) | continue the playbook execution |
q(uit) | quit debugger, playbook execution is aborted |
$ pip install ansible-lint
.ansible-lint
is the local configuration file. Can be located elsewhere and
passed with -c path/to/file
parameter. Accepts either list of playbook files or
role directories.
Configuration section | Description |
---|---|
exclude_paths | path to dirs and files to skip |
parseable | output in the pip8 format |
quite | less output |
rulesdir | specify one or more rules dirs; overrides default rules, unless use_default_rules is also set |
skip_list | only check rules whose id/tags do not match these values |
tags | only check rules whose id/tags match these values |
use_default_rules | use default rules at /path/to/ansible-lint/lib/ansiblelint/rules/ in addition to any extra rules dirs set by rulesdirs
|
verbosity | set verbosity level |
Custom rule dir can also be passed in command line with -r path/to/rule/dir
parameter. -R
parameter enables the use_default_rules section. -x tag_one, tag_two
parameter works as skip_list section. ansible-lint -T
-
view all available tags and ids. To skip the particular default check, pass the
id (number in brackets) of the rule to -x
or skip_list in the
configuration.
exclude_paths:
- ./path/one/
- ./path/two/
parseable: true
quite: true
rulesdir:
- ./rule/dir/
skip_list:
- skip_this_tag
- skip_this_id
tags:
- run_this_tag
use_default_rules: true
verbose: 1
Ansible configuration file may contain the path to inventory (default is
etc/anisble/hosts
- contains examples):
[defaults]
inventory = ./inventory
Verify inventory:
# -i inventory to specify an inventory file, if not defined
$ ansible-inventory
$ ansible-inventory -y --list # display inventory in yaml format
Check if host is contained in inventory file:
$ ansible some.example.com --list-hosts
It's easy to organize hosts into groups in ini
file; one host can be in
multiple groups; hosts can be FGDN or IP address; group names shouldn't
contain dashes (underscores are fine); groups can be nested using :children
suffix:
[web_servers]
web1.example.com
192.168.1.1
[databases]
data1.example.com
data2.example.com
[prod]
data1.example.com
192.168.1.1
[vms:children]
databases
Two special groups that always exist:
-
all
- all defined hosts -
ungrouped
- hosts that are not member of another group
It is possible to specify range of hosts, [START:END]
both included:
- 192.168.[4:7].[0:255] - 192.168.4.0 - 192.168.7.255 (192.168.4.0/22)
- server[01:20].example.com - server01.example.com - server20.example.com
- [a:c].dns.example.com - a.dns.example.com, b.dns.example.com, c.dns.example.com
Multiple inventory files can be passed to Ansible by pointing ansible.cgf
or
command line argument to a directory containing all the files.
Small executable program that can build inventory. Must have execute permissions. Can be written in any language that can provide inventory output in json format.
Developing dynamic inventory guide
-
list declared hosts:
$ ansible-inventory --list
-
output in graph mode with groups and connections:
$ ansible-inventory --graph
-
include variables as well:
$ ansible-inventory --graph --vars
-
given ini file outputs inventory in json format (as example of what dynamic script should generate)
$ ansible-inventory -i INI_INVENTORY_FILE --list
The idea is to separate variables' name and value into different files, f.e.
vars
and vault
. Vars will state the name of the variable and value will
hold the value of the variable in vault
file, which will be encrypted later.
vars
:
password: "{{ vault_password }}"
vault
:
vault_password: actual_password_value
Suppress the output of the task (sensitive data):
- name: some task
debug:
msg: "{{ secret }}"
no_log: true
# New file
$ ansible-vault create FILE_NAME # create secret file
$ ansible-vault view FILE_NAME # view encrypted file
$ ansible-vault edit FILE_NAME # edit encrypted file in place
# Existing file
$ ansible-vault encrypt FILE_NAME [--output=NEW_FILE_NAME] # encrypt existing file
$ ansible-vault decrypt FILE_NAME # encrypt existing file
# Change password
$ ansible-vault rekey FILE_NAME
# Supply password to playbook, ansible will prompt for password
$ ansible-playbook --vault-id @prompt playbook.yml
# Set labels on each prompt for multiple passwords
$ ansible-playbook --vault-id vars@prompt --vault-id playbook@prompt playbook.yaml
REPL tool for Ansible that can be used to run individual commands and modules. Tab completion is on my default (module names, parameter list, etc).
Can target single host or group of hosts:
$ ansible-console HOST
List contents of the available option of the plugin:
$ ansible-doc --type <plugin> --list
$ ansible-doc -t connections -l
Info on module usage:
$ ansible-doc <module>
$ ansible-doc git_config