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 - 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.always
section
8.4. Handlers
8.5. Importing a playbook
8.6. Tags - Modules
9.1. Executing modules from the command line
9.2. Executing modules from playbooks - Plugins
10.1. Filter - Roles
11.1. Creating a role (task)
11.2. Using roles
11.2.1. Play level
11.2.2. Task level - Vault
12.1. Vault Password
12.2. Variable-level encryption
12.2.1. Encrypting variables
12.2.2. Viewing encrypted variables
12.3. File-level encryption
12.3.1. Encrypting files
12.3.2. Decrypting files
12.3.3. Rotating password
12.4. Vault ID - Multiple passwords - Developing Modules
13.1. Verifying your module locally
13.1.1. Using Ansible adhoc command
13.1.2. Using the module in a Playbook
13.1.3. Using Python - Developing Collections
- Developing Plugins
15.1. Action Plugin
15.2. Cache Plugin
15.3. Callback Plugin
15.4. Connection Plugin
15.5. Filter Plugin
15.6. Inventory Plugin
15.7. Lookup Plugin
15.8. Test Plugin
15.9. Vars Plugin
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.
- Taks 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.
- 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 ~/.bashrc
sudo mkdir -p /etc/ansible
sudo chown $USER:$USER /etc/ansible
ansible-config init --disabled -t all > /etc/ansible/ansible.cfg
touch /etc/ansible/hosts
Example 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 --version
Show inventory
ansible-inventory --list
ansible-inventory --graph
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 file
Doc
ansible-doc shell # shows documentation of the shell module
Run 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 collections
Vault
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-id
The 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 name
In the inventory file add the group name ending with :
.
my_group1: # group name
hosts: # hosts of the group
Add 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:
- 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 device1
output
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
- midwest
Referencing list variables
region: "{{ region[0] }}"
Dictionary
foo:
field1: one
field2: two
Referencing 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
group_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 localhost
Multiple 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_settings
ansible_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 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_facts
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
Play
- name: Sample
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
strategy: free # Strategy of task execution, linear or free
collections: # Using a collection.
- my_namespace.my_collection
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: '-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
block:
tasks:
handlers:
roles:
Play
tasks:
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 resported as changed
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: value
Logic 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
- orange
with_list
keyword is equivalent to loop
.
with_list:
- banana
- apple
- orange
Variables
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: 1
A 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'
Add tags to your tasks to skip or run tasks individually.
- name:
debug:
msg: "Tag 1"
tags: tag1
- name:
debug:
msg: "Tag 2"
tags:
- tag2
List 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 args
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 defined
Mandatory values
{{ variable | mandatory }}
Ternary
{{ condition | ternary(true_value, false_value) }}
{{ condition | ternary(true_value, false_value, omit) }} # Third value on null
myvar: "value={{ 'value_if_true' if some_condition else 'value_if_false' }}"
Base64
{{ 'bG9sYQ==' | b64decode }} # Decode a Base64 string
{{ 'Jose'| b64encode }} # Encode a string as Base64
Path 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 boolean
Checksum
{{ '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
{{ 'Plain style (default)' | comment }}
#
# Plain style (default)
#
{{ "C style" | comment('c') }}
{{ "C block style" | comment('cblock') }}
{{ "Erlang style" | comment('erlang') }}
{{ "XML style" | comment('xml') }}
{{ "My Special Case" | comment(decoration="! ") }}
!
! My Special Case
!
{{ "Custom style" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }}
#######
#
# Custom style
#
#######
###
#
ansible_managed = This file is managed by Ansible.%n
template: {file}
date: %Y-%m-%d %H:%M:%S
user: {uid}
host: {host}
{{ ansible_managed | comment }}
#
# This file is managed by Ansible.
#
# template: /home/ansible/env/dev/ansible_managed/roles/role1/templates/test.j2
# date: 2015-09-10 11:02:58
# user: ansible
# host: myhost
#
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:
- example
- common:
vars:
dir: '/opt/a'
app_port: 5000
Including 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: example
Including roles: static use
The behavior is the same as using the roles keyword.
- hosts: all
tasks:
- name: Import the example role
import_role:
name: example
Ansible 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_password
to 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.py
ansible-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
6239
You 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-pass
Output:
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 file
Encrypted file content:
$ANSIBLE_VAULT;1.1;AES256
61663463663838653230363163373233323739663930396238346462666466663332373334396537
3932623330646639653130316530353937666634643638350a616238653639363334623437353337
33306234353239656264643633613938316537626237653264383161326635623962383630383363
3064383438663031630a663963333937393464326335356666323733366230313531613431336135
61373930343333303665363532656634376339373637626466353436626633343863313566323665
3166353736346239346166346166393530373532616231343530
ansible-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 key
You 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@source
Kind 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 script
Examples
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 passwords
NOTE 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.
[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.yml
Output
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=0
Create 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.json
Output
{
"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=SECRET
You can change the default collections path in the ansible.cfg file by changin the property collections_path=
.
Filter sample
Create the filter file in the collection path/to/my_namespace/my_collection/plugins/filter/string.py
or change the ansible.cfg
setting the directory with the filter files.
# /etc/ansible/filter_plugins/string_filters.py
class FilterModule(object):
def filters(self):
return {
'to_upper': self.to_upper,
}
def to_upper(self, value):
if isinstance(value, str):
return value.upper()
else:
raise ValueError("The value must have a string")
Using the filter
- hosts: localhost
tasks:
- name: Test the custom filter
ansible.builtin.debug:
msg: "{{ 'hello world' | to_upper }}"
# msg: "{{ 'hello world' | my_namespace.my_collection.to_upper }}"