Aller au contenu principal

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.

danger

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 ";"
  • -NoTypeInformation supprime une ligne d'en-tête technique inutile (#TYPE ...) que PowerShell ajoute par défaut ;
  • -Delimiter ";" et -Encoding UTF8 produisent 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
attention

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-ScheduledTaskAction définit quoi exécuter ;
  • New-ScheduledTaskTrigger définit quand (-Daily -At 07:00, mais aussi -Weekly, -AtStartup, ou -Once -RepetitionInterval) ;
  • New-ScheduledTaskPrincipal définit avec quel compte (SYSTEM s'exécute sans session ouverte, avec des droits élevés) ;
  • Register-ScheduledTask enregistre 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.

attention

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


Pourquoi Get-Process | Format-Table | Export-Csv rapport.csv produit-il un fichier inutilisable ?


Quelle cmdlet enregistre réellement une tâche dans le Planificateur Windows ?


Que signifie LastTaskResult = 0 dans Get-ScheduledTaskInfo ?


TP pour réfléchir et résoudre des problèmes

astuce

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


Bonne pratique - Un rapport horodaté ne s'écrase jamais

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


Bonne pratique - Format-* en fin de pipeline seulement

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


Bonne pratique - L'équivalent d'une ligne de crontab

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


Bonne pratique - Toujours valider une tâche planifiée

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

NotionRésumé
Comptes et groupes locauxNew-LocalUser, Add-LocalGroupMember, mots de passe en SecureString
Services et processusGet-Service/Start-Service vs Get-Process/Stop-Process ; Get-CimInstance pour interroger à distance
RemotingInvoke-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
ModulesUn .psm1 dans un dossier de même nom, Export-ModuleMember pour choisir les fonctions publiques
Active DirectoryGet-ADUser/New-ADUser -Path, -Filter côté serveur, toujours -WhatIf avant une action destructive
Rapports et planificationExport-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.