Skip to content

leofds/ansible-essentials

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 

Repository files navigation

Ansible

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

Summary

  1. Introduction
  2. Ansible concepts
  3. How to install on Ubuntu 24
    3.1. Creating configuration file
    3.2. Creating inventory (YAML)
  4. Commands
  5. Inventory
    5.1. Adding Hosts
    5.2. Adding Groups
  6. Introducing Playbooks
    6.1. Runnig playbook locally (localhost)
  7. 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
  8. 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 with rescue
    8.3.2. always section
    8.4. Handlers
    8.5. Importing a playbook
    8.6. Tags
  9. Modules
    9.1. Executing modules from the command line
    9.2. Executing modules from playbooks
  10. Plugins
    10.1. Filter
  11. Roles
    11.1. Creating a role (task)
    11.2. Using roles
    11.2.1. Play level
    11.2.2. Task level
  12. 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
  13. 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
  14. Developing Collections
  15. 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

1. Introduction

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.

2. Ansible concepts

  • 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).
  • 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.
  • 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.

3 How to install on Ubuntu 24

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

3.1 Creating configuration file

sudo mkdir -p /etc/ansible
sudo chown $USER:$USER /etc/ansible
ansible-config init --disabled -t all > /etc/ansible/ansible.cfg

3.2 Creating inventory (YAML)

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"

4 Commands

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

5 Inventory

[doc]

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.

5.1 Adding Hosts

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

5.2 Adding Groups

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:

6 Introducing Playbooks

Execution

  • 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

6.1 Runnig playbook locally (localhost)

Set the connection plugin to local (prefered) or exchange the SSH key locally. (cat "${HOME}/.ssh/id_ed25519.pub" >> "${HOME}/.ssh/authorized_keys")

7 Variables

[doc]

Ansible uses Jinja2 to access variables dynamically using {{ variable_name }}.

...
- name: Display a debug message
  debug:
    msg: "Hello {{ username }}"

7.1 Variable types

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 }}"

7.2 Variables in the Playbook file (.yml)

- name: Sample Playbook
  hosts: all
  vars:
    username: 'leo'
    password: '*****'
  vars_files:
    - /vars/external_vars.yml

  tasks:
    - name: Display a debug message
      vars:
        username: 'leo'

7.3 External Variables file (.yml)

username: 'leo'
password: '*****'

7.4 Inventory Variables

mygroup:
  hosts:
    device1:
      username: "leo"       # host variable
  vars:
    dummy: "superserver"    # group variable

7.5 group_vars & host_vars

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

7.6 Special Variables

[doc]

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"

7.7 Ansible Facts

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

7.8 Registering variables

[doc]

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 }}"

7.9 Variables precedence

The order of precedence from least to greatest (the last listed variables override all other variables):

  1. command line values (for example, -u my_user, these are not variables)
  2. role defaults (as defined in Role directory structure) 1
  3. inventory file or script group vars 2
  4. inventory group_vars/all 3
  5. playbook group_vars/all 3
  6. inventory group_vars/* 3
  7. playbook group_vars/* 3
  8. inventory file or script host vars 2
  9. inventory host_vars/* 3
  10. playbook host_vars/* 3
  11. host facts / cached set_facts 4
  12. play vars
  13. play vars_prompt
  14. play vars_files
  15. role vars (as defined in Role directory structure)
  16. block vars (only for tasks in block)
  17. task vars (only for the task)
  18. include_vars
  19. set_facts / registered vars
  20. role (and include_role) params
  21. include params
  22. extra vars (for example, -e "user=my_user")(always win precedence)

8 Playbooks

8.1 Keywords

[doc]

  • 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

8.2 Tasks

8.2.1 Conditionals (when)

[doc]

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")

8.2.2 Loops

[doc]

8.2.2.1 Loop

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 }}"

8.2.2.2 Loop control

Until condition

  tasks:
    - name: Test
      shell: /usr/bin/foo
      register: result
      until: result.stdout.find("all systems go") != -1
      retries: 3
      delay: 1

8.3 Blocks

[doc]

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'

8.3.1 Handling tasks failures with rescue

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'

8.3.2 always section

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'

8.4 Handlers

[doc]

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
      # ...

8.5 Importing a playbook

[doc] [doc]

- 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'

8.6 Tags

[doc]

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

9 Modules

[doc]

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.

9.1 Executing modules from the command line

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

9.2 Executing modules from playbooks

- 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

10 Plugins

Plugins are pieces of code that augment Ansible’s core functionality. This section covers the various types of plugins that are included with Ansible:

10.1 Filter

[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
#

11 Roles

[doc]

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

11.1 Creating a role (task)

/etc/ansible/roles/example/tasks/main.yml

- name: Install service
  # ....

11.2 Using roles

11.2.1 Play level

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

11.2.2 Task level

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

12 Vault

[doc]

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.

12.1 Vault Password

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.
  • In third-party tools with client scripts.
    • ansible-playbook --vault-password-file client.py
    • ansible-playbook --vault-id dev@client.py

12.2 Variable-level encryption

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.

12.2.1 Encrypting variables

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

12.2.2 Viewing encrypted variables

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"
}

12.3 File-level encryption

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.

12.3.1 Encrypting files

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

12.3.2 Decrypting files

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 

12.3.3 Rotating password

ansible-vault rekey variables.yml        # Change the ecryption key

12.4 Vault ID - Multiple passwords

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.

13 Developing Modules

[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

13.1 Verifying your module locally

13.1.1 Using Ansible adhoc command

Command

ansible localhost -m my_test -a 'name=hello new=true'

Output

localhost | CHANGED => {
    "changed": true,
    "message": "goodbye",
    "original_message": "hello"
}

13.1.2 Using the module in a Playbook

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

13.1.3 Using Python

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
    }
  }
}

14 Developing Collections

[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:

  1. Create a new collection with a template running the command:
ansible-galaxy collection init my_namespace.my_collection
  1. Add modules and other content to the collection, and edit the file galaxy.yml.

  2. 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
  1. (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=.

15 Developing Plugins

[doc] [Working with plugins]

15.1 Action Plugin

[doc] [dev]

15.2 Cache Plugin

[doc] [dev]

15.3 Callback Plugin

[doc] [dev] [examples]

15.4 Connection Plugin

[doc] [dev]

15.5 Filter Plugin

[doc] [dev]

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 }}"

15.6 Inventory Plugin

[doc] [dev]

15.7 Lookup Plugin

[doc] [dev]

15.8 Test Plugin

[doc] [dev]

15.9 Vars Plugin

[doc] [dev]

About

A summary of Ansible

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages