Ansible
Liens
https://learnxinyminutes.com/docs/ansible/ https://www.ansible.com/blog/using-ansible-to-manage-rhel-5-yesterday-today-and-tomorrow
Présentation
Ansible est une plateforme logicielle libre pour la configuration et la gestion des ordinateurs. Elle combine le déploiement de logiciels multi-nœuds, l'exécution des tâches ad-hoc, et la gestion de configuration. Elle gère les différents nœuds à travers SSH et ne nécessite l'installation d'aucun logiciel supplémentaire sur ceux-ci. Les modules communiquent via la sortie standard en notation JSON et peuvent être écrits dans n'importe quel langage de programmation. Le système utilise YAML pour exprimer des descriptions réutilisables de systèmes, appelés playbook. Elle permet :
- D'automatiser : déployer des applications, gérer des systèmes
- De gagner du temps : grâce aux playbooks, on peut résoudre les problèmes une fois et partager facilement la solution
- De collaborer : en créant une "culture de l'automatisation"
- D'intégrer plus facilement en automatisant les technologies que l'on utilise déjà.
Mise en place
Source :
https://www.tartarefr.eu/ansible-par-la-pratique-premiere-partie-les-bases/
J'utilise deux machines :
- Mon PC sous Ubuntu 18.10, sur lequel j'installe Ansible et sshpass
- Une VM Debian basique, qui sera ma cible (192.168.1.49)
Avant toute chose, il faut que notre VM soit dans les known_hosts de la machine Ansible. Il suffit de s'y connecter une fois en ssh pour ajouter la clef !
Je commence par installer Ansible sur mon PC. Ensuite, je peux commencer à lancer une commande via Ansible :
justine@Justine-pc:/etc/ansible$ ansible 192.168.1.49 -m ping
Mais ça ne fonctionne pas : Ansible ne connait pas la cible, et on a pas d'utilisateur sur celle-ci !
Dans mon dossier home, je créée deux répertoires : prod et preprod, et dans chacun d'entre eux je met un fichier hosts.ini vierge. Ce fichier contiendra notre inventaire de machines.
Ensuite, dans preprod/hosts.ini, je mets :
[server] 192.168.1.49 [ansible] localhost
Je peux ensuite rééessayer en précisant le répertoire de notre environnement et un utilisateur et ça fonctionne :
justine@Justine-pc:~/Ansible$ ansible -i ~/Ansible/preprod/hosts.ini 192.168.1.49 --user user --ask-pass -m ping SSH password: 192.168.1.49 | SUCCESS => { "changed": false, "ping": "pong" }
Je peux tenter une commande :
justine@Justine-pc:~/Ansible$ ansible -i ~/Ansible/preprod/hosts.ini 192.168.1.49 --user user --ask-pass -a whoami SSH password: 192.168.1.49 | CHANGED | rc=0 >> user
Par défaut, Ansible utilise le même utilisateur que l'utilisateur local sur l'hôte distant. C'est pour cela qu'il faut bien gérer les utilisateurs. De plus, il faut penser que la connexion root en ssh est désactivée par défaut sur Debian !
Lancer une série de commandes
Le fichier permettant de lister les commande à lancer s’appelle un playbook. Il peut être monolithique ou divisé en plusieurs parties. Ansible recommande la séparation par rôle, ce qui permet de réutiliser les commandes d’un playbook et de les partager facilement avec la communauté.
Les rôles
Il faut bien réfléchir à la séparation des rôles qu'aura notre serveur, afin de garder une bonne modularité. Si on prend l'exemple d'un serveur web personnel qui contiendrait un blog Wordpress, un outil d'analyse de trafic web Piwik et du monitoring, on a déjà 3 rôles. Mais chacun de ces rôles en nécéssite d'autres (bdd, serveur web...), et que la machine doit rester à jour, on arrive a un grand nombre de rôles :
- system: concernera la post-installation du système
- Mettre le système à jour
- Définition des pools ntp (fr)
- Définition du nom d’hôte
- Définition basique du firewall: SSH (22)
- Redirection des mails locaux vers une adresse externe
- etc…
- pki: Concernera l’obtention et la mise à jour des certificats SSL
- Copie de nos clés privées
- Copies de nos fichiers et scripts gérant la pki (systemd)
- webserver: Concernera l’installation et la configuration d’apache
- Installation et configuration d’apache
- Mise à jour des extensions (ex modsecurity), hors RPM (les paquets RPMs seront mis à jour avec le rôle system)
- Ajout des ports HTTP (80) et HTTPS (443) au firewall
- database: Concernera l’installation et la configuration de mariadb
- Installation et configuration de mariadb
- blog: Concernera l’installation et la configuration de wordpress
- Installation et configuration du blog
- définition de l’alias ou du vhost pour le webserver
- import de la sauvegarde si elle existe
- Mise à jour des extensions, hors RPM (les paquets RPMs seront mis à jour avec le rôle system)
- webstat: Concernera l’installation et la configuration de piwik
- Installation et configuration de piwik
- définition de l’alias ou du vhost pour le webserver
- import de la sauvegarde si elle existe
- monitoring: Concernera l’installation et la configuration du monitoring
- Installation et configuration des outils
- ids: Concernera l’installation et la configuration des outils de détection et de vérification d’intrusion
- Installation et configuration des outils
Du coup, le changement d'un rôle impactera pas ou peu les autres, et on pourra facilement changer certaines choses. Changer de blog n'impactera pas le reste par exemple, mais changer de serveur web impactera les autres rôles.
Les Playbooks
Il s'agit de fichiers au format YAML, qui contient une succession de tâche à accomplir. Par défaut, une erreur sur l'une d'entre elles entraîne l'arrêt du processus. Il existe beaucoup de modules différents et la doc les présente tous :
https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
À propos du YAML :
https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
À propos des playbooks :
https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#playbook-language-example
J'ai créé un playbook tout simple qui se contente d'installer apache2 :
#site.yml --- - hosts: server #Je choisis mon hôte tasks: - name: update installation de serveur web #Je nomme ma tâche apt: #Différentes commandes apt name: apache2 force_apt_get: yes #Sinon, il utilise aptitude update_cache: yes become: true #Sert à utiliser sudo
Que je peux ensuite lancer :
justine@Justine-pc:~/Ansible/preprod$ ansible-playbook -i ~/Ansible/preprod/hosts.ini --user user --ask-pass --ask-become-pass site.yml SSH password: SUDO password[defaults to SSH password]: PLAY [server] ****************************************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************************************* ok: [192.168.1.49] TASK [update installation de serveur web] ************************************************************************************************** changed: [192.168.1.49] PLAY RECAP ********************************************************************************************************************************* 192.168.1.49 : ok=2 changed=1 unreachable=0 failed=0
Ici :
- ansible-playbook -i ~/Ansible/preprod/hosts.ini : J'utilise un playbook en précisant l'environnement
- --user user --ask-pass : utilisateur et demande de mot de passe pour ssh
- --ask-become-pass : mot de passe pour sudo
- site.yml : mon playbook
Si je relance le playbook, les tâches déjà effectuées seront ingorées. Cela évite les effets de bord : Les playbooks sont idempotents, c'est-à-dire qu'on peut bien les réutiliser si on veut ! Ici, je pourrais rajouter des fonctions à mon playbook et le relancer si j'en ai envie.
Chiffrer les variables des playbooks
Les playbooks peuvent s'avérer dangereux au niveau sécurité : si je dois donner un mot de passe à un utilisateur via un playbook, je ne vais pas par la suite le commit tel quel sur un dépôt public par la suite ! Pour ça, Ansible-vault propose deux façon de chiffrer les fichiers de variables :
- Chiffrer tout le fichier de variables et utiliser les commandes ad-hoc pour éditer le fichier
- Chiffrer seulement la valeur de la variable dans un fichier texte en clair
Chiffrer tout le fichier
On créée notre fichier chiffré et on inscrit notre variable une fois le fichier ouvert par l'éditeur par défaut (variable EDITOR) avec la commande :
justine@Justine-pc:~/Ansible/preprod$ ansible-vault create undossier/unfichierdevariables #Je peux ensuite l'éditer : justine@Justine-pc:~/Ansible/preprod$ ansible-vault edit undossier/unfichierdevariables
On peut aussi créer un fichier en clair et le chiffrer après :
echo 'mysql_root_password = "monsupermotdepasse"' > host_vars/server ansible-vault encrypt host_vars/server
Chiffrer la valeur de la variable
On chiffre la valeur :
ansible-vault encrypt_string --vault-id @prompt 'monsupermotdepasse' --name 'mysql_root_password' >> host_vars/server New vault password (default): Confirm vew vault password (default): Encryption successful
Mais je n'ai pas trop compris ce point-là...
Un playbook plus abouti
Le playbook suivant donne une idée un peu complète des possibilités offertes par l'outil, et devrait parler de lui-même.
Fichier de variables
--- name: johndoe #pubkey est un lien vers un git de type https://mongit.fr/user.keys #Il faut bien sûr avoir au préalable injecté une clef dans le git en question pubkey: https://monsuperlien.com/johndoe.keys file: /home/justine/Nextcloud/Ansible/resultat.txt inventoryfile: /home/justine/Nextcloud/Ansible/inventaire.txt
Fichier hosts
[serv] serv.innertech.ovh
Playbook
--- - hosts: nest tasks: - name: Dis bonjour debug: msg: "Hi, I will be adding a user named {{ name }} and inject his key from {{ pubkey }}" msg: "After that, the user {{ name }} shall be able to sudo without a passwd (as he won't have any)" - name: Creation de l'utilisateur user: name: "{{ name }}" shell: /bin/bash groups: sudo append: yes become: yes - name: Ajout de sudo NOPASSWD copy: content: '%sudo ALL=(ALL:ALL) NOPASSWD:ALL' dest: /etc/sudoers.d/sudo_nopasswd mode: 0440 become: yes - name: Injection de la clef publique authorized_key: user: "{{ name }}" state: present key: "{{ pubkey }}" become: yes - name: Informations sur Lynis debug: msg: "Next, we will try to install Lynis and run it" - name: Installation de Lynis apt: name: lynis force_apt_get: yes update_cache: yes become: yes - name: Lancement de Lynis et renvoi du resultat shell: lynis --no-colors audit system register: resultat args: executable: /bin/bash become: yes - name: Enregistrement du resultat de Lynis local_action: module: copy content: "{{ resultat }}" dest: "{{ file }}" - name: Recuperation d'informations setup: filter: ansible_default_ipv4.address - name: ajout de la machine à un fichier d'inventaire local_action: module: copy content: "{{ ansible_default_ipv4.address }} User: {{ name }} Pub_Key: {{ pubkey }}" dest: "{{ inventoryfile }}"
Un role entier
À partir du playbook vu au-dessus, je peux l'améliorer et en faire un rôle. Mon rôle est plus complet et est une base pour n'importe quelle machine.
Je commence par initialiser mon rôle. C'est assez similaire à git sur ce point-là :
ansible-galaxy init monrole
Je récupère un dossier contenant tous les dossiers et fichiers de base dont j'ai besoin :
- defaults : variable par défaut
- files : fichier à envoyer avec le playbook
- handlers : les handlers
- meta
- README.md : idem à git
- tasks : Contient le "gros" du travail, c'est-à-dire les tâches à effectuer
- templates
- vars: mes variables par machine.
Dans chacun de ces dossiers se trouve un main.yml. Ces fichiers sont exécutés automatiquement et doivent contenir tout notre travail.
Détail du role hello.base
Je vais résumer ici mon role hello.base, qui est une installation de base de mes machines, sauf files (qui contient un fichier de conf de fail2ban et une commande cron pour la synchro ntp):
defaults/main.yml:
--- name: johndoe #pubkey est un lien vers un git de type https://mongit.fr/user.keys #Il faut bien sûr avoir au préalable injecté une clef dans le git en question pubkey: https://book.innertech.ovh/johndoe.keys file: /home/justine/Nextcloud/Ansible/resultat.txt inventoryfile: /home/justine/Nextcloud/Ansible/inventaire.txt ports: 22/tcp
tasks/main.yml:
--- # tasks file for hello.base - name: Dis bonjour debug: msg: "Hi, I will be adding a user named {{ name }} and inject his key from {{ pubkey }}" msg: "After that, the user {{ name }} shall be able to sudo without a passwd (as he won't have any)" - name: Creation de l'utilisateur user: name: "{{ name }}" shell: /bin/bash groups: sudo append: yes become: yes - name: Ajout de sudo NOPASSWD copy: content: '%sudo ALL=(ALL:ALL) NOPASSWD:ALL' dest: /etc/sudoers.d/sudo_nopasswd mode: 0440 become: yes - name: Injection de la clef publique authorized_key: user: "{{ name }}" state: present key: "{{ pubkey }}" become: yes - name: Informations sur Lynis debug: msg: "Next, we will try to install Lynis and run it" - name: Installation de Lynis apt: name: lynis force_apt_get: yes update_cache: yes become: yes - name: Lancement de Lynis et renvoi du resultat shell: lynis --no-colors audit system register: resultat args: executable: /bin/bash become: yes - name: Enregistrement du resultat de Lynis local_action: module: copy content: "{{ resultat }}" dest: "{{ file }}" - name: Informations sécurité debug: msg: "We will then proceed to secure the system" - name: Installation de UFW apt: name: ufw force_apt_get: yes update_cache: no become: yes - name: Mise en place de NTP apt: name: ntpdate force_apt_get: yes update_cache: no - name: Copie des fichiers copy: src: files/cron.ntpdate dest: /etc/cron.d/cron.ntpdate owner: root group: root mode: 0644 become: yes - name: ouverture des ports shell: ufw allow {{ ports }} become: yes - name: Sauvegarde de la configuration ufw shell: ufw reload become: yes - name: Recuperation d'informations setup: filter: ansible_default_ipv4.address - name: Installation outils de sécurité apt: name: "{{ packages }}" force_apt_get: yes vars: packages: - fail2ban - clamav - logrotate become: yes - name: Configuration de fail2ban copy: src: files/jail.conf dest: /etc/fail2ban/jail.conf owner: root group: root mode: 0644 force: yes - name: redémarrage de fail2ban shell: systemctl restart fail2ban become: yes - name: ajout de la machine à un fichier d'inventaire local_action: module: copy content: "{{ ansible_default_ipv4.address }} User: {{ name }} Pub_Key: {{ pubkey }}" dest: "{{ inventoryfile }}"
Utiliser ce rôle dans un playbook
Maintenant que j'ai créé mon rôle, je vais pouvoir l'utiliser dans le cadre de mes playbooks. Pour cela, c'est assez simple. Voici un playbook d'exemple :
--- - hosts: ancible become: yes roles: - roles/hello.base
Son dossier contient :
- Un fichier hosts
- le playbook en lui-même
- un dossier roles contenant mon hello.base
Il suffit ensuite de lancer mon playbook pour voir le rôle s'exécuter. Bien évidemment, je pourrais avoir besoin de modifier les variable de mon rôle.
Quelles sont les variables qu'Ansible récupère sur les hôtes ?
Lors d'un gather facts normal, ansible récupère des variables sur les cibles. Pour les voir :
ansible -m setup hostname
Ce qui donne:
scdev | success >> { "ansible_facts": { "ansible_all_ipv4_addresses": [ "10.0.2.15", "192.168.10.10" ], "ansible_all_ipv6_addresses": [ "fe80::a00:27ff:fe12:9698", "fe80::a00:27ff:fe74:1330" ], "ansible_architecture": "i386", "ansible_bios_date": "12/01/2006", "ansible_bios_version": "VirtualBox", "ansible_cmdline": { "BOOT_IMAGE": "/vmlinuz-3.2.0-23-generic-pae", "quiet": true, "ro": true, "root": "/dev/mapper/precise32-root" }, "ansible_date_time": { "date": "2013-09-17", "day": "17", "epoch": "1379378304", "hour": "00", "iso8601": "2013-09-17T00:38:24Z", "iso8601_micro": "2013-09-17T00:38:24.425092Z", "minute": "38", "month": "09", "second": "24", "time": "00:38:24", "tz": "UTC", "year": "2013" }, "ansible_default_ipv4": { "address": "10.0.2.15", "alias": "eth0", "gateway": "10.0.2.2", "interface": "eth0", "macaddress": "08:00:27:12:96:98", "mtu": 1500, "netmask": "255.255.255.0", "network": "10.0.2.0", "type": "ether" }, "ansible_default_ipv6": {}, "ansible_devices": { "sda": { "holders": [], "host": "SATA controller: Intel Corporation 82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (rev 02)", "model": "VBOX HARDDISK", "partitions": { "sda1": { "sectors": "497664", "sectorsize": 512, "size": "243.00 MB", "start": "2048" }, "sda2": { "sectors": "2", "sectorsize": 512, "size": "1.00 KB", "start": "501758" }, }, "removable": "0", "rotational": "1", "scheduler_mode": "cfq", "sectors": "167772160", "sectorsize": "512", "size": "80.00 GB", "support_discard": "0", "vendor": "ATA" }, "sr0": { "holders": [], "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)", "model": "CD-ROM", "partitions": {}, "removable": "1", "rotational": "1", "scheduler_mode": "cfq", "sectors": "2097151", "sectorsize": "512", "size": "1024.00 MB", "support_discard": "0", "vendor": "VBOX" }, "sr1": { "holders": [], "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)", "model": "CD-ROM", "partitions": {}, "removable": "1", "rotational": "1", "scheduler_mode": "cfq", "sectors": "2097151", "sectorsize": "512", "size": "1024.00 MB", "support_discard": "0", "vendor": "VBOX" } }, "ansible_distribution": "Ubuntu", "ansible_distribution_release": "precise", "ansible_distribution_version": "12.04", "ansible_domain": "", "ansible_eth0": { "active": true, "device": "eth0", "ipv4": { "address": "10.0.2.15", "netmask": "255.255.255.0", "network": "10.0.2.0" }, "ipv6": [ { "address": "fe80::a00:27ff:fe12:9698", "prefix": "64", "scope": "link" } ], "macaddress": "08:00:27:12:96:98", "module": "e1000", "mtu": 1500, "type": "ether" }, "ansible_eth1": { "active": true, "device": "eth1", "ipv4": { "address": "192.168.10.10", "netmask": "255.255.255.0", "network": "192.168.10.0" }, "ipv6": [ { "address": "fe80::a00:27ff:fe74:1330", "prefix": "64", "scope": "link" } ], "macaddress": "08:00:27:74:13:30", "module": "e1000", "mtu": 1500, "type": "ether" }, "ansible_form_factor": "Other", "ansible_fqdn": "scdev", "ansible_hostname": "scdev", "ansible_interfaces": [ "lo", "eth1", "eth0" ], "ansible_kernel": "3.2.0-23-generic-pae", "ansible_lo": { "active": true, "device": "lo", "ipv4": { "address": "127.0.0.1", "netmask": "255.0.0.0", "network": "127.0.0.0" }, "ipv6": [ { "address": "::1", "prefix": "128", "scope": "host" } ], "mtu": 16436, "type": "loopback" }, "ansible_lsb": { "codename": "precise", "description": "Ubuntu 12.04 LTS", "id": "Ubuntu", "major_release": "12", "release": "12.04" }, "ansible_machine": "i686", "ansible_memfree_mb": 23, "ansible_memtotal_mb": 369, "ansible_mounts": [ { "device": "/dev/mapper/precise32-root", "fstype": "ext4", "mount": "/", "options": "rw,errors=remount-ro", "size_available": 77685088256, "size_total": 84696281088 }, { "device": "/dev/sda1", "fstype": "ext2", "mount": "/boot", "options": "rw", "size_available": 201044992, "size_total": 238787584 }, { "device": "/vagrant", "fstype": "vboxsf", "mount": "/vagrant", "options": "uid=1000,gid=1000,rw", "size_available": 42013151232, "size_total": 484145360896 } ], "ansible_os_family": "Debian", "ansible_pkg_mgr": "apt", "ansible_processor": [ "Pentium(R) Dual-Core CPU E5300 @ 2.60GHz" ], "ansible_processor_cores": "NA", "ansible_processor_count": 1, "ansible_product_name": "VirtualBox", "ansible_product_serial": "NA", "ansible_product_uuid": "NA", "ansible_product_version": "1.2", "ansible_python_version": "2.7.3", "ansible_selinux": false, "ansible_swapfree_mb": 766, "ansible_swaptotal_mb": 767, "ansible_system": "Linux", "ansible_system_vendor": "innotek GmbH", "ansible_user_id": "neves", "ansible_userspace_architecture": "i386", "ansible_userspace_bits": "32", "ansible_virtualization_role": "guest", "ansible_virtualization_type": "virtualbox" }, "changed": false }
Un autre exemple
ansible -m setup 192.168.1.3 --ask-pass --ask-become-pass -i invent -u justine ############Donne 192.168.1.3 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.1.3" ], "ansible_all_ipv6_addresses": [ "fe80::dc0f:6bff:feef:3c09" ], "ansible_apparmor": { "status": "disabled" }, "ansible_architecture": "x86_64", "ansible_bios_date": "04/01/2014", "ansible_bios_version": "rel-1.11.1-0-g0551a4be2c-prebuilt.qemu-project.org", "ansible_cmdline": { "BOOT_IMAGE": "/boot/vmlinuz-4.9.0-8-amd64", "quiet": true, "ro": true, "root": "UUID=e3ec661d-def0-4549-9627-ba6ddf0d15ab" }, "ansible_date_time": { "date": "2019-07-18", "day": "18", "epoch": "1563457682", "hour": "15", "iso8601": "2019-07-18T13:48:02Z", "iso8601_basic": "20190718T154802247424", "iso8601_basic_short": "20190718T154802", "iso8601_micro": "2019-07-18T13:48:02.247572Z", "minute": "48", "month": "07", "second": "02", "time": "15:48:02", "tz": "CEST", "tz_offset": "+0200", "weekday": "jeudi", "weekday_number": "4", "weeknumber": "28", "year": "2019" }, "ansible_default_ipv4": { "address": "192.168.1.3", "alias": "ens18", "broadcast": "192.168.1.255", "gateway": "192.168.1.254", "interface": "ens18", "macaddress": "de:0f:6b:ef:3c:09", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.1.0", "type": "ether" }, "ansible_default_ipv6": {}, "ansible_device_links": { "ids": { "sda": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0" ], "sda1": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part1" ], "sda2": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part2" ], "sda5": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part5" ], "sr0": [ "ata-QEMU_DVD-ROM_QM00003" ] }, "labels": { "sr0": [ "Debian\\x209.8.0\\x20amd64\\x20n" ] }, "masters": {}, "uuids": { "sda1": [ "e3ec661d-def0-4549-9627-ba6ddf0d15ab" ], "sda5": [ "b914f733-6b22-42b9-b9c1-886651230752" ], "sr0": [ "2019-02-16-12-00-40-00" ] } }, "ansible_devices": { "sda": { "holders": [], "host": "SCSI storage controller: Red Hat, Inc Virtio SCSI", "links": { "ids": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0" ], "labels": [], "masters": [], "uuids": [] }, "model": "QEMU HARDDISK", "partitions": { "sda1": { "holders": [], "links": { "ids": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part1" ], "labels": [], "masters": [], "uuids": [ "e3ec661d-def0-4549-9627-ba6ddf0d15ab" ] }, "sectors": "123729920", "sectorsize": 512, "size": "59.00 GB", "start": "2048", "uuid": "e3ec661d-def0-4549-9627-ba6ddf0d15ab" }, "sda2": { "holders": [], "links": { "ids": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part2" ], "labels": [], "masters": [], "uuids": [] }, "sectors": "2", "sectorsize": 512, "size": "1.00 KB", "start": "123734014", "uuid": null }, "sda5": { "holders": [], "links": { "ids": [ "scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part5" ], "labels": [], "masters": [], "uuids": [ "b914f733-6b22-42b9-b9c1-886651230752" ] }, "sectors": "2093056", "sectorsize": 512, "size": "1022.00 MB", "start": "123734016", "uuid": "b914f733-6b22-42b9-b9c1-886651230752" } }, "removable": "0", "rotational": "1", "sas_address": null, "sas_device_handle": null, "scheduler_mode": "cfq", "sectors": "125829120", "sectorsize": "512", "size": "60.00 GB", "support_discard": "4096", "vendor": "QEMU", "virtual": 1 }, "sr0": { "holders": [], "host": "IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]", "links": { "ids": [ "ata-QEMU_DVD-ROM_QM00003" ], "labels": [ "Debian\\x209.8.0\\x20amd64\\x20n" ], "masters": [], "uuids": [ "2019-02-16-12-00-40-00" ] }, "model": "QEMU DVD-ROM", "partitions": {}, "removable": "1", "rotational": "1", "sas_address": null, "sas_device_handle": null, "scheduler_mode": "cfq", "sectors": "598016", "sectorsize": "2048", "size": "292.00 MB", "support_discard": "0", "vendor": "QEMU", "virtual": 1 } }, "ansible_distribution": "Debian", "ansible_distribution_file_parsed": true, "ansible_distribution_file_path": "/etc/os-release", "ansible_distribution_file_variety": "Debian", "ansible_distribution_major_version": "9", "ansible_distribution_release": "stretch", "ansible_distribution_version": "9", "ansible_dns": { "nameservers": [ "193.51.100.100", "1.1.1.1" ], "search": [ "u-pec.fr" ] }, "ansible_domain": "u-pec.fr", "ansible_effective_group_id": 1000, "ansible_effective_user_id": 1000, "ansible_ens18": { "active": true, "device": "ens18", "features": { "busy_poll": "off [fixed]", "fcoe_mtu": "off [fixed]", "generic_receive_offload": "on", "generic_segmentation_offload": "on", "highdma": "off [fixed]", "hw_tc_offload": "off [fixed]", "l2_fwd_offload": "off [fixed]", "large_receive_offload": "off [fixed]", "loopback": "off [fixed]", "netns_local": "off [fixed]", "ntuple_filters": "off [fixed]", "receive_hashing": "off [fixed]", "rx_all": "off", "rx_checksumming": "off", "rx_fcs": "off", "rx_vlan_filter": "on [fixed]", "rx_vlan_offload": "on", "rx_vlan_stag_filter": "off [fixed]", "rx_vlan_stag_hw_parse": "off [fixed]", "scatter_gather": "on", "tcp_segmentation_offload": "on", "tx_checksum_fcoe_crc": "off [fixed]", "tx_checksum_ip_generic": "on", "tx_checksum_ipv4": "off [fixed]", "tx_checksum_ipv6": "off [fixed]", "tx_checksum_sctp": "off [fixed]", "tx_checksumming": "on", "tx_fcoe_segmentation": "off [fixed]", "tx_gre_csum_segmentation": "off [fixed]", "tx_gre_segmentation": "off [fixed]", "tx_gso_partial": "off [fixed]", "tx_gso_robust": "off [fixed]", "tx_ipxip4_segmentation": "off [fixed]", "tx_ipxip6_segmentation": "off [fixed]", "tx_lockless": "off [fixed]", "tx_nocache_copy": "off", "tx_scatter_gather": "on", "tx_scatter_gather_fraglist": "off [fixed]", "tx_sctp_segmentation": "off [fixed]", "tx_tcp6_segmentation": "off [fixed]", "tx_tcp_ecn_segmentation": "off [fixed]", "tx_tcp_mangleid_segmentation": "off", "tx_tcp_segmentation": "on", "tx_udp_tnl_csum_segmentation": "off [fixed]", "tx_udp_tnl_segmentation": "off [fixed]", "tx_vlan_offload": "on [fixed]", "tx_vlan_stag_hw_insert": "off [fixed]", "udp_fragmentation_offload": "off [fixed]", "vlan_challenged": "off [fixed]" }, "hw_timestamp_filters": [], "ipv4": { "address": "192.168.1.3", "broadcast": "192.168.1.255", "netmask": "255.255.255.0", "network": "192.168.1.0" }, "ipv6": [ { "address": "fe80::dc0f:6bff:feef:3c09", "prefix": "64", "scope": "link" } ], "macaddress": "de:0f:6b:ef:3c:09", "module": "e1000", "mtu": 1500, "pciid": "0000:00:12.0", "promisc": false, "speed": 1000, "timestamping": [ "tx_software", "rx_software", "software" ], "type": "ether" }, "ansible_env": { "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus", "HOME": "/home/justine", "LANG": "fr_FR.UTF-8", "LOGNAME": "justine", "MAIL": "/var/mail/justine", "PATH": "/usr/local/bin:/usr/bin:/bin:/usr/games", "PWD": "/home/justine", "SHELL": "/bin/bash", "SHLVL": "1", "SSH_CLIENT": "192.168.1.208 50176 22", "SSH_CONNECTION": "192.168.1.208 50176 192.168.1.3 22", "SSH_TTY": "/dev/pts/1", "TERM": "xterm-256color", "USER": "justine", "XDG_RUNTIME_DIR": "/run/user/1000", "XDG_SESSION_ID": "450", "_": "/bin/sh" }, "ansible_fibre_channel_wwn": [], "ansible_fips": false, "ansible_form_factor": "Other", "ansible_fqdn": "vm-deb-justine.u-pec.fr", "ansible_hostname": "vm-deb-justine", "ansible_hostnqn": "", "ansible_interfaces": [ "lo", "ens18" ], "ansible_is_chroot": false, "ansible_iscsi_iqn": "", "ansible_kernel": "4.9.0-8-amd64", "ansible_lo": { "active": true, "device": "lo", "features": { "busy_poll": "off [fixed]", "fcoe_mtu": "off [fixed]", "generic_receive_offload": "on", "generic_segmentation_offload": "on", "highdma": "on [fixed]", "hw_tc_offload": "off [fixed]", "l2_fwd_offload": "off [fixed]", "large_receive_offload": "off [fixed]", "loopback": "on [fixed]", "netns_local": "on [fixed]", "ntuple_filters": "off [fixed]", "receive_hashing": "off [fixed]", "rx_all": "off [fixed]", "rx_checksumming": "on [fixed]", "rx_fcs": "off [fixed]", "rx_vlan_filter": "off [fixed]", "rx_vlan_offload": "off [fixed]", "rx_vlan_stag_filter": "off [fixed]", "rx_vlan_stag_hw_parse": "off [fixed]", "scatter_gather": "on", "tcp_segmentation_offload": "on", "tx_checksum_fcoe_crc": "off [fixed]", "tx_checksum_ip_generic": "on [fixed]", "tx_checksum_ipv4": "off [fixed]", "tx_checksum_ipv6": "off [fixed]", "tx_checksum_sctp": "on [fixed]", "tx_checksumming": "on", "tx_fcoe_segmentation": "off [fixed]", "tx_gre_csum_segmentation": "off [fixed]", "tx_gre_segmentation": "off [fixed]", "tx_gso_partial": "off [fixed]", "tx_gso_robust": "off [fixed]", "tx_ipxip4_segmentation": "off [fixed]", "tx_ipxip6_segmentation": "off [fixed]", "tx_lockless": "on [fixed]", "tx_nocache_copy": "off [fixed]", "tx_scatter_gather": "on [fixed]", "tx_scatter_gather_fraglist": "on [fixed]", "tx_sctp_segmentation": "on", "tx_tcp6_segmentation": "on", "tx_tcp_ecn_segmentation": "on", "tx_tcp_mangleid_segmentation": "on", "tx_tcp_segmentation": "on", "tx_udp_tnl_csum_segmentation": "off [fixed]", "tx_udp_tnl_segmentation": "off [fixed]", "tx_vlan_offload": "off [fixed]", "tx_vlan_stag_hw_insert": "off [fixed]", "udp_fragmentation_offload": "on", "vlan_challenged": "on [fixed]" }, "hw_timestamp_filters": [], "ipv4": { "address": "127.0.0.1", "broadcast": "host", "netmask": "255.0.0.0", "network": "127.0.0.0" }, "ipv6": [ { "address": "::1", "prefix": "128", "scope": "host" } ], "mtu": 65536, "promisc": false, "timestamping": [ "rx_software", "software" ], "type": "loopback" }, "ansible_local": {}, "ansible_lsb": { "codename": "stretch", "description": "Debian GNU/Linux 9.8 (stretch)", "id": "Debian", "major_release": "9", "release": "9.8" }, "ansible_machine": "x86_64", "ansible_machine_id": "4a102f96174047ff8dfaaad49ccee1af", "ansible_memfree_mb": 292, "ansible_memory_mb": { "nocache": { "free": 683, "used": 313 }, "real": { "free": 292, "total": 996, "used": 704 }, "swap": { "cached": 0, "free": 1002, "total": 1021, "used": 19 } }, "ansible_memtotal_mb": 996, "ansible_mounts": [ { "block_available": 13416944, "block_size": 4096, "block_total": 15157979, "block_used": 1741035, "device": "/dev/sda1", "fstype": "ext4", "inode_available": 3746499, "inode_total": 3866624, "inode_used": 120125, "mount": "/", "options": "rw,relatime,errors=remount-ro,data=ordered", "size_available": 54955802624, "size_total": 62087081984, "uuid": "e3ec661d-def0-4549-9627-ba6ddf0d15ab" } ], "ansible_nodename": "vm-deb-justine", "ansible_os_family": "Debian", "ansible_pkg_mgr": "apt", "ansible_proc_cmdline": { "BOOT_IMAGE": "/boot/vmlinuz-4.9.0-8-amd64", "quiet": true, "ro": true, "root": "UUID=e3ec661d-def0-4549-9627-ba6ddf0d15ab" }, "ansible_processor": [ "0", "GenuineIntel", "Common KVM processor" ], "ansible_processor_cores": 1, "ansible_processor_count": 1, "ansible_processor_threads_per_core": 1, "ansible_processor_vcpus": 1, "ansible_product_name": "Standard PC (i440FX + PIIX, 1996)", "ansible_product_serial": "NA", "ansible_product_uuid": "NA", "ansible_product_version": "pc-i440fx-2.12", "ansible_python": { "executable": "/usr/bin/python", "has_sslcontext": true, "type": "CPython", "version": { "major": 2, "micro": 13, "minor": 7, "releaselevel": "final", "serial": 0 }, "version_info": [ 2, 7, 13, "final", 0 ] }, "ansible_python_version": "2.7.13", "ansible_real_group_id": 1000, "ansible_real_user_id": 1000, "ansible_selinux": { "status": "disabled" }, "ansible_selinux_python_present": true, "ansible_service_mgr": "systemd", "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD6CDpuGoWFchVwwsVX4hhOcveEJD7hqA9WWMTcO0injcRlSvxEehDlVuRoG+8h2mOumx6s4CuA3fthe7ZCE/xs=", "ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIDn2yv+4fLATLNDq9B3hMm1YlZHNYZZmwylNmrQLupOD", "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDPslznquuniKvkFC2ACBP1TrwLeGQFzd7mjL3yRzjrKWSW1p4roTKbuK5zVymheuoWVPFWlJataTFuWzzYs0khQgSP2o5p6zOK6Luj7jD6Td5ueEONakBhS9z1LzLQgenyQdQO86uUdpRYvSRBPBkQNjNqEuC+Xwjii96JWvxGEu+V5TppFMjLfS/P3W62J1nQwcgcB0wK1AZXYaSSvC98TQw9qwNEUh48t7d/3fhLodDjRHIO3QaVXSnWDgdY+FMXTBArP3KaDDhmXQIiUsVxkFtRz7I3Zj3vJhQakuf3Cjn/dGIctXFRXT6dEFJF5TP6UKomurIyI5aBamUkR7kp", "ansible_swapfree_mb": 1002, "ansible_swaptotal_mb": 1021, "ansible_system": "Linux", "ansible_system_capabilities": [ "" ], "ansible_system_capabilities_enforced": "True", "ansible_system_vendor": "QEMU", "ansible_uptime_seconds": 1134249, "ansible_user_dir": "/home/justine", "ansible_user_gecos": "justine,,,", "ansible_user_gid": 1000, "ansible_user_id": "justine", "ansible_user_shell": "/bin/bash", "ansible_user_uid": 1000, "ansible_userspace_architecture": "x86_64", "ansible_userspace_bits": "64", "ansible_virtualization_role": "guest", "ansible_virtualization_type": "kvm", "discovered_interpreter_python": "/usr/bin/python", "gather_subset": [ "all" ], "module_setup": true }, "changed": false }
Fichiers d'inventaire
Les fichiers d'inventaire peuvent prendre deux formes : INI (la plus simple selon moi) et yaml.
À savoir : notation par crochets
Mettons que j'ai plusieurs machines apache : apache1, apache2, apache3... Je peux noter comme ça pour avoir les machines 1 à 3:
apache[1:3]
groupes avec variables de groupes
INI:
[servs] 192.168.1.1 [servs:vars] new_name: host
YAML:
servs: hosts: host1: vars: new_name: hostname
Variables d'hôtes
INI
[targets] localhost ansible_connection=local other1.example.com ansible_connection=ssh ansible_user=mpdehaan other2.example.com ansible_connection=ssh ansible_user=mdehaan
YAML
atlanta: host1: http_port: 80 maxRequestsPerChild: 808 host2: http_port: 303 maxRequestsPerChild: 909
Héritage
On peut faire des groupes de groupes avec le mot-clef "children". On peut ainsi faire hériter aux enfants des variables de leurs parents.
INI:
[atlanta] host1 host2 [raleigh] host2 host3 [southeast:children] atlanta raleigh [southeast:vars] some_server=foo.southeast.example.com halon_system_timeout=30 self_destruct_countdown=60 escape_pods=2 [usa:children] southeast northeast southwest northwest
YAML
all: children: usa: children: southeast: children: atlanta: hosts: host1: host2: raleigh: hosts: host2: host3: vars: some_server: foo.southeast.example.com halon_system_timeout: 30 self_destruct_countdown: 60 escape_pods: 2 northeast: northwest: southwest:
Commandes Ad-Hoc
Les commandes Ad-Hoc sont parfois très utiles. Ci-dessous, un exemple simple utilisant un fichier d'inventaires (serv est le nom du groupe d'hôtes):
<syntaxhighlight lang='bash'> ansible serv -i inventaire.ini -a who -r -u ansible --key-file id_ansible ansible servs -i inventaire.ini -u ansible --key-file id_ansible --become -m raw -a "cat /var/log/apt/history.log"
- Syntaxe : ansible group -i inventaire [options] [module]
</syntaxhighlight>
-a sert à envoyer des commandes directement, du coup on ne fait pas de -m ici (même si c'est bien entendu possible).
Si je n'ai pas de fichier d'inventaire, je peux le construire à la main avec -i et préciser que je lance sur all (noter la virgule à la fin de la liste de machines) :
<syntaxhighlight lang='bash'> ansible all -i "machine1, machine2," [...] </syntaxhighlight>