# Provisioning

# Ansible

CommandMeaningdefaultSaltStack equivalent
--checkDry runno dry runtest=True
-b, --becomerun operations with becomeno password prompting
-K, --ask-become-passask for privilege escalation password
--become-method=BECOME_METHODprivilege escalation method to use valid choices: [ sudo su pbrun pfexec doas dzdo ksu runas pmrun enable machinectl sudosudo
--become-user=BECOME_USERrun operations as this userroot
Examplemeaning
ansible-playbook playbook.yml --user=b.dauphin --become-method=su -b -Ksu b.dauphin + password prompting
ansible-playbook playbook.yml --check --diff --limit 1.2.3.4Dry run + show only diff + limit inventory to host 1.2.3.4

# Examples

cd inventory
ls *.yml | xargs -I % ansible --user=baptiste -i % all -m shell  -a 'grep 8.8.8.8 /etc/resolv.conf || echo MISCONFIGURED'
1
2

Specify python interpreter path

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

list available variables

ansible 10.10.10.10 -m setup
1

get specific fact

ansible 10.10.10.10 -m setup -a 'filter=ansible_python_version'
1

Playbook start at a specific task

ansible-playbook --start-at-task="Gather Networks Facts into Variable"
1
ansible webservers -m service -a "name=httpd state=restarted"
ansible all -m ping -u user1 --private-key /home/baptiste/.ssh/id_rsa
1
2

enable usage of operations like < > | &
the remote system has to got the package python-apt

apt install python-apt
- debug: var=ansible_facts
1
2
# start only specific task
ansible-playbook --tags "docker_login"
1
# Debug ansible playbook
[...]
msg: "{{ lookup('vars', ansible_dns) }}"
[...]
1
2
3
[...]
- name: Gather Networks Facts into Variable
  setup:
  register: setup

- name: Debug Set Facts
  debug:
    var: setup.ansible_facts.ansible_python_version
1
2
3
4
5
6
7
8

# Variables

# Inside Playbook


---
- hosts: webservers
  vars:
    syslog_protocol_lvl_4: udp
    syslog_port: 514
    ansible_python_interpreter: /bin/python
    ansible_ssh_user: root
1
2
3
4
5
6
7
8

# Cli override

ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'
1
2

override playbook-defined variables (keep your playbook unmodified)

ansible-playbook lvm.yml --extra-vars "host=es_data_group remote_user=b.dauphin" -i ../inventory/es_data_staging.yml

1
2

Full doc of passing-variables-on-the-command-lineopen in new window

# Precedence

Where should I put a variable ? Here is the order of precedence from least to greatest (the last listed variables winning prioritization):

  • command line values (eg “-u user”)
  • role defaults [1]
  • inventory file or script group vars [2]
  • inventory group_vars/all [3]
  • playbook group_vars/all [3]
  • inventory group_vars/* [3]
  • playbook group_vars/* [3]
  • inventory file or script host vars [2]
  • inventory host_vars/* [3]
  • playbook host_vars/* [3]
  • host facts / cached set_facts [4]
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • include_vars
  • set_facts / registered vars
  • role (and include_role) params
  • include params
  • extra vars (always win precedence)

# Lookup plugin

You can load data from various sources like : k8s, ini, file, vault, mongoDB, env var, etcd To get list of available lists of lookup plugins, you can use below command: –

ansible-doc -t lookup -l
1

Example

ansible_become_pass: "{{ lookup('hashi_vault', 'secret=secret/data/path/to/secret'+ ':data url=https://vault.example.com validate_certs=true cacert=/etc/ssl/certs/ca-certificates.crt').root}}"


1
2
3

# SaltStack

# Key management

--list=$ARGdefinition
pre,un,unacceptedlist unaccepted/unsigned keys.
acc or acceptedlist accepted/signed keys.
rej or rejectedlist rejected keys
den or deniedlist denied keys
alllist all above keys

# Targeting

salt -S 192.168.40.20 test.version
salt -S 192.168.40.0/24 test.version
1
2

Compound match

salt -C 'S@10.0.0.0/24 and G@os:Debian' test.version
salt -C '( G@environment:staging or G@environment:production ) and G@soft:redis*' test.ping
1
2

full docopen in new windowCompound matchersopen in new window

# Various useful module

salt '*' network.ip_addrs
salt '*' cmd.run
salt '*' state.Apply
salt '*' test.ping
salt '*' test.version
salt '*' grains.get
salt '*' grains.item
salt '*' grains.items
salt '*' grains.ls
1
2
3
4
5
6
7
8
9

# Diff

Diff between 2 servers

salt-run survey.diff '*' cmd.run "ls /home"
1

# Saltutils

Signal the minion to refresh the pillar data.

salt '*' saltutil.refresh_pillar
1

synchronizes custom modules, states, beacons, grains, returners, output modules, renderers, and utils.

salt '*' saltutil.sync_all
1

Forcibly removes all caches on a minion. WARNING: The safest way to clear a minion cache is by first stopping the minion and then deleting the cache files before restarting it.
soft way

salt '*' saltutil.clear_cache
1

sure way

systemctl stop salt-minion \
&& rm -rf /var/cache/salt/minion/ \
&& systemctl start salt-minion
1
2
3

# Grains

# Common

    - SSDs
    - biosreleasedate
    - biosversion
    - cpu_flags
    - cpu_model
    - cpuarch
    - disks
    - dns
    - domain
    - fqdn
    - fqdn_ip4
    - fqdn_ip6
    - gid
    - gpus
    - groupname
    - host
    - hwaddr_interfaces
    - id
    - init
    - ip4_gw
    - ip4_interfaces
    - ip6_gw
    - ip6_interfaces
    - ip_gw
    - ip_interfaces
    - ipv4
    - ipv6
    - kernel
    - kernelrelease
    - kernelversion
    - locale_info
    - localhost
    - lsb_distrib_codename
    - lsb_distrib_id
    - machine_id
    - manufacturer
    - master
    - mdadm
    - mem_total
    - nodename
    - num_cpus
    - num_gpus
    - os
    - os_family
    - osarch
    - oscodename
    - osfinger
    - osfullname
    - osmajorrelease
    - osrelease
    - osrelease_info
    - path
    - pid
    - productname
    - ps
    - pythonexecutable
    - pythonpath
    - pythonversion
    - saltpath
    - saltversion
    - saltversioninfo
    - selinux
    - serialnumber
    - server_id
    - shell
    - swap_total
    - systemd
    - uid
    - username
    - uuid
    - virtual
    - zfs_feature_flags
    - zfs_support
    - zmqversion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

# grains containing 'os'

    os:
        Debian
    os_family:
        Debian
    osarch:
        amd64
    oscodename:
        stretch
    osfinger:
        Debian-9
    osfullname:
        Debian
    osmajorrelease:
        9
    osrelease:
        9.5
    osrelease_info:
        - 9
        - 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Upgrade Salt-Minion

# State format

Upgrade Salt-Minion:
  cmd.run:
    - name: |
        exec 0>&- # close stdin
        exec 1>&- # close stdout
        exec 2>&- # close stderr
        nohup /bin/sh -c 'salt-call --local pkg.install salt-minion && salt-call --local service.restart salt-minion' &
    - onlyif: "[[ $(salt-call --local pkg.upgrade_available salt-minion 2>&1) == *'True'* ]]" 
1
2
3
4
5
6
7
8

# Bash script format

Upgrade salt-minion bash scriptopen in new window

# Useful exemple

# Netcat
salt -C "minion.local or minion2.local" \
> cmd.run "docker run debian /bin/bash -c 'http_proxy=http://10.100.100.100:1598 apt update ; http_proxy=http://10.100.100.100:1598 apt install netcat -y ; nc -zvn 10.3.3.3 3306' | grep open"
minion.local:
    
    WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
    
    
    WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
    
    debconf: delaying package configuration, since apt-utils is not installed
    (UNKNOWN) [10.3.3.3] 3306 (?) open

minion2.local:
    
    WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
    
    
    WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
    
    debconf: delaying package configuration, since apt-utils is not installed
    (UNKNOWN) [10.3.3.3] 3306 (?) open
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# MySQL connexion

Will print you the GRANTS for the user

echo "enter your password" ; read -s password ; \
salt "*" \
cmd.run "docker pull imega/mysql-client ; docker run --rm imega/mysql-client mysql --host=10.10.10.10 --user=b.dauphin --password=$password --database=db1 --execute='SHOW GRANTS FOR CURRENT_USER();'" \
env='{"http_proxy": "http://10.10.10.10:9999"}'
1
2
3
4

# Events

Salt provides a runner that displays events in real-time as they are received on the Salt master.

salt-run state.event pretty=True
1

Sourcesopen in new window

# Jinja2

# Filter list

https://www.tailored.cloud/devops/how-to-filter-and-map-lists-in-ansible/

# Playing with empty lines

{%   %}
{%-  %}
{%  -%}
{%- -%}
1
2
3
4

(By default) add an empty line before jinja rendering and add one after

{%  set site_url = 'www.' + domain  %}
1

remove the empty line before jinja rendering and add one after

{%- set site_url = 'www.' + domain  %}
1

add the empty line before jinja rendering and remove one after

{%  set site_url = 'www.' + domain -%}
1

remove the empty line before jinja rendering and remove one after

{%- set site_url = 'www.' + domain -%}
1

# Test variable existence

{% if min_verbose_level is defined
      and min_verbose_level      %}
    and level({{ min_verbose_level }} .. emerg);
{% endif %}
1
2
3
4

# Templating

Jinja2 is a rendering layer applied before the actual python (i.e. saltstack instruction) is interpreted.

{% set ipaddr = grains['fqdn_ip4'][0] %}
{% if (key | regex_match('.*dyn.company.tld.*', ignorecase=True)) != None %}
1
2
{% set syslog_java_application =
     pillar.get('syslog_java_application', {}) %}

{%  if syslog_java_application %}

{% set syslog_module_name = 
  syslog_java_application.get('name') %}
{% set types = 
  syslog_java_application.get('types') %}
{% set log_folder_path = 
  syslog_java_application.get('log_folder_path') %}
{% set log_file_name_prefix =
  syslog_java_application.get('log_file_name_prefix') %}

{% endif %}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% for type in types %}
  
{% set source_name = syslog_module_name ~ '_' ~ type %}
{% set file_full_path = log_folder_path ~ '/' ~ log_file_name_prefix ~ type ~ '.log' %}
{% set program_override = syslog_module_name ~ '_' ~ type %}

syslog-ng app_function_{{ type }} configuration:
  file.managed:
    - name: /etc/syslog-ng/conf.d/{{ syslog_module_name }}_{{ type }}.conf
    - source: salt://template.tmpl
    - template: jinja
    - user: root
    - group: root
    - mode: 644
    - watch_in:
      - service: syslog-ng
    - context:
        name: {{ source_name }}
        file_full_path: {{ file_full_path }}
        program_override: {{ program_override }}

{% endfor %}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# JSON parsing

# json_query_example.sls
{% set services = '
  {"services": [
    {"name": "http", "host": "1.2.3.4", "port": 80},
    {"name": "smtp", "host": "1.2.3.5", "port": 25},
    {"name": "ssh",  "host": "1.2.3.6", "port": 22}
  ]}' | load_json %}

{% set ports = services | json_query("services[].port") %}
1
2
3
4
5
6
7
8
9