Code Monkey home page Code Monkey logo

ansible_demo's Introduction

Ansible

Tutorials

Performance

Installation

$ python3 -m venv env
source env/bin/activate
(ansible) $ pip install ansible
(ansible) $  which ansible
/home/gigi/Documents/python/ansible/env/bin/ansible
(ansible) $ deactivate
$

Version

$ ansible --version
ansible [core 2.15.6]
  config file = None
  configured module search path = ['/home/gigi/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/gigi/Documents/python/ansible/env/lib/python3.11/site-packages/ansible
  ansible collection location = /home/gigi/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/gigi/Documents/python/ansible/env/bin/ansible
  python version = 3.11.6 (main, Oct  8 2023, 05:06:43) [GCC 13.2.0] (/home/gigi/Documents/python/ansible/env/bin/python3)
  jinja version = 3.1.2
  libyaml = True

Development

Plugins

SSH

Copy key

ssh-copy-id -i /path/ssh/private_key user@server

Edit .ssh/authorized_keys and add security options at the beginning of the file (use authorized_key module to deploy with Ansible):

from="192.168.1.1",no-X11-forwarding ssh-rsa ...

See also:

Agent

  • Add:
$ eval $(ssh-agent)
$ ssh-add
$ ssh-add -l
$ ps -p ${SSH_AGENT_PID}
  • SSH config ~/.ssh/config:
Host *
  User ansible
  Port 22
  IdentityFile /path/ssh/private_key
  LogLevel INFO
  Compression yes
  ForwardAgent yes
  ForwardX11 yes

Host bastion
   User username
   Hostname bastion.example.com

Host private-server-*.example.com
   ProxyJump bastion
  • Or you can add to the inventory these lines to use a ssh bastion:
ansible_user: vagrant
ansible_ssh_common_args: -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="ssh -p 22 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p -q vagrant@bastion"

Inventory

export ANSIBLE_INVENTORY="$PWD/ansible_hosts"
  • Or use argument -i from ansible command:
ansible -i ./ansible_hosts
  • Or by default in /etc/ansible/hosts

  • Example of inventory:

ungrouped:
  hosts:
    mail.example.com:
dbservers:
  hosts:
    foo.example.com:
    bar.example.com:
webservers:
  hosts:
    www[01:50:2].example.com:
    webserver01:
      ansible_host: 192.0.2.140
      http_port: 80
    webserver02:
      ansible_host: 192.0.2.150
      http_port: 443
  vars:
    ansible_user: my_server_user
    ntp_server: ntp.atlanta.example.com
east:
  hosts:
    foo.example.com:
    one.example.com:
    two.example.com:
west:
  hosts:
    bar.example.com:
    three.example.com:
prod:
  children:
    east:
test:
  children:
    west:
  • Example of inventory for localhost:
ungrouped:
  hosts:
    localhost:
      host: 127.0.0.1
      ansible_connection: local
      ansible_python_interpreter: /usr/bin/env python3

Or also use from command line -c local or --connection=local (Cf https://docs.ansible.com/ansible/latest/collections/ansible/builtin/#connection-plugins):

ansible -i "127.0.0.1," all -c=local -m ping

Environment variables

ansible localhost -m ping -e 'ansible_python_interpreter=/usr/bin/python3'

Custom facts

By default ansible search custom facts on each inventory servers in the /etc/ansible/facts.d/ directory. The fact must finished by the .fact extension and must be in the ini or json format, for example: /etc/ansible/facts.d/preference.fact:

[general]
function=War
family=Destruction

And the result will be:

"ansible_local": {
    "preference": {
        "general": {
            "family": "Destruction",
            "function": "War"
        }
    }
},

It is also possible to use script like bash or python (custom_fact_generator.py):

#!/usr/bin/env python
import json
custom_fact_data = {
    "my_custom_fact": {
        "category": "Awesome",
        "description": "This is a custom fact generated by my Python script!"
    }
}
print(json.dumps(custom_fact_data))

Is it also possible to force a custom directory:

- name: Update OS of servers
  hosts: webservers
  become: true
  gather_facts: false

  tasks:
    - name: Collects facts
      ansible.builtin.setup:
        fact_path: /custom/facts/path/
        gather_subset: !all,min

Cli

ansible

  • Cli

  • Examples:

$ ansible all --ssh-extra-args="-o 'PreferredAuthentications=password'" -m ping
$ ansible -i "127.0.0.1," all -k -K -b -m raw -a "id -u" -o
$ ansible -i "127.0.0.1," all -e "who=world" -m debug -a msg="'Hello {{ who }}'"
$ ansible -i "127.0.0.1," all -m command -a "uptime"
$ ansible -i "127.0.0.1," all -m shell -a "getent passwd | egrep -w root"
# target group1
ansible all -m ping -l group1
# target gigi-debian-1 and gigi-debian-3
ansible all -m ping -l 'gigi-debian-[1-3:]'
# target gigi-debian-1, gigi-debian-2 and gigi-debian-3
ansible all -m ping -l 'gigi-debian-[1:3:]'
# target group1 and gigi-debian-1
ansible all -m ping -l group1,gigi-debian-1
  • Debugging Ansible:
ANSIBLE_DEBUG=1 ansible all -m ping
  • Add verbosity (add more v to have more verbosity):
ansible all -vvv -m ping

ansible-inventory

  • Cli
  • Examples:
$ ansible-inventory --list
$ ansible-inventory --list --yaml
$ ansible-inventory --graph
$ ansible-inventory --graph --vars

ansible-playbook

ansible-config

  • Cli

  • Documentation location files

  • ANSIBLE_CONFIG: environment variable if set

  • ansible.cfg: in the current directory

  • ~/.ansible.cfg: in the home directory

  • /etc/ansible/ansible.cfg

  • Generate a a configuration file:

ansible-config init --disabled > ansible.cfg

List all configuration options

  • Print all config options:
$ ansible-config list -t all
$ ansible-config list -t become

Dump all configuration options

  • Dump configuration:
$ ansible-config dump -t all
$ ansible-config dump -t become
$ ansible-config dump --only-changed -t all

View current configuration

  • View configuration file:
$ ansible-config view

Generate configuration file

$ ansible-config init --disabled > ansible.cfg
$ ansible-config init -t shell --disabled > ansible.cfg

ansible-pull

ansible-doc

  • Cli

  • List all callback

$ ansible-doc -t callback -l
$ ansible-doc -t callback -F
$ ansible-doc -t module -l
  • Detail how to use a specific callback:
ansible-doc -t callback ansible.posix.json
  • Use ANSIBLE_STDOUT_CALLBACK env var to change the callback for the playbook:
ANSIBLE_STDOUT_CALLBACK=ansible.posix.json ansible-playbook playbook.yml

ansible-galaxy

ansible-galaxy init myrole
ansible-galaxy install --roles-path roles -r requirements.yml

ansible-console

ansible-vault

ansible-vault simple

ansible all --ask-vault -m debug -a "var=encrypted_var"
  • Use a password file:
ansible all --vault-password-file ./vault_password -m debug -a "var=encrypted_var"
  • Environment file:
export ANSIBLE_VAULT_PASSWORD_FILE=$(realpath ./vault_password)
  • How to find var files that contain a specific secret ?
$ VAR_SEARCH="secret"
$ for file in $(find . -type f -name "*.yml" -not -path "./env/*" -exec egrep -El '^\$ANSIBLE_VAULT;' {} \;); do ansible-vault view "$file" | egrep -w "^${VAR_SEARCH}:" >/dev/null && echo "$file"; done
./group_vars/all/vault.yml
$ ansible-vault view ./group_vars/all/vault.yml
secret: This is very secret!

ansible-vault multi-password

  • Youtube Xavki multipassword ansible-vault create --vault-id prod@prompt prod-secrets.yml

  • Creation d'un fichier sécurisé prod-secrets.yml avec l'id prod en demandant le password @prompt:

ansible-vault create --vault-id prod@prompt prod-secrets.yml
  • Encryption du fichier prod-secrets.yml en utilisant le fichier de password mypasswd_file.txt:
ansible-vault create --vault-id prod@mypasswd_file.txt prod-secrets.yml

ansible-lint

Special attributes

Delegation

Local playbook

- hosts: 127.0.0.1
  connection: local

Or from the cli:

ansible-playbook playbook.yml --connection=local
ansible-playbook playbook.yml -c local

Delegate to a remote host

- name:
    ansible.builtin.command:
      cmd: hostname
    delegate_to: remote_host

Delegate facts

By default, any fact gathered by a delegated task are assigned to the inventory_hostname (the current host) instead of the host which actually produced the facts (the delegated to host). The directive delegate_facts may be set to True to assign the task’s gathered facts to the delegated host instead of the current one.:

- hosts: app_servers
  tasks:
    - name: gather facts from db servers
      setup:
      delegate_to: "{{ item }}"
      delegate_facts: True
      loop: "{{ groups['dbservers'] }}"

The above will gather facts for the machines in the dbservers group and assign the facts to those machines and not to app_servers. This way you can lookup hostvars[‘dbhost1’][‘ansible_default_ipv4’][‘address’] even though dbservers were not part of the play, or left out by using –limit.

Run locally

- name: Local action
  local_action:
    module: ansible.builtin.command
    cmd: hostname

Only run once time

- name: Send summary mail
  local_action:
    module: community.general.mail
    subject: "Summary Mail"
    to: "{{ mail_recipient }}"
    body: "{{ mail_body }}"
  run_once: True

Import / Include

If you really want include_role to apply tags to all tasks inside the role, then you need to use the apply option:

- hosts: localhost
  gather_facts: no

  tasks:
    - name: Apply tags to only this task (include_role)
      include_role:
        name: example
        apply:
          tags:
            - install
      tags:
        - install

    - debug:
        msg: "include_role completed"
      tags:
        - install

    - name: Apply tags to tasks inside the role (import_role)
      import_role:
        name: example
      tags:
        - install

And run ansible-playbook test.yml --tags=install.

Roles

Collections

mynamespace/
└── mycollection/
  ├── docs/
  ├── galaxy.yml
  ├── plugins/
  │   ├── modules/
  │   │   └── module1.py
  │   ├── inventory/
  │   └── .../
  ├── README.md
  ├── roles/
  │   ├── role1/
  │   ├── role2/
  │   └── .../
  ├── playbooks/
  │   ├── files/
  │   ├── vars/
  │   ├── templates/
  │   └── tasks/
  └── tests/
  • To create a collection with roles:
$ mkdir collections/ansible_collections/
$ cd collections/ansible_collections/
$ ansible-galaxy collection init gigix.demo
$ cd gigix/demo
# https://docs.ansible.com/ansible/latest/dev_guide/developing_collections_structure.html#playbooks-directory
$ mkdir playbooks/{files,vars,templates,tasks}
$ cd roles
$ ansible-galaxy init test
  • Now you need to configure the collection PATH with one of them (cf documentation):

    • environment variable: ANSIBLE_COLLECTIONS_PATH=collections:/usr/share/ansible/collections
    • configuration file ansible.cfg: collections_path=collections:/usr/share/ansible/collections
  • Using a playbook from a collection

ansible-playbook my_namespace.my_collection.playbook1 -i ./myinventory

In this demo you can try ansible-playbook -i inventory.yml -c local gigix.demo.playbook to call the playbook (cf gigix.demo.playbook).

  • Call a module and a role from an ansible collection:
tasks:
  - name: Test my module
    gigix.demo.mymodule:
      number: 11

  roles:
  - gigix.demo.myrole

But if you use references to the same collection and namespace, you can simplify the code without specifying the collection.namespace (gigix.demo) like that:

tasks:
  - name: Test my module
    mymodule:
      number: 11

  roles:
  - myrole

Variables

Special variables

omit

Examples:

id: "{{ (openstack_networks | default({})).id | default(omit) }}"
id: "{{ omit if openstack_networks.id is not defined else openstack_networks.id }}"
- name: Touch files with an optional mode
  ansible.builtin.file:
    dest: "{{ item.path }}"
    state: touch
    mode: "{{ item.mode | default(omit) }}"
  loop:
    - path: /tmp/foo
    - path: /tmp/bar
    - path: /tmp/baz
      mode: "0444"

undef

The undef filter is a convenient way, for example for a role, to ask the user to define a variable upstream. This example will fail if demo_variable is undefined.

demo_variable: "{{ undef(hint='Please provide a demo variable') }}"

If demo_variable is not defined, it fails:

$ ansible-playbook playbook.yml
...
fatal: [localhost]: FAILED! =>
  msg: |-
    The task includes an option with an undefined variable. The error was: {{ undef(hint='Please provide a demo variable') }}: Please provide a demo variable. Please provide a demo variable. {{ undef(hint='Please provide a demo variable') }}: Please provide a demo variable. Please provide a demo variable

    The error appears to be in '/home/gigi/Documents/python/ansible/roles/demo/tasks/main.yml': line 34, column 7, but may
    be elsewhere in the file depending on the exact syntax problem.

    The offending line appears to be:


        - name: test
          ^ here
...

To success you need to pass the variable in inventory, group_vars, etc...:

ansible-playbook playbook.yml -e demo_variable=gigix

Loop

loop_control:
  extended: true
  • Simple loop:
- name: Fail if return code is not 0
  ansible.builtin.fail:
    msg: "The command ({{ item.cmd }}) did not have a 0 return code"
  when: item.rc != 0
  loop: "{{ echo.results }}"
  • register and changed_when:
- name: Place the result of the current item in the variable
  ansible.builtin.shell: echo "{{ item }}"
  loop:
    - one
    - two
  register: echo
  changed_when: echo.stdout != "one"
  • until:
- name: Retry a task until a certain condition is met
  ansible.builtin.shell: /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10
  • query:
- name: Show all the hosts matching the pattern, ie all but the group www
  ansible.builtin.debug:
    msg: "{{ item }}"
  loop: "{{ query('inventory_hostnames', 'all:!www') }}"
  • control output with label:
- name: Create servers
  digital_ocean:
    name: "{{ item.name }}"
    state: present
  loop:
    - name: server1
      disks: 3gb
      ram: 15Gb
      network:
        nic01: 100Gb
        nic02: 10Gb
        ...
  loop_control:
    label: "{{ item.name }}"
  • Add pause between loop:
- name: Create servers, pause 3s before creating next
  community.digitalocean.digital_ocean:
    name: "{{ item }}"
    state: present
  loop:
    - server1
    - server2
  loop_control:
    pause: 3
  • Add an index variable (index_var is 0 indexed):
- name: Count our fruit
  ansible.builtin.debug:
    msg: "{{ item }} with index {{ my_idx }}"
  loop:
    - apple
    - banana
    - pear
  loop_control:
    index_var: my_idx

Query / Lookup

loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"

Conditions

when: inventory_hostname != 'hostname_to_exclude'
  • test condition from a command:
- name: Register a variable, ignore errors and continue
  ansible.builtin.command: /bin/false
  register: result
  ignore_errors: true

- name: Run only if the task that registered the "result" variable fails
  ansible.builtin.command: /bin/something
  when: result is failed

- name: Run only if the task that registered the "result" variable succeeds
  ansible.builtin.command: /bin/something_else
  when: result is succeeded

- name: Run only if the task that registered the "result" variable is skipped
  ansible.builtin.command: /bin/still/something_else
  when: result is skipped

- name: Run only if the task that registered the "result" variable changed something.
  ansible.builtin.command: /bin/still/something_else
  when: result is changed
  • test if not defined
- include_tasks: other_tasks.yml
  when: x is not defined

- name: Fail if "bar" is undefined
  ansible.builtin.fail: msg="Bailing out. This play requires 'bar'"
  when: bar is undefined
  • multiple conditions:
- name: Shut down CentOS 6 and Debian 7 systems
  ansible.builtin.command: /sbin/shutdown -t now
  when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
        (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
  • set fact and test from it:
- name: Get the CPU temperature
  set_fact:
    temperature: "{{ ansible_facts['cpu_temperature'] }}"

- name: Restart the system if the temperature is too high
  when: temperature | float > 90
  shell: "reboot"
  • loop condition:
- name: Run with items greater than 5
  ansible.builtin.command: echo {{ item }}
  loop: [ 0, 2, 4, 6, 8, 10 ]
  when: item > 5

Error handling in playbooks

ignore_errors: true
ignore_unreachable: true
- name: Check if a file exists in temp and fail task if it does
  ansible.builtin.command: ls /tmp/this_should_not_be_here
  register: result
  failed_when:
    - result.rc == 0 or result.rc >= 10
    - '"No such" not in result.stdout'

- name: example of many failed_when conditions with OR
  ansible.builtin.shell: "./myBinary"
  register: ret
  failed_when: >
    ("No such file or directory" in ret.stdout) or
    (ret.stderr != '') or
    (ret.rc == 10)
- name: Report 'changed' when the return code is not equal to 2
  ansible.builtin.shell: /usr/bin/billybass --mode="take me to the river"
  register: bass_result
  changed_when: "bass_result.rc != 2"

- name: This will never report 'changed' status
  ansible.builtin.shell: wall 'beep'
  changed_when: False

- name: Combine multiple conditions to override 'changed' result
  ansible.builtin.command: /bin/fake_command
  register: result
  ignore_errors: True
  changed_when:
    - '"ERROR" in result.stderr'
    - result.rc == 2
- name: Run this command and ignore the result
  ansible.builtin.shell: /usr/bin/somecommand || /bin/true
- hosts: somehosts
  any_errors_fatal: true
  roles:
    - myrole

- hosts: somehosts
  tasks:
    - block:
        - include_tasks: mytasks.yml
      any_errors_fatal: true

Check mode

To enable check mode ansible-playbook foo.yml --check or ansible-playbook foo.yml -C.

- name: This task will always make changes to the system
  ansible.builtin.command: /something/to/run --even-in-check-mode
  check_mode: false

- name: This task will never make changes to the system
  ansible.builtin.lineinfile:
    line: "important config"
    dest: /path/to/myconfig.conf
    state: present
  check_mode: true
  register: changes_to_important_config

- name: This task will be skipped in check mode
  ansible.builtin.git:
    repo: ssh://[email protected]/mylogin/hello.git
    dest: /home/mylogin/hello
  when: not ansible_check_mode

- name: This task will ignore errors in check mode
  ansible.builtin.git:
    repo: ssh://[email protected]/mylogin/hello.git
    dest: /home/mylogin/hello
  ignore_errors: "{{ ansible_check_mode }}"

Diff mode

To enable check mode ansible-playbook foo.yml --diff or ansible-playbook foo.yml -D.

- name: This task will not report a diff when the file changes
  ansible.builtin.template:
    src: secret.conf.j2
    dest: /etc/secret.conf
    owner: root
    group: root
    mode: '0600'
  diff: false

Block

- name: Attempt and graceful roll back demo
  block:
    - name: Print a message
      ansible.builtin.debug:
        msg: 'I execute normally'

    - name: Force a failure
      ansible.builtin.command: /bin/false

    - name: Never print this
      ansible.builtin.debug:
        msg: 'I never execute, due to the above task failing, :-('
  rescue:
    - name: Print when errors
      ansible.builtin.debug:
        msg: 'I caught an error'

    - name: Force a failure in middle of recovery! >:-)
      ansible.builtin.command: /bin/false

    - name: Never print this
      ansible.builtin.debug:
        msg: 'I also never execute :-('
  always:
    - name: Always do this
      ansible.builtin.debug:
        msg: "This always executes"

Timeout

- name: without timeout (check every 2 seconds if the task has finished with a timeout set to 10s)
  ansible.builtin.command:
    cmd: sleep 2
  async: 10
  poll: 2
  changed_when: false

- name: With timeout (check every 1 second to a timeout set to 1 sec)
  ansible.builtin.command:
    cmd: sleep 2
  async: 1
  poll: 1
  changed_when: false

Strategy

ansible-doc -t strategy -l

Filters

Some Jinja2 filter can be used with tests functions:

  • boolean(value: Any) → bool: return true if the object is a boolean value
  • callable(obj, /): return whether the object is callable (i.e., some kind of function). Note that classes are callable, as are instances of classes with a __call__() method.
  • defined(value: Any) → bool: return true if the variable is defined.
  • divisibleby(value: int, num: int) → bool: check if a variable is divisible by a number.
  • eq | == | equalto(a, b, /): same as a == b.
  • escaped(value: Any) → bool: check if the value is escaped.
  • even(value: int) → bool: return true if the variable is even.
  • false(value: Any) → bool: return true if the object is False.
  • filter(value: str) → bool: check if a filter exists by name. Useful if a filter may be optionally available.
  • float(value: Any) → bool: return true if the object is a float.
  • ge | >=(a, b, /): same as a >= b.
  • gt | > | greaterthan(a, b, /): same as a > b.
  • in(value: Any, seq: Container[Any]) → bool: check if value is in seq.
  • integer(value: Any) → bool: return true if the object is an integer.
  • iterable(value: Any) → bool: check if it’s possible to iterate over an object.
  • le | <=(a, b, /): same as a <= b.
  • lower(value: str) → bool: return true if the variable is lowercased.
  • lt | < | lessthan(a, b, /): same as a < b.
  • mapping(value: Any) → bool: return true if the object is a mapping (dict etc.).
  • ne | !=(a, b, /): same as a != b.
  • none(value: Any) → bool: return true if the variable is none.
  • number(value: Any) → bool: return true if the variable is a number.
  • odd(value: int) → bool: return true if the variable is odd.
  • sameas(value: Any, other: Any) → bool: check if an object points to the same memory address than another object.
  • sequence(value: Any) → bool: return true if the variable is a sequence. Sequences are variables that are iterable.
  • string(value: Any) → bool: return true if the object is a string.
  • test(value: str) → bool: check if a test exists by name. Useful if a test may be optionally available.
  • true(value: Any) → bool: return true if the object is True.
  • undefined(value: Any) → bool: like defined() but the other way round
  • upper(value: str) → bool: return true if the variable is uppercased.

select / reject

Filter select can be used with a test function.

{{ numbers | select("odd") }}
{{ numbers | select("divisibleby", 3) }}
{{ numbers | select("lessthan", 42) }}
{{ strings | select("equalto", "mystring") }}
$ ansible -i "127.0.0.1," all -c local -m debug -a msg="{{ range(1, 10, 4) | select('odd') }}"
$ ansible -i "127.0.0.1," all -c local -e myvar="[1,2,3]" -m debug -a msg="{{ map('int') | reject('even') }}"

selectattr / rejectattr

Filter selectattr can be used with a test function.

{{ _cmd.results | selectattr('rc', 'defined') | selectattr('rc', '==', 0) | map(attribute='stdout') | last }}
$ ansible -i "127.0.0.1," all -c local -m debug -a msg="{{ [{'key1':'val1', 'key2': 'val2'}, {'key1':'val1', 'key3': 'val3'}] | rejectattr('key1') }}"
$ ansible -i "127.0.0.1," all -c local -m debug -a msg="{{ [{'key1':'val1', 'key2': 'val2'}, {'key1':'val1', 'key3': 'val3'}] | selectattr('key2', 'undefined') }}"

sort

{{ (ansible_facts.mounts | selectattr('mount', 'in', path) | list | sort(attribute='mount'))[-1]['mount'] }}

map

{{ users | map(attribute='username') | join(', ') }}
ansible -i "127.0.0.1," all -c local -e myvar="[1,2,3]" -m debug -a msg="{{ myvar | map('int') | select('odd') }}"

ansible.builtin.flatten

- name: Show extracted list of keys from a list of dictionaries
  ansible.builtin.debug:
    msg: "{{ chains | map('extract', chains_config) | map(attribute='configs') | flatten | map(attribute='type') | flatten | unique }}"
  vars:
    chains: [1, 2]
    chains_config:
        1:
            foo: bar
            configs:
                - type: routed
                  version: 0.1
                - type: bridged
                  version: 0.2
        2:
            foo: baz
            configs:
                - type: routed
                  version: 1.0
                - type: bridged
                  version: 1.1

ansible.builtin.ternary

- name: Enable a list of Windows features, by name
  ansible.builtin.set_fact:
    win_feature_list: "{{ namestuff | reject('equalto', omit) | list }}"
  vars:
    namestuff:
      - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}"
      - "foo"
      - "bar"

community.general.groupby_as_dict

- name: Output mount facts grouped by device name
  ansible.builtin.debug:
    var: ansible_facts.mounts | community.general.groupby_as_dict('device')

- name: Output mount facts grouped by mount point
  ansible.builtin.debug:
    var: ansible_facts.mounts | community.general.groupby_as_dict('mount')

Custom module

Modules

ansible.builtin.ping

ansible-doc -t module ansible.builtin.ping
ansible all -m ping

ansible.builtin.raw

ansible-doc -t module ansible.builtin.raw
ansible all -i "127.0.0.1," -m raw -a "id -u"

ansible.builtin.shell

ansible-doc -t module ansible.builtin.shell
ansible -i "127.0.0.1," all -m shell -a "getent passwd | egrep -w root"

ansible.builtin.async_status

- name: Async (no wait)
  ansible.builtin.command:
    cmd: sleep 10
  async: 20
  poll: 0
  register: _full_async
  changed_when: false

- name: Wait full async task
  ansible.builtin.async_status:
    jid: "{{ _full_async.ansible_job_id }}"
  register: _async_result
  until: _async_result.finished
  retries: 10
  delay: 10

module ansible.builtin.setup

ansible-doc -t module ansible.builtin.setup
$ ansible all -m setup
$ ansible all -m setup -a 'filter=*ip*'
$ ansible all -m setup -a 'gather_subset=default_ipv4'

ansible.builtin.gather_facts

ansible-doc -t module ansible.builtin.setup
  • Gathers facts about remote hosts:
# Display facts from all hosts and store them indexed by hostname at /tmp/facts.
ansible all -m ansible.builtin.gather_facts --tree /tmp/facts/localhost

ansible.builtin.debug

ansible-doc -t module ansible.builtin.debug
$ ansible all -m debug -a msg="'Hello {{ var1 }}'"
$ ansible all -m debug -a var=var1

ansible.builtin.service

ansible-doc -t module ansible.builtin.service
ansible all -m service -a "name=ssh state=started"

ansible.builtin.user

ansible-doc -t module ansible.builtin.user
ansible all -m user -a "'name=foo password={{ \'password\' | password_hash(\'sha512\') }}'"

ansible.builtin.group

ansible-doc -t module ansible.builtin.group
ansible all -m group -a "name=team state=present"

ansible.builtin.file

ansible-doc -t module ansible.builtin.file
ansible all -m file -a "dest=/opt/bmc.txt mode=755 owner=ec2-user"

module ansible.builtin.stat

ansible-doc -t module ansible.builtin.stat
ansible all -m stat -a "path=/etc/passwd"

ansible.builtin.lineinfile

ansible-doc -t module ansible.builtin.lineinfile
ansible all -m ansible.builtin.lineinfile -C -D -a 'dest=/etc/passwd regexp="^root:(.*)$" line="gigix:\1" state=present backrefs=yes backup=yes'
- name: Create MySQL client config (Debian os family)
  copy:
    dest: "/root/.my.cnf"
    content: |
      [client]
      user=root
      password={{ root_password }}
    mode: 0400

ansible.builtin.blockinfile

- name: Insert/Update "Match User" configuration block in /etc/ssh/sshd_config prepending and appending a new line
  ansible.builtin.blockinfile:
    path: /etc/ssh/sshd_config
    append_newline: true
    prepend_newline: true
    block: |
      Match User ansible-agent
      PasswordAuthentication no

- name: Insert/Update configuration using a local file and validate it
  ansible.builtin.blockinfile:
    block: "{{ lookup('ansible.builtin.file', './local/sshd_config') }}"
    path: /etc/ssh/sshd_config
    backup: yes
    validate: /usr/sbin/sshd -T -f %s

module ansible.builtin.template

Additional variables listed below can be used in templates:

  • ansible_managed (configurable via the defaults section of ansible.cfg) contains a string which can be used to describe the template name, host, modification time of the template file and the owner uid.

  • template_host contains the node name of the template’s machine.

  • template_uid is the numeric user id of the owner.

  • template_path is the path of the template.

  • template_fullpath is the absolute path of the template.

  • template_destpath is the path of the template on the remote system (added in 2.8).

  • template_run_date is the date that the template was rendered.

  • Template a file out to a target host

ansible-doc -t module ansible.builtin.template
- name: Declare interface ib0
  register: ib0
  template:
    src=files/ifcfg-ib0.j2
    dest=/etc/sysconfig/network/ifcfg-ib0
    owner=root
    group=root
    mode=0644
  with_items:
    - { ip: 192.168.1.{{ ansible_eth0.ipv4.address.split(".")[-1] }}' }
  notify: UP interface ib0

ansible.posix.synchronize

ansible-doc -t module ansible.posix.synchronize
- name: Synchronization using rsync protocol (push)
  ansible.posix.synchronize:
    src: some/relative/path/
    dest: rsync://somehost.com/path/

- name: Synchronization using rsync protocol (pull)
  ansible.posix.synchronize:
    mode: pull
    src: rsync://somehost.com/path/
    dest: /some/absolute/path/

- name:  Synchronization using rsync protocol on delegate host (push)
  ansible.posix.synchronize:
    src: /some/absolute/path/
    dest: rsync://somehost.com/path/
  delegate_to: delegate.host

- name: Synchronization using rsync protocol on delegate host (pull)
  ansible.posix.synchronize:
    mode: pull
    src: rsync://somehost.com/path/
    dest: /some/absolute/path/
  delegate_to: delegate.host

ansible.builtin.copy

ansible-doc -t module ansible.builtin.copy
ansible all -m copy -a 'src=/tmp/src.txt dest=/tmp/dest.txt'

ansible.builtin.fetch

ansible-doc -t module ansible.builtin.fetch
ansible all -m fetch -a 'src=/tmp/src.txt dest=/tmp/dest.txt'

ansible.builtin.reboot

ansible-doc -t module ansible.builtin.reboot
ansible all -m reboot -a "reboot_timeout=3600"

ansible.builtin.include_vars

ansible-doc -t module ansible.builtin.include_vars
  • Include vars file from a role and merge all. The last file override the others:
- name: Merge all file vars found
  ansible.builtin.include_vars: "{{ item }}"
  loop:
    # - default.yml # use main.yml instead (this is the default file)
    - "{{ ansible_os_family }}.yml"
    - "{{ ansible_distribution }}.yml"
    - "{{ ansible_distribution }}{{ ansible_distribution_version }}.yml"
  when:
    - ([path, item] | path_join) in query("ansible.builtin.fileglob", "{}/*.yml".format(path))
  vars:
    path: "{{ [role_path, 'vars'] | path_join }}"
  • Include vars file from a role and stop at the 1st file found (don't continue on other files):
- name: Include vars with first_found
  ansible.builtin.include_vars: "{{ lookup('ansible.builtin.first_found', params) }}"
  vars:
    params:
      files:
        - "{{ ansible_distribution }}{{ ansible_distribution_version }}.yml"
        - "{{ ansible_distribution }}.yml"
        - "{{ ansible_os_family }}.yml"
        - default.yml
      paths:
        - "{{ [role_path, 'vars'] | path_join }}"
  • Or with nested directories structure:
- name: Merge all file vars found
  ansible.builtin.include_vars: "{{ item }}"
  loop:
    - "{{ ansible_distribution }}/main.yml"
    - "{{ ansible_distribution }}/{{ ansible_distribution_major_version }}/main.yml"
    - "{{ ansible_distribution }}/{{ ansible_distribution_major_version }}/{{ ansible_distribution_version }}.yml"
  when:
    - ([path, item] | path_join) in query("community.general.filetree", path) | selectattr("state", "in", "file") | map(attribute="src") | list
  vars:
    path: "{{ [role_path, 'vars'] | path_join }}"
  • Or with nested directories structure and also search with absolute paths:
- name: Merge all file vars found
  ansible.builtin.include_vars: "{{ item }}"
  loop:
    - "{{ ansible_distribution }}/main.yml"
    - "{{ ansible_distribution }}/{{ ansible_distribution_major_version }}/main.yml"
    - "{{ ansible_distribution }}/{{ ansible_distribution_major_version }}/{{ ansible_distribution_version }}.yml"
    - "{{ role_path }}/defaults/main.yml"
  when:
    - item.startswith('/') | ternary(item, [path, item] | path_join) in query("community.general.filetree", item.startswith('/') | ternary(item | dirname, path)) | selectattr("state", "in", "file") | map(attribute="src") | list
  vars:
    path: "{{ [role_path, 'vars'] | path_join }}"

ansible.posix.authorized_key

ansible-doc -t module ansible.posix.authorized_key
- name: Set authorized key taken from file
  ansible.posix.authorized_key:
    user: charlie
    state: present
    key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
    key_options: 'no-port-forwarding,from="10.0.1.1"'

ansible.builtin.expect

ansible-doc -t module ansible.builtin.expect module
- name: Case insensitive password string match
  ansible.builtin.expect:
    command: passwd username
    responses:
      (?i)password: "MySekretPa$$word"
  # you don't want to show passwords in your logs
  no_log: true

ansible.builtin.fail

ansible-doc -t module ansible.builtin.fail
ansible all -m ansible.builtin.fail -a "msg='Oops!'"

ansible.builtin.assert

ansible-doc -t module ansible.builtin.assert
$ ansible all -m assert -a "that='\'test\' != \'test\'' msg='OMG'"
$ ansible all -m assert -a "that='\'test\' == \'test\'' fail_msg='OMG' success_msg='Bingo'"
- name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of
    authselect current profile
  ansible.builtin.command:
    cmd: authselect check
  register: result_authselect_check_cmd
  changed_when: false
  failed_when: false

- name: Ensure PAM Displays Last Logon/Access Notification - Informative message
    based on the authselect integrity check result
  ansible.builtin.assert:
    that:
    - result_authselect_check_cmd.rc == 0
    fail_msg:
    - authselect integrity check failed. Remediation aborted!
    - This remediation could not be applied because an authselect profile was
      not selected or the selected profile is not intact.
    - It is not recommended to manually edit the PAM files when authselect tool
      is available.
    - In cases where the default authselect profile does not cover a specific
      demand, a custom authselect profile is recommended.
    success_msg:
    - authselect integrity check passed

ansible.builtin.meta

ansible-doc -t module ansible.builtin.meta
ansible all -m ansible.builtin.meta -a "refresh_inventory"

ansible.builtin.group_by

- name: Group_by playbook
  hosts: all
  tasks:
    - name: Create a group of all hosts by operating system
      ansible.builtin.group_by:
        key: "{{ ansible_distribution }}-{{ ansible_distribution_version }}"

- name: CentOS-6.2
  hosts: CentOS-6.2
  tasks:
    - name: Ping all CentOS 6.2 hosts
      ansible.builtin.ping:

- name: CentOS-6.3
  hosts: CentOS-6.3
  tasks:
    - name: Ping all CentOS 6.3 hosts
      ansible.builtin.ping:

Testing

Molecule

molecule init scenario
molecule init scenario --verifier-name ansible --driver-name vagrant
  • Files within molecule/default:

  • create.yml is a playbook file used for creating the instances and storing data in instance-config

  • destroy.yml has the Ansible code for destroying the instances and removing them from instance-config

  • molecule.yml is the central configuration entry point for Molecule per scenario. With this file, you can configure each tool that Molecule will employ when testing your role. converge.yml is the playbook file that contains the call for your role. Molecule will invoke this playbook with ansible-playbook and run it against an instance created by the driver.

  • Example with Podman

  • Example molecule.yml for Vagrant:

---
dependency:
  name: galaxy
driver:
  name: vagrant
platforms:
  - name: instance
    box: generic/ubuntu2204
    memory: 512
    cpus: 1
provisioner:
  name: ansible
verifier:
  name: ansible
lint: |
  yaml-lint

ansible-test

sanity

ansible-test sanity --test pep8
# Run all sanity tests
ansible-test sanity

# Run all sanity tests against against certain files
ansible-test sanity plugins/modules/files/eos_acls.py

# Run all tests with a specific version of python (3.7 in this case)
ansible-test sanity --python 3.7

# Run all tests inside docker (good if you don't have dependencies installed)
ansible-test sanity --docker default

# Run validate-modules against a specific file
ansible-test sanity --test validate-modules lib/ansible/modules/files/template.py
  • To list all the sanity tests available:
ansible-test sanity --list-tests

units

# Run all tests inside docker (good if you don't have dependencies installed)
ansible-test units --docker -v

# Only runs if the module directory path and unit test file path are similar
ansible-test units --docker -v apt

# Or against a specific python version by doing:
ansible-test units --docker -v --python 2.7 apt

# If you are running unit tests against things other than modules, such as module utilities, specify the whole file path:
ansible-test units --docker -v test/units/module_utils/basic/test_imports.py

coverage

ansible-test coverage erase
ansible-test units --coverage apt
ansible-test coverage html

Unit Tests

Testinfra

ansible_demo's People

Contributors

gigi206 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.