Produire un rapport et automatiser son exécution
Objectifs de la séance
- Exporter un résultat en CSV, JSON et HTML
- Éviter le piège des cmdlets
Format-*placées trop tôt dans un pipeline - Planifier l'exécution automatique d'un script avec le Planificateur de tâches Windows
- Faire le bilan des 7 séances de PowerShell niveau 2
Notions théoriques
Objets et mise en forme
Rappel central du niveau 1 : PowerShell fait circuler des objets, pas du texte. Les cmdlets Format-* (Format-Table, Format-List) transforment ces objets en texte de présentation — elles doivent donc toujours être la dernière commande d'un pipeline.
Get-Service | Format-Table | Export-Csv rapport.csv produit un fichier inexploitable : Format-Table a déjà transformé les objets d'origine en simples instructions de mise en forme, et Export-Csv se retrouve à exporter des colonnes techniques du type ClassId2e4f875a... au lieu des données attendues. La règle est stricte : jamais de Format-* avant un Export-* ou un ConvertTo-*.
Mise en forme à l'écran
Get-Service | Format-Table -AutoSize
Get-Service | Format-List
Get-Process | Out-GridView
Select-Object accepte aussi des propriétés calculées, très utiles pour un rapport lisible :
Get-CimInstance Win32_LogicalDisk | Select-Object DeviceID, @{Name="LibreGo"; Expression={ [math]::Round($_.FreeSpace/1GB,1) }}
Export CSV
$inventaire | Export-Csv -Path "inventaire.csv" -NoTypeInformation -Encoding UTF8 -Delimiter ";"
-NoTypeInformationsupprime une ligne d'en-tête technique inutile (#TYPE ...) que PowerShell ajoute par défaut ;-Delimiter ";"et-Encoding UTF8produisent un fichier directement lisible par un Excel installé en français, sans caractères accentués corrompus.
Import-Csv permet de relire ce fichier plus tard, exactement comme au TP de la séance sur Active Directory.
Export JSON
$inventaire | ConvertTo-Json -Depth 3 | Out-File -FilePath "inventaire.json" -Encoding UTF8
$donnees = Get-Content "inventaire.json" | ConvertFrom-Json
ConvertTo-Json limite par défaut la profondeur de conversion à 2 niveaux d'imbrication (-Depth 2) : un objet complexe (objet contenant un objet contenant un objet...) peut se retrouver tronqué silencieusement. Augmentez -Depth dès que vos données sont imbriquées.
Export HTML
$inventaire | ConvertTo-Html -Title "Inventaire du parc" -PreContent "<h1>Inventaire</h1>" | Out-File "inventaire.html"
Un rapport HTML, contrairement à un CSV, peut être ouvert et lu directement par un collègue non technicien, sans logiciel spécifique.
Nommer ses fichiers de rapport
$fichier = Join-Path "C:\Rapports" "inventaire_$(Get-Date -Format 'yyyy-MM-dd').csv"
Join-Path construit un chemin de façon portable, sans se soucier de la présence ou non d'un \ final ; horodater le nom du fichier évite qu'un rapport n'en écrase un autre.
Le Planificateur de tâches Windows
Le Planificateur de tâches est l'équivalent Windows du cron déjà rencontré en Bash niveau 2 : il exécute un script à intervalle régulier, sans intervention humaine. Une tâche planifiée se compose de trois briques :
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Rapport-Parc.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 07:00
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
Register-ScheduledTask -TaskName "Inventaire du parc" -Action $action -Trigger $trigger -Principal $principal -Description "Genere l'inventaire quotidien du parc"
New-ScheduledTaskActiondéfinit quoi exécuter ;New-ScheduledTaskTriggerdéfinit quand (-Daily -At 07:00, mais aussi-Weekly,-AtStartup, ou-Once -RepetitionInterval) ;New-ScheduledTaskPrincipaldéfinit avec quel compte (SYSTEMs'exécute sans session ouverte, avec des droits élevés) ;Register-ScheduledTaskenregistre réellement la tâche dans le Planificateur.
Exploiter les tâches planifiées
Get-ScheduledTask -TaskName "Inventaire*"
Start-ScheduledTask -TaskName "Inventaire du parc"
Get-ScheduledTaskInfo -TaskName "Inventaire du parc"
Unregister-ScheduledTask -TaskName "Inventaire du parc" -Confirm:$false
Get-ScheduledTaskInfo renvoie notamment LastRunTime et LastTaskResult : un LastTaskResult à 0 signifie que la dernière exécution a réussi — le même principe que le code de retour $? déjà vu côté Bash.
Un script planifié s'exécute sans répertoire courant garanti : utilisez toujours des chemins absolus, aussi bien pour le script lui-même que pour les fichiers qu'il lit ou écrit. -ExecutionPolicy Bypass dans l'argument permet à la tâche de s'exécuter même si la stratégie d'exécution de la session interactive est plus restrictive.
Exemple pratique
# Script : Rapport-Parc.ps1
# Role : produire l'inventaire du parc au format CSV et HTML, puis journaliser
# Planifie tous les jours a 07h00
Import-Module AdminParc
$dossierRapports = "C:\Rapports"
$horodatage = Get-Date -Format "yyyy-MM-dd"
$fichierCsv = Join-Path $dossierRapports "inventaire_$horodatage.csv"
$fichierHtml = Join-Path $dossierRapports "inventaire_$horodatage.html"
if (-not (Test-Path $dossierRapports)) {
New-Item -Path $dossierRapports -ItemType Directory | Out-Null
}
try {
$inventaire = Get-Content "C:\Scripts\machines.txt" | ForEach-Object {
Get-EspaceDisque -Machine $_
}
$inventaire | Export-Csv -Path $fichierCsv -NoTypeInformation -Encoding UTF8 -Delimiter ";"
$inventaire | ConvertTo-Html -Title "Inventaire du parc - $horodatage" `
-PreContent "<h1>Inventaire du parc au $horodatage</h1>" |
Out-File -FilePath $fichierHtml -Encoding UTF8
Write-Journal "Rapport genere : $fichierCsv"
}
catch {
Write-Journal "Echec de generation du rapport : $($_.Exception.Message)" -Niveau "ERREUR"
exit 1
}
Puis l'enregistrement de la tâche planifiée :
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Rapport-Parc.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 07:00
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
Register-ScheduledTask -TaskName "Inventaire du parc" -Action $action -Trigger $trigger -Principal $principal -Description "Genere l'inventaire quotidien du parc"
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Les étapes 1 et 2 s'exécutent partout, sans droits particuliers. Register-ScheduledTask avec un principal SYSTEM exige l'élévation : si elle est refusée, enregistrez la tâche sans -Principal, ce qui la crée dans le contexte de l'utilisateur courant, possible sans droits administrateur sur la plupart des postes.
Étape 1 — Nommer le fichier avec la date du jour
Inclure la date dans le nom du fichier généré évite qu'une exécution n'efface le rapport de la veille — utile pour conserver un historique.
Étape 2 — Exporter en CSV exploitable par Excel
N'utilisez jamais Format-Table ou Format-List avant un Export-* : ces cmdlets transforment les objets en texte de présentation, incompatible avec un export exploitable.
Étape 3 — Créer le déclencheur quotidien
New-ScheduledTaskTrigger -Daily -At 07:00 joue le même rôle qu'une ligne 0 7 * * * dans une crontab Linux : les deux mondes ont chacun leur propre planificateur, avec la même idée derrière.
Étape 4 — Enregistrer la tâche et la tester
Après l'enregistrement, testez immédiatement avec Start-ScheduledTask -TaskName "Inventaire du parc" puis Get-ScheduledTaskInfo, plutôt que d'attendre 7h du matin pour découvrir un problème. Pensez aussi à nettoyer en fin de TP avec Unregister-ScheduledTask -TaskName "Inventaire du parc" -Confirm:$false, pour ne pas laisser tourner une tâche inutile sur un poste de salle.
📌 Une solution
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
| Comptes et groupes locaux | New-LocalUser, Add-LocalGroupMember, mots de passe en SecureString |
| Services et processus | Get-Service/Start-Service vs Get-Process/Stop-Process ; Get-CimInstance pour interroger à distance |
| Remoting | Invoke-Command -ComputerName pour exécuter en parallèle, $using: pour transmettre une variable locale |
| Gestion d'erreurs | -ErrorAction Stop rend une erreur attrapable, try/catch/finally, $_.Exception.Message |
| Modules | Un .psm1 dans un dossier de même nom, Export-ModuleMember pour choisir les fonctions publiques |
| Active Directory | Get-ADUser/New-ADUser -Path, -Filter côté serveur, toujours -WhatIf avant une action destructive |
| Rapports et planification | Export-Csv/ConvertTo-Json/ConvertTo-Html en fin de pipeline, Register-ScheduledTask pour automatiser |
Aperçu de la prochaine séance
Vous savez désormais administrer un parc de machines Windows avec PowerShell : comptes et services, exécution à distance sur plusieurs machines, gestion d'erreurs professionnelle, modules réutilisables, Active Directory et rapports automatisés. Ce cours d'automatisation continue avec Interfaces en console (dialog/whiptail côté Linux), pour transformer un script en véritable petite application, quelle que soit la plateforme sur laquelle vous administrez.