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.