Aller au contenu
Infrastructure as Code medium

Gestion des fichiers avec Salt : file.managed et file.recurse

10 min de lecture

Configurer un serveur, c'est surtout déployer des fichiers au bon endroit, avec le bon propriétaire, le bon mode et le bon contenu. Le module file de Salt fait tout cela de façon idempotente et sait rendre des templates. Ce module déploie un site web complet, du fichier unique à l'arborescence. Public visé : intermédiaires à l'aise avec states et Jinja.

  • Déployer un fichier avec propriétaire, mode et contenu vérifié.
  • Déployer une arborescence avec file.recurse.
  • Rendre un fichier depuis un template Jinja.
  • Déclencher un rechargement de service sur changement.

file.managed déploie un fichier depuis une source servie par le master (protocole salt://) ou depuis un contenu inline. Il gère en une déclaration le propriétaire, le groupe, le mode (toujours quoté pour éviter l'interprétation YAML), et vérifie le contenu à chaque passage.

Deux réflexes de sécurité valent pour tout fichier système. Le mode reste entre guillemets : écrit "0644", jamais 0644 non quoté, que YAML lirait comme un entier décimal et non comme une valeur octale. Le user et le group sont explicites dès que vous écrivez hors du répertoire personnel : ne laissez pas un fichier de /etc hériter du compte qui exécute Salt, nommez le propriétaire attendu.

# mode quoté, propriétaire explicite
/etc/nginx/sites-available/appsite.conf:
file.managed:
- source: salt://webdeploy/site.conf.j2
- user: root
- group: root
- mode: "0644"

Pour un fichier qui varie selon le serveur, on active le rendu Jinja avec template: jinja. La source peut alors lire grains et pillars : un seul gabarit produit la bonne configuration partout. Voici la source site.conf.j2 du TP, un bloc serveur Nginx paramétré par les grains du minion :

# Généré par Salt depuis un template Jinja - ne pas éditer à la main
# Minion : {{ grains['id'] }} ({{ grains['os'] }} {{ grains['osrelease'] }})
server {
listen 8088;
server_name {{ grains['fqdn'] }};
root /var/www/appsite;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

Le state qui rend ce fichier active simplement template: jinja :

/etc/nginx/sites-available/appsite.conf:
file.managed:
- source: salt://webdeploy/site.conf.j2
- template: jinja
- user: root
- group: root
- mode: "0644"

À l'application sur le minion web1, Salt résout les grains dans le fichier déployé. On le vérifie directement sur la machine :

# Généré par Salt depuis un template Jinja - ne pas éditer à la main
# Minion : web1 (Debian 13)
server {

Le même gabarit produirait server_name et commentaires différents sur un autre minion, sans toucher au state : c'est tout l'intérêt du rendu par données.

Quand il faut déployer tout un répertoire (le contenu d'un site, un ensemble de scripts), file.recurse copie l'arborescence entière depuis une source salt:// en conservant permissions et structure.

# déployer le contenu d'un site
/var/www/appsite:
file.recurse:
- source: salt://webdeploy/public
- user: www-data
- group: www-data
- dir_mode: "0755"
- file_mode: "0644"

Le répertoire source webdeploy/public du master contient l'index.html et le style.css du site. Après application, ils atterrissent sur le minion avec le bon propriétaire et le bon mode, file_mode s'appliquant à chaque fichier :

644 www-data:www-data /var/www/appsite/index.html

Recharger un service avec une configuration invalide le laisse cassé ou l'empêche de redémarrer. Quand un fichier managé peut casser un service, insérez un état de vérification dédié entre le fichier et le rechargement, plutôt que de compter sur l'argument check_cmd. L'état de contrôle exécute la commande de test du service, déclenché par onchanges sur le fichier : il n'agit que si le fichier a bougé, et son échec stoppe le state avant que le service ne recharge une conf fautive.

# vérifier la conf avant le rechargement
/etc/nginx/sites-available/appsite.conf:
file.managed:
- source: salt://webdeploy/site.conf.j2
- template: jinja
- user: root
- group: root
- mode: "0644"
verifier-conf-nginx:
cmd.run:
- name: nginx -t
- onchanges:
- file: /etc/nginx/sites-available/appsite.conf
nginx-appsite:
service.running:
- name: nginx
- watch:
- file: /etc/nginx/sites-available/appsite.conf
- require:
- cmd: verifier-conf-nginx

Cet ordre donne une barrière claire : le fichier change, la vérification s'exécute, et le service ne recharge qu'ensuite. Un test qui échoue fait remonter l'erreur au bon endroit, sans laisser le service redémarrer sur une configuration invalide.

Assemblons tout dans un state webdeploy.sls qui déploie un site complet sur le minion web1 : la configuration Nginx rendue depuis un template Jinja, le contenu du site copié avec file.recurse, un état de vérification nginx -t déclenché par onchanges, et le service rechargé par watch uniquement sur changement.

/etc/nginx/sites-available/appsite.conf:
file.managed:
- source: salt://webdeploy/site.conf.j2
- template: jinja
- user: root
- group: root
- mode: "0644"
/etc/nginx/sites-enabled/appsite.conf:
file.symlink:
- target: /etc/nginx/sites-available/appsite.conf
- require:
- file: /etc/nginx/sites-available/appsite.conf
/var/www/appsite:
file.recurse:
- source: salt://webdeploy/public
- user: www-data
- group: www-data
- dir_mode: "0755"
- file_mode: "0644"
verifier-conf-nginx:
cmd.run:
- name: nginx -t
- onchanges:
- file: /etc/nginx/sites-available/appsite.conf
nginx-appsite:
service.running:
- name: nginx
- watch:
- file: /etc/nginx/sites-available/appsite.conf
- file: /var/www/appsite
- require:
- cmd: verifier-conf-nginx

Au premier passage, chaque brique agit et le service redémarre une fois la conf validée :

Fenêtre de terminal
sudo salt web1 state.apply webdeploy
ID: /etc/nginx/sites-available/appsite.conf
Function: file.managed
Comment: File /etc/nginx/sites-available/appsite.conf updated
ID: /var/www/appsite
Function: file.recurse
Comment: Recursively updated /var/www/appsite
ID: verifier-conf-nginx
Function: cmd.run
Comment: Command "nginx -t" run
ID: nginx-appsite
Function: service.running
Comment: Service restarted
Summary for web1
Succeeded: 5 (changed=5)
Failed: 0

Le site répond alors sur le port choisi dans le template :

Fenêtre de terminal
sudo salt web1 cmd.run 'curl -s http://localhost:8088/ | grep h1'
web1:
<h1>Déploiement piloté par Salt</h1>

Le second passage ne doit rien changer : la conf est déjà en place, donc onchanges ne déclenche pas la vérification et watch ne recharge pas le service.

Fenêtre de terminal
sudo salt web1 state.apply webdeploy
Comment: State was not run because none of the onchanges reqs changed
Summary for web1
Succeeded: 5
Failed: 0

Aucun changement, nginx -t non rejoué, service non redémarré : la boucle fichier - vérification - rechargement est idempotente. Elle ne s'active que le jour où le template ou le contenu du site bouge réellement.

  1. file.managed déploie un fichier avec propriétaire, mode quoté et contenu vérifié.
  2. template: jinja rend un fichier à partir des grains et pillars.
  3. file.recurse déploie une arborescence complète.
  4. Un watch recharge le service uniquement quand le fichier change.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn