
Le module ansible.builtin.replace substitue toutes les occurrences d'un motif regex dans un fichier, sans toucher au reste de la ligne ni du fichier. C'est l'outil pour les changements transversaux : mettre à jour une URL d'API, changer un nom d'hôte qui apparaît à 15 endroits, propager une nouvelle version. Public visé : intermédiaires Ansible. Cette page distingue replace de lineinfile (différence cruciale), montre les patterns d'usage avec before et after pour limiter la zone de remplacement, et liste les pièges classiques (regex gourmande, idempotence cassée, capture group mal échappée).
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Remplacer un motif regex par une nouvelle valeur dans un fichier.
- Limiter la zone de remplacement avec
before:etafter:. - Distinguer
replace(modifie un motif) delineinfile(modifie une ligne entière). - Utiliser des groupes de capture pour préserver une partie du motif d'origine.
- Diagnostiquer une idempotence cassée par un double run.
Prérequis
Section intitulée « Prérequis »- Bases Ansible (cf. Premiers pas).
- Notions de regex Python (les regex sont du
rePython, pas du POSIX strict). - Un fichier cible avec des motifs à remplacer (config, JSON, YAML…).
L'usage de base, substituer toutes les occurrences
Section intitulée « L'usage de base, substituer toutes les occurrences »- name: Mettre à jour l'URL de l'API dans tous les services ansible.builtin.replace: path: /etc/myapp/services.yml regexp: 'https://api-old\.example\.com' replace: 'https://api.example.com'Comportement :
- Toutes les occurrences du motif
https://api-old.example.comdans le fichier sont remplacées. - Si aucune occurrence n'est trouvée → task
ok(idempotent). - Si le fichier n'existe pas → task échoue (pas de
create:dansreplace).
C'est le contraste fondamental avec lineinfile : replace ne sait pas ajouter une ligne, il modifie ce qui existe déjà.
La différence cruciale avec lineinfile
Section intitulée « La différence cruciale avec lineinfile »C'est la confusion la plus fréquente. Mémorisez ce tableau :
| Aspect | lineinfile | replace |
|---|---|---|
| Granularité | Une ligne entière | Un motif (peut être partie de ligne) |
| Cas d'usage | Ajouter/modifier/supprimer une ligne identifiée | Substituer un motif partout dans le fichier |
| Si motif absent | Ajoute la ligne (par défaut) | Ne fait rien (idempotent) |
| Multi-occurrences | Une seule ligne traitée par run | Toutes les occurrences traitées |
| Multi-lignes | Pas supporté | Supporté via (?s) ou multiline: false |
| Regex style | Python re simplifié | Python re complet |
Règle de décision :
- « Je veux que cette ligne existe avec cette valeur » →
lineinfile. - « Je veux remplacer ce motif partout » →
replace.
Limiter la zone, before et after
Section intitulée « Limiter la zone, before et after »Pour ne remplacer que dans une section d'un fichier, utiliser before (limite supérieure) et after (limite inférieure) :
- name: Activer SSL UNIQUEMENT dans la section [server] ansible.builtin.replace: path: /etc/myapp/config.ini after: '^\[server\]' # commence à matcher après cette ligne before: '^\[' # arrête au début de la section suivante regexp: '^ssl_enabled\s*=\s*false' replace: 'ssl_enabled = true'Le bloc actif est entre after (exclu) et before (exclu). Si une seule des deux est définie, l'autre prend respectivement le début ou la fin du fichier.
Groupes de capture pour préserver une partie
Section intitulée « Groupes de capture pour préserver une partie »Comme avec lineinfile, on peut utiliser des backrefs Python \g<1>, \g<2> pour conserver une partie du motif :
- name: Bumper la version d'un paquet npm dans package.json ansible.builtin.replace: path: /opt/app/package.json regexp: '"express":\s*"\^?\d+\.\d+\.\d+"' replace: '"express": "^4.18.2"'
# Ou avec capture pour préserver le préfixe- name: Forcer toutes les URL http vers https en gardant le path ansible.builtin.replace: path: /etc/myapp/links.txt regexp: 'http://(api\.example\.com)(/[^\s]*)?' replace: 'https://\g<1>\g<2>'Avec replace, contrairement à lineinfile, pas besoin de backrefs: true, c'est le comportement par défaut.
Validation, validate: aussi disponible
Section intitulée « Validation, validate: aussi disponible »Comme lineinfile, le module accepte un validator pour ne pas écrire un fichier syntaxiquement cassé :
- name: Désactiver SELinux uniquement si le validator passe ansible.builtin.replace: path: /etc/selinux/config regexp: '^SELINUX=\w+' replace: 'SELINUX=permissive' validate: "/usr/sbin/sestatus -v %s || /bin/true"%s est remplacé par le chemin temporaire candidat. Le validator doit retourner 0 pour qu'Ansible commit le fichier.
Multi-lignes, (?s) ou (?m)
Section intitulée « Multi-lignes, (?s) ou (?m) »Par défaut, le regex Python . ne matche pas les nouvelles lignes. Pour des remplacements multi-lignes :
# Remplacer un bloc multi-lignes (?s) = DOTALL- name: Réinitialiser une section JSON ansible.builtin.replace: path: /etc/myapp/config.json regexp: '(?s)"servers":\s*\[.*?\]' replace: '"servers": []'(?s) active le mode DOTALL, . matche aussi \n. Combiné avec *? (non gourmand), on peut remplacer un bloc complet sans risquer de manger trop loin.
Idempotence et double run
Section intitulée « Idempotence et double run »Comme tout module modificateur de fichier, replace doit être idempotent :
# 1er run : `changed` si le motif est trouvé et remplacéansible-playbook update-urls.yml
# 2e run immédiat : DOIT être `ok` (le motif a disparu après le 1er run)ansible-playbook update-urls.ymlSi le 2e run rapporte encore changed, c'est que la substitution n'a pas fait disparaître le motif, typiquement parce que regexp matche aussi le résultat de replace :
# ❌ NON-IDEMPOTENT : chaque run ajoute encore "https://"- name: Ajuster links.txt ansible.builtin.replace: path: /etc/myapp/links.txt regexp: 'http' replace: 'https://http'
# ✓ IDEMPOTENT : la regex ne match plus après remplacement- name: Ajuster links.txt ansible.builtin.replace: path: /etc/myapp/links.txt regexp: 'http://' replace: 'https://'Règle de design : la regex doit matcher uniquement la valeur d'origine, pas la valeur d'arrivée.
Cas d'usage typiques RHCE / RHCSA
Section intitulée « Cas d'usage typiques RHCE / RHCSA »Les épreuves RHCSA et RHCE demandent souvent de modifier une valeur déjà présente dans un fichier système, sans le réécrire. Les trois exemples ci-dessous couvrent ce geste : une ancre ^ sur le nom du paramètre, une classe de caractères qui désigne l'ancienne valeur, et la nouvelle en clair. Le premier montre aussi ce qu'un examen peut demander sans que ce soit une bonne pratique en production : désactiver SELinux règle un symptôme et retire une protection, un module sefcontext ou seboolean traite la cause.
# Désactiver SELinux à chaud (config avant reboot)- name: Ajuster config ansible.builtin.replace: path: /etc/selinux/config regexp: '^SELINUX=\w+' replace: 'SELINUX=disabled'
# Changer le default runlevel systemd- name: Ajuster default.target ansible.builtin.replace: path: /etc/systemd/system/default.target regexp: 'multi-user\.target' replace: 'graphical.target'
# Remplacer toutes les références à un domaine déprécié- name: Ajuster hosts ansible.builtin.replace: path: /etc/hosts regexp: 'old\.corp\.local' replace: 'new.corp.local'
# Forcer un timeout dans tous les fichiers d'un dossier (loop)- name: Ajuster le fichier de configuration ansible.builtin.replace: path: "{{ item }}" regexp: 'timeout\s*=\s*\d+' replace: 'timeout = 30' loop: - /etc/myapp/conf.d/db.conf - /etc/myapp/conf.d/cache.conf - /etc/myapp/conf.d/api.confPièges courants
Section intitulée « Pièges courants »Quatre des sept lignes portent sur l'expression régulière, ce qui n'a rien d'étonnant pour un module dont c'est l'unique moyen de désigner ce qu'il modifie. La première est la plus retorse : une regex qui matche encore son propre résultat produit un changed perpétuel, alors même que le fichier est déjà dans l'état voulu. Les trois dernières rappellent les limites du module, il ne crée pas le fichier absent, ne traverse les sauts de ligne qu'avec (?s), et lit tout en UTF-8.
| Symptôme | Cause | Solution |
|---|---|---|
changed à chaque run | La regex matche aussi le résultat | Concevoir la regex pour ne plus matcher après remplacement |
| Trop de remplacements | Regex gourmande (.*) | Utiliser non-gourmand (.*?) ou ancres ^/$ |
| Aucun remplacement alors que le motif existe | Caractères spéciaux non échappés | Échapper ., \, (, ), [, ], etc. |
replace ne traverse pas les newlines | Mode DOTALL non activé | Préfixer la regex avec (?s) |
| Service pas reload après modification | notify: manquant | Ajouter notify: + handler |
| Fichier inexistant, task fail | replace ne crée pas | Utiliser copy avec force: false ou template à la place |
| Encoding cassé sur fichier non-UTF8 | replace lit en UTF-8 par défaut | Pour les fichiers latin1 ou autre, considérer script ou command avec sed |
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »replacesubstitue un motif partout dans le fichier, pas un seul cas.- N'ajoute jamais de ligne, utiliser
lineinfilepour ça. beforeetafterlimitent la zone de remplacement à une section identifiée par regex.(?s)active DOTALL pour les remplacements multi-lignes.- Toujours utiliser
*?non gourmand sauf intention contraire. - La regex ne doit plus matcher après remplacement, sinon idempotence cassée.
validate:disponible comme danslineinfilepour les fichiers critiques.- Pas de
create:, le fichier doit exister.
Mettre en pratique
Section intitulée « Mettre en pratique »Une substitution globale devient dangereuse dès que le fichier comporte plusieurs sections. Ce lab part d'un /etc/myapp.conf découpé en [server] et [client] : vous y remplacez une URL sur tout le fichier, activez une option dans la seule section [server] grâce à before: et after:, puis changez un numéro de port via un groupe de capture. La correction contrôle que [client] est resté intact et que le second passage ne change plus rien.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Module yum_repository : un dépôt se déclare avec son module, pas par substitution dans un fichier
.repo. - Module sudoers : une règle sudo validée par visudo, bien plus sûre qu'une expression régulière sur
/etc/sudoers. - Module sysctl : modifier un paramètre kernel sans réécrire
sysctl.confà coups de regex.