This repository is a summary of Ansible, check the official documentation on the links below.
Web site: https://www.ansible.com/
Documentation: https://docs.ansible.com/
GitHub: https://github.com/ansible/ansible
- Introduction
- Ansible concepts
- How to install on Ubuntu 24
3.1. Creating configuration file
3.2. Creating inventory (YAML) - Commands
- Inventory
5.1. Adding Hosts
5.2. Adding Groups
5.3. Dynamic Inventory - Introducing Playbooks
6.1. Runnig playbook locally (localhost) - Variables
7.1. Variable types
7.2. Variables in the Playbook file (.yml)
7.3. External Variables file (.yml)
7.4. Inventory Variables
7.5. group_vars & host_vars
7.6. Special Variables
7.7. Ansible Facts
7.8. Registering variables
7.9. Variables precedence - Playbooks
8.1. Keywords
8.2. Tasks
8.2.1. Conditionals (when)
8.2.2. Loops
8.2.2.1. Loop
8.2.2.2. Loop control
8.3. Blocks
8.3.1. Handling tasks failures withrescue
8.3.2.alwayssection
8.4. Handlers
8.5. Importing a playbook
8.6. Importing tasks from another file
8.7. Tags - Modules
9.1. Executing modules from the command line
9.2. Executing modules from playbooks - Templates
10.1. Variable Expressions
10.2. Conditionals
10.3. Loops - Plugins
11.1. Filter - Roles
12.1. Creating a role (task)
12.2. Using roles
12.2.1. Play level
12.2.2. Task level
12.2.3. Running specific task files - Vault
13.1. Vault Password
13.2. Variable-level encryption
13.2.1. Encrypting variables
13.2.2. Viewing encrypted variables
13.3. File-level encryption
13.3.1. Encrypting files
13.3.2. Decrypting files
13.3.3. Rotating password
13.4. Vault ID - Multiple passwords - Collection
- Developing Modules
15.1. Verifying your module locally
15.1.1. Using Ansible adhoc command
15.1.2. Using the module in a Playbook
15.1.3. Using Python - Developing Collections
- Developing Plugins
- Env Vars
- Troubleshooting
Ansible is an open source IT automation engine that automates provisioning, configuration management, application deployment, orchestration, and many other IT processes.
In brief, Ansible connects to remote hosts via SSH to execute commands and Python scripts previously sent by SCP.
- Control node (controller) The machine from which you run the Ansible Commands. Ansible needs to be installed only on this machine.
- Managed nodes (hosts) Target devices you aim to manage with Ansible.
- Inventory List of hosts and groups.
- Playbook A collection of plays. A file coded in YAML.
- Play Run tasks on a host or a collection of hosts.
- Tasks Call functions defined as Ansible Modules (coded in Python)
- Roles A reusable Ansible content (tasks, variables, ...) for user inside a Play.
- Handlers Handlers are tasks that only run when notified (when the task returns a ‘changed=True’ status).
- Play Run tasks on a host or a collection of hosts.
- Variables Variables store and retrieve values that can be referenced in playbooks, roles, templates and other Ansible components.
- Templates Templates allow you to create new files on the nodes using predefined models based on the Jinja2 templating system.
- Vault Ansible Vault is a feature of ansible that allows you to keep sensitive data such as passwords or keys in encrypted files.
- Modules Usually a Python script sent to each host (when needed) to accomplish the action in each Task.
- Plugins Expands the Ansible's core capactibilities.
- Action Plugins let you integrate local processing and local data with module functionality
- Cache Plugins store gathered facts and data retrieved by inventory plugins.
- Callback Plugins add new behaviors to Ansible when responding to events.
- Connection Plugins allow Ansible to connect to target hosts so it can execute tasks on them.
- Filter Plugins manipulate data.
- Inventory Plugins parse inventory sources and form an in-memory representation of the inventory.
- Lookup Plugins pull in data from external data stores.
- Test Plugins verify data.
- Vars Plugins inject additional variable data into Ansible runs that did not come from an inventory source, playbook, or command line.
- Action Plugins let you integrate local processing and local data with module functionality
- Collections A format in which Ansible content is distributed that can contain playbooks, roles, modules, and plugins. You can install and use collections through Ansible Galaxy.
Installing in isolated environments with pipx (Recommended)
sudo apt update
sudo apt install pipx -y
pipx install --include-deps ansible
pipx ensurepath
source ~/.bashrc
# Updating
pipx upgrade --include-injected ansible
# Installing aditional python modules
pipx runpip ansible -- install <module>Installing with pip3
sudo apt update
sudo apt install python3-pip -y
# Choose only one of the two lines below, choosing Global or Local installation
sudo pip3 install ansible # Global
pip3 install --user ansible # Local
echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
source ~/.bashrcsudo mkdir -p /etc/ansible
sudo chown $USER:$USER /etc/ansible
ansible-config init --disabled -t all > /etc/ansible/ansible.cfgtouch /etc/ansible/hostsExample for localhost and one remote host called device1.
all:
hosts:
localhost:
ansible_connection: local
device1:
ansible_host: "192.168.0.10"
ansible_ssh_user: "leo"
ansible_ssh_private_key_file: "/home/leo/.ssh/id_ed25519"
vars:
ansible_python_interpreter: "/usr/bin/python3"Version
ansible --versionShow inventory
ansible-inventory --list
ansible-inventory --graph
ansible-inventory --graph <group_name>
ansible-inventory --host <host>Run Playbook
# General
ansible-playbook myplaybook.yml # run the playbook for all hosts defined inside the playbook
ansible-playbook p1.yml p2.yml # running multiple playbooks
ansible-playbook myplaybook.yml -l localhost,device1 # (--limit) limit selected hosts (comma separated)
ansible-playbook myplaybook.yml -i /tmp/inventory.yml # (--inventory) specify the inventory (comma separated)
ansible-playbook myplaybook.yml -vvv # verbose mode (-v, -vvv, -vvvv)
ansible-playbook myplaybook.yml --check # Check mode is just a simulation
# Extra variables at runtime (variables defined in the command line with -e take precedence over other variable definitions and remain immutable throughout the playbook execution)
ansible-playbook myplaybook.yml -e username=leo # (--extra_vars) set additional variables as key=value or YAML/JSON
ansible-playbook myplaybook.yml -e "username=leo password=*****" # Multiples key=value
ansible-playbook myplaybook.yml -e '{"username":"leo"}' # inline JSON
ansible-playbook myplaybook.yml -e @/var/external_vars.yml # if filename prepend with @
# Tags and Tasks
ansible-playbook myplaybook.yml --list-tags # list all available tags
ansible-playbook myplaybook.yml --list-tasks # list all tasks that would be executed
ansible-playbook myplaybook.yml --tags # (-t) only run plays and tasks tagged with these values
ansible-playbook myplaybook.yml --skip-tags # only run plays and tasks whose tags do not match these values
# Vault pass
ansible-playbook myplaybook.yml --ask-vault-pass # ask for vault password
ansible-playbook myplaybook.yml --vault-password-file pass_file # vault password fileDoc
ansible-doc shell # shows documentation of the shell module
ansible-doc -t callback -l # shows callback pluginsRun Module
ansible localhost -m ping # connection test
ansible localhost -m setup --tree facts.d/ # write facts to file
ansible webservers -m command -a "/sbin/reboot -t now"
ansible webservers -m service -a "name=httpd state=started"Installing collections with Ansible Galaxy
ansible-galaxy collection init my_namespace.my_collection # Create collection with a template
ansible-galaxy collection build path/to/my_namespace/my_collection # Build collection
ansible-galaxy collection install path/to/my_namespace/my_collection # Install collection
ansible-galaxy collection install path/to/my_namespace-my_collection-1.0.0.tar.gz # Install builded collection
ansible-galaxy collection install ansible.utils # Install the collection ansible.utils
ansible-galaxy collection list # List installed collectionsVault
ansible-vault create myfile.yml # Create new vault encrypted file
ansible-vault decrypt myfile.yml # Decrypt vault encrypted file
ansible-vault edit myfile.yml # Edit vault encrypted file
ansible-vault view myfile.yml # View vault encrypted file
ansible-vault encrypt myfile.yml # Encrypt YAML file
ansible-vault encrypt_string 'value' --name 'key' # Encrypt a string
ansible-vault rekey myfile.yml # Re-key a vault encrypted file
# Arguments
--vault-password-file
--ask-vault-pass
--vault-idThe default location for this file is /etc/ansible/hosts. You can specify a different inventory file at the command line using the -i <path> option or in the ansible.cfg file updating the entry inventory=.
The most common inventory file formats are INI and YAML (preffered).
Ansible has two special groups, all and ungrouped. The all group contains every host. The ungrouped group contains all hosts that don't have another group aside from all.
In the inventory file add the host name to a group in the hosts: session, eding with :.
all:
hosts:
device1: # host name
device2: # host nameIn the inventory file add the group name ending with :.
my_group1: # group name
hosts: # hosts of the groupAdd to this group the session hosts: to specify hosts and the session children: to specify other groups as child of this group.
my_group3: # group name
hosts: # hosts of the group
children: # groups of the group
my_group1:
my_group2:The inventory can be defined in runtime, in a task with the module add_host
- A playbook runs in order from top to bottom, by default, each taks one at a time, against all machines of hosts in parallel. After executed on all target machines, Ansible moves on to the next task. If a task fail on a host, Ansible takes that host out of the rotation for the rest of the playbook.
Sample Playbook file (myplaybook.yml)
- name: Sample Playbook
hosts: all
gather_facts: false # Disable facts (optional)
tasks:
- name: Display a debug message
debug:
msg: "Hello Admin"Running the playbook on device1 (if device1 is in the inventory)
ansible-playbook myplaybook.yml -l device1output
PLAY [Sample PLaybook] ***********************************************************************************************************************************************
TASK [Display a debug message] ***************************************************************************************************************************************
ok: [device1] => {
"msg": "Welcome Admin"
}
PLAY RECAP ***********************************************************************************************************************************************************
device1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Set the connection plugin to local (prefered) or exchange the SSH key locally. (cat "${HOME}/.ssh/id_ed25519.pub" >> "${HOME}/.ssh/authorized_keys")
Ansible uses Jinja2 to access variables dynamically using {{ variable_name }}.
...
- name: Display a debug message
debug:
msg: "Hello {{ username }}"Simple variable
base_path: '/etc/config'Referencing a simple variable
app_path: "{{ base_path }}"Multiple lines string
message: >
Your long
string here.message: |
this is a very
long string>,|: "clip" keep the line feed, remove the trailing blank lines.>-,|-: "strip": remove the line feed, remove the trailing blank lines.>+,|+: "keep": keep the line feed, keep trailing blank lines.
List
region:
- northeast
- southeast
- midwestReferencing list variables
region: "{{ region[0] }}"Dictionary
foo:
field1: one
field2: twoReferencing key:value dictionary variables
field: "{{ foo['field1'] }}"
field: "{{ foo.field1 }}"- name: Sample Playbook
hosts: all
vars:
username: 'leo'
password: '*****'
vars_files:
- /vars/external_vars.yml
tasks:
- name: Display a debug message
vars:
username: 'leo'username: 'leo'
password: '*****'mygroup:
hosts:
device1:
username: "leo" # host variable
vars:
dummy: "superserver" # group variable
vars_file: group_vars/webservers_vars.ymlgroup_vars/host_vars variables files are automatically loaded when running a playbook.
group_vars
Create the dir /etc/ansible/group_vars.
/etc/ansible/group_vars/mygroup # can optionally end in '.yml', '.yaml', or '.json'host_vars
Create the dir /etc/ansible/host_vars.
/etc/ansible/host_vars/localhost # Variables file of localhostMultiple files for a host/group
Create directories named instead of a file. Ansible will read all the files in these directories.
/etc/ansible/group_vars/mygroup/network_settings
/etc/ansible/group_vars/mygroup/cluster_settingsansible_host: "192.168.0.10"
ansible_ssh_user: "leo"
ansible_ssh_pass: "******"
ansible_ssh_private_key_file: "/home/leo/.ssh/id_ed25519"
ansible_python_interpreter: "/usr/bin/python3"ansible_verbosityAnsible facts are data related to your remote systems. By default, Ansible gathers facts at the beginning of each play.
- name: Print all available facts
debug:
var: ansible_factsDisabling facts
- name: Sample Playbook
hosts: all
gather_facts: false # Disable facts (optional)A variable can be created from the Task output with the keyword register.
- hosts: all
tasks:
- name: Runs a shell command registering the output to a variable
shell: whoami
register: result
- name: Reads the variable
debug:
msg: "Output: {{ result.stdout }}, return code: {{ result.rc }}"The order of precedence from least to greatest (the last listed variables override all other variables):
- command line values (for example, -u my_user, these are not variables)
- role defaults (as defined in Role directory structure) 1
- inventory file or script group vars 2
- inventory group_vars/all 3
- playbook group_vars/all 3
- inventory group_vars/* 3
- playbook group_vars/* 3
- inventory file or script host vars 2
- inventory host_vars/* 3
- playbook host_vars/* 3
- host facts / cached set_facts 4
- play vars
- play vars_prompt
- play vars_files
- role vars (as defined in Role directory structure)
- block vars (only for tasks in block)
- task vars (only for the task)
- include_vars
- set_facts / registered vars
- role (and include_role) params
- include params
- extra vars (for example, -e "user=my_user")(always win precedence)
- Play
- Role
- Block
- Task
- name: Sample # Identifier. Can be used for documentation, or in tasks/handlers.
hosts: <pattern> # Common patterns (they can be combined)
# all - All hosts
# host1 - One host
# host1:host2 (host1,host2) - Multiple hosts/groups
# all:!host1 - All hosts/groups except one
# group1:&group2 - Any hosts in the group1 that are also in the group2
gather_facts: false # Disable facts to improve performance (default true)
connection: <plugin> # Change the connection plugin. Lists available plugins with `ansible-doc -t connection -l`.
# ssh - connect via ssh client binary (default)
# local - execute on controller
collections: # Using a collection.
- my_namespace.my_collection
strategy: free # Strategy of task execution, linear or free
become: yes # Ansible allows you to ‘become’ another user, different from the user that logged into the machine (remote user).
# This is done using existing privilege escalation tools such as sudo, su, pfexec, doas, pbrun, dzdo, ksu, runas, machinectl and others.
become_method: su
become_user: nobody # default root
become_pass:
become_flags: "-i" # loads the user's login shell as if the user logged in interactively. It loads the environment variables from the user's profile, such as /etc/profile, ~/.bash_profile, or ~/.bashrc, depending on the shell configuration. Ansible uses non-interactive sessions.
become_flags: '-s /bin/sh'
service: # Controls services on remote hosts. Ensure the httpd service is running
name: httpd
state: started
timeout: # Time limit for the task to execute in, if exceeded Ansible will interrupt and fail the task.
vars: # Dictionary/map of variables
username: 'leo'
vars_files: # List of files that contain vars to include in the play.
- /vars/external_vars.yml
vars_prompt: # list of variables to prompt for
- name: username # variable name
prompt: What is your username? # prompt message
private: false # makes the user input visible (hidden by default)
default: "1.0" # default value
confirm: true # request confirmation
encrypt: sha512_crypt # encript. (use private = true)
unsafe: true # allow special chars
salt_size: 7
check_mode: <boolean> # If you want certain tasks to run in check mode always, or never.
# true - this task will never make changes to the system
# false - this task will always make changes to the system
any_errors_fatal: <boolean> # If you set any_errors_fatal and a task returns an error, Ansible finishes the fatal task on all hosts in the current batch and then stops executing the play on all hosts. Subsequent tasks and plays are not executed. You can recover from fatal errors by adding a rescue section to the block. You can set any_errors_fatal at the play or block level.
ignore_errors: <boolean> # Ignore task failures and continue with play. It does not affect connection errors.
# By default, Ansible stops executing tasks on a host when a task fails on that host. You can use ignore_errors to continue despite of the failure. The ignore_errors directive only works when the task can run and returns a value of ‘failed’. It does not make Ansible ignore undefined variable errors, connection failures, execution issues (for example, missing packages), or syntax errors.
ignore_unreachable: <boolean> # Ignore task failures due to an unreachable host and continue with the play. This does not affect other task errors
max_fail_percentage: <number> # Can be used to abort the run after a given percentage of hosts in the current batch has failed. This only works on linear or linear-derived strategies.
debugger: <options> # Enable debugging tasks based on the state of the task result.
# Options: always, never, on_failed, on_unreachable, on_skipped
diff: <boolean> # Toggle to make tasks return ‘diff’ information or not
no_log: <boolean> # Controls information disclosure.
order: <options> # Controls the sorting of hosts as they are used for executing the play.
# options: inventory (default), sorted, reverse_sorted, reverse_inventory and shuffle
tags # Tags applied to the task or included tasks
throttle: <number> # Limit the number of concurrent task runs on task, block and playbook level. This is independent of the forks and serial settings, but cannot be set higher than those limits. For example, if forks is set to 10 and the throttle is set to 15, at most 10 hosts will be operated on in parallel.
environment
fact_path
force_handlers
gather_facts
gather_subset
gather_timeout
module_defaults
port
remote_user
run_once
serial
strategy
timeout
tasks
post_tasks
pre_tasks
handlers
roles tasks:
delegate_to: localhost
run_once: true
notify: # List of handlers to notify when the task returns a ‘changed=True’ status.
ignore_errors: # Boolean to ignore the task failures and continue with the play.
failed_when: # Conditional expression that overrides the task 'failed' status.
changed_when: # with true: the task is always reported as changedBlock
Role
Similar to if. The code below skipes the task.
- name: Sample
hosts: localhost
vars:
value: false
tasks:
- name: Test
debug:
msg: "Value is true"
when: valueLogic operators
when: status == "enabled"
when: status != "enabled"
when: status > 5
when: contents.stdout == ""
when: version | int >= 4
when: temperature | float > 90
when: epic or monumental | bool
when: not epic
when: contents.stdout.find('hi') != -1
when: contents.rc == 127
when: result is failed
when: result is succeeded
when: result is skipped
when: result is changed
when: foo is defined
when: bar is undefined
when: x is not defined
when: my_string.startswith('prefix_')
when: "'substring' in my_string"
when: "'servers' in group_names"Boolean operators AND/OR
when:
ansible_facts['distribution'] == "Ubuntu" and ansible_facts['distribution_major_version'] == "24"
when:
- ansible_facts['distribution'] == "Ubuntu"
- ansible_facts['distribution_major_version'] == "24"when: value == "10" or value == "5"when: (name == "leo" and version == "5") or
(name == "admin" and version == "6")Simple list
The loop will run the task once per list item.
tasks:
- name: Test
debug:
msg: "Fruit is {{ item }}"
loop:
- banana
- apple
- orangewith_list keyword is equivalent to loop.
with_list:
- banana
- apple
- orangeVariables
loop: "{{ list_of_fruits }}"
loop: "{{ ['banana', 'apple', 'orange'] }}"Subkeys
tasks:
- name: Test
debug:
msg: "User {{ item.name }}"
loop:
- { name: 'user1', group: 'root' }
- { name: 'user2', group: 'local' }Dictionary
tasks:
- name: Test
vars:
user_data:
name: 'leo'
group: 'root'
debug:
msg: "User {{ item.key }} = {{ item.value }}"
loop: "{{ user_data | dict2items }}"Until condition
tasks:
- name: Test
shell: /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 3
delay: 1A block is a group of tasks. All taks in the block inherit the block directives.
tasks:
- name: Install, Setup, Start
block:
- name: Install
# ...
- name: Setup
# ...
- name: Start
# ...
when: ansible_facts['distribution'] == 'Ubuntu'Similar to exception handling in many programming languages, rescue block specify tasks to run when a task in the block fails.
tasks:
- name: Handle the error
block:
- name: Some command
# ...
rescue:
- name: Print errors
debug:
msg: 'Error'No matter what the task status in the block is, the tasks in the sessions always are always executed after the block tasks.
tasks:
- name: Always do
block:
- name: Some command
# ...
always:
- name: Always do this
debug:
msg: 'This always executes'Handlers are tasks that only run when notified. Usually when a task made a change in a machine.
tasks:
- name: Install service
# ...
notify:
- Restart service
handlers:
- name: Restart service
# ...- ansible.builtin.import_playbook: myplaybook1.yml
- ansible.builtin.import_playbook: myplaybook1.yml
- name: Include a play after/before another play
ansible.builtin.import_playbook: otherplays.yml
when: not my_file.stat.exists
vars:
value: 'dummy'- name: Run tasks from another file
hosts: localhost
tasks:
- import_tasks: tasks_file.ymlSample file tasks_file.yml
- name: Sample task 1
debug:
msg: "Hello 1"
- name: Sample task 2
debug:
msg: "Hello 2"Add tags to your tasks to skip or run tasks individually.
- name:
debug:
msg: "Tag 1"
tags: tag1
- name:
debug:
msg: "Tag 2"
tags:
- tag2Running the playbook
ansible-playbook myplaybook.yml --tags tag1,tag2 # (-t) only run plays and tasks tagged with these values
ansible-playbook myplaybook.yml --skip-tags tag1 # only run plays and tasks whose tags do not match these valuesList of common builtin modules
Modules (also referred to as “task plugins” or “library plugins”) are discrete units of code that can be used from the command line or in a playbook task. A module is a script that Ansible runs locally or remotely, and collects return values.
All modules return JSON format data [doc]. This means modules can be written in any programming language, but Python is a common choice.
NOTE: Modules should be idempotent, and should avoid making any changes if they detect that the current state matches the desired final state.
ansible device1 -m ping
ansible device1 -m command -a "/sbin/reboot -t now" # With argument
ansible device1 -m service -a "name=httpd state=started" # With arguments key=value- name: reboot the servers # Task name
command: /sbin/reboot -t now # Module name and arguments- name: restart webserver # Task name
service: # Module name
name: httpd # Module args
state: restarted # Module argsIn Ansible, templates files are files written using Jinja2 templating language that allow dynamic content to be generated during playbook execution. They are commonly used to configure services, such as configuration files for Nginx, Apache, systemd, and others.
- File extension is usually .j2 (e.g., nginx.conf.j2).
- Used with the template module in Ansible.
- Allow you to insert variables, conditionals, loops, and more into files that will be rendered and copied to target machines.
Example
foo.conf.j2
server {
name {{ server_name }};
}
- hosts: all
vars:
server_name: "example"
tasks:
- name: Template a file to /etc/files.conf
template:
src: /mytemplates/foo.conf.j2
dest: /etc/foo.confIn Ansible templates, you can use various types of statements to control logic and flow in the template. These include variable expressions, control statements like conditionals and loops, and filters:
Used to insert variable values into the file.
Hello, my name is {{ name }}Allow logic based on variable values.
{% if env == "production" %}
ENV=production
{% elif env == "staging" %}
ENV=staging
{% else %}
ENV=development
{% endif %}Used to iterate over a list or dictionary.
{% for user in users %}
- name: {{ user.name }}
uid: {{ user.uid }}
{% endfor %}Plugins are pieces of code that augment Ansible’s core functionality. This section covers the various types of plugins that are included with Ansible:
[filter-plugins] [playbook-filters]
Default values
{{ some_variable | default(5) }}Optional Variable
mode: "{{ item.mode | default(omit) }}" # mode does not send a value for mode if the item.mode is not definedMandatory values
{{ variable | mandatory }}Ternary
{{ condition | ternary(true_value, false_value) }}
{{ condition | ternary(true_value, false_value, omit) }} # Third value on nullmyvar: "value={{ 'value_if_true' if some_condition else 'value_if_false' }}"Base64
{{ 'bG9sYQ==' | b64decode }} # Decode a Base64 string
{{ 'Jose'| b64encode }} # Encode a string as Base64Path basename
{{ mypath | basename }} # Get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt'Cast
{{ (a == b) | bool }} # Cast into a booleanChecksum
{{ 'test2' | checksum }} # Checksum (SHA-1) of input data. => "109f4b3c50d7b0df729d299bc6f8e9ef9066971f"Combination
{{ [1,2,3,4,5] | combinations(2) }} # Combinations from the elements of a list. => [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 1, 5 ], [ 2, 3 ], [ 2, 4 ], [ 2, 5 ], [ 3, 4 ], [ 3, 5 ], [ 4, 5 ] ]
{{ {'a':1, 'b':2} | ansible.builtin.combine({'b':3, 'c':4}) }} # Combine two dictionaries. => {'a':1, 'b':3, 'c': 4}Comment ansible.builtin.comment filter – comment out a string
Roles are a reusable Ansible content (tasks, variables, ...) for user inside a Play.
An Ansible role has a defined directory structure with seven main standard directories. You must include at least one of these directories in each role. You can omit any directories the role does not use.
roles/
common/ # this hierarchy represents a "role"
tasks/ #
main.yml # <-- tasks file can include smaller files if warranted
handlers/ #
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/ #
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/ #
main.yml # <-- variables associated with this role
defaults/ #
main.yml # <-- default lower priority variables for this role
meta/ #
main.yml # <-- role dependencies
library/ # roles can also include custom modules
module_utils/ # roles can also include custom module_utils
lookup_plugins/ # or other types of plugins, like lookup in this case
Default locations:
- in collections, if you are using them
- in a directory called roles/, relative to the playbook file
- in the configured roles_path. The default search path is
~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles. - in the directory where the playbook file is located
You can store the roles in a different location settings roles_path in the ansible.cfg
/etc/ansible/roles/example/tasks/main.yml
- name: Install service
# ....Roles in the session roles run before any other tasks in a play.
- hosts: all
roles:
- role: '/etc/ansible/roles/example'- hosts: all
roles:
- my_namespace.my_collection.role1- hosts: all
roles:
- example
- common:
vars:
dir: '/opt/a'
app_port: 5000Including roles: dynamic use
The roles in tasks run in the order they are defined. Variables can be defined in the runtime.
- hosts: all
tasks:
- name: Include the example role
include_role:
name: exampleIncluding roles: static use
The behavior is the same as using the roles keyword.
- hosts: all
tasks:
- name: Import the example role
import_role:
name: exampleYou can avoid running the default main.yml by using tasks_from
- name: Run specific task from the role
include_role:
name: example
tasks_from: your_tasks_file.ymlAnother approach is to modify main.yml only to run certain tasks when a condition is met.
# In main.yml
- name: Run specific tasks
include_tasks: your_tasks_file.yml
when: some_conditionAnsible Vault is a feature of Ansible that allows you to keep sensitive data such as passwords or keys in encrypted files or variables, it can encrypt any data file used by Ansible. To use Ansible Vault you need one or more passwords to encrypt or decrypt content.
The command to use Ansible Vault is ansible-vault and the available arguments are create,decrypt,edit,view,encrypt,encrypt_string,rekey.
Running any ansible-vault command you will prompted for a password. To enforce password prompt when running a playbook with ansible-playbook command, you can add the argument --ask-vault-pass to the command line.
The vault password can be stored:
- In a file:
- Adding the argument
--vault-password-file /path/to/vault_passwordto the command line. - Setting the file path in the ansible.cfg file updating the entry
vault_password_file=. - Setting the file path in the environment variable
ANSIBLE_VAULT_PASSWORD_FILE.
- Adding the argument
- In third-party tools with client scripts.
ansible-playbook --vault-password-file client.pyansible-playbook --vault-id dev@client.py
Variable-level encryption only works with variables and keeps your files still legible. You can mix plaintext and encrypted variables. However, password rotation is not possible to do with the rekey command.
Example: Encrypting the string '1234' using the variable name 'my_secret'
Command:
ansible-vault encrypt_string '1234' --name 'my_secret'Output:
my_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
30656465653266303161616131336562316661656331356231633330343131626264643864313335
3031656537383066363337383964646462383938336630650a626330633762356266313366373464
62386332653766316462343530323832303432353738313265633766653263633035313034313963
3138613132386239660a383365393161323363383061353866656639633732326465336261646662
6239You can view the original value using the debug module.
Command:
ansible localhost -m ansible.builtin.debug -a var="my_secret" -e "@variables.yml" --ask-vault-passOutput:
localhost | SUCCESS => {
"my_secret": "1234"
}File-level encryption is easy to use, encrypting variables, tasks, or other Ansible content files. It also allows password rotation with rekey, but all the file content will be encrypted, you will not
be able to read the variable name without decrypting it.
Variable file content (variables.yml):
username: 'leo'
password: '1234'Command:
ansible-vault encrypt variables.yml # Encrypt an existing file
ansible-vault create variables.yml # Optionaly this command open a text editor creating a new encrypted fileEncrypted file content:
$ANSIBLE_VAULT;1.1;AES256
61663463663838653230363163373233323739663930396238346462666466663332373334396537
3932623330646639653130316530353937666634643638350a616238653639363334623437353337
33306234353239656264643633613938316537626237653264383161326635623962383630383363
3064383438663031630a663963333937393464326335356666323733366230313531613431336135
61373930343333303665363532656634376339373637626466353436626633343863313566323665
3166353736346239346166346166393530373532616231343530ansible-vault decrypt variables.yml # Decrypt the entire file
ansible-vault view variables.yml # View the content decrypted
ansible-vault edit variables.yml # Open a editor to edit the ansible-vault rekey variables.yml # Change the ecryption keyYou can encrypt files and variables with different passwords. For that you can specify an label (Vault ID) for each encrypted content, using the argument --vault-id.
In additonal to the label, you must provide a source.
--vault-id label@sourceKind of sources:
--vault-id leo@prompt # The password will be prompted
--vault-id leo@my_pass # Password file called 'my_pass'
--vault-id leo@client.py # Third-party tool scriptExamples
ansible-vault encrypt vars.yaml --vault-id leo@prompt # Encrypting with label
ansible-playbook hello.yml --vault-id leo@prompt # Run playbook asking for the password of the label 'leo'
ansible-playbook hello.yml --vault-id leo@prompt --vault-id dev@prompt # Asing multiple passwordsNOTE 1: You can encrypt contents with different passwords for the same label (Vault ID), Anisble does not validate it.
NOTE 2: Even if the label is wrong, the decryption will work if the password is right. Ansible will try to decrypt files/variables with any password given, first trying to do it with the password of the matching label to increase the performance.
A collection is a data structure that can contain these directories and files:
collection/
docs/
galaxy.yml
meta/
runtime.yml
plugins/
modules/
module1.py
inventory/
.../
README.md
roles/
role1/
role2/
.../
playbooks/
files/
vars/
templates/
tasks/
tests/
galaxy.yml
A collection must have a galaxy.yml file that contains the necessary information to build a collection artifact. See Collection Galaxy metadata structure for details.
[doc] [Should you develop a module?]
If you need functionality that is not available in any of the thousands of Ansible modules found in collections, you can easily write your own custom module. Modules can be written in any language, but must of guides use Python.
This guide helps you to develop Python modules.
Start copying the Custom Module Template file to your workspace $HOME/library/my_test.py, modify and extend the code to do what you want.
Ansible won't find this module automatically, for that you have these options:
- Put your module in the default module path
.ansible/plugins/modules/by creating the directories first:mkdir -p $HOME/.ansible/plugins/modules/. The default module path can be changed in the ansible.cfg file. - Set the environment variable
export ANSIBLE_LIBRARY="$HOME/library"
Module Return Values
Command
ansible localhost -m my_test -a 'name=hello new=true'Output
localhost | CHANGED => {
"changed": true,
"message": "goodbye",
"original_message": "hello"
}Create the playbook file testmod.yml
- name: test my new module
hosts: localhost
gather_facts: false
tasks:
- name: run the new module
my_test:
name: 'hello'
new: true
register: testout
- name: dump test output
debug:
msg: '{{ testout }}'Command
ansible-playbook testmod.ymlOutput
PLAY [test my new module] **********************************************************************************************************************************
TASK [run the new module] **********************************************************************************************************************************
changed: [localhost]
TASK [dump test output] ************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": true,
"failed": false,
"message": "goodbye",
"original_message": "hello"
}
}
PLAY RECAP *************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0Create a JSON file /tmp/args.json.
{
"ANSIBLE_MODULE_ARGS": {
"name": "hello",
"new": true
}
}Command
source $HOME/.local/share/pipx/venvs/ansible/bin/activate # Only if ansible installed with pipx
python3 library/my_test.py /tmp/args.jsonOutput
{
"changed": true,
"original_message": "hello",
"message": "goodbye",
"invocation": {
"module_args": {
"name": "hello",
"new": true
}
}
}[doc] [Creating a new collection]
Collections are a distribution format for Ansible content. You can package and distribute playbooks, roles, modules, and plugins using collections.
To create a collection:
- Create a new collection with a template running the command:
ansible-galaxy collection init my_namespace.my_collection-
Add modules and other content to the collection, and edit the file
galaxy.yml. -
Install or build collection:
# Install collection
ansible-galaxy collection install path/to/my_namespace/my_collection
# Build collection
ansible-galaxy collection build path/to/my_namespace/my_collection
ansible-galaxy collection install path/to/my_namespace-my_collection-1.0.0.tar.gz # Install builded collection- (Optional) Publish the collection artifact to Galaxy:
ansible-galaxy collection publish path/to/my_namespace-my_collection-1.0.0.tar.gz --api-key=SECRETYou can change the default collections path in the ansible.cfg file by changin the property collections_path=.
Developing Ansible plugins allows you to extend Ansible's functionality beyond what's available by default.
Ansible supports several plugin types:
- Action Plugin
- Cache Plugin
- Callback Plugin
- Connection Plugin
- Filter Plugin
- Inventory Plugin
- Lookup Plugin
- Test Plugin
- Vars Plugin
Check the page Developing Plugins
ANSIBLE_DEBUG=1 ansible-config dump | grep CALLBACK # Check if the callback plugin was loaded