Aller au contenu principal

Ajouter, remplacer des lignes avec Ansible

· 6 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Pour créer le contenu d'un fichier nous avons vu les templates Jinja, mais parfois vous ne devez en modifier qu'une seule ligne. Ce sont les module Ansible LineInFile et BlockInFile qui vont vous aider à atteindre à votre objectif.

Les modules Ansible LineInFile et BlockInFile permettent d'ajouter, de supprimer ou de modifier des lignes dans des fichiers. Je ne parlerai pas du module replace, car on arrive aux mêmes résultats avec LineInFile.

Quelles différences entre LineInFile et BlockInfile ?

Ils font les deux la même chose, mais BlockInFile lui ajoutera des repères personnalisés autour des modifications.

Quand utiliser les modules LineInFile et BlockInFile ?

Les modules LineInFile et BlockInFile vous permettent de :

  • Valider si une ligne est présente sans faire de modification
  • Validez si une ligne est présente dans le fichier et l'ajouter si elle n'existe pas
  • Remplacer une ligne dans un fichier si elle est déjà présente
  • Supprimer une ligne du fichier si elle est présente
  • Insérer avant/après une ligne cherchée avec le paramètre insertbefore et insertafter
  • Valider les modifications sont correctes avant d'enregistrer

Description des options

Ces modules utilisent ces paramètres principaux:

  • path : qui définit le chemin du fichier à traiter
  • regexp : L'expression régulière à utiliser pour identifier la ligne à ajouter ou supprimer
    • insertbefore et insertafter : utilisé avec state present. Permet d'ajouter une ligne avant ou après un modèle.
  • line : La ligne à modifier ou à supprimer
  • state : Définit si la ligne doit être retranché ou ajouté present ou absent
  • backup : Sauvegarde le fichier avant modification avec un horodatage
  • user : Propriétaire du fichier
  • mode : Permissions du fichier
  • backrefs : utilisé avec state present et mets en commentaire la ligne original
  • validate : Une commande permettant de valider la modification apportée.

Vous voyez ces modules comportent bcp de paramètres et pour les expliquer rien de mieux que des exemples d'utilisation.

attention

Faites bien attention que votre playbook soit idempotent! C'est à dire que chaque action produit un seul et même résultat si on l'exécute plusieurs fois. Vous pouvez,pour contrôler, utiliser le mode --diff lors du lancement de votre playbook.

Ajouter une ou des lignes à un fichier

On peut demander au module lineinfile d'ajouter du contenu à un fichier. Pour ajouter plusieurs lignes il suffit d'ajouter des \n !

---
  - name: Ajouter des commentaires en début de fichier
    hosts: all
    become: true
    tasks:
      - name: Ajouter un commentaire
        lineinfile:
          dest: /etc/hosts
          line: '# Mon commentaire1 \n# Mon commentaire2'

Ajouter du contenu avant ou après un repère

Pour placer précisément du contenu avant ou après un texte précis, il suffit d'utiliser les options insertbefore ou insertafter. Ces options utilisent soit des expressions régulières soit deux chaînes qui indiquent le début ou la fin du fichier: BOF ou EOF

---
  - name: Ajouter des commentaires en début de fichier
    hosts: all
    become: true
    tasks:
      - name: Ajouter un commentaire
        lineinfile:
          dest: /etc/hosts
          insertbefore: BOF
          line: '# Mon commentaire'

Remplacer du contenu

Il suffit d'ajouter l'option regexp.

---
  - name: Ajouter des commentaires en début de fichier
    hosts: all
    become: true
    tasks:
      - name: Ajouter un commentaire
        lineinfile:
          dest: /etc/ssh/sshd_config
          regexp: '^Port'
          line: 'Port 2222'

Cet exemple n'est pas idempotent ! Et oui à la prochaine execution la machine ne réponds plus puisque sont port ssh à changer. Il faudra donc intégrer au playbook quelque chose qui permet de contrôler si le port 22 réponds sinon on utilisera le port spécial 2222, avec le module wait_for par exemple.

Valider la présence d'une ligne dans un fichier

En combinant checkmode et register on peut demander à lineinfile de valider la présence d'une ligne dans un fichier et conditionner l'execution d'autres commandes avec un when.

---
  - name: Vérifier la présence d'une ligne
    hosts: all

    tasks:
      - name: La ligne est bien présente ?
        lineinfile:
          path: /etc/hosts
          regexp: '^127\.0\.0\.1.*'
          state: present
        check_mode: yes
        register: out
      - debug:
          msg: "Oui la ligne existe."
        when: out.found

Valider qu'une modification via une commande.

Imaginons que nous modifions un fichier sudoers et bien pour valider que cette modification est valide on peut demander au module lineinfile de lancer la commande `visudo -cf %s'

---
  - name: Vérifier la présence d'une ligne
    hosts: all

    tasks:
      - name: La ligne est bien présente ?
        lineinfile:
          regexp: '^%wheel'
          line: '%wheel ALL=(ALL) NOPASSWD: ALL'
          validate: 'visudo -cf %s'

Le module BlockInfile

Le module BLockInfile fonctionne comme LineInfile mais il ajoutera des repères autour des modifications.

  - name: Vérifier la présence d'une ligne
    hosts: all
    become: true

    tasks:
      - name: Ajout d'IP dans le fichier /etc/hosts
        blockinfile:
          path: /etc/hosts
          block: |
            {{ item.ip }} {{ item.name }}
        loop:
          - { name: host1, ip: 10.10.1.10 }
          - { name: host2, ip: 10.10.1.11 }
          - { name: host3, ip: 10.10.1.12 }

Produirait ces lignes :

# BEGIN Mes modifications host3
10.10.1.12 host3
# END Mes modifications host3

Euh elles sont où mes lignes host1 et host2 !!! Et bien comme les lignes de marqueurs sont identiques elles sont écrasées à chaque itération.

Il est possible de modifier les marqueurs avec les options:

  • marker : Par défaut {{mark}} BEGIN ANSIBLE MANAGED BLOCK
  • marker_begin : Le marqueur de DEBUT, par défaut BEGIN
  • marker_after : Le marqueur de FIN, par défaut END

Notre exemple devient :

  - name: Vérifier la présence d'une ligne
    hosts: all
    become: true

    tasks:
      - name: Ajout d'IP dans le fichier /etc/hosts
        blockinfile:
          path: /etc/hosts
          block: |
            {{ item.ip }} {{ item.name }}
          marker: '# {mark} de mes modifications : {{ item.name }}'
          marker_begin: 'DEBUT'
          marker_end: 'FIN'
        loop:
          - { name: host1, ip: 10.10.1.10 }
          - { name: host2, ip: 10.10.1.11 }
          - { name: host3, ip: 10.10.1.12 }

produit :

# DEBUT de mes modifications : host1
10.10.1.10 host1
# FIN de mes modifications : host1
# DEBUT de mes modifications : host2
10.10.1.11 host2
# FIN de mes modifications : host2
# DEBUT de mes modifications : host3
10.10.1.12 host3
# FIN de mes modifications : host3

Voila pour aujourd'hui. Si vous voulez plus d'exemples d'utilisation d'Ansible je vous renvoie à mon introduction Ansible où la liste complète de tous les billets sur Ansible s'y trouve.