PowerShell avanzato — Automatizzare in sicurezza l'analisi dei log, l'archiviazione e la reportistica
· Go Komura · PowerShell, Windows, Automazione, Analisi dei log, Miglioramento operativo, Riutilizzo di asset esistenti
1. Cosa sapere prima di iniziare
Nell’articolo precedente abbiamo riassunto i comandi di base di PowerShell, la pipeline, l’esportazione in CSV, il JSON, gli script .ps1 e le verifiche di sicurezza con -WhatIf. In questo articolo, come seguito, costruiremo uno script un po’ più grande e utile nel lavoro quotidiano. L’argomento è un’attività operativa come questa:
- Analizzare i log
- Raccogliere le righe di errore in un CSV
- Elencare i log obsoleti come candidati per l’archiviazione
- Spostare i log obsoleti, se necessario
- Conservare il risultato dell’esecuzione come traccia di audit
Ciò che conta nell’uso avanzato di PowerShell non è allungare i comandi, ma costruire il seguente flusso:
- Separare la configurazione
- Costruire l’elaborazione di sola lettura
- Conservare l’output
- Suddividere le operazioni di modifica in funzioni
- Fare una prova con
-WhatIf - Valutare l’esecuzione automatica per ultima
Automatizzare non significa «eliminare la verifica umana». Significa fissare il punto in cui va effettuata la verifica e garantire che venga prodotta la stessa traccia di audit ogni volta.
Il codice presentato in questo articolo è pubblicato su GitHub come set di esempi pronti all’uso (lo script completo, il file di configurazione, uno script per creare un ambiente di log fittizio e i test Pester che verificano l’analisi, la simulazione e lo spostamento).
powershell-script-log-maintenance-automation - komurasoft-blog-samples (GitHub)
2. Cosa costruiremo
In questo articolo costruiremo uno script chiamato Invoke-LogMaintenance.ps1.
Le funzionalità principali sono le seguenti.
| Funzionalità | Descrizione |
|---|---|
| Ricerca dei log | Cerca i file .log sotto la cartella specificata |
| Intervallo temporale | Analizza solo i log modificati negli ultimi N giorni |
| Estrazione errori | Estrae le righe contenenti ERROR, WARN, FATAL, ecc. |
| Esportazione CSV | Salva i risultati della ricerca in log-hits.csv |
| Elenco log obsoleti | Salva i log più vecchi di N giorni in archive-targets.csv |
| Archiviazione | Sposta i log obsoleti in una cartella separata |
| Esecuzione di prova | Con -Preview non sposta nulla, mostra solo il piano previsto |
| Registrazione dell’esecuzione | Salva transcript, JSON di riepilogo e i CSV dei risultati |
Da notare che non viene effettuata alcuna eliminazione. In questo primo capitolo avanzato ci limitiamo allo «spostamento», più sicuro della cancellazione.
3. Struttura delle cartelle
Come esempio, adottiamo la seguente struttura.
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
Tenere separati il corpo dello script e il file di configurazione rende più semplice adattarli a ciascun ambiente. È possibile impostare un’operatività in cui cambia solo il percorso, ad esempio C:\Test\Logs in ambiente di sviluppo e D:\App\Logs in produzione.
4. Creare il file di configurazione
Per prima cosa creiamo log-maintenance.json.
{
"LogPath": "C:\\App\\Logs",
"OutputPath": "C:\\App\\Reports",
"Days": 7,
"Patterns": [
"ERROR",
"WARN",
"FATAL"
],
"ArchiveDays": 90,
"ArchivePath": "C:\\App\\Archive"
}
I significati sono i seguenti.
| Voce | Significato |
|---|---|
LogPath |
La cartella dei log da analizzare |
OutputPath |
La destinazione dei report |
Days |
Quanti giorni recenti di log analizzare |
Patterns |
Le stringhe/pattern da cercare |
ArchiveDays |
Da quanti giorni un log deve essere obsoleto per diventare candidato all’archiviazione |
ArchivePath |
La cartella di destinazione dell’archivio |
Usare il JSON permette di modificare le condizioni senza toccare il corpo dello script.
In PowerShell, ConvertFrom-Json permette di trattare il JSON come un oggetto. Viceversa, per salvare il risultato di un’elaborazione come JSON si usa ConvertTo-Json. ConvertTo-Json è il cmdlet che converte un oggetto in una stringa JSON; quando si ha a che fare con strutture annidate in profondità, specificare -Depth diventa importante.
5. Costruire prima solo la lettura
Senza scrivere subito l’elaborazione di spostamento, all’inizio ci limitiamo a cercare i log e a trasformarli in un 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
Successivamente, cerchiamo all’interno del contenuto dei log.
$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
Fino a questo punto si tratta solo di lettura. Anche quando si prova sulla cartella di produzione, ci si ferma prima a questa fase.
6. Elencare i log obsoleti
Ora elenchiamo i candidati all’archiviazione.
$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
Anche a questo punto non spostiamo ancora nulla. Osservando archive-targets.csv verifichiamo che i candidati non siano troppi e che la cartella non sia sbagliata.
7. Separare le operazioni di modifica in funzioni
Le operazioni di modifica, come lo spostamento, vanno separate dall’elaborazione di sola lettura.
In PowerShell, aggiungendo SupportsShouldProcess a una funzione, questa diventa in grado di gestire -WhatIf e -Confirm. -WhatIf mostra «cosa si intende modificare» senza eseguire l’operazione, mentre -Confirm è il meccanismo che chiede conferma prima dell’esecuzione. Per maggiori dettagli si può consultare about_Functions_CmdletBindingAttribute su 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 = ""
}
}
}
}
Il punto chiave è che $PSCmdlet.ShouldProcess() viene collocato subito prima di Move-Item. La decisione se procedere con la modifica non viene presa fuori dalla funzione, ma proprio a ridosso della modifica stessa.
8. Lo script completo
Ecco la versione completa che riunisce tutto quanto visto finora. Il nome del file è 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. Esempi di esecuzione
Per prima cosa eseguiamo solo l’analisi dei log, senza effettuare l’archiviazione.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json -SkipArchive
Successivamente verifichiamo il piano di archiviazione.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json -Preview
Aggiungendo -Preview, l’elaborazione di spostamento dei log obsoleti viene trattata come -WhatIf.
I tre file da controllare a questo punto sono:
log-hits.csvarchive-targets.csvarchive-result.csv
Se non ci sono problemi, si esegue rimuovendo -Preview.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json
Per vedere i dettagli, si aggiunge -Verbose.
.\Invoke-LogMaintenance.ps1 -ConfigPath .\log-maintenance.json -Preview -Verbose
10. I file generati in output
All’esecuzione, sotto OutputPath viene creata una cartella con timestamp come C:\App\Reports\20260602-030000.
Al suo interno vengono generati i seguenti file.
| File | Contenuto |
|---|---|
log-hits.csv |
Le righe rilevate come errori o avvisi |
archive-targets.csv |
I log obsoleti diventati candidati all’archiviazione |
archive-result.csv |
Il risultato dello spostamento, oppure il risultato in modalità Preview |
summary.json |
Un riepilogo dei conteggi e delle condizioni di esecuzione |
transcript.txt |
La registrazione della sessione PowerShell |
error.txt |
I dettagli in caso di errore |
Start-Transcript è il cmdlet che registra in un file di testo i comandi e l’output della console di una sessione PowerShell. Negli script operativi, questo rende molto più facile verificare in seguito «quando, con quali condizioni e cosa è stato prodotto».
11. Eseguire periodicamente con l’Utilità di pianificazione
Se l’esecuzione manuale non presenta problemi, è possibile eseguire lo script periodicamente tramite l’Utilità di pianificazione.
Ecco un esempio iniziale che esegue lo script ogni giorno alle 3 del mattino.
$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 crea un oggetto che rappresenta il comando eseguito dall’attività, mentre New-ScheduledTaskTrigger crea le condizioni di avvio: giornaliera, settimanale, all’accesso e così via. Infine, Register-ScheduledTask registra l’attività sul computer locale.
Nell’operatività di produzione, verificare anche i seguenti punti.
- L’utente di esecuzione ha i permessi di lettura sulla cartella dei log
- Sono presenti i permessi di scrittura sulla destinazione dell’archivio
- Il percorso di
pwsh.exeè raggiungibile - Lo script rispetta la execution policy e le regole di firma
- L’esecuzione manuale e quella pianificata producono lo stesso risultato
- In caso di fallimento è possibile controllare
error.txte la cronologia dell’attività
12. Problemi comuni
| Sintomo | Causa | Rimedio |
|---|---|---|
| I log non vengono trovati | LogPath è errato |
Verificare con Test-Path e Get-ChildItem |
| Il CSV risulta vuoto | Non ci sono log nel periodo considerato | Ampliare Days e verificare |
| Il testo giapponese appare corrotto | La codifica dei log è diversa da quella prevista | Verificare le codifiche di input/output |
| Non funziona come attività pianificata | L’utente di esecuzione o la cartella di lavoro sono diversi | Verificare WorkingDirectory e i permessi |
| Troppi candidati all’archiviazione | ArchiveDays è troppo breve |
Osservare archive-targets.csv e regolare il valore |
| La destinazione dello spostamento non è quella prevista | Non si è compresa la regola di mantenimento del percorso relativo | Verificare Destination con -Preview |
| Fallisce solo in produzione | Differenze di permessi, policy o file bloccati | Verificare error.txt e transcript.txt |
In particolare, quando si esegue tramite l’Utilità di pianificazione, «l’utente con cui si esegue manualmente» e «l’utente di esecuzione dell’attività» possono essere diversi. Se lo script funziona manualmente ma fallisce come attività pianificata, il primo sospetto deve ricadere sui permessi e sulla cartella di lavoro.
13. Come pensare alle personalizzazioni
Questo script è pensato più per essere adattato gradualmente al contesto operativo che per essere usato così com’è. Ecco alcuni esempi comuni di personalizzazione.
| Cosa si vuole fare | Dove intervenire |
|---|---|
Includere anche i file .txt |
Modificare Get-ChildItem -Filter *.log |
Vedere anche alcune righe prima e dopo ERROR |
Usare i risultati di Select-String per recuperare le righe circostanti con Get-Content |
| Comprimere prima di archiviare | Aggiungere Compress-Archive prima di Move-OldLogFile |
| Inviare notifiche via email | Aggiungere un’elaborazione di notifica basata su summary.json |
| Separare la configurazione per applicazione | Predisporre più file JSON e suddividere le attività |
| Automatizzare anche l’eliminazione | Prima verificare per un certo periodo l’operatività basata sullo spostamento, poi valutare |
Tuttavia, è più sicuro non aggiungere tutto fin dall’inizio. Per uno script operativo, conta più la possibilità di risalire alla causa in caso di fallimento che il numero di funzionalità.
14. Checklist operativa sul campo
Prima di eseguire periodicamente uno script PowerShell, verificare i seguenti punti.
- Si è eseguita prima solo l’elaborazione di lettura con
-SkipArchive - Si è poi verificato il piano di spostamento con
-Preview - I candidati in
archive-targets.csverano coerenti con le attese - Il valore
Destinationinarchive-result.csvcorrispondeva alle attese - Nella cartella di output è rimasta una traccia di audit con timestamp
- Si è verificato che
error.txtvenga prodotto in caso di errore - Si sono verificati i permessi dell’utente di esecuzione dell’attività
- Si sono verificate la execution policy, la firma e le regole aziendali interne
- Si opera prima con lo spostamento, non con l’eliminazione immediata
- È stata stabilita la destinazione di ripristino in caso di recupero
15. Conclusione
L’uso avanzato di PowerShell non significa dover ricorrere a una sintassi complessa. Ciò che dà davvero risultati nel lavoro quotidiano è aver costruito uno schema come questo:
- Separare la configurazione in JSON
- Costruire prima l’elaborazione di sola lettura
- Conservare le tracce di audit in CSV e JSON
- Suddividere le operazioni di modifica in funzioni
- Predisporre una prova equivalente a
-WhatIf - Rendere tutto tracciabile tramite il transcript ed
error.txt - Verificare con l’esecuzione manuale prima di passare a quella pianificata
Lo script presentato in questo articolo prende come esempio l’analisi dei log e l’archiviazione, ma l’approccio si applica anche ad altri ambiti di lavoro.
- Organizzazione dei file
- Generazione di report
- Aggregazione di dati CSV
- Sostituzione di elaborazioni batch
- Inventario di asset obsoleti
- Verifiche operative giornaliere e mensili
PowerShell è comodo anche come singolo comando, ma per un uso professionale è più sicuro rispettare il seguente ordine:
Osservare → Registrare → Simulare → Eseguire → Conservare le tracce
Adottando questa forma, PowerShell smette di essere un semplice strumento per abbreviare il lavoro e diventa una piccola applicazione gestionale in grado di stabilizzare l’operatività.
Link di riferimento
- Il set completo di codice di esempio di questo articolo (script completo, file di configurazione, test 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
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Outsourcing e sviluppo su commissione di app Windows: cosa chiarire prima di affidare l'incarico
Prima di affidare in outsourcing o su commissione lo sviluppo di un'app Windows, ecco i punti da chiarire: revisione del software esisten...
Il curioso amore di uno sviluppatore, ovvero: come ho imparato a non preoccuparmi e ad amare Windows
Windows è complicato. Ma questa complicazione è anche quella di un sistema operativo che da decenni porta sulle spalle il lavoro reale.
Guida pratica a Process Monitor (ProcMon) — Individuare in 10 minuti "la configurazione non viene letta" e "ACCESS DENIED"
"Ho modificato il file di configurazione ma le modifiche non vengono applicate", "funzionava fino a ieri, ma oggi non si avvia più" — pri...
Quando non conviene portare un'app Windows sul Web: tabella decisionale e la soluzione realistica della "divisione"
Le richieste di trasformare in Web le applicazioni Windows aziendali sono in aumento, ma per le app che gestiscono integrazione con dispo...
Internazionalizzazione delle app WinForms/WPF — la pratica di resx, satellite assembly e cambio cultura
Analizziamo dal punto di vista pratico l'internazionalizzazione delle applicazioni desktop Windows: la differenza tra CurrentCulture e Cu...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Sviluppo di applicazioni Windows
Applicazioni aziendali, integrazione di dispositivi e strumenti di comunicazione, dai requisiti allo sviluppo.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- Quando si automatizza l'analisi dei log con PowerShell, da dove conviene iniziare?
- Non bisogna scrivere subito operazioni di modifica come lo spostamento o l'eliminazione: prima si costruisce solo la parte di lettura. Si completa per prima cosa la ricerca dei log target con Get-ChildItem e l'estrazione delle righe di errore con Select-String, esportandole in CSV. Anche quando si prova sulla cartella di produzione, conviene fermarsi a questa fase di sola lettura e verificare i risultati. In seguito, è più sicuro procedere gradualmente elencando i target da archiviare e trasformando lo spostamento in una funzione dedicata.
- Come si integra -WhatIf in uno script PowerShell?
- Aggiungendo SupportsShouldProcess all'attributo CmdletBinding di una funzione, quella funzione può usare -WhatIf e -Confirm. Il punto chiave è collocare $PSCmdlet.ShouldProcess() subito prima dell'operazione che modifica effettivamente qualcosa (ad esempio Move-Item), in modo che la decisione se procedere venga presa proprio a ridosso della modifica. Nello script di questo articolo, lo switch -Preview viene passato come -WhatIf:$Preview, così è possibile verificare solo il piano previsto senza spostare realmente nulla.
- Perché uno script PowerShell funziona se eseguito manualmente ma fallisce nell'Utilità di pianificazione?
- La causa tipica è che l'utente con cui viene eseguita l'attività pianificata è diverso da quello con cui si è eseguito lo script manualmente. Bisogna verificare che l'utente di esecuzione abbia i permessi di lettura sulla cartella dei log e di scrittura sulla destinazione dell'archivio, che la cartella di lavoro (WorkingDirectory) sia corretta, che il percorso di pwsh.exe sia raggiungibile e che tutto rispetti la execution policy e le regole di firma. È importante predisporre in anticipo error.txt, transcript.txt e la cronologia dell'attività, così da poter risalire alla causa in caso di fallimento.
- È opportuno automatizzare anche l'eliminazione dei log obsoleti?
- Automatizzare l'eliminazione fin dall'inizio non è consigliabile. Anche lo script presentato in questo articolo si ferma allo spostamento, un'operazione più sicura rispetto alla cancellazione. Conviene prima gestire l'operazione come spostamento per un certo periodo, verificare che non emergano problemi osservando le tracce di audit come archive-targets.csv e archive-result.csv, e solo a quel punto valutare l'automazione dell'eliminazione. È importante anche stabilire in anticipo dove ripristinare i file in caso sia necessario un recupero.
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.
Link pubblici