Nesta aula, vamos consolidar todos os conhecimentos adquiridos ao longo do curso em um projeto prático e útil: um monitor de serviços do Windows. O objetivo é criar um script PowerShell que verifica periodicamente o status de serviços críticos, envia alertas quando algo está errado, registra eventos em um log e pode ser agendado para execução automática. Este projeto simula uma situação real de administração de sistemas, onde a disponibilidade de serviços é essencial.

Vamos abordar cada etapa do desenvolvimento, desde a lógica de verificação até a implementação de notificações e a configuração de uma tarefa agendada. Ao final, você terá um script funcional e aprenderá boas práticas de código, tratamento de erros e automação no PowerShell.

Verificação periódica

A base do monitor é a capacidade de verificar o status de serviços em intervalos regulares. O PowerShell oferece o cmdlet Get-Service para consultar serviços, mas para uma verificação mais detalhada (como verificar se o serviço está realmente respondendo), podemos usar Get-CimInstance ou até mesmo testar portas de rede. Neste projeto, vamos focar em serviços Windows e usar Get-Service por sua simplicidade e eficiência.

Para implementar a verificação periódica, temos duas abordagens: usar um loop infinito dentro do script ou deixar o script executar uma única vez e agendá-lo para rodar repetidamente. Neste projeto, vamos fazer o script executar uma verificação única, pois será agendado pelo Agendador de Tarefas. No entanto, mostraremos também como implementar um loop para execução contínua, caso você prefira.

Vamos começar definindo uma lista de serviços a monitorar. Em um ambiente real, essa lista pode ser armazenada em um arquivo de configuração ou diretamente no script. Para simplicidade, usaremos uma lista fixa, mas você pode adaptar.

# Lista de serviços críticos a monitorar
$services = @(
    'Spooler',
    'W3SVC',
    'MSSQLSERVER'
)

foreach ($serviceName in $services) {
    $service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
    if ($null -eq $service) {
        Write-Host "Serviço $serviceName não encontrado!" -ForegroundColor Red
        continue
    }
    $status = $service.Status
    Write-Host "$serviceName : $status"
}

Este trecho verifica o status básico. Mas para um monitor mais robusto, podemos querer verificar se o serviço está rodando há muito tempo, se está em estado 'Running' ou 'Stopped', e até mesmo testar a conectividade de uma porta associada. Vamos expandir nossa verificação para incluir mais detalhes e preparar o terreno para os alertas.

Alertas

Alertas são essenciais para notificar administradores quando um serviço para de funcionar. Podemos enviar alertas por e-mail, registrar eventos no Log de Eventos do Windows, ou até mesmo enviar notificações para um canal do Slack ou Teams via webhook. Neste projeto, vamos implementar dois tipos de alerta: e-mail e log de eventos.

Para e-mail, usaremos o cmdlet Send-MailMessage (embora esteja obsoleto, ainda é comum) ou Send-MailKitMessage se tiver o módulo. Para maior compatibilidade, usaremos Send-MailMessage com SMTP. Vamos criar uma função para enviar alertas.

function Send-Alert {
    param(
        [string]$ServiceName,
        [string]$Status
    )

    $smtpServer = "smtp.empresa.com"
    $from = "monitor@empresa.com"
    $to = "admin@empresa.com"
    $subject = "ALERTA: Serviço $ServiceName está $Status"
    $body = "O serviço $ServiceName está com status $Status às $(Get-Date)."

    try {
        Send-MailMessage -SmtpServer $smtpServer -From $from -To $to -Subject $subject -Body $body -ErrorAction Stop
        Write-Host "Alerta enviado por e-mail."
    } catch {
        Write-Error "Falha ao enviar e-mail: $_"
    }
}

# Exemplo de uso
Send-Alert -ServiceName "Spooler" -Status "Stopped"

Além do e-mail, podemos registrar um evento no Log de Eventos do Windows usando Write-EventLog ou New-EventLog. Isso é útil para auditoria e integração com ferramentas de monitoramento. Vamos criar uma função para isso também.

function Write-EventLogAlert {
    param(
        [string]$ServiceName,
        [string]$Status
    )

    $eventSource = "ServiceMonitor"
    if (-not [System.Diagnostics.EventLog]::SourceExists($eventSource)) {
        New-EventLog -LogName Application -Source $eventSource
    }

    $message = "O serviço $ServiceName está com status $Status às $(Get-Date)."
    Write-EventLog -LogName Application -Source $eventSource -EventId 1001 -EntryType Error -Message $message
    Write-Host "Evento registrado no log."
}

Integrando esses alertas ao nosso loop de verificação, podemos notificar imediatamente quando um serviço não estiver no estado esperado.

Logging

Logging é crucial para manter um histórico do comportamento dos serviços e das ações tomadas. Vamos criar um log em arquivo texto, com data/hora, nome do serviço, status e mensagem. Também podemos usar o cmdlet Start-Transcript para capturar toda a saída, mas um log customizado é mais flexível.

Vamos implementar uma função para escrever entradas de log em um arquivo. O diretório do log deve ser configurável, e devemos incluir tratamento de erros para criar o diretório se não existir.

$logFile = "C:\Logs\ServiceMonitor.log"

function Write-Log {
    param(
        [string]$Message,
        [ValidateSet('Info','Warning','Error')]
        [string]$Level = 'Info'
    )

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "[$timestamp] [$Level] $Message"

    try {
        $directory = Split-Path $logFile -Parent
        if (-not (Test-Path $directory)) {
            New-Item -ItemType Directory -Path $directory -Force | Out-Null
        }
        Add-Content -Path $logFile -Value $logEntry -ErrorAction Stop
    } catch {
        Write-Error "Falha ao escrever log: $_"
    }
}

# Uso
Write-Log -Message "Monitoramento iniciado." -Level Info
Write-Log -Message "Serviço Spooler parado." -Level Error

O log é essencial para análise posterior e para detectar padrões de falhas. Podemos também configurar a rotação de logs para evitar arquivos gigantes, mas isso fica como exercício.

Tarefa agendada

Para que o monitor funcione continuamente, precisamos agendá-lo para executar em intervalos regulares. O Agendador de Tarefas do Windows é a ferramenta ideal. Podemos criar a tarefa manualmente pela interface gráfica ou via comando schtasks ou o módulo ScheduledTasks do PowerShell. Vamos mostrar como criar uma tarefa que executa o script a cada 5 minutos.

Primeiro, vamos garantir que o script esteja em um local seguro, como C:\Scripts\ServiceMonitor.ps1. Depois, criamos a tarefa usando New-ScheduledTask e Register-ScheduledTask.

$scriptPath = "C:\Scripts\ServiceMonitor.ps1"
$taskName = "ServiceMonitor"

$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File `"$scriptPath`""
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Days 3650)
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable

Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Force

Write-Host "Tarefa agendada criada com sucesso."

Essa abordagem cria uma tarefa que roda imediatamente e depois a cada 5 minutos, por um período de 10 anos. Você pode ajustar os parâmetros conforme necessário. Também é importante considerar a execução com privilégios elevados, caso o script precise acessar recursos que exigem administração.

Boas práticas e observações finais

Ao desenvolver este projeto, você deve considerar os seguintes pontos:

  • Tratamento de erros: Sempre use try/catch para lidar com falhas de rede, e-mail ou permissões.
  • Configuração centralizada: Use um arquivo JSON ou XML para armazenar a lista de serviços, credenciais SMTP, etc., facilitando a manutenção.
  • Testes: Teste o script manualmente antes de agendar, e depois monitore os logs para verificar se está funcionando.
  • Segurança: Evite armazenar senhas em texto plano; use credenciais gerenciadas ou cofres de segredos.
  • Documentação: Comente o código e mantenha um README explicando como configurar.

Este projeto é uma base sólida que você pode expandir com notificações via Teams, integração com APIs, ou até mesmo um dashboard web. O importante é entender os princípios de monitoramento e automação.

Referências

Exercícios

  1. Modifique o script para incluir um serviço da sua escolha e teste a verificação manualmente.

    ✓ Resposta: Basta editar a lista $services no script, adicionando o nome do serviço desejado. Exemplo:
    $services = @('Spooler', 'W3SVC', 'MSSQLSERVER', 'Dhcp')
  2. Implemente uma função que verifique se um serviço está em execução há mais de 24 horas e registre um log 'Info' com essa informação.

    ✓ Resposta: Use Get-Process ou Get-CimInstance Win32_Service para obter o tempo de início. Exemplo:
    function Get-ServiceUptime {
        param([string]$ServiceName)
        $service = Get-CimInstance -ClassName Win32_Service -Filter "Name='$ServiceName'"
        if ($service) {
            $startTime = $service.ProcessId
            $process = Get-Process -Id $startTime -ErrorAction SilentlyContinue
            if ($process) {
                $uptime = (Get-Date) - $process.StartTime
                if ($uptime.TotalHours -gt 24) {
                    Write-Log -Message "Serviço $ServiceName em execução há mais de 24 horas (uptime: $($uptime.TotalHours) horas)" -Level Info
                }
            }
        }
    }
  3. Crie um alerta que, além de e-mail, envie uma notificação para um canal do Slack usando um webhook.

    ✓ Resposta: Use Invoke-RestMethod para enviar um POST para o webhook. Exemplo:
    function Send-SlackAlert {
        param([string]$Message)
        $webhookUrl = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
        $payload = @{ text = $Message } | ConvertTo-Json
        Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $payload -ContentType "application/json"
    }
  4. Configure uma tarefa agendada para executar o script a cada 10 minutos, mas apenas entre 8h e 18h.

    ✓ Resposta: Use New-ScheduledTaskTrigger com -Daily e -RepetitionInterval. Exemplo:
    $trigger = New-ScheduledTaskTrigger -Daily -At 8am -RepetitionInterval (New-TimeSpan -Minutes 10) -RepetitionDuration (New-TimeSpan -Hours 10)
    Register-ScheduledTask -TaskName "ServiceMonitor" -Action $action -Trigger $trigger -Settings $settings -Force
  5. Adicione uma política de retenção de logs: mantenha apenas os últimos 30 dias de logs, excluindo arquivos mais antigos.

    ✓ Resposta: Use Get-ChildItem e Where-Object para filtrar por data de criação e excluir. Exemplo:
    $logDir = "C:\Logs"
    $cutoffDate = (Get-Date).AddDays(-30)
    Get-ChildItem -Path $logDir -Filter "*.log" | Where-Object { $_.CreationTime -lt $cutoffDate } | Remove-Item -Force