# ansible

![ansible](images/ansible.png "ansible")<!-- .element width="30%" -->


## ansible

* outil de
  * provisioning
  * gestion de config
  * déploiemenet d'application

* racheté par RedHat en octobre 2015

* outils équivalents
  * puppet, chief, salt ...


## ansible Φ

* idempotence
* héritage
* réutilisabilité
* parallélisation
* automatisation

#### toute intervention manuelle sur un système est une faute ...

## ... GRAVE!


## ansible

* écrit en python
  * python 2 par défaut
  * marche bien en python 3 <3
    * virtualenv

* [<i class="fa fa-book" aria-hidden="true"></i> ansible](https://docs.ansible.com/)


## ansible

* prérequis
  * sur la machine pilote (mgmt node)
    * ansible (donc python)
  * sur le(s) noeud(s)
    * une connextion ssh ou PowerShell
    * python


## ansible

[![gestion ansible](images/ansible_mgmt.png "gestion ansible")](https://medium.com/formcept/configuration-management-and-continuous-deployment-cd0892dce998)


## terminologie

* **mgmt node** (machine pilote)
  * machine sur laquelle ansible est installé
  * pilote la configuration de toutes les machines de l'inventaire
* **inventory** (inventaire)
  * fichier contenant les ip ou les noms de domaine de toutes les machines à configurer
* **playbook**
  * gère la configuration à déployer sur chaque machine


## terminologie

* **task**
  * fichier où sont définies les actions réalisées par le playbook
* **module**
  * actions plus ou moins complexes, utilisables à partir des **tasks**
  * ansible possède de nombreux [<i class="fa fa-book" aria-hidden="true"></i> modules natifs](https://docs.ansible.com/ansible/latest/modules/modules_by_category.html)
  * il est possible d'écrire ses propres modules.
* **role**
  * permet d'organiser les playbooks en parties claires et réutilisables


## terminologie

* **play**
  * est l'exécution d'un playbook sur une machine
* **facts**
  * information collectée par ansible sur le système d'une machine à configurer
* **handlers**
  * similaire aux **tasks** mais appelable à partir d'une **task**
    * redémarrage de service par exemple


## inventory

* fichier texte au format *ini*
* organiser par groupes
* fixer des configurations
  * pour tous / par groupe / par machine
* déclarer des variables
  * pour tous / par groupe / par machine


## inventory

* [<i class="fa fa-book" aria-hidden="true"></i> inventory](https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html)

```ini
[criprod]
pvecriprod2.isima.fr
py.criprod.isima.fr
gitlab-runner1.criprod.isima.fr

[all:vars]
environment                = production
ansible_python_interpreter = /usr/bin/python3
ansible_user               = limosadm
```


## tips ssh

* utilisez ~/.ssh/config

```
Host pvecriprod2.isima.fr
  User limosadm
  IdentityFile ~/.ssh/keys/limosadm

Host py.criprod.isima.fr
  User limosadm
  IdentityFile ~/.ssh/keys/limosadm
  ProxyCommand ssh pvecriprod2.isima.fr -W %h:%p
```


## ad-hoc command

```
$ ansible all --inventory-file=inventory.ini --module-name ping

pvecriprod2.isima.fr | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
py.criprod.isima.fr | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
gitlab-runner1.criprod.isima.fr | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
```


## ad-hoc command

```
$ ansible criprod -a "/usr/bin/uptime"

pvecriprod2.isima.fr | CHANGED | rc=0 >>
 16:18:08 up 41 days, 23:11,  3 users,  load average: 0.63, 0.18, 0.10

py.criprod.isima.fr | CHANGED | rc=0 >>
 15:18:15 up 1 day, 21:51,  1 user,  load average: 0.13, 0.03, 0.01

gitlab-runner1.criprod.isima.fr | CHANGED | rc=0 >>
 15:18:15 up 1 day, 22:14,  1 user,  load average: 0.00, 0.00, 0.00
```


## ad-hoc command

* [<i class="fa fa-book" aria-hidden="true"></i> ad-hoc command](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html)
  * [Parallelism and Shell Commands](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#parallelism-and-shell-commands)
  * [File Transfer](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#file-transfer)
  * [Managing Packages](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#managing-packages)
  * [Users and Groups](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#users-and-groups)
  * [Deploying From Source Control](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#deploying-from-source-control)
  * [Managing Services](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#managing-services)
  * [Time Limited Background Operations](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#time-limited-background-operations)
  * [Gathering Facts](https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#gathering-facts)


## playbook

[<i class="fa fa-book" aria-hidden="true"></i> playbooks](https://docs.ansible.com/ansible/latest/user_guide/playbooks.html)

my-playbook.yml

```yaml
- name: my-playbook # ce que vous voulez
  hosts: criprod # ou all
                 # ou n'importe quel nom de machine
                 # ou n'importe quel nom de groupe
  remote_user: limosadm # prioritaire sur ansible_user de l'inventaire

  roles:

    - role: debug # le rôle debug sera exécuté par le playbook
      tags: debug # le tag debug sera ajouté à toutes les tâches du role debug
      my-variable: "pipo" # il ya d'autres endroit où mettre les variables
                          # à suivre ...
    - role: vault-cli # le rôle vault-cli sera exécuté par le playbook
      tags: vault # le tag vault sera ajouté à toutes les tâches du role vault-cli

```


## playbook

```shell
$ ansible-playbook my-playbook.yml
```

exécute le playbook sur toutes les machines de l'inventaire


## playbook

```shell
$ ansible-playbook my-playbook.yml --check --diff
```

`--check`

simule les tâches à effectuer sans les effectuer (dry-run)

`--diff`

indique ce qui change


## playbook

```shell
$ ansible-playbook my-playbook.yml -vvv
```

`-v`, `-vv`, `-vvv`, `-vvvv`

pour la verbosité


## playbook

```shell
$ ansible-playbook my-playbook.yml --list-tags
```

liste tous les tags disponibles dans le playbook

```shell
$ ansible-playbook my-playbook.yml --tags debug
```

n'exécute que les tâches du playbook ayant un tag `debug`

```shell
$ ansible-playbook my-playbook.yml --skip-tags debug
```

exécute toutes les tâches du playbook sauf celles ayant un  tag `debug`


## playbook

```shell
$ ansible-playbook my-playbook.yml --limit=py.isima.fr
```

exécute toutes les tâches du playbook sur py.isima.fr uniquement


## variables

* nommage
  * pas de `-` pas de `.`
  * pas de numérique pure


## variables

* affectation
  * [<i class="fa fa-book" aria-hidden="true"></i>  inventaire](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#defining-variables-in-inventory)
    * [<i class="fa fa-book" aria-hidden="true"></i> `group_vars`, `host_vars`, ou `inventaire.ini`](https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#hosts-and-groups)
  * [<i class="fa fa-book" aria-hidden="true"></i> playbook](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#defining-variables-in-a-playbook)
  * role
    * [<i class="fa fa-book" aria-hidden="true"></i> `default`](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-default-variables)
  * [<i class="fa fa-book" aria-hidden="true"></i> ligne de commande](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#passing-variables-on-the-command-line)

#### [précédence des variables](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable)


## jinja

[<i class="fa fa-book" aria-hidden="true"></i> templating](https://docs.ansible.com/ansible-container/container_yml/template.html)

group_vars/all.yml

```yaml
my-role: my-awesome-role
```

playbook.yml

```yaml
roles:

  - role: "{{ my-role }}"
```

utilisable partout (playbook, role, tasks, template)


## filters

[<i class="fa fa-book" aria-hidden="true"></i> filters](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html)

```yaml
"{{ item.path[:4] | replace('/', '-') }}"
```

* renvoie un path
  * sans les 4 derniers caractères
  * avec les `/` remplacés par des `-`


## lookup

[<i class="fa fa-book" aria-hidden="true"></i> lookup](https://docs.ansible.com/ansible/latest/plugins/lookup.html)

```yaml
vars:
  file_contents: "{{lookup('file', 'path/to/file.txt')}}"
```

[<i class="fa fa-book" aria-hidden="true"></i> lookup list](https://docs.ansible.com/ansible/latest/plugins/lookup.html#plugin-list)

[<i class="fa fa-book" aria-hidden="true"></i> callback](https://docs.ansible.com/ansible/latest/plugins/callback.html)


## variable & secret

```yaml
- debug:
  msg: "{{ lookup('env','PVE_NODE') }}"
```

lit une valeur à partir d'une variable d'environement

```yaml
- debug:
  msg: "{{ lookup('hashi_vault', 'secret=secret/hi:value token=xxx url=http://myvault')}}"
```

lit une valeur à partir Vault (<strike>`ansible-vault`</strike>)

```yaml
vars_prompt:
  - name: "name"
    prompt: "what is your name?"
```

les [<i class="fa fa-book" aria-hidden="true"></i> var_prompts](https://docs.ansible.com/ansible/latest/user_guide/playbooks_prompts.html)
permettent de lire les variables à partir de l'entrée standard.


## task

[<i class="fa fa-book" aria-hidden="true"></i> debug](https://docs.ansible.com/ansible/latest/modules/debug_module.html)

```yaml
- debug:
    msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"
```

* [<i class="fa fa-book" aria-hidden="true"></i> raw](https://docs.ansible.com/ansible/latest/modules/raw_module.html)
  * n'utilise que ssh et pas python
    * permet d'installer python
* [<i class="fa fa-book" aria-hidden="true"></i> command](https://docs.ansible.com/ansible/latest/modules/command_module.html#command-module)
* [<i class="fa fa-book" aria-hidden="true"></i> shell](https://docs.ansible.com/ansible/latest/modules/shell_module.html)
  * comme **command** mais au travers d'un shell


## task

```yaml
- shell: somescript.sh >> somelog.txt
  args:
    chdir: somedir/
    creates: somelog.txt
```

[<i class="fa fa-book" aria-hidden="true"></i> register](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#registering-variables)

```yaml
shell: /usr/bin/foo
register: foo_result
ignore_errors: True
```
[<i class="fa fa-gitlab" aria-hidden="true"></i> register dans une liste](https://gitlab.isima.fr/cri/ansible-playbook-vault/blob/master/tasks/initialize.yml#L17)


## task

[<i class="fa fa-book" aria-hidden="true"></i> loop](https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html)

```yaml
- user:
    name: "{{ item }}"
    state: present
  loop:
     - testuser1
     - testuser2
```

* marche avec
  * n'importe quelle variable itérable    
  * [fileglob](https://docs.ansible.com/ansible/latest/plugins/lookup/fileglob.html) - list files matching a pattern
  * [filetree](https://docs.ansible.com/ansible/latest/plugins/lookup/filetree.html) - recursively match all files in a directory tree
  * ...


## task

[<i class="fa fa-book" aria-hidden="true"></i> when](https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html)

[<i class="fa fa-book" aria-hidden="true"></i> conditions](https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html)

```yaml
- shell: echo "only on Red Hat 6, derivatives, and later"
  when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']|int >= 6
```


## [<i class="fa fa-book" aria-hidden="true"></i> set_fact](https://docs.ansible.com/ansible/latest/modules/set_fact_module.html) & pre_task

```yaml
criprod:
  pvecriprod1:
    api_users:
      - proxmoxapi
      - vimazeno
```

```yaml
- name: provisionner l'environnement du noeud (pour y accéder plus facilement dans les roles)
  set_fact:
    _pve: "{ 'cluster': '{{ lookup('env','PVE_CLUSTER') }}', 'node': '{{ lookup('env','PVE_NODE') }}', 'host': '{{ lookup('env','PVE_HOST') }}'}"

- name: provisionner les utilisateurs d'api pve uniquement du noeud (pour y accéder plus facilement dans les roles)
  set_fact:
    api_users: "{ 'api_users': {{ hostvars[inventory_hostname][_pve.cluster][_pve.node]['api_users'] }}}"

- name: fusionner l'environnement du noeud (pour y accéder plus facilement dans les roles)
  set_fact:
    pve: "{{ _pve | combine(api_users) }}"
```

[<i class="fa fa-gitlab" aria-hidden="true"></i> exemple permettant de réorganiser les variables](https://gitlab.isima.fr/cri/stack/blob/master/ansible/pre-tasks/set-pve-vars.yml)


## tags

tags au niveau tâches

```yaml
- name: s'assurer que le fichier user.cfg existe
  file:
    dest: /etc/pve/user.cfg
    state: touch
  tags: [pve-users]
```

tags au niveau roles à l'inclusion dans le playbook

```yaml
roles:

  - role: debug
    tags: debug
```

`always` tag spécial exécuté à tous les coups


## modules

* [<i class="fa fa-book" aria-hidden="true"></i> file](https://docs.ansible.com/ansible/latest/modules/file_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> lineinfile](https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> copy](https://docs.ansible.com/ansible/latest/modules/copy_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> template](https://docs.ansible.com/ansible/latest/modules/template_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> stat](https://docs.ansible.com/ansible/latest/modules/stat_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> get_url](https://docs.ansible.com/ansible/latest/modules/get_url_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> unarchive](https://docs.ansible.com/ansible/latest/modules/unarchive_module.html)


## modules

* [<i class="fa fa-book" aria-hidden="true"></i> package](https://docs.ansible.com/ansible/latest/modules/package_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> user](https://docs.ansible.com/ansible/latest/modules/user_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> systemd](https://docs.ansible.com/ansible/latest/modules/systemd_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> pip](https://docs.ansible.com/ansible/latest/modules/pip_module.html)
* [<i class="fa fa-book" aria-hidden="true"></i> expect](https://docs.ansible.com/ansible/latest/modules/exepect_module.html)
  * `pip install pexpect`
* [<i class="fa fa-book" aria-hidden="true"></i> ...](https://docs.ansible.com/ansible/latest/modules/modules_by_category.html)


## roles

[<i class="fa fa-book" aria-hidden="true"></i> debops](https://docs.debops.org/en/master/)

* [<i class="fa fa-github" aria-hidden="true"></i> bau-sec/ansible-openvpn-hardened](https://github.com/bau-sec/ansible-openvpn-hardened)
* [<i class="fa fa-github" aria-hidden="true"></i> ...](https://github.com/)

### skeleton

```
ansible-galaxy init --role-skeleton /path/to/stack/ansible/roles/skeletons/role-with-vagrant gitlab
```
[<i class="fa fa-gitlab" aria-hidden="true"></i>ansible/roles/skeletons/role-with-vagrant](https://gitlab.isima.fr/cri/stack/tree/master/ansible/roles/skeletons/role-with-vagrant)


## remote roles

`requirements.yml`

```yaml
- name: vault-server
  src: git+ssh://git@gitlab.isima.fr/cri/ansible-playbook-vault.git
  path: ./ansible/roles/remotes
```

```
$ ansible-galaxy install -f -r requirements.yml
```


## ansible.cfg

```ini
[defaults]
roles_path = ./ansible/roles/remotes:./ansible/roles/apps:./ansible/roles/commons:./ansible/roles/services
inventory = ./ansible/inventory.ini
filter_plugins = ./ansible/plugins/filter:
lookup_plugins = ./ansible/plugins/lookup:
callback_plugins = ./ansible/plugins/callback:
module_utils = ./ansible/module_utils:
stdout_callback = anstomlog
deprecation_warnings = False

[privilege_escalation]
become: yes
become_user: root
become_method: sudo
```


## extend

[<i class="fa fa-book" aria-hidden="true"></i> developing plugins](https://docs.ansible.com/ansible/latest/dev_guide/developing_plugins.html)

[<i class="fa fa-book" aria-hidden="true"></i> developing modules](https://docs.ansible.com/ansible/latest/dev_guide/developing_modules.html)