Scripts per a mantenir la salut dels teus serveis a Windows Server
El manteniment preventiu de serveis a Windows Server poques vegades rep l’atenció que mereix, almenys no fins que una cosa crítica deixa de funcionar. La monitorització clàssica (Nagios, Zabbix, SCOM) cobreix moltes necessitats, però no sempre arriba a temps o amb el nivell de granularitat que un necessita quan l’incident afecta serveis no estàndard, tasques programades, processos dependents entre si o serveis amb configuracions particulars. Aquí és on els scripts personalitzats, ben pensats i ben provats, tenen sentit.
1. Serveis que estan “corrent” però no funcionant
Una de les fallades més comunes en monitoritzar serveis és assumir que si Get-Service retorna Running, tot està bé. A la pràctica, hi ha serveis que es queden en aquest estat encara que la seva funcionalitat estigui afectada. Serveis que depenen d’una connexió de xarxa, una base de dades o una llibreria externa poden estar tècnicament “actius” però no respondre correctament.
La validació real ha d’ anar més enllà de l’ estat del servei. Aquí, un exemple pràctic que ajuda a detectar aquestes situacions:
$service = Get-Service -Name "W3SVC"
if ($service.Status -eq 'Running') {
try {
$response = Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing -TimeoutSec 5
if ($response.StatusCode -ne 200) {
Write-EventLog -LogName Application -Source "ServiceHealth" -EntryType Error -EventId 1001 -Message "W3SVC està aixecat però no respon correctament."
}
} catch {
Write-EventLog -LogName Application -Source "ServiceHealth" -EntryType Error -EventId 1002 -Message "W3SVC està aixecat però localhost no respon."
}
}
Aquest tipus de validacions són crítiques amb serveis com IIS, SQL Server, RabbitMQ, o fins i tot serveis propis que exposin endpoints. No n’hi ha prou amb que el procés estigui aixecat.
2. Verificacions creuades entre serveis relacionats
Hi ha escenaris on un servei depèn d’un altre de forma no declarada, i Windows no ho reflecteix amb dependències formals. Exemple: un servei que necessita una cua MSMQ disponible, o un servei web que carrega configuració dinàmica des d’una ruta de xarxa.
En aquests casos, convé tenir un script que faci validacions creuades. Per exemple, comprovar que ServiceA no només està Running, sinó que ServiceB també està operatiu abans de validar el conjunt com a sa.
$mainService = Get-Service -Name "MyApp.Service"
$dependency = Get-Service -Name "MyApp.Dependency"
if ($mainService.Status -eq 'Running' -and $dependency.Status -ne 'Running') {
Restart-Service -Name "MyApp.Service" -Force
Write-EventLog -LogName Application -Source "ServiceHealth" -EntryType Warning -EventId 1010 -Message "Servei principal reiniciat perquè la dependència no estava disponible."
}
Aquest patró evita estats inconsistents. Més d’una vegada, un backend queda “enganxat” perquè la seva dependència es va reiniciar de forma descontrolada i ningú ho va notar.
3. Logs d’esdeveniments i registres interns com a font de veritat
L’Event Log pot dir molt si es fa servir bé. Molts serveis llencen advertències o errors abans de fallar, i un script que revisi els últims esdeveniments pot detectar aquests símptomes.
No cal complicar-ho. Una consulta senzilla pot identificar patrons de fallada:
$events = Get-WinEvent -FilterHashtable @{LogName='Application'; StartTime=(Get-Date).AddMinutes(-10)} | Where-Object {
$_.ProviderName -eq "MyService" -and $_.LevelDisplayName -in @("Error", "Warning")
}
if ($events.Count -gt 0) {
$events | ForEach-Object {
Write-Output "Esdeveniment detectat: $($_.Message)"
}
}
Aquest tipus de monitorització pot acoblar-se amb alertes (per correu o webhook) sense necessitat d’un sistema extern.
Una mala pràctica habitual és deixar que els serveis escriguin els seus propis logs en arxius locals sense cap tipus de rotació o neteja. Si el servei ho permet, configura log rotation o esborra els logs antics amb un script diari. Alguns servidors fallen simplement perquè els discos s’omplen amb arxius .log de mesos anteriors.
4. Control de recursos: memòria, CPU i handles
Un servei pot semblar sa fins que comença a consumir recursos fora del raonable. En entorns Windows Server, PowerShell permet accedir a aquesta informació sense eines externes.
$proc = Get-Process -Name "w3wp" -ErrorAction SilentlyContinue
if ($proc) {
if ($proc.WorkingSet64 -gt 2GB) {
Write-EventLog -LogName Application -Source "ServiceHealth" -EntryType Warning -EventId 1020 -Message "w3wp està fent servir més de 2GB de RAM."
}
}
Aquest tipus de llindars són específics per a cada aplicació. Cal ajustar-los amb observació, no amb intuïció. En particular, a .NET el GC pot retenir memòria encara que estigui lliure. Convé observar el comportament normal del procés durant dies abans de posar límits.
Altres mètriques útils: nombre de handles oberts, threads actius, connexions obertes a ports específics. Sempre que es facin servir, és recomanable registrar la tendència en lloc d’ actuar de forma immediata.
5. Tasques programades com a part del sistema
En molts entorns Windows hi ha tasques crítiques executant-se des del Programador de tasques. El problema és que ningú les monitoritza de forma activa.
Una forma de revisar el seu estat:
$tasks = Get-ScheduledTask | Where-Object { $_.TaskName -like "*Backup*" }
foreach ($task in $tasks) {
$result = Get-ScheduledTaskInfo -TaskName $task.TaskName
if ($result.LastTaskResult -ne 0) {
Write-EventLog -LogName Application -Source "TaskHealth" -EntryType Error -EventId 2001 -Message "Fallada en la tasca $($task.TaskName): codi $($result.LastTaskResult)"
}
}
No sempre està clar què significa cada codi de sortida. Alguns serveis deixen codis arbitraris. Convé mapejar-los amb la documentació interna de l’ equip o amb proves controlades.
També és bona idea registrar quan va ser l’última execució i quant va durar. Un backup que “acaba bé” però triga 4 hores més de l’habitual pot indicar un problema que no ha donat la cara.
6. Registre i control centralitzat d’alertes
Evitar alertes duplicades, sorolloses o inconsistents és tan important com detectar fallades. Si s’ automatitzen verificacions, convé que cada script tingui lògica per a evitar enviar el mateix avís cada 5 minuts.
Una forma simple de fer això és mantenir un arxiu d’estat amb la marca temporal de l’última alerta enviada per a cada esdeveniment crític. Una altra opció és registrar les alertes com a esdeveniments de sistema i filtrar en base a això.
També es pot integrar amb sistemes com PagerDuty, OpsGenie o Teams fent servir webhooks, però això requereix una passarel·la ben definida per no sobrecarregar l’equip amb alertes irrellevants.
Un patró que pot ajudar: no alertar si una fallada dura menys de X minuts. Per això, l’script pot executar cada 5 minuts, i només generar una alerta si troba el mateix problema dues vegades seguides. Això redueix els falsos positius.
7. Auto-recuperació amb cura
El reinici automàtic de serveis pot ser una solució temporal o una forma d’ agreujar el problema. Cal avaluar amb cura quins serveis convé reiniciar automàticament i en quines condicions.
Alguns criteris útils:
- Reiniciar només si el servei no s’ ha reiniciat en els últims X minuts.
- No reiniciar si hi ha un arxiu de lock temporal (per exemple, no_restart.lock).
- Registrar tot reinici amb marca de temps i motiu.
- Validar si el reinici resol el problema (per exemple, reintentar un test funcional després de 30 segons).
Un exemple simple que inclou control de freqüència:
$lastRestartFile = "C:\Scripts\last_restart.txt"
$lastRestart = if (Test-Path $lastRestartFile) { Get-Content $lastRestartFile | Get-Date } else { (Get-Date).AddHours(-1) }
if ((Get-Date) - $lastRestart).TotalMinutes -gt 15) {
Restart-Service -Name "MyService"
(Get-Date).ToString("s") | Out-File $lastRestartFile
}
Aquest tipus de lògica preveu bucles de reinici que acaben en indisponibilitat total o corrupció d’estat.
8. Agendament i entorn controlat
Si s’ executen aquests scripts des de tasques programades, cal assegurar-se que:
- El compte tingui permisos suficients (però no excessius).
- La política d’execució (Set-ExecutionPolicy) estigui controlada.
- S’ especifiqui el perfil de PowerShell per a evitar comportaments diferents en execució manual vs automàtica.
- S’aconsegueix la sortida en un arxiu amb rotació.
Exemple d’ execució agendada:
powershell.exe -ExecutionPolicy Bypass -NoProfile -File "C:\Scripts\CheckServices.ps1" >> "C:\Logs\ServiceHealth.log" 2>>&1
I per rotació bàsica:
$logPath = "C:\Logs\ServiceHealth.log"
if ((Get-Item $logPath).Length -gt 10MB) {
Rename-Item $logPath "$logPath.bak"
New-Item $logPath -ItemType File
}
9. Versionat i proves
Tot script que toca serveis en producció ha de tenir control de versions. Git funciona bé fins i tot en servidors si es porta amb ordre. És recomanable provar els scripts en un entorn clonat o almenys amb -WhatIf abans de desplegar.
Quan es treballi en equip, convé deixar comentaris clars als scripts, no per explicar l’obvi, sinó per justificar decisions específiques (“es força reinici si falla X perquè l’error persisteix sense ell”).
10. Detalls que solen passar-se per alt
Hi ha una sèrie de decisions aparentment menors que amb el temps acaben generant problemes:
a. Verificar que el servei existeix abans de fer res
Els scripts han d’assumir que poden ser executats en servidors on el servei no està instal·lat (per exemple, en clústers heterogenis o després de migracions parcials).
if (-not (Get-Service -Name "MyService" -ErrorAction SilentlyContinue)) {
return
}
b. Scripts que fallen per l’entorn de PowerShell
Molts errors provenen del fet que l’ script corre en una sessió on no es carreguen mòduls necessaris, o on les polítiques d’ execució bloquegen certs comandaments. Sempre validar l’ entorn amb proves programades, no només manuals.
c. Manca de codificació en UTF-8
Alguns scripts generen logs amb caràcters rars si no es codifiquen correctament, especialment quan inclouen noms de serveis o missatges en altres idiomes. És bona pràctica escriure arxius amb:
Out-File -Encoding utf8
d. Ús de -Force sense necessitat
L’ús indiscriminat de -Force en Restart-Service o Stop-Process pot acabar matant processos que estan tancant transaccions. És millor intentar un tancament net, esperar uns segons, i només forçar si no hi ha alternativa.
11. Enfocament modular
En entorns amb desenes o centenars de servidors, els scripts individuals deixen d’escalar. El més sostenible és:
- Un mòdul de PowerShell comú (ServiceHealthTools.psm1) que encapsula les validacions.
- Un arxiu de configuració per servidor (JSON, XML o fins i tot CSV simple) que defineix els serveis i validacions específiques.
- Un programador central que executa les validacions segons rols o etiquetes.
Exemple de configuració per servidor:
{
"services": [
{
"name": "MyService",
"checkHttp": "http://localhost/status",
"restartOnFail": true
},
{
"name": "BackupService",
"checkLastRunEvent": true
}
]
}
I en el script:
$config = Get-Content -Raw -Path "C:\Configs\services.json" | ConvertFrom-Json
foreach ($svc in $config.services) {
if ($svc.checkHttp) {
# validar endpoint
}
if ($svc.restartOnFail) {
# reinici condicional
}
}
Aquest enfocament evita tenir scripts duplicats en cada servidor, redueix errors i permet actualitzacions centralitzades.
12. Revisió periòdica i desconfiança sistemàtica
Un cop els scripts estan en producció, cal revisar-los cada certs mesos. Els serveis canvien, les seves dependències també, i els scripts es desactualitzen ràpid. El que avui és útil, demà pot generar soroll o directament fallar en silenci.
A més, sempre convé revisar si l’script realment està executant el que s’espera. Hi ha serveis que canvien de nom després d’una actualització, o nous processos que reemplacen binaris antics però mantenen el mateix nom de servei, cosa que pot confondre el sistema de monitorització.
Una pràctica que ajuda: fer que l’ script registri un resum final de l’ estat general, com a mínim en log local, i opcionalment en un sistema d’ auditoria centralitzat. Alguna cosa com:
Write-Output "[$(Get-Date -Format 's')] Resultat: 4 serveis verificats, 1 reiniciat, 0 fallades crítiques"
Conclusió
Mantenir la salut dels serveis a Windows Server amb scripts personalitzats no és un reemplaçament de la monitorització tradicional, però cobreix llacunes crítiques que cap sistema genèric detecta amb precisió: fallades funcionals, dependències ocultes, degradació progressiva, o tasques oblidades.
L’ enfocament ha de ser modular, amb tolerància a la fallada, controls de freqüència i validacions que vagin més enllà de l’ estat superficial del sistema. L’objectiu no és reaccionar més ràpid, sinó evitar que la decisió arribi a produir-se o que el descobreixi l’usuari abans que l’equip tècnic.
Un script ben escrit per verificar i mantenir la salut de serveis pot no semblar cridaner, però quan porta anys executant-se sense fallades, generant logs clars i actuant amb criteri, demostra el seu veritable valor. Perquè el contrari —els scripts improvisats que creixen sense control— acaben sent part del problema.
I quan un servei crític deixa de funcionar, ningú agraeix un error de sintaxi a PowerShell.

