PowerShell avancé — automatiser en toute sécurité l'investigation, l'archivage et le reporting des journaux
· Go Komura · PowerShell, Windows, Automatisation, Investigation des journaux, Amélioration opérationnelle, Réutilisation des actifs existants
1. Ce qu’il faut retenir avant de commencer
La dernière fois, nous avons passé en revue les commandes PowerShell de base, le pipeline, l’export CSV, le JSON, les scripts .ps1 et la vérification de sécurité avec -WhatIf. Dans la suite de cet article, nous allons construire un script un peu plus conséquent, facile à utiliser dans un contexte professionnel. Le sujet est le type de tâche opérationnelle suivant :
- Investiguer les journaux
- Regrouper les lignes d’erreur dans un CSV
- Lister les anciens journaux comme candidats à l’archivage
- Déplacer les anciens journaux si nécessaire
- Conserver le résultat de l’exécution comme preuve
Ce qui compte dans l’usage avancé de PowerShell, ce n’est pas de rallonger les commandes, mais de mettre en place le flux suivant :
- Séparer la configuration
- Construire le traitement en lecture seule
- Conserver la sortie
- Séparer le traitement de modification dans des fonctions
- Faire une répétition avec
-WhatIf - Envisager l’exécution automatique en dernier
L’automatisation ne consiste pas à « supprimer la vérification humaine ». Elle consiste à fixer l’endroit où cette vérification doit avoir lieu, de sorte que la même preuve d’exécution soit conservée à chaque fois.
Par ailleurs, le code présenté dans cet article est publié sur GitHub sous la forme d’un ensemble d’exemples directement exécutables (le script final, le fichier de configuration, un script de création d’un environnement de journaux factices, ainsi que des tests Pester qui vérifient l’investigation, la répétition et le déplacement).
powershell-script-log-maintenance-automation - komurasoft-blog-samples (GitHub)
2. Ce que nous allons construire
Nous allons construire cette fois un script nommé Invoke-LogMaintenance.ps1.
Voici ses principales fonctionnalités.
| Fonctionnalité | Description |
|---|---|
| Recherche des journaux | Recherche les fichiers .log sous le dossier indiqué |
| Fenêtre temporelle | N’examine que les journaux modifiés au cours des N derniers jours |
| Extraction des erreurs | Extrait les lignes contenant ERROR, WARN, FATAL, etc. |
| Export CSV | Enregistre les résultats de recherche dans log-hits.csv |
| Liste des anciens journaux | Enregistre dans archive-targets.csv les journaux plus anciens que N jours |
| Archivage | Déplace les anciens journaux vers un autre dossier |
| Exécution en aperçu | Avec -Preview, affiche uniquement le plan sans déplacer |
| Journal d’exécution | Enregistre un transcript, un résumé JSON et les CSV de résultats |
Notez que nous ne supprimons rien. Dans ce premier volet avancé, nous nous limitons au « déplacement », plus sûr que la suppression.
3. Organisation des dossiers
Prenons, à titre d’exemple, l’organisation suivante.
C:\Ops
Invoke-LogMaintenance.ps1
log-maintenance.json
C:\App\Logs
app.log
batch.log
old
app-202401.log
C:\App\Reports
20260602-030000
log-hits.csv
archive-targets.csv
archive-result.csv
summary.json
transcript.txt
C:\App\Archive
20260602-030000
old
app-202401.log
Séparer le corps du script du fichier de configuration facilite le remplacement selon l’environnement. Vous pouvez ainsi ne faire varier que le chemin, par exemple C:\Test\Logs en environnement de développement et D:\App\Logs en production.
4. Créer le fichier de configuration
Commençons par créer log-maintenance.json.
{
"LogPath": "C:\\App\\Logs",
"OutputPath": "C:\\App\\Reports",
"Days": 7,
"Patterns": [
"ERROR",
"WARN",
"FATAL"
],
"ArchiveDays": 90,
"ArchivePath": "C:\\App\\Archive"
}
Voici la signification de chaque champ.
| Champ | Signification |
|---|---|
LogPath |
Le dossier de journaux à examiner |
OutputPath |
La destination des rapports |
Days |
Le nombre de jours récents de journaux à examiner |
Patterns |
Les chaînes/motifs à rechercher |
ArchiveDays |
À partir de combien de jours d’ancienneté un journal devient une cible d’archivage |
ArchivePath |
Le dossier de destination de l’archivage |
En utilisant le JSON, vous pouvez modifier les conditions sans toucher au corps du script.
Dans PowerShell, ConvertFrom-Json permet de manipuler du JSON sous forme d’objets. Inversement, pour conserver un résultat de traitement en JSON, on utilise ConvertTo-Json. ConvertTo-Json est la cmdlet qui convertit un objet en chaîne JSON ; lorsqu’on manipule une hiérarchie profonde, préciser -Depth devient important.
5. Commencer par construire uniquement la lecture
Sans écrire tout de suite le traitement de déplacement, contentons-nous d’abord de rechercher les journaux et de les exporter en CSV.
$config = Get-Content .\log-maintenance.json -Raw -Encoding UTF8 | ConvertFrom-Json
$since = (Get-Date).AddDays(-[int]$config.Days)
$files = Get-ChildItem -LiteralPath $config.LogPath -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since }
$files |
Select-Object FullName, Length, LastWriteTime
Recherchons ensuite le contenu des journaux.
$patterns = [string[]]$config.Patterns
Select-String -LiteralPath ($files | Select-Object -ExpandProperty FullName) -Pattern $patterns |
Select-Object Path, LineNumber, Pattern, Line |
Export-Csv .\log-hits.csv -NoTypeInformation -Encoding UTF8
Jusqu’ici, tout est en lecture seule. Même en testant sur un dossier de production, arrêtez-vous d’abord à ce stade.
6. Dresser la liste des anciens journaux
Dressons maintenant la liste des cibles d’archivage.
$limit = (Get-Date).AddDays(-[int]$config.ArchiveDays)
$targets = Get-ChildItem -LiteralPath $config.LogPath -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit } |
Sort-Object LastWriteTime
$targets |
Select-Object FullName, Length, LastWriteTime |
Export-Csv .\archive-targets.csv -NoTypeInformation -Encoding UTF8
À ce stade non plus, on ne déplace encore rien. Consultez archive-targets.csv pour vérifier que le nombre de cibles n’est pas excessif et que le dossier n’est pas erroné.
7. Séparer le traitement de modification dans une fonction
Un traitement de modification comme le déplacement doit être séparé du traitement en lecture seule.
Dans PowerShell, ajouter SupportsShouldProcess à une fonction lui permet de gérer -WhatIf et -Confirm. -WhatIf affiche « ce qui serait modifié » sans exécuter réellement l’action, tandis que -Confirm est le mécanisme qui demande une confirmation avant l’exécution. Vous trouverez plus de détails dans about_Functions_CmdletBindingAttribute sur Microsoft Learn.
function Move-OldLogFile {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium")]
param(
[Parameter(Mandatory)]
[System.IO.FileInfo[]]$File,
[Parameter(Mandatory)]
[string]$SourceRoot,
[Parameter(Mandatory)]
[string]$ArchiveRoot
)
foreach ($item in $File) {
$relativePath = [System.IO.Path]::GetRelativePath($SourceRoot, $item.FullName)
$destination = Join-Path $ArchiveRoot $relativePath
$destinationDirectory = Split-Path -Path $destination -Parent
if ($PSCmdlet.ShouldProcess($item.FullName, "Move to $destination")) {
if (-not [System.IO.Directory]::Exists($destinationDirectory)) {
[System.IO.Directory]::CreateDirectory($destinationDirectory) | Out-Null
}
Move-Item -LiteralPath $item.FullName -Destination $destination -ErrorAction Stop
[pscustomobject]@{
Source = $item.FullName
Destination = $destination
Status = "Moved"
Message = ""
}
}
else {
[pscustomobject]@{
Source = $item.FullName
Destination = $destination
Status = "Preview"
Message = ""
}
}
}
}
Le point clé est que $PSCmdlet.ShouldProcess() est placé juste avant Move-Item. La décision de modifier ou non se prend non pas à l’extérieur de la fonction, mais juste avant la modification elle-même.
8. Le script final
Voici la version finale qui rassemble tout ce qui précède. Le nom du fichier est Invoke-LogMaintenance.ps1.
# Invoke-LogMaintenance.ps1
#Requires -Version 7.0
[CmdletBinding()]
param(
[ValidateNotNullOrEmpty()]
[string]$ConfigPath = ".\log-maintenance.json",
[switch]$Preview,
[switch]$SkipArchive,
[switch]$SkipTranscript
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Ensure-Directory {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path
)
if (-not [System.IO.Directory]::Exists($Path)) {
[System.IO.Directory]::CreateDirectory($Path) | Out-Null
}
}
function Import-LogMaintenanceConfig {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path
)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Config file not found: $Path"
}
$config = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
foreach ($name in @("LogPath", "OutputPath", "Days", "Patterns", "ArchiveDays", "ArchivePath")) {
if (-not ($config.PSObject.Properties.Name -contains $name)) {
throw "Config value missing: $name"
}
}
if ([string]::IsNullOrWhiteSpace([string]$config.LogPath)) {
throw "LogPath is empty."
}
if (-not (Test-Path -LiteralPath $config.LogPath)) {
throw "LogPath not found: $($config.LogPath)"
}
if ([string]::IsNullOrWhiteSpace([string]$config.OutputPath)) {
throw "OutputPath is empty."
}
if ([string]::IsNullOrWhiteSpace([string]$config.ArchivePath)) {
throw "ArchivePath is empty."
}
if (@($config.Patterns).Count -eq 0) {
throw "Patterns is empty."
}
if ([int]$config.Days -lt 1) {
throw "Days must be 1 or greater."
}
if ([int]$config.ArchiveDays -lt 1) {
throw "ArchiveDays must be 1 or greater."
}
return $config
}
function Export-CsvWithHeader {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[object[]]$InputObject,
[Parameter(Mandatory)]
[string]$Path,
[Parameter(Mandatory)]
[string[]]$Header
)
if ($InputObject.Count -gt 0) {
$InputObject |
Export-Csv -LiteralPath $Path -NoTypeInformation -Encoding UTF8
}
else {
($Header -join ",") |
Set-Content -LiteralPath $Path -Encoding UTF8
}
}
function Get-LogHit {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$LogPath,
[Parameter(Mandatory)]
[ValidateRange(1, 3650)]
[int]$Days,
[Parameter(Mandatory)]
[string[]]$Pattern
)
$since = (Get-Date).AddDays(-$Days)
$files = @(
Get-ChildItem -LiteralPath $LogPath -Filter *.log -File -Recurse -ErrorAction Stop |
Where-Object { $_.LastWriteTime -ge $since }
)
Write-Verbose "Recent log files: $($files.Count)"
if ($files.Count -eq 0) {
return @()
}
$paths = $files | Select-Object -ExpandProperty FullName
Select-String -LiteralPath $paths -Pattern $Pattern -ErrorAction Stop |
ForEach-Object {
[pscustomobject]@{
Path = $_.Path
LineNumber = $_.LineNumber
Pattern = $_.Pattern
Line = $_.Line.Trim()
}
}
}
function Get-OldLogFile {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$LogPath,
[Parameter(Mandatory)]
[ValidateRange(1, 3650)]
[int]$ArchiveDays
)
$limit = (Get-Date).AddDays(-$ArchiveDays)
Get-ChildItem -LiteralPath $LogPath -Filter *.log -File -Recurse -ErrorAction Stop |
Where-Object { $_.LastWriteTime -lt $limit } |
Sort-Object LastWriteTime
}
function Move-OldLogFile {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium")]
param(
[Parameter(Mandatory)]
[System.IO.FileInfo[]]$File,
[Parameter(Mandatory)]
[string]$SourceRoot,
[Parameter(Mandatory)]
[string]$ArchiveRoot
)
foreach ($item in $File) {
$relativePath = [System.IO.Path]::GetRelativePath($SourceRoot, $item.FullName)
$destination = Join-Path $ArchiveRoot $relativePath
$destinationDirectory = Split-Path -Path $destination -Parent
if (Test-Path -LiteralPath $destination) {
$name = [System.IO.Path]::GetFileNameWithoutExtension($item.Name)
$ext = $item.Extension
$destination = Join-Path $destinationDirectory ("{0}_{1:yyyyMMddHHmmss}{2}" -f $name, $item.LastWriteTime, $ext)
}
if ($PSCmdlet.ShouldProcess($item.FullName, "Move to $destination")) {
Ensure-Directory -Path $destinationDirectory
Move-Item -LiteralPath $item.FullName -Destination $destination -ErrorAction Stop
[pscustomobject]@{
Source = $item.FullName
Destination = $destination
Status = "Moved"
Message = ""
}
}
else {
[pscustomobject]@{
Source = $item.FullName
Destination = $destination
Status = "Preview"
Message = ""
}
}
}
}
$config = Import-LogMaintenanceConfig -Path $ConfigPath
$runStamp = Get-Date -Format "yyyyMMdd-HHmmss"
$reportDir = Join-Path ([string]$config.OutputPath) $runStamp
Ensure-Directory -Path $reportDir
$transcriptStarted = $false
$transcriptPath = Join-Path $reportDir "transcript.txt"
try {
if (-not $SkipTranscript) {
Start-Transcript -Path $transcriptPath -Force | Out-Null
$transcriptStarted = $true
}
Write-Host "Report directory: $reportDir"
$hits = @(
Get-LogHit `
-LogPath ([string]$config.LogPath) `
-Days ([int]$config.Days) `
-Pattern ([string[]]$config.Patterns)
)
$hitCsv = Join-Path $reportDir "log-hits.csv"
Export-CsvWithHeader `
-InputObject $hits `
-Path $hitCsv `
-Header @("Path", "LineNumber", "Pattern", "Line")
$oldFiles = @(
Get-OldLogFile `
-LogPath ([string]$config.LogPath) `
-ArchiveDays ([int]$config.ArchiveDays)
)
$archiveTargets = @(
$oldFiles |
Select-Object FullName, Length, LastWriteTime
)
$archiveTargetCsv = Join-Path $reportDir "archive-targets.csv"
Export-CsvWithHeader `
-InputObject $archiveTargets `
-Path $archiveTargetCsv `
-Header @("FullName", "Length", "LastWriteTime")
$moveResults = @()
if ($SkipArchive) {
Write-Host "Archive skipped."
}
elseif ($oldFiles.Count -eq 0) {
Write-Host "No archive targets."
}
else {
$archiveRunRoot = Join-Path ([string]$config.ArchivePath) $runStamp
$moveResults = @(
Move-OldLogFile `
-File $oldFiles `
-SourceRoot ([string]$config.LogPath) `
-ArchiveRoot $archiveRunRoot `
-WhatIf:$Preview
)
}
$archiveResultCsv = Join-Path $reportDir "archive-result.csv"
Export-CsvWithHeader `
-InputObject $moveResults `
-Path $archiveResultCsv `
-Header @("Source", "Destination", "Status", "Message")
$summary = [pscustomobject]@{
CheckedAt = (Get-Date).ToString("s")
ComputerName = $env:COMPUTERNAME
LogPath = [string]$config.LogPath
ReportDirectory = $reportDir
HitCount = $hits.Count
ArchiveTargetCount = $oldFiles.Count
ArchiveResultCount = $moveResults.Count
Preview = [bool]$Preview
SkipArchive = [bool]$SkipArchive
}
$summaryPath = Join-Path $reportDir "summary.json"
$summary |
ConvertTo-Json -Depth 5 |
Set-Content -LiteralPath $summaryPath -Encoding UTF8
Write-Host "Finished."
Write-Host "Hits: $($hits.Count)"
Write-Host "Archive targets: $($oldFiles.Count)"
}
catch {
$errorPath = Join-Path $reportDir "error.txt"
$_ | Out-String | Set-Content -LiteralPath $errorPath -Encoding UTF8
Write-Error "Failed: $($_.Exception.Message)"
exit 1
}
finally {
if ($transcriptStarted) {
Stop-Transcript | Out-Null
}
}
9. Exemples d’exécution
Commençons par n’effectuer que l’investigation des journaux, sans exécuter l’archivage.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json -SkipArchive
Vérifions ensuite le plan d’archivage.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json -Preview
Avec -Preview, le traitement de déplacement des anciens journaux est traité comme un -WhatIf.
Les trois fichiers à vérifier à ce stade sont les suivants :
log-hits.csvarchive-targets.csvarchive-result.csv
Si tout est correct, exécutez sans -Preview.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json
Pour voir plus de détails, ajoutez -Verbose.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json -Preview -Verbose
10. Les fichiers générés en sortie
Lors de l’exécution, un dossier horodaté tel que C:\App\Reports\20260602-030000 est créé sous OutputPath.
Il contient les fichiers suivants.
| Fichier | Contenu |
|---|---|
log-hits.csv |
Les lignes détectées comme erreurs ou avertissements |
archive-targets.csv |
Les anciens journaux devenus cibles d’archivage |
archive-result.csv |
Le résultat du déplacement, ou le résultat en mode Preview |
summary.json |
Un résumé des nombres et des conditions d’exécution |
transcript.txt |
L’enregistrement de la session PowerShell |
error.txt |
Les détails en cas d’erreur |
Start-Transcript est la cmdlet qui enregistre dans un fichier texte les commandes et la sortie console d’une session PowerShell. Dans un script opérationnel, cela facilite grandement la vérification a posteriori de « quand, dans quelles conditions, et avec quel résultat » le script s’est exécuté.
11. Exécuter périodiquement avec le Planificateur de tâches
Si l’exécution manuelle ne pose pas de problème, vous pouvez passer à une exécution périodique via le Planificateur de tâches.
Voici, pour commencer, un exemple d’exécution quotidienne à 3 heures du matin.
$scriptPath = "C:\Ops\Invoke-LogMaintenance.ps1"
$configPath = "C:\Ops\log-maintenance.json"
$action = New-ScheduledTaskAction `
-Execute "pwsh.exe" `
-Argument "-NoProfile -File `"$scriptPath`" -ConfigPath `"$configPath`"" `
-WorkingDirectory "C:\Ops"
$trigger = New-ScheduledTaskTrigger -Daily -At 3:00
Register-ScheduledTask `
-TaskName "AppLogMaintenance" `
-Action $action `
-Trigger $trigger `
-Description "Collect app log errors and archive old logs"
New-ScheduledTaskAction crée l’objet représentant la commande que la tâche exécutera, et New-ScheduledTaskTrigger définit les conditions de déclenchement : quotidien, hebdomadaire, à l’ouverture de session, etc. Enfin, Register-ScheduledTask enregistre la tâche sur l’ordinateur local.
En exploitation en production, vérifiez également les points suivants.
- L’utilisateur d’exécution dispose des droits de lecture sur le dossier de journaux
- Il dispose des droits d’écriture sur la destination d’archivage
- Le chemin de
pwsh.exeest bien résolu - Le script est conforme à la politique d’exécution et aux règles de signature
- L’exécution manuelle et l’exécution via la tâche produisent le même résultat
- En cas d’échec,
error.txtet l’historique de la tâche peuvent être consultés
12. Difficultés fréquentes
| Symptôme | Cause | Solution |
|---|---|---|
| Les journaux ne sont pas trouvés | LogPath est incorrect |
Vérifier avec Test-Path et Get-ChildItem |
| Le CSV est vide | Aucun journal ne correspond à la période cible | Élargir Days et vérifier |
| Le texte est mal encodé | L’encodage des journaux diffère de ce qui était prévu | Vérifier les encodages d’entrée et de sortie |
| Ne fonctionne pas en tant que tâche | L’utilisateur d’exécution ou le dossier de travail diffère | Vérifier WorkingDirectory et les droits |
| Trop de cibles d’archivage | ArchiveDays est trop court |
Consulter archive-targets.csv et ajuster |
| La destination diffère de ce qui était prévu | La règle de conservation du chemin relatif n’est pas comprise | Vérifier Destination avec -Preview |
| Échoue uniquement en production | Différences de droits, de politique ou de fichiers verrouillés | Vérifier error.txt et transcript.txt |
En particulier, lorsqu’on exécute le script via le Planificateur de tâches, « l’utilisateur avec lequel on l’a exécuté soi-même » et « l’utilisateur d’exécution de la tâche » peuvent être différents. Si le script fonctionne manuellement mais échoue en tant que tâche, suspectez d’abord les droits et le dossier de travail.
13. Comment aborder les personnalisations
Ce script est conçu pour être adapté progressivement au terrain plutôt qu’utilisé tel quel. Voici quelques exemples courants de personnalisation.
| Ce que l’on veut faire | Ce qu’il faut modifier |
|---|---|
Inclure aussi les .txt |
Modifier Get-ChildItem -Filter *.log |
Voir aussi quelques lignes autour de chaque ERROR |
Récupérer les lignes voisines avec Get-Content à partir des résultats de Select-String |
| Compresser avant d’archiver | Ajouter Compress-Archive avant Move-OldLogFile |
| Envoyer une notification par e-mail | Ajouter un traitement de notification basé sur summary.json |
| Séparer la configuration par application | Préparer plusieurs fichiers JSON et diviser les tâches |
| Automatiser jusqu’à la suppression | D’abord vérifier pendant une certaine période avec le déplacement, puis envisager |
Cependant, il est plus sûr de ne pas tout intégrer dès le départ. Pour un script opérationnel, il est plus important de pouvoir retracer ce qui s’est passé en cas d’échec que d’avoir de nombreuses fonctionnalités.
14. Liste de contrôle opérationnelle sur le terrain
Avant de planifier l’exécution périodique d’un script PowerShell, vérifiez les points suivants.
- D’abord exécuté uniquement le traitement en lecture seule avec
-SkipArchive - Ensuite vérifié le plan de déplacement avec
-Preview - Les cibles de
archive-targets.csvétaient pertinentes - Le champ
Destinationdearchive-result.csvcorrespondait aux attentes - Une preuve horodatée a bien été conservée dans le dossier de sortie
- Vérifié que
error.txtest bien généré en cas d’erreur - Vérifié les droits de l’utilisateur d’exécution de la tâche
- Vérifié la politique d’exécution, la signature et les règles internes
- Exploiter d’abord avec le déplacement, pas la suppression immédiate
- Défini à l’avance la destination de restauration en cas de récupération
15. Conclusion
Même quand on parle d’usage avancé de PowerShell, il n’est pas nécessaire d’utiliser une syntaxe compliquée en abondance. Ce qui porte vraiment ses fruits dans le travail quotidien, c’est de mettre en place ce type de schéma :
- Séparer la configuration dans un JSON
- Construire d’abord le traitement en lecture seule
- Conserver des preuves en CSV et en JSON
- Séparer le traitement de modification dans des fonctions
- Prévoir une répétition équivalente à
-WhatIf - Permettre le suivi grâce au transcript et à
error.txt - Vérifier par une exécution manuelle avant de planifier l’exécution périodique
Le script de cet article prend l’investigation et l’archivage des journaux comme sujet, mais cette approche s’applique aussi à d’autres tâches.
- Organisation de fichiers
- Génération de rapports
- Agrégation de CSV
- Remplacement de traitements par lots
- Inventaire des anciens actifs
- Vérifications opérationnelles quotidiennes et mensuelles
PowerShell est pratique même sous forme de commandes d’une ligne, mais pour un usage professionnel, il est plus sûr de respecter l’ordre suivant.
Observer → Enregistrer → Répéter → Exécuter → Conserver les preuves
En adoptant cette structure, PowerShell cesse d’être un simple outil pour raccourcir les tâches et devient une petite application métier capable de stabiliser l’exploitation.
Liens de référence
- L’ensemble du code d’exemple de cet article (script final, fichier de configuration, tests Pester) - komurasoft-blog-samples (GitHub)
- PowerShell Documentation - Microsoft Learn
- ConvertTo-Json - Microsoft Learn
- about_Functions_CmdletBindingAttribute - Microsoft Learn
- Start-Transcript - Microsoft Learn
- New-ScheduledTaskAction - Microsoft Learn
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Externalisation et développement sur mesure d'une application Windows : ce qu'il faut clarifier avant de se lancer
Avant de confier l'externalisation ou le développement sur mesure d'une application Windows, voici les points à clarifier : révision d'un...
L'étrange amour d'un développeur, ou : comment j'ai appris à ne plus m'en faire et à aimer Windows
Windows est contraignant. Mais cette contrainte est aussi celle d'un système d'exploitation qui porte depuis des décennies le poids d'act...
Guide pratique de Process Monitor (ProcMon) — identifier en 10 minutes un « paramètre non pris en compte » ou un ACCESS DENIED
« J'ai corrigé le fichier de configuration, mais rien ne change » ; « ça marchait hier, mais ça ne démarre plus aujourd'hui » — avant de ...
Migrer une application Windows vers le Web : les cas à éviter — tableau de décision et la solution réaliste du « fractionnement »
Les demandes de migration d'applications Windows internes vers le Web se multiplient, mais pour les applications reposant sur l'intégrati...
Internationalisation des applications WinForms/WPF : resx, assemblies satellites et changement de culture en pratique
Un guide pratique pour internationaliser une application de bureau Windows : la différence entre CurrentCulture et CurrentUICulture, le f...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Quand on automatise l'investigation de journaux avec PowerShell, par quoi faut-il commencer ?
- Ne commencez pas directement par écrire du code de modification (déplacement, suppression, etc.) : construisez d'abord uniquement le traitement en lecture seule. Terminez d'abord la partie qui recherche les journaux concernés avec Get-ChildItem, extrait les lignes d'erreur avec Select-String et les exporte en CSV. Même lorsque vous testez sur un dossier de production, arrêtez-vous à ce stade de lecture pour vérifier le résultat. Il est plus sûr de progresser ensuite par étapes : dresser la liste des cibles d'archivage, puis transformer le traitement de déplacement en fonction.
- Comment intègre-t-on -WhatIf dans un script PowerShell ?
- En ajoutant SupportsShouldProcess à l'attribut CmdletBinding d'une fonction, celle-ci devient capable de gérer -WhatIf et -Confirm. Le point clé est de placer $PSCmdlet.ShouldProcess() juste avant le traitement qui modifie réellement quelque chose (par exemple Move-Item), afin que la décision de modifier ou non se prenne au tout dernier moment, juste avant la modification. Dans le script de cet article, le switch -Preview est transmis sous la forme -WhatIf:$Preview, ce qui permet de vérifier uniquement le plan d'action sans déplacer réellement les fichiers.
- Pourquoi un script PowerShell fonctionne-t-il manuellement mais échoue-t-il dans le Planificateur de tâches ?
- La cause typique est que l'utilisateur d'exécution de la tâche diffère de celui utilisé lors de l'exécution manuelle. Vérifiez que l'utilisateur d'exécution dispose des droits de lecture sur le dossier de journaux et des droits d'écriture sur la destination d'archivage, que le dossier de travail (WorkingDirectory) est correct, que le chemin de pwsh.exe est bien résolu, et que la politique d'exécution ainsi que les règles de signature sont respectées. Il est important de pouvoir retracer les échecs grâce à error.txt, transcript.txt et à l'historique de la tâche.
- Peut-on aller jusqu'à automatiser la suppression des anciens journaux ?
- Il n'est pas recommandé d'automatiser la suppression dès le départ. Le script de cet article lui-même s'arrête au « déplacement », qui est plus sûr que la suppression. Faites d'abord fonctionner l'opération de déplacement pendant une certaine période, examinez les preuves d'exécution telles que archive-targets.csv et archive-result.csv, et ce n'est qu'après avoir confirmé l'absence de problème que vous pourrez envisager d'automatiser la suppression. Il est également important de définir à l'avance où restaurer les fichiers en cas de besoin de récupération.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics