-
Notifications
You must be signed in to change notification settings - Fork 0
ansible
- Ansible
- Installation
- Inventory
- Playbooks
- Security
- Best practises
- Misc
- Troubleshooting
- yml reference (YAML 1.1)
is an automation framework that works agentless by leveraging SSH to access remote machines.
Main components:
- Control node: where Ansible is installed (you run commands like
ansible,ansible-inventoryoransible-vaultfrom here)- Multiple are possible but Ansible does not coordinate across them (see AAP if this is desired)
- Inventory (aka hostfile): one or more lists of managed nodes
- Managed nodes (aka hosts): systems to manage via Ansible
pip install ansibleAn inventory manages your nodes (e.g. hosts).
- Nodes are specified by FQDN or IP address
- Make sure you added the SSH key of the control node to all of your managed nodes
The inventory consists of a .ini or .yml file structure:
[someHostGroup]
myhost.lan
102.23.02.01We'll focus, however, on the yml format.
---
all: # All hosts in the inventory (=reserved keyword); optional => group = implied automatically; no need to explicitly define it
hosts:
192.168.1.10:
api.example.com:
children: # Define nested group (=reserved keyword)
webservers:
hosts:
web1.example.com:
dbservers:
hosts:
db1.example.com:
db2.example.com:---
someHostGroup:
hosts:
a_host: # Reachable via this name as hostname
my_host:
ansible_host: host_alias_fqdn.lan
another_host:
ansible_host: 192.168.1.222
ansible_user: user_used_to_log_in_via_ssh
webserver:
ansible_host: 102.23.02.01:5555 # To define a non-standard SSH port
# or via:
ansible_port: 5555
# Some variable for a specific host
http_port: 443 # Has precedence over http_port in vars below
# Variables valid for all hosts in a group
vars:
a_group_variable: 1234
http_port: 443
http_port: 999 # Variables can be overwritten (order matters!)
someOtherHostGroup:
hosts:
another_host:
ansible_host: another_host.lan
www[01:10:2].helloworld.com: # Matches www01.helloworld.com, www03.helloworld.com, www05.helloworld.com, ... (01 until 10 (inclusive); step size = 2); see also: https://docs.ansible.com/ansible/2.7/user_guide/intro_patterns.html#working-with-patterns
~(sftp|portal)\.helloworld\.com: # Tell ansible you are using a RegEx by prefixing `~`
someMetaGroup:
children:
someHostGroup:
someOtherHostGroup:- 2 spaces per indent level
-
ansible_*,hostsare reserved keywords - More about host patterns
- groups are usually clustered as what (topology, type, ...) / where (geographic location) or when (stage)
-
metagroups are used to organise multiple groups
-
childrenis a reserved keyword to include groups (notice the trailing:after the group names) - Circular relationships are not allowed
-
-
default groups created by Ansible
-
all: Every host in the inventory- If you want to set variables for the
allgroup inside the inventory file, the all group must be the first entry in the file
- If you want to set variables for the
-
ungrouped: Any host which does not belong to a group
-
ansible-inventory -i inventory.ini --list# Run only on region1:
ansible-playbook site.yml -i ./inventory.yml --limit region1
# or on a local host file:
ansible-playbook site.yml --limit @my_local_host_list.txt # Note the prefix: `@`; file needs to be comma separatedPing a inventory group (here someHostGroup):
ansible -u myUsername someHostGroup -m ping -i inventory.ini-> you only need -u if your user name on the managed node(s) is different from the username from the control node
└── inventory
├── 01-inventory-loaded-first.yml
├── 02-inventory-loaded-second.yml
└── 03-inventory-groups-loaded-third.yml
- Internally Ansible merges all inventories into one
- Load order is based on the ASCII character sequence
- If you cross-file reference hosts or groups make sure they are already loaded (-> check load order)
- In our example we define groups in the last file (loaded) to ensure that all hosts mentioned in the group definitions are already loaded
= automation blueprints in .yml
Hierarchy:
Playbook
├── Play
│ ├── Task
│ │ └── Module
│ └── Role
│ ├── Tasks
│ │ └── Task
│ │ └── Module
│ ├── Templates
│ ├── Handlers
│ └── Vars
- Module
- Smallest executable unit (e.g.
ansible.builtin.copy) in Ansible - = code or binary that Ansible runs on the managed nodes
-
Collections=> built-in "action"/module groups- Can contain playbooks, roles, modules and plugins
- Install via Ansible Galaxy
- Smallest executable unit (e.g.
- Task: Call of a module (setting the modules parameters if exists)
-
Handlers: special form of a Task: executes only when notified by a previous Task (status change tochanged)
-
- Play: Ordered list of tasks to be executed on a group of hosts (mandatory)
- Playbook: Collection of plays
-
Roles: Reusable Ansible content (tasks, handlers, variables, plugins, templates and files) to be used inside a Play- Roles can be included in plays within a playbook
-
Plugins: Code that expands Ansibles core capabilities
Plug-ins (executed on the control node) vs. modules (executed on the managed nodes)
---
- name: My first play # Start of Play
hosts: someHostGroup
tasks:
- name: Ping my hosts # Tasks
ansible.builtin.ping: # Doc: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ping_module.html
- name: Print message
ansible.builtin.debug: # Doc: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html#ansible-collections-ansible-builtin-debug-module
msg: Hello world
- name: Print multiple messages
ansible.builtin.debug:
msg:
- "Hello"
- "Hello again!"
- name: Apt package management
hosts: someOtherHostGroup
tasks:
- name: Run equivalent of `apt-get update` as a separate step
become: true # Do something as root (see also: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_privilege_escalation.html)
ansible.builtin.apt: # Doc: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html
update_cache: yes # Equivalent of apt-get update
- block: # Use a `block` (reserved keyword) to apply certain parameters to all tasks (here: become)
- name: Update the repo cache and install the package `pwgen`
ansible.builtin.apt:
name: pwgen
update_cache: yes
- name: Install the package `pwgen` with a more or less (due to the wildcard) specific version or below and allow downgrade
ansible.builtin.apt:
name: pwgen=2.08*
allow_downgrade: yes # Allow downgrading the package
autoremove: true # Automatically invoke `apt autoremove`
- name: Install multiple packages
ansible.builtin.apt:
pkg:
- pwgen # Here attributes like above are not allowed
- git>=2.4*
become: true
- name: Change files
hosts: someOtherHostGroup
tasks:
- name: Ensure X11 forwarding is activated
become: yes
ansible.builtin.lineinfile: # Doc: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html
path: /etc/ssh/sshd_config
regexp: '^(\s*X11Forwarding\s+)(\w+)$'
line: '\1yes' # Use a backreferences in our replacement string (first group: `\1`)
backrefs: yes # Use backreferences in regex (basically if you have groups in the regex you can fill in the groups as part of the result)
validate: 'sshd -t -f %s' # Command to verify the configuration before file replacement (%s provides the temporary file path (must be present))= automatically gathered information about the managed hosts (collected by the setup module)
- Read-only (you cannot modify facts directly)
- Dynamically generated
- System-specific
# ...
tasks:
- name: Print all available facts
ansible.builtin.debug:
var: ansible_facts
- name: Print Ansible fact via dict access
ansible.builtin.debug:
var: ansible_facts.distribution
- name: Print Ansible fact via Unsafe Text Access
ansible.builtin.debug:
var: ansible_facts["distribution"]
- name: Print Ansible fact via Unsafe Text and list access
ansible.builtin.debug:
var: ansible_facts["all_ipv6_addresses"][1]
- name: Print Ansible fact type we try to access via type_debug filter
ansible.builtin.debug:
var: ansible_facts["all_ipv6_addresses"][1]|type_debugThe syntax is exactly how you would expect it in Python (lists: [2], dict: ansible_facts.memory.mb_real or ["key access"]).
Directly access fact on local host:
ansible <hostname> -m ansible.builtin.setupUsage for all:
# playbook.yml
- hosts: localhost
tasks:
- name: Display variables
debug:
msg: "Global: {{ my_global_var }}, Host: {{ my_host_var }}"Scope: All hosts in the group; host variables apply to a specific host
# inventory/group_vars/all.yml
my_global_var: "global value"
# inventory/host_vars/localhost.yml
my_host_var: "host specific value" # Host variable, here it applies only to localhostScope: Within the playbook
# playbook.yml
- hosts: localhost
vars:
playbook_var: "playbook value"
vars_files:
- vars.yml # Variables from external file
tasks:
- name: Display playbook variable
debug:
msg: "Playbook: {{ playbook_var }}, file: {{ file_var }}"Scope: Entire playbook run
Via file:
# vars.yml
file_var: "file value"Via command-line (--extra-vars):
ansible-playbook playbook.yml --extra-vars "cli_var=cli value"Scope: Within the play
register keyword:
- hosts: localhost
tasks:
- name: Get current date
command: date +%Y-%m-%d
register: date_output # Register the command output
- name: display the date
debug:
msg: "the date is {{ date_output.stdout }}"ansible-playbook -i ./inventory.yml hello.yml
# Multiple inventories
# ... mentioned individually
ansible-playbook -i ./inventory-eu.yml -i ./inventory-na.yml hello.yml
# ... or a whole folder
ansible-playbook -i ./inventory hello.yml
# Ask for become password
ansible-playbook install.yml -i ./inventory.yml -K # -K == --ask-become-pass; -k == --ask-pass (connection password)- More about
become
Testing Ansible roles and tasks without creating a dedicated test playbook:
ansible-playbook -i <inventory> <role_tasks_file> --extra-vars "<role_vars>" --check
# or alternatively
ansible -i <inventory> all -m ansible.builtin.include_role -a "name=<role_name> <role_vars>" --check-
<inventory>: Path to your inventory file or list of comma-separated hosts -
<role_tasks_file>: Path to the role'stasks/main.yml -
--extra-vars/-e: Define required role variables-
<role_vars>: Any variables needed by the role (e.g.,username=test_user) - Specifying a host with
ansible_host=<target_host>might confuse Ansible if it's also in the inventory and might raise an error
-
-
--check: Runs in dry-run mode (simulates changes)
Examples:
ansible-playbook -i 'ansible_test_user@ubuntu-24-10-base,' roles/some_ansible_role/tasks/main.yml -e "some_req_arg=arg_value" --check
# or
ansible -i 'ansible_test_user@ubuntu-24-10-base,' all -m ansible.builtin.include_role -a "name=some_ansible_role some_req_arg=arg_value" --checkLimitations:
- Handlers are not executed
- Role dependencies (defined in
meta/main.yml) are not resolved - Behaviour might differ from execution within a full playbook
-
Passwords: Avoid interactive password prompts.
- Use Ansible Vault for secure password storage.
- Use the
expectmodule for unavoidable interactive prompts.
-
no_log: true: Use to prevent sensitive data from being logged.
production # inventory file for production servers
staging # inventory file for staging environment
group_vars/
group1.yml # here we assign variables to particular groups
group2.yml
host_vars/
hostname1.yml # here we assign variables to particular systems
hostname2.yml
library/ # if any custom modules, put them here (optional)
module_utils/ # if any custom module_utils to support modules, put them here (optional)
filter_plugins/ # if any custom filter plugins, put them here (optional)
site.yml # master playbook
webservers.yml # playbook for webserver tier
dbservers.yml # playbook for dbserver tier
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
webtier/ # same kind of structure as "common" was above, done for the webtier role
monitoring/ # ""
fooapp/ # ""
(from https://docs.ansible.com/ansible/2.8/user_guide/playbooks_best_practices.html#content-organization)
- Special variables
-
Ansible configuration
- Special command line options and other things can be configured statically in Ansible via the
ansible.cfgfile
- Special command line options and other things can be configured statically in Ansible via the
-
[WARNING]: Could not match supplied host pattern, ignoring: myhosts- The host group mentioned in your playbook might not be in the inventory
-
?: Key indicator -
:: Value indicator -
-: Nested series entry indicator -
,: Separate in-line branch entries -
[]: Surround in-line series branch -
{}: Surround in-line keyed branch
-
'': Surround in-line unescaped scalar ('' escaped ') -
": Surround in-line escaped scalar (see escape codes below) -
|: Block scalar indicator -
>: Folded scalar indicator -
-: Strip chomp modifier ('|-' or '>-') -
+: Keep chomp modifier ('|+' or '>+') -
1-9: Explicit indentation modifier ('|1' or '>2') # Modifiers can be combined ('|2-', '>+1')
-
&: Anchor property -
*: Alias indicator
Usually unspecified
-
none: Unspecified tag (automatically resolved by application) -
!: Non-specific tag (by default, "!!map"/"!!seq"/"!!str") -
!foo: Primary (by convention, means a local "!foo" tag) -
!!foo: Secondary (by convention, means "tag:yaml.org,2002:foo") -
!h!foo: Requires "%TAG !h! " (and then means "foo") -
!<foo>: Verbatim tag (always means "foo")
-
%: Directive indicator -
---: Document header -
...: Document terminator
-
#: Throwaway comment indicator - ``@`: Both reserved for future use
-
=: Default "value" mapping key -
<<: Merge keys from another mapping
Default automatic tags
-
!!map: { Hash table, dictionary, mapping } -
!!seq: { List, array, tuple, vector, sequence } -
!!str: Unicode string
-
!!set: { cherries, plums, apples } -
!!omap: [ one: 1, two: 2 ]
{ ~, null } : Null (no value)
[ 1234, 0x4D2, 02333 ] : [ Decimal int, Hexadecimal int, Octal int ]
[ 1_230.15, 12.3015e+02 ]: [ Fixed float, Exponential float ]
[ .inf, -.Inf, .NAN ] : [ Infinity (float), Negative, Not a number ]
{ Y, true, Yes, ON } : Boolean true
{ n, FALSE, No, off } : Boolean false
? !!binary >: e.g.: R0lG...BADS=
: >-: Base 64 binary value
- Numeric :
{ "\x12": 8-bit, "\u1234": 16-bit, "\U00102030": 32-bit } - Protective:
{ "\\": '\', "\"": '"', "\ ": ' ', "\<TAB>": TAB } - C :
{ "\0": NUL, "\a": BEL, "\b": BS, "\f": FF, "\n": LF, "\r": CR, "\t": TAB, "\v": VTAB } - Additional:
{ "\e": ESC, "\_": NBSP, "\N": NEL, "\L": LS, "\P": PS } - ...
from https://yaml.org/refcard.html (formatting modified)
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License *.
Code (snippets) are licensed under a MIT License *.
* Unless stated otherwise
