La gestione remota dei servizi Windows è una delle attività quotidiane. Che si tratti di applicare una patch, risolvere un blocco applicativo o eseguire una manutenzione programmata, riavviare un servizio su un server remoto tramite PowerShell consente di intervenire rapidamente senza dover aprire una sessione RDP.
Prima di eseguire qualsiasi comando remoto, è necessario verificare che l’ambiente sia correttamente configurato. Windows Remote Management (WinRM) è il protocollo su cui si basa PowerShell Remoting. Per abilitarlo, eseguire il seguente comando sul server di destinazione con privilegi di amministratore
Enable-PSRemoting -ForceAssicurarsi inoltre che le porte necessarie siano aperte qualora sia presente un firewall
- Porta 5985, PowerShell Remoting standard
- Porta 5986, PowerShell Remoting sicuro
Inoltre, se i server non appartengono allo stesso dominio Active Directory, aggiungere il server remoto ai Trusted Hosts
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "NomeServer" -ForceMetodo 1: Invoke-Command con Restart-Service
Il metodo più versatile per riavviare un servizio da remoto è combinare Invoke-Command con Restart-Service. Questo approccio sfrutta appieno PowerShell Remoting e funziona su tutte le versioni recenti di PowerShell
Invoke-Command -ComputerName "NomeServer" -ScriptBlock {
Restart-Service -Name "NomeServizio" -Force
}Il parametro -Force è fondamentale in quanto forza il riavvio anche quando il servizio ha dipendenze attive o processi figlio in esecuzione. Una buona pratica è includere nella stessa sessione remota una verifica dello stato finale del servizio
Invoke-Command -ComputerName "NomeServer" -ScriptBlock {
Restart-Service -Name "NomeServizio" -Force
Start-Sleep -Seconds 3
$svc = Get-Service -Name "NomeServizio"
Write-Output "Stato attuale: $($svc.Status)"
}Metodo 2: Gestione con Credenziali Esplicite
In scenari in cui l’account con cui viene lanciato il comando non dispone dei privilegi necessari sul server remoto, è possibile specificare credenziali alternative. Questa è, ad esempio, la pratica consigliata per operazioni in ambienti con accesso privilegiato gestito (PAM)
$cred = Get-Credential
Invoke-Command -ComputerName "NomeServer" -Credential $cred -ScriptBlock {
Restart-Service -Name "NomeServizio" -Force
Get-Service -Name "NomeServizio" | Select-Object Name, Status, StartType
}L’esecuzione di Get-Credential aprirà una finestra di dialogo sicura per l’inserimento delle credenziali, evitando di scrivere password in chiaro negli script.
Metodo 3: Riavvio su più server contemporaneamente
Una delle funzionalità più potenti di Invoke-Command è la possibilità di eseguire operazioni su più server in parallelo con un singolo comando. Questa capacità riduce drasticamente i tempi di intervento in caso di manutenzioni su infrastrutture distribuite
$servers = @("Server01", "Server02", "Server03")
Invoke-Command -ComputerName $servers -ScriptBlock {
Restart-Service -Name "NomeServizio" -Force
[PSCustomObject]@{
Server = $env:COMPUTERNAME
Stato = (Get-Service -Name "NomeServizio").Status
Orario = (Get-Date).ToString("HH:mm:ss")
}
}L’output viene restituito in forma tabellare con il nome del server, lo stato del servizio e il timestamp dell’operazione per tenere traccia dell’esito del comando sui server specificati.
Per rendere lo script ancora più pratico è possibile gestire l’elenco dei server tramite un file di testo, specificando i server uno per riga, evitando di modificare lo script ad ogni necessità. Creiamo quindi il file servers.txt come di seguito
Server01
Server02
Server03
Server04.domain.local
192.168.0.100Il file può contenere nomi NetBIOS, FQDN o indirizzi IP
# Percorso del file con l'elenco dei server
$filePath = "C:\Scripts\servers.txt"
# Lettura delle righe presenti nel file
$servers = Get-Content -Path $filePath |
Where-Object { $_ -match '\S' } |
ForEach-Object { $_.Trim() }
Invoke-Command -ComputerName $servers -ScriptBlock {
Restart-Service -Name "NomeServizio" -Force
[PSCustomObject]@{
Server = $env:COMPUTERNAME
Stato = (Get-Service -Name "NomeServizio").Status
Orario = (Get-Date).ToString("HH:mm:ss")
}
}Script Completo con Logging e Gestione degli Errori
Quando la gestione dei servizi remoti diventa un’attività ricorrente e strutturata occorre avere uno script che, oltre ad essere di semplice utilizzo, possa dare informazioni sulle operazioni fatte e che sia in grado di gestire eventuali errori. Questo permette di automatizzare le operazioni modificando solo il file di testo con l’elenco dei server sia manualmente che in automatico recuperando, ad esempio, le informazioni da un sistema di monitoraggio.
function Restart-RemoteService {
param(
[Parameter(Mandatory)]
[string]$ServersFile,
[Parameter(Mandatory)]
[string]$ServiceName,
[Parameter(Mandatory)]
[PSCredential]$Credential = (Get-Credential),
[string]$LogPath = "C:\Logs\ServiceRestart_$(Get-Date -Format 'yyyyMMdd').log"
)
if (-not (Test-Path -Path $ServersFile)) {
Write-Error "Elenco server non trovato: $ServersFile"
return
}
$computerNames = Get-Content -Path $ServersFile |
Where-Object { $_ -match '\S' } |
ForEach-Object { $_.Trim() }
if ($computerNames.Count -eq 0) {
Write-Error "Il file '$ServersFile' non contiene nomi server validi."
return
}
Write-Host "Server trovati ($($computerNames.Count)): $($computerNames -join ', ')" -ForegroundColor Cyan
$params = @{
ComputerName = $computerNames
ScriptBlock = {
param($svc)
try {
Restart-Service -Name $svc -Force -ErrorAction Stop
Start-Sleep -Seconds 3
$status = (Get-Service -Name $svc).Status
[PSCustomObject]@{
Server = $env:COMPUTERNAME
Servizio = $svc
Stato = $status
Successo = $true
Errore = $null
Timestamp = Get-Date
}
}
catch {
[PSCustomObject]@{
Server = $env:COMPUTERNAME
Servizio = $svc
Stato = "Errore"
Successo = $false
Errore = $_.Exception.Message
Timestamp = Get-Date
}
}
}
ArgumentList = $ServiceName
}
$params.Credential = $Credential
$risultati = Invoke-Command @params
$risultati | Export-Csv -Path $LogPath -Append -NoTypeInformation -Encoding UTF8
Write-Host "Log salvato in: $LogPath" -ForegroundColor Green
$risultati | Format-Table -AutoSize
}Questo script può essere salvato con il nome Restart-RemoteService.ps1 nella stessa directory dove è presente il file servers.txt
# Caricare la funzione nella sessione corrente
. "C:\Scripts\Restart-RemoteService.ps1"
# Eseguire il comando
Restart-RemoteService -ServersFile "C:\Scripts\servers.txt" -ServiceName "Spooler"Lo script verifica l’esistenza del file prima di procedere, segnala quanti server ha trovato e gestisce il caso in cui il file sia vuoto.
Abbiamo visto come con PowerShell siamo in grado di gestire i servizi Windows da remoto, adattando i comandi a qualsiasi scenario: dalla semplice operazione su singolo server fino agli interventi automatizzati su centinaia di macchine in parallelo.
