Automatiser : PowerShell et scripts Linux
Ce que ce chapitre apporte
- Expliquer ce que l'automatisation apporte au-delà du gain de temps.
- Lire un script PowerShell : cmdlets, pipeline, variables, conditions, boucles.
- Reconnaître les cmdlets d'administration de l'annuaire.
- Automatiser la création de comptes à partir d'un fichier, avec règle de nommage.
- Écrire un script de maintenance Linux et le planifier.
- Rendre un script sûr : mode simulation, journalisation, idempotence.
Pourquoi automatiser
L'automatisation est l'exécution d'une tâche sans intervention humaine permanente. Un script en est le support : une suite d'instructions exécutées par un interpréteur.
Quatre raisons sont couramment avancées. Elles ne se valent pas.
| Raison | Portée |
|---|---|
| Gagner du temps | réelle, mais la moins importante |
| Réduire les erreurs | un script ne se trompe pas de case à cocher |
| Standardiser les opérations | la raison décisive chez Ardexia |
| Simplifier les tâches répétitives | conséquence des précédentes |
Et elle se relit. « Comment crée-t-on un compte ici ? » a alors une réponse unique et vérifiable : le script.
PowerShell, les bases
PowerShell est le langage de scripting et d'administration de Microsoft. Sa particularité tient en un point : il manipule des objets, pas du texte.
Une commande PowerShell s'appelle une cmdlet et suit toujours la forme Verbe-Nom : Get-Service, New-ADUser, Set-Content. La convention rend le vocabulaire devinable.
# Une variable commence par $
$Nom = "Dupont"
$Age = 22
# Une condition. Les operateurs sont textuels : -gt, -lt, -eq, -ne
if ($Age -gt 18) {
Write-Host "Majeur"
} else {
Write-Host "Mineur"
}
# Une boucle sur une collection
$Utilisateurs = @("marie", "marc", "melanie")
foreach ($User in $Utilisateurs) {
Write-Host "Traitement de $User"
}
# Une fonction
function Get-EspaceLibre {
param([string]$Lecteur = "C")
$Disque = Get-PSDrive $Lecteur
return [math]::Round($Disque.Free / 1GB, 2)
}
Write-Host "Espace libre : $(Get-EspaceLibre 'C') Go"
Les blocs Python de ce chapitre, eux, tournent : ils servent à éprouver la logique d'un script, ce qui est la partie qu'on rate le plus souvent.
Le pipeline
C'est la construction qui donne sa puissance au langage : la sortie d'une cmdlet devient l'entrée de la suivante, sous forme d'objets et non de texte.
-NoTypeInformation, et pourquoi il compte#TYPE System.ServiceProcess.ServiceController. Cette ligne n'est pas un en-tête de colonnes : elle décale tout, et le tableur comme le script qui relit le fichier prennent alors les vrais noms de colonnes pour des données.L'argument supprime cette ligne et produit un CSV ordinaire, lisible par un tableur, par un moteur SQL ou par un autre script. C'est une habitude à prendre systématiquement dès qu'un fichier sort de PowerShell pour entrer ailleurs.
Sur les versions récentes de PowerShell, cette ligne n'est plus écrite par défaut et l'argument ne fait plus rien. Le conserver reste sans risque, et garantit le même résultat quelle que soit la version en service, ce qui est exactement ce qu'on attend d'un script d'administration.
awk ou cut pour en extraire une colonne. Le script casse le jour où la mise en forme change.En PowerShell,
$_.Status désigne une propriété de l'objet. Il n'y a rien à découper, et rien à casser. C'est ce qui rend les scripts d'administration Windows plus robustes qu'on ne s'y attend.
Administrer l'annuaire par script
Le module Active Directory fournit les cmdlets qui recouvrent tout le cycle de vie d'un compte.
| Besoin | Cmdlet |
|---|---|
| Créer un compte | New-ADUser |
| Chercher des comptes | Get-ADUser -Filter |
| Modifier un attribut | Set-ADUser |
| Ajouter à un groupe | Add-ADGroupMember |
| Désactiver | Disable-ADAccount |
| Déplacer dans une OU | Move-ADObject |
# Creer un compte dans la bonne unite d'organisation
New-ADUser -Name "Marie Dupont" `
-SamAccountName "m.dupont" `
-UserPrincipalName "m.dupont@ardexia.lan" `
-Path "OU=Facturation,OU=Utilisateurs,DC=ardexia,DC=lan" `
-AccountPassword (Read-Host -AsSecureString "Mot de passe") `
-ChangePasswordAtLogon $true `
-Enabled $true
Add-ADGroupMember -Identity "GG_Facturation" -Members "m.dupont"
# Les comptes encore actifs, mais inutilises depuis 90 jours :
# la revue que personne ne fait
Search-ADAccount -UsersOnly -AccountInactive -TimeSpan 90.00:00:00 |
Where-Object { $_.Enabled } |
Select-Object Name, SamAccountName, LastLogonDate |
Export-Csv comptes-inactifs.csv -NoTypeInformation
Get-ADUser -Filter { LastLogonDate -lt $Limite }, et elle ne fonctionne pas. LastLogonDate est un attribut construit : il n'existe pas dans l'annuaire, il est calculé après coup par le module à partir d'un autre attribut. Un filtre porte sur ce que l'annuaire sait chercher, donc jamais sur lui.Le symptôme est trompeur : selon la version du module, la commande échoue franchement ou rend une liste vide, ce qu'on lit volontiers comme « aucun compte inactif, tout va bien ».
Search-ADAccount existe précisément pour cette question et calcule l'inactivité là où il faut.La leçon dépasse cette commande : avant d'écrire un filtre, il faut savoir si l'attribut visé est stocké ou calculé.
Ce n'est pas un exploit technique : c'est ce que permet un annuaire correctement alimenté. Sans annuaire, la question n'avait pas de réponse. Avec un annuaire et une cmdlet, elle en a une, tous les lundis matin.
La logique d'un script de création de comptes
Trois interlocuteurs pour une seule ligne du fichier, et l'opération se répète autant de fois qu'il y a de lignes. La consultation avant création est ce qui distingue un script utilisable d'un script qui écrase le compte d'un homonyme.
La partie délicate n'est pas la cmdlet, c'est la règle de nommage et ce qu'elle doit gérer : les accents, les homonymes, la longueur maximale d'un identifiant. C'est cette logique que le bloc Python ci-dessous éprouve, avant de l'écrire en PowerShell.
Ces trois contraintes se combinent, et c'est leur combinaison qui pose problème. L'attribut historique sAMAccountName d'Active Directory est plafonné à 20 caractères, et ce plafond ne se négocie pas. Un identifiant qui atteint déjà 20 caractères ne peut donc pas recevoir en plus le suffixe qui le distinguerait d'un homonyme : il faut d'abord lui retirer autant de caractères que le suffixe en apporte. C'est ce que fait la ligne base[:LONGUEUR_MAX - len(suffixe)] + suffixe, et c'est la seule ligne du script qu'il vaut la peine de relire deux fois. Écrite naïvement, en concaténant le suffixe à la fin, elle produit un identifiant de 21 caractères et un échec de création au dixième homonyme, jamais au premier.
Ces trois cas n'apparaissent jamais lors du test avec « Jean Dupont ». Ils apparaissent en production, la semaine de la rentrée.
Côté Linux
Les mêmes besoins, avec d'autres outils. Un script bash, planifié par cron.
#!/bin/bash
# Surveillance de l'espace disque, alerte au-dela d'un seuil
SEUIL=85
JOURNAL=/var/log/espace.log
DESTINATAIRE="admin@ardexia.lan"
df -h --output=pcent,target | tail -n +2 | while read pourcent point; do
valeur=${pourcent%\%} # retire le signe %
valeur=${valeur// /} # retire les espaces d'alignement
if [ "$valeur" -ge "$SEUIL" ]; then
echo "$(date '+%F %T') $point a ${valeur}%" >> "$JOURNAL"
echo "$point occupe a ${valeur}%" \
| mail -s "Disque plein" "$DESTINATAIRE"
fi
done
La planification par cron se lit dans cinq champs, suivis de la commande à exécuter.
# m h jm mois js commande
0 6 * * * /opt/scripts/espace-disque.sh
0 2 * * 0 /opt/scripts/sauvegarde-complete.sh
0 2 * * 1-6 /opt/scripts/sauvegarde-differentielle.sh
*/15 * * * * /opt/scripts/verifie-services.sh
La ligne
0 2 * * 0 se lit donc : à 2 h 00, tous les jours du mois, tous les mois, le dimanche seulement. C'est la sauvegarde complète hebdomadaire du chapitre 5. La ligne suivante, 1-6, couvre les six autres jours de la semaine : ce sont les différentielles, et deux lignes suffisent à écrire toute la politique.
Rendre un script digne de confiance
Un script qui modifie l'annuaire ou supprime des fichiers mérite trois garanties. Aucune n'est optionnelle.
Mode simulation : le script sait afficher ce qu'il ferait, sans le faire. En PowerShell, -WhatIf sur les cmdlets qui le supportent.
Journalisation : chaque action écrite dans un fichier daté, avec qui, quoi, et le résultat. C'est ce qui permet de comprendre après coup, et c'est la traçabilité du chapitre 3.
Idempotence : exécuter deux fois le script produit le même état qu'une fois. Un script qui ajoute un utilisateur à un groupe doit vérifier qu'il n'y est pas déjà, plutôt que d'échouer ou de dupliquer.
D'où la règle : tout script destructeur commence par une exécution en simulation, sur un périmètre réduit, dont on relit la sortie. Et il journalise, pour qu'on puisse défaire.
Exercices type
Que fait `Get-Service | Where-Object { $_.Status -eq 'Stopped' }` ?
Get-Service produit la liste des services sous forme d'objets. Le pipeline la transmet à Where-Object, qui filtre.
$_ désigne l'objet en cours de traitement, et $_.Status sa propriété d'état. La commande rend donc les services arrêtés.
Le point de méthode : on n'a rien découpé ni analysé textuellement. Si le format d'affichage de Get-Service change demain, le filtre continue de fonctionner, parce qu'il porte sur une propriété et non sur une colonne.
Deux salariés s'appellent Dupont. Que doit faire le script de création ?
Détecter la collision avant de créer, et dériver un identifiant distinct : m.dupont puis m.dupont2.
Ce qu'il ne doit surtout pas faire : échouer en laissant la moitié du traitement fait, ou créer un doublon qui rendra les droits impossibles à suivre.
Deux précautions supplémentaires que montre le bloc Python : normaliser les accents, car Mélanie ne doit pas produire un identifiant différent selon l'encodage ; et respecter la limite de 20 caractères, en tronquant avant d'ajouter le suffixe, sinon le suffixe se fait couper.
Pourquoi journaliser un script qui fonctionne bien ?
Parce que la question ne se pose jamais pendant qu'il fonctionne bien.
Elle se pose trois semaines plus tard : « qui a désactivé ce compte ? », « quand ce dossier a-t-il été supprimé ? », « le script a-t-il tourné dimanche ? ». Sans journal, la réponse est « on ne sait pas ».
Un journal utile contient la date, le compte qui a exécuté, l'action tentée, le résultat, et les éléments concernés. Il se conserve avec une durée décidée, comme l'audit du chapitre 3.
Qu'est-ce qu'un script idempotent, et pourquoi est-ce important ?
Un script dont la deuxième exécution produit le même état que la première.
Contre-exemple : un script qui ajoute une ligne à un fichier de configuration. Exécuté trois fois, il produit trois lignes, et une configuration invalide.
Version idempotente : vérifier si la ligne existe déjà, et ne l'ajouter que sinon.
Cela compte parce qu'un script est relancé plus souvent qu'on ne le prévoit : après une interruption, par erreur, ou parce que personne ne sait s'il a déjà tourné. L'idempotence rend la relance sans danger, et c'est ce qui permet d'automatiser sereinement.
Lire `0 2 * * 1-6 /opt/scripts/sauvegarde-differentielle.sh`
À 2 h 00, tous les jours du mois, tous les mois, du lundi au samedi.
Combinée à la ligne 0 2 * * 0 qui lance la sauvegarde complète le dimanche, on obtient exactement la politique du chapitre 5 : une complète hebdomadaire, six différentielles.
Une vérification à faire : que les deux tâches ne se chevauchent pas, et que la fenêtre de 2 h suffit à la complète. Sinon, la différentielle du lundi démarre pendant que la complète du dimanche tourne encore.
Faut-il automatiser la suppression des comptes inactifs ?
La détection, oui, et sans hésiter : c'est une requête, elle est sans risque, et elle produit la liste que personne ne prend le temps d'établir.
La suppression, non. Une suppression automatique de comptes est irréversible et se trompera un jour : un congé de longue durée, un compte de service qui n'ouvre jamais de session, un salarié en mission.
La démarche raisonnable est graduée : détecter automatiquement, désactiver après validation humaine, et supprimer après un délai de conservation, une fois les données personnelles traitées conformément au chapitre 6.
Le principe général : automatiser ce qui se vérifie, faire valider ce qui se défait mal.
Vérification
1.Quel est le principal apport de l'automatisation ?
2.La règle « première lettre du prénom, puis nom » échoue sur quels cas ?
3.En quoi un script est-il plus dangereux qu'un technicien ?
4.À quoi sert un essai à blanc ?
La méthode
- Écrire la règle avant le script. Nommage, cas particuliers, ce qui se passe si l'objet existe déjà.
- Éprouver la logique sur des données de test qui contiennent les cas tordus : accents, homonymes, noms trop longs.
- Commencer par une simulation sur un périmètre réduit, et relire la sortie ligne à ligne.
- Journaliser chaque action avec sa date, son auteur et son résultat.
- Rendre le script idempotent : il doit pouvoir être relancé sans dégât.
- Automatiser la détection, faire valider la destruction.
- Ranger le script quelque part de partagé et versionné. Un script sur le poste d'un technicien n'est pas une procédure d'entreprise.
Synthèse
- L'automatisation apporte surtout la reproductibilité : une procédure écrite dans un script ne dérive pas.
- PowerShell manipule des objets, pas du texte. Cmdlets en
Verbe-Nom, composées par le pipeline. - Le module Active Directory couvre tout le cycle de vie :
New-ADUser,Get-ADUser,Add-ADGroupMember,Disable-ADAccount. - Une règle de nommage doit gérer les accents, les homonymes et la longueur maximale.
- Sous Linux, bash et cron en cinq champs : minute, heure, jour du mois, mois, jour de la semaine.
- Trois garanties : simulation, journalisation, idempotence.
- Un script se trompe plus vite et plus largement qu'un humain. Il se teste en conséquence.
Mettre en pratique
Ce qu'un script de création oublie, l'erreur qui se répète trois cents fois, et la reproductibilité.
- Ce qu'un script de création oublieNiveau 3
- Un script se trompe en grandNiveau 3
- Reproductible, avant d'être rapideNiveau 2