PowerShell скрипты для сетевого инженера — 10 инструментов

PowerShell скрипты для сетевого инженера - 10 инструментов
Быстрый ответ
PowerShell — это не язык программирования для избранных. Это твоя вторая смена на дежурстве. Пять групп задач закрывают 80% ежедневной рутины сетевого инженера: диагностика портов, поиск заблокированных файлов, управление ICMP, контроль дискового пространства и анализ Event Log. Все скрипты ниже — рабочие, проверены в продакшне, копируй и используй.
Содержание: показать

PowerShell-скрипты на каждый день для сетевого инженера

Садимся, завариваем кофе погуще. Ежедневная рутина сетевого инженера — это не красивые дашборды и IaC-пайплайны. Это война с мелочами: «что слушает 5000-й порт?», «кто сожрал весь диск C:?» и «почему пинг не ходит в 3 ночи?».

Вместо того чтобы сто раз делать одно и то же руками — напишем арсенал быстрых и метких скриптов. Не монстров на 500 строк, а инструменты для сиюминутной диагностики. PowerShell Core (7.x) — наш выбор, но всё работает и на классической 5.1.

Статья выросла из исходной версии. Добавил новые скрипты: сканер доступности хостов, мониторинг служб Windows, анализ топ-пожирателей диска, проверка DNS, сводный отчёт о системе. Плюс troubleshooting, FAQ и раздел про профиль PowerShell.

Что нужно перед стартом

Компонент Минимум Рекомендую Зачем
PowerShell 5.1 (встроен в Win 10+) 7.x (Core) Кросс-платформа, нет устаревших командлетов
Права Обычный пользователь Локальный администратор Firewall, WMI, Event Log требуют прав
ОС Windows 10 / Server 2016 Windows 11 / Server 2022 Командлеты Net* появились в Server 2012 R2
Execution Policy RemoteSigned RemoteSigned Без этого скрипты не запустятся

Проверь политику выполнения скриптов:


Get-ExecutionPolicy
# Если Restricted - исправь:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

На момент публикации актуальна версия PowerShell 7.4. Перед установкой проверь свежие релизы на github.com/PowerShell/PowerShell.

Архитектура: как это всё работает вместе

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    A["$PROFILE (профиль PS)"] --> B["Get-OpenPorts"]
    A --> C["Get-LockedFile"]
    A --> D["Enable-ICMPEcho"]
    A --> E["Get-DiskSpaceReport"]
    A --> F["Get-RecentEvents"]
    A --> G["Test-HostsAlive"]
    A --> H["Get-ServiceStatus"]
    A --> I["Get-TopDiskConsumers"]
    A --> J["Get-DNSCheck"]
    A --> K["Get-SystemSummary"]

    B --> L["Get-NetTCPConnection / netstat"]
    C --> M["System.IO.File / WMI"]
    D --> N["NetFirewallRule"]
    E --> O["Get-PSDrive / Get-WmiObject"]
    F --> P["Get-WinEvent"]
    G --> Q["Test-Connection"]
    H --> R["Get-Service"]
    I --> S["Get-ChildItem (рекурсия)"]
    J --> T["Resolve-DnsName"]
    K --> U["CIM / WMI / Net*"]

    style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style L fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style M fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style N fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style O fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style P fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style Q fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style R fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style S fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style T fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
    style U fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d

Все функции живут в профиле PowerShell. Один раз прописал — всегда доступны в любом терминале.

1. Просмотр открытых портов: кто здесь слушает без спросу

Команда netstat -ano — старый друг, но её вывод просится в структурированный объект. Чтобы не пялиться в столбики текста, сделаем красиво и с пользой. Get-NetTCPConnection даёт нам PowerShell-объект, а значит — фильтрацию, сортировку и цвет.

Скрипт подсвечивает системные порты ниже 1024 красным. Это не случайно — именно там чаще всего обнаруживается что-то незапланированное.


function Get-OpenPorts {
    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [string]$FilterByState = 'LISTENING'
    )

    Write-Host "Ищем открытые порты (состояние: '$FilterByState')..." -ForegroundColor Cyan
    Write-Host "PID   Протокол Локальный адрес          Внешний адрес        Состояние    Процесс" -ForegroundColor Yellow
    Write-Host "---   -------- -------------          ------------        --------    -------" -ForegroundColor Yellow

    try {
        $networkData = Get-NetTCPConnection -ErrorAction Stop | Where-Object State -eq $FilterByState
    }
    catch {
        Write-Warning "Get-NetTCPConnection не сработал. Пробую netstat."
        $netstatResult = netstat -ano -p TCP | Select-String -Pattern '\s+(LISTENING|ESTABLISHED)\s+'
        foreach ($line in $netstatResult) {
            $parts = $line -split '\s+'
            Write-Host ($parts[4..$($parts.Length)] -join ' ') -ForegroundColor Gray
        }
        return
    }

    foreach ($conn in $networkData) {
        $proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue
        $procName = if ($proc) { $proc.ProcessName } else { "N/A" }
        $color = if ($conn.LocalPort -lt 1024) { 'Red' } else { 'Green' }

        Write-Host ("{0,-5} {1,-8} {2,-22} {3,-20} {4,-10} {5}" -f
            $conn.OwningProcess,
            'TCP',
            "$($conn.LocalAddress):$($conn.LocalPort)",
            "$($conn.RemoteAddress):$($conn.RemotePort)",
            $conn.State,
            $procName) -ForegroundColor $color
    }
}

Запусти так:


# Все слушающие порты
Get-OpenPorts

# Только установленные соединения
Get-OpenPorts -FilterByState 'ESTABLISHED'

# Посмотреть конкретный порт
Get-OpenPorts | Where-Object { $_ -match ':8080' }

Результат: список портов с именем процесса, PID и состоянием. Системные порты — красным.

2. Поиск заблокированного файла: кто прилепился к файлу как репей

Блокировка файла — классика жанра. Особенно весело когда лог-файл не ротируется, а база не бэкапится. Встроенные средства Windows работают через WMI-запрос к командной строке процесса. Это не идеально, но в большинстве случаев достаточно.

Если хочешь гарантированный результат — скачай handle.exe от Sysinternals. Но сначала попробуй встроенное.


function Get-LockedFile {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]$Path
    )

    process {
        Write-Host "Пробуем выяснить, кто держит файл '$Path'..." -ForegroundColor Cyan
        try {
            $fileStream = [System.IO.File]::Open($Path, 'Open', 'Read', 'None')
            $fileStream.Close()
            Write-Host "Файл свободен." -ForegroundColor Green
        }
        catch [System.IO.IOException] {
            Write-Host "Файл заблокирован! Ищем виновника..." -ForegroundColor Red

            $lockingProcess = Get-CimInstance -ClassName Win32_Process |
                Where-Object { $_.CommandLine -like "*$Path*" } |
                Select-Object ProcessId, Name, CommandLine

            if ($lockingProcess) {
                $lockingProcess | Format-Table -AutoSize
                Write-Host "Принудительно закрыть: Stop-Process -Id $($lockingProcess.ProcessId) -Force" -ForegroundColor Yellow
            }
            else {
                Write-Host "WMI не нашёл процесс. Скачай handle.exe от Sysinternals для точного ответа." -ForegroundColor Yellow
                Write-Host "handle.exe `"$Path`"" -ForegroundColor Gray
            }
        }
    }
}

Get-LockedFile -Path "C:\logs\app.log"

3. Включаем ICMP: возвращаем пинг в строй

«Сервер не пингуется!» — эта фраза будит в 3 ночи. Часто дело в фаерволе Windows, который по умолчанию блокирует входящий ICMP. Скрипт проверяет существующее правило и либо создаёт новое, либо включает отключённое.

Запускать от администратора
Изменение правил брандмауэра требует прав администратора. Запускай PowerShell через «Запуск от имени администратора» или в сессии с UAC-повышением.

function Enable-ICMPEcho {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Position=0)]
        [string]$RuleName = "Allow ICMPv4 Echo Request"
    )

    Write-Host "Включаем ICMP echo для этого хоста..." -ForegroundColor Cyan

    $existingRule = Get-NetFirewallRule -DisplayName $RuleName -ErrorAction SilentlyContinue

    if ($existingRule) {
        Write-Host "Правило уже есть. Включаем." -ForegroundColor Gray
        Set-NetFirewallRule -DisplayName $RuleName -Enabled True
    }
    else {
        Write-Host "Создаём правило: '$RuleName'" -ForegroundColor Gray
        New-NetFirewallRule `
            -DisplayName $RuleName `
            -Description "Allow ICMP Echo Request" `
            -Protocol ICMPv4 `
            -IcmpType 8 `
            -Enabled True `
            -Profile Any `
            -Action Allow
    }

    $checkRule = Get-NetFirewallRule -DisplayName $RuleName | Select-Object DisplayName, Enabled, Action
    Write-Host "Результат:" -ForegroundColor Green
    $checkRule | Format-Table -AutoSize
    Write-Host "Готово. Проверь пинг с другой машины." -ForegroundColor Green
}

Enable-ICMPEcho
# Откатить:
# Set-NetFirewallRule -DisplayName "Allow ICMPv4 Echo Request" -Enabled False

4. Дисковое пространство: диск C: снова на голодном пайке

Классика с визуализацией. Не просто сухие цифры — прогресс-бар прямо в консоли и предупреждение при критическом заполнении.

Формула в скрипте считает процент заполнения, а не процент свободного места. Не перепутай при анализе результата.


function Get-DiskSpaceReport {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]$DriveLetter = "C",
        [int]$WarnThresholdPct = 20,
        [int]$CritThresholdPct = 10
    )

    Write-Host "Анализируем диск $DriveLetter`:\" -ForegroundColor Cyan

    $disk = Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue
    if (-not $disk) {
        Write-Error "Диск $DriveLetter не найден."
        return
    }

    $totalGB = [math]::Round(($disk.Used + $disk.Free) / 1GB, 2)
    $freeGB  = [math]::Round($disk.Free / 1GB, 2)
    $usedGB  = [math]::Round($disk.Used / 1GB, 2)
    $freePct = [math]::Round(($disk.Free / ($disk.Used + $disk.Free)) * 100, 1)

    $color = if ($freePct -lt $CritThresholdPct) { 'Red' }
             elseif ($freePct -lt $WarnThresholdPct) { 'Yellow' }
             else { 'Green' }

    $filledBlocks = [math]::Round((100 - $freePct) / 5)
    $bar = '[' + ('#' * $filledBlocks) + ('-' * (20 - $filledBlocks)) + ']'

    Write-Host "Диск:    $($disk.Root)" -ForegroundColor White
    Write-Host "Всего:   $totalGB GB" -ForegroundColor Gray
    Write-Host "Занято:  $usedGB GB" -ForegroundColor Gray
    Write-Host "Свободно: $freeGB GB ($freePct%)" -ForegroundColor $color
    Write-Host "Заполнение: $bar" -ForegroundColor $color

    if ($freePct -lt $CritThresholdPct) {
        Write-Host "" 
        Write-Host "КРИТИЧНО: свободного места меньше $CritThresholdPct%!" -ForegroundColor Red -BackgroundColor Black
        Write-Host "Запусти Get-TopDiskConsumers чтобы найти виновника." -ForegroundColor Yellow
    }
    elseif ($freePct -lt $WarnThresholdPct) {
        Write-Host "Предупреждение: свободного места меньше $WarnThresholdPct%." -ForegroundColor Yellow
    }
}

Get-DiskSpaceReport
Get-DiskSpaceReport -DriveLetter D -WarnThresholdPct 30

5. Event Log: что там натворила система

Листать Event Log руками — как искать иголку в стоге сена. Скрипт фильтрует только ошибки и предупреждения за нужный период. Достаточно чтобы понять, почему разбудили в 3 ночи.


function Get-RecentEvents {
    [CmdletBinding()]
    param(
        [int]$HoursBack = 24,
        [ValidateSet('System', 'Application', 'Security')]
        [string]$LogName = 'System',
        [int]$MaxEvents = 50
    )

    Write-Host "Журнал '$LogName', последние $HoursBack часов..." -ForegroundColor Cyan
    $startTime = (Get-Date).AddHours(-$HoursBack)

    $events = Get-WinEvent -LogName $LogName -MaxEvents $MaxEvents -ErrorAction SilentlyContinue |
        Where-Object { $_.TimeCreated -ge $startTime -and ($_.Level -eq 2 -or $_.Level -eq 3) } |
        Select-Object TimeCreated, LevelDisplayName, ProviderName, Id, Message |
        Sort-Object TimeCreated -Descending

    if (-not $events) {
        Write-Host "Ничего серьёзного за этот период. Хорошая ночь." -ForegroundColor Green
        return
    }

    Write-Host "Найдено событий: $($events.Count)" -ForegroundColor Yellow

    foreach ($event in $events) {
        $levelColor = if ($event.LevelDisplayName -eq 'Error') { 'Red' } else { 'Yellow' }
        Write-Host "$($event.TimeCreated) [$($event.LevelDisplayName)] $($event.ProviderName) | ID: $($event.Id)" -ForegroundColor $levelColor
        $msg = $event.Message -replace '\r?\n', ' '
        if ($msg.Length -gt 200) { $msg = $msg.Substring(0, 200) + "..." }
        Write-Host "  $msg" -ForegroundColor Gray
        Write-Host ("-" * 60) -ForegroundColor DarkGray
    }

    Write-Host "Детали: eventvwr.msc -> фильтр по ID" -ForegroundColor Cyan
}

Get-RecentEvents -HoursBack 2 -LogName Application
Get-RecentEvents -HoursBack 48 -LogName System -MaxEvents 100

6. Сканер доступности хостов: пинг по списку

Новый скрипт. Берёшь список IP или имён хостов, скрипт параллельно проверяет каждый и выдаёт таблицу: живой / мёртвый, время отклика. Незаменим когда нужно быстро проверить сегмент сети или список серверов из инвентаря.

Параллельность через ForEach-Object -Parallel работает только в PowerShell 7+. На 5.1 скрипт автоматически переключается на обычный цикл.


function Test-HostsAlive {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string[]]$Hosts,
        [int]$TimeoutMs = 1000,
        [int]$Count = 2
    )

    Write-Host "Проверяем $($Hosts.Count) хостов..." -ForegroundColor Cyan

    $results = @()
    $psVersion = $PSVersionTable.PSVersion.Major

    if ($psVersion -ge 7) {
        $results = $Hosts | ForEach-Object -Parallel {
            $pingResult = Test-Connection -ComputerName $_ -Count $using:Count -TimeoutSeconds ($using:TimeoutMs / 1000) -ErrorAction SilentlyContinue
            [PSCustomObject]@{
                Host    = $_
                Status  = if ($pingResult) { "ALIVE" } else { "DEAD" }
                AvgMs   = if ($pingResult) { [math]::Round(($pingResult.Latency | Measure-Object -Average).Average, 1) } else { "-" }
                Packets = if ($pingResult) { $pingResult.Count } else { 0 }
            }
        } -ThrottleLimit 20
    }
    else {
        foreach ($h in $Hosts) {
            $pingResult = Test-Connection -ComputerName $h -Count $Count -ErrorAction SilentlyContinue
            $results += [PSCustomObject]@{
                Host    = $h
                Status  = if ($pingResult) { "ALIVE" } else { "DEAD" }
                AvgMs   = if ($pingResult) { [math]::Round(($pingResult.ResponseTime | Measure-Object -Average).Average, 1) } else { "-" }
                Packets = if ($pingResult) { $pingResult.Count } else { 0 }
            }
        }
    }

    $alive = ($results | Where-Object Status -eq "ALIVE").Count
    $dead  = ($results | Where-Object Status -eq "DEAD").Count

    $results | Sort-Object Status, Host | Format-Table -AutoSize | Out-String | ForEach-Object {
        $color = if ($_ -match "DEAD") { 'Red' } else { 'White' }
        Write-Host $_ -ForegroundColor $color
    }

    Write-Host "Итого: $alive живых, $dead недоступных из $($Hosts.Count)" -ForegroundColor $(if ($dead -gt 0) { 'Yellow' } else { 'Green' })
}

# Проверить несколько хостов
Test-HostsAlive -Hosts "192.168.1.1", "192.168.1.2", "8.8.8.8", "google.com"

# Из файла со списком
Test-HostsAlive -Hosts (Get-Content "servers.txt")

# Просканировать подсеть /24 быстро
$subnet = "192.168.1"
Test-HostsAlive -Hosts (1..254 | ForEach-Object { "$subnet.$_" }) -TimeoutMs 500

7. Мониторинг служб Windows: кто упал пока ты не смотрел

Классическая задача: пришёл утром, а что-то не работает. Скрипт показывает все остановленные службы с типом запуска «Автоматически» — именно они должны работать. Если служба остановлена и должна стартовать — это повод для расследования.


function Get-ServiceStatus {
    [CmdletBinding()]
    param(
        [string[]]$ServiceNames,
        [switch]$ShowOnlyStopped,
        [switch]$AutoStartOnly
    )

    Write-Host "Проверяем службы Windows..." -ForegroundColor Cyan

    $services = if ($ServiceNames) {
        $ServiceNames | ForEach-Object { Get-Service -Name $_ -ErrorAction SilentlyContinue }
    }
    else {
        Get-Service
    }

    if ($AutoStartOnly) {
        $services = $services | Where-Object {
            (Get-WmiObject -Class Win32_Service -Filter "Name='$($_.Name)'" -ErrorAction SilentlyContinue).StartMode -eq 'Auto'
        }
    }

    if ($ShowOnlyStopped) {
        $services = $services | Where-Object { $_.Status -ne 'Running' }
    }

    if (-not $services) {
        Write-Host "Все службы работают нормально." -ForegroundColor Green
        return
    }

    Write-Host ("{0,-40} {1,-12} {2}" -f "Имя службы", "Статус", "Отображаемое имя") -ForegroundColor Yellow

    foreach ($svc in $services | Sort-Object Status, DisplayName) {
        $color = switch ($svc.Status) {
            'Running'  { 'Green' }
            'Stopped'  { 'Red' }
            default    { 'Yellow' }
        }
        Write-Host ("{0,-40} {1,-12} {2}" -f $svc.Name, $svc.Status, $svc.DisplayName) -ForegroundColor $color
    }

    $stopped = ($services | Where-Object { $_.Status -ne 'Running' }).Count
    if ($stopped -gt 0) {
        Write-Host "`nОстановлено служб: $stopped" -ForegroundColor Red
        Write-Host "Перезапустить: Start-Service -Name 'имя_службы'" -ForegroundColor Yellow
    }
}

# Все службы с автозапуском которые не работают
Get-ServiceStatus -AutoStartOnly -ShowOnlyStopped

# Проверить конкретные службы
Get-ServiceStatus -ServiceNames "Spooler", "wuauserv", "BITS"

# Все службы
Get-ServiceStatus

8. Топ пожирателей диска: найди кто съел всё место

Диск заполнен, нужно найти виновника. Скрипт рекурсивно обходит папку и возвращает топ-N самых жирных файлов или папок. Незаменим когда 200 гигабайт «куда-то ушли» и ты не знаешь куда.

Осторожно с рекурсией на системных папках — это может занять несколько минут на медленных дисках.


function Get-TopDiskConsumers {
    [CmdletBinding()]
    param(
        [string]$Path = "C:\",
        [int]$Top = 20,
        [switch]$ByFolder,
        [string]$Extension
    )

    Write-Host "Ищем топ-$Top пожирателей пространства в $Path ..." -ForegroundColor Cyan
    Write-Host "Это может занять минуту. Потерпи." -ForegroundColor Gray

    if ($ByFolder) {
        $items = Get-ChildItem -Path $Path -Directory -ErrorAction SilentlyContinue | ForEach-Object {
            $size = (Get-ChildItem -Path $_.FullName -Recurse -File -ErrorAction SilentlyContinue |
                     Measure-Object -Property Length -Sum).Sum
            [PSCustomObject]@{
                Path    = $_.FullName
                SizeGB  = [math]::Round($size / 1GB, 3)
                SizeMB  = [math]::Round($size / 1MB, 1)
            }
        } | Sort-Object SizeGB -Descending | Select-Object -First $Top
    }
    else {
        $filter = if ($Extension) { "*.$Extension" } else { "*" }
        $items = Get-ChildItem -Path $Path -Recurse -File -Filter $filter -ErrorAction SilentlyContinue |
            Sort-Object Length -Descending |
            Select-Object -First $Top |
            ForEach-Object {
                [PSCustomObject]@{
                    File   = $_.FullName
                    SizeGB = [math]::Round($_.Length / 1GB, 3)
                    SizeMB = [math]::Round($_.Length / 1MB, 1)
                    Modified = $_.LastWriteTime
                }
            }
    }

    $items | Format-Table -AutoSize

    $total = ($items | Measure-Object -Property SizeMB -Sum).Sum
    Write-Host "Суммарно топ-$Top занимают: $([math]::Round($total / 1024, 2)) GB" -ForegroundColor Yellow
}

# Топ-20 самых больших файлов на диске C:
Get-TopDiskConsumers -Path "C:\" -Top 20

# Топ папок
Get-TopDiskConsumers -Path "C:\" -ByFolder -Top 10

# Только .log файлы
Get-TopDiskConsumers -Path "C:\logs" -Extension "log" -Top 15

# Быстро найти .dmp файлы (дампы памяти)
Get-TopDiskConsumers -Path "C:\Windows" -Extension "dmp" -Top 5

9. Проверка DNS: резолвится или нет

Простой, но часто нужный инструмент. Проверяет резолвинг хостов через системный DNS, дополнительный DNS и через публичный резолвер. Сразу видно где рвётся цепочка.


function Get-DNSCheck {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string[]]$Hostnames,
        [string[]]$DNSServers = @("8.8.8.8", "1.1.1.1")
    )

    Write-Host "Проверяем DNS-резолвинг..." -ForegroundColor Cyan

    foreach ($hostname in $Hostnames) {
        Write-Host "`n[$hostname]" -ForegroundColor White

        # Системный DNS
        try {
            $sysResult = Resolve-DnsName -Name $hostname -ErrorAction Stop
            $sysIPs = ($sysResult | Where-Object { $_.Type -in 'A','AAAA' }).IPAddress -join ', '
            Write-Host "  Системный DNS: $sysIPs" -ForegroundColor Green
        }
        catch {
            Write-Host "  Системный DNS: ОШИБКА - $($_.Exception.Message)" -ForegroundColor Red
        }

        # Альтернативные DNS-серверы
        foreach ($dns in $DNSServers) {
            try {
                $altResult = Resolve-DnsName -Name $hostname -Server $dns -ErrorAction Stop
                $altIPs = ($altResult | Where-Object { $_.Type -in 'A','AAAA' }).IPAddress -join ', '
                Write-Host "  $dns : $altIPs" -ForegroundColor Green
            }
            catch {
                Write-Host "  $dns : ОШИБКА" -ForegroundColor Red
            }
        }
    }
}

Get-DNSCheck -Hostnames "google.com", "internal.corp", "api.example.com"
Get-DNSCheck -Hostnames "mail.corp" -DNSServers "10.0.0.1", "8.8.8.8"

10. Сводный отчёт о системе: всё сразу одной командой

Иногда нужно быстро осмотреться на новом сервере или при старте диагностики. Этот скрипт собирает в одном месте: информацию о железе, сетевых интерфейсах, топ-процессах по памяти и CPU, и статус критических служб. Вроде fastfetch, но для инженера.


function Get-SystemSummary {
    [CmdletBinding()]
    param(
        [switch]$SkipNetwork,
        [switch]$SkipProcesses
    )

    Write-Host "=== СИСТЕМНЫЙ ОТЧЁТ ===" -ForegroundColor Cyan
    Write-Host "Дата: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray

    # Базовая информация
    $os  = Get-CimInstance -ClassName Win32_OperatingSystem
    $cpu = Get-CimInstance -ClassName Win32_Processor
    $cs  = Get-CimInstance -ClassName Win32_ComputerSystem

    Write-Host "`n[Система]" -ForegroundColor Yellow
    Write-Host "  Хост:       $($env:COMPUTERNAME)" -ForegroundColor White
    Write-Host "  ОС:         $($os.Caption) $($os.OSArchitecture)" -ForegroundColor White
    Write-Host "  Аптайм:     $(New-TimeSpan -Start $os.LastBootUpTime -End (Get-Date) | Select-Object -ExpandProperty ToString)" -ForegroundColor White
    Write-Host "  CPU:        $($cpu.Name)" -ForegroundColor White
    Write-Host "  RAM всего:  $([math]::Round($cs.TotalPhysicalMemory / 1GB, 1)) GB" -ForegroundColor White
    $freeRam = [math]::Round($os.FreePhysicalMemory / 1MB, 1)
    Write-Host "  RAM свободно: $freeRam GB" -ForegroundColor $(if ($freeRam -lt 1) { 'Red' } else { 'Green' })

    # Диски
    Write-Host "`n[Диски]" -ForegroundColor Yellow
    Get-PSDrive -PSProvider FileSystem | ForEach-Object {
        if ($_.Used -gt 0 -or $_.Free -gt 0) {
            $total = $_.Used + $_.Free
            $freePct = [math]::Round(($_.Free / $total) * 100, 1)
            $color = if ($freePct -lt 10) { 'Red' } elseif ($freePct -lt 20) { 'Yellow' } else { 'Green' }
            Write-Host ("  {0,-4} Всего: {1,6} GB  Свободно: {2,6} GB ({3}%)" -f
                "$($_.Name):\",
                [math]::Round($total/1GB,1),
                [math]::Round($_.Free/1GB,1),
                $freePct) -ForegroundColor $color
        }
    }

    # Сеть
    if (-not $SkipNetwork) {
        Write-Host "`n[Сеть]" -ForegroundColor Yellow
        Get-NetIPAddress -AddressFamily IPv4 |
            Where-Object { $_.IPAddress -ne '127.0.0.1' } |
            Select-Object InterfaceAlias, IPAddress, PrefixLength |
            ForEach-Object {
                Write-Host "  $($_.InterfaceAlias): $($_.IPAddress)/$($_.PrefixLength)" -ForegroundColor White
            }
    }

    # Топ-процессов
    if (-not $SkipProcesses) {
        Write-Host "`n[Топ-7 процессов по памяти]" -ForegroundColor Yellow
        Get-Process |
            Sort-Object WorkingSet -Descending |
            Select-Object -First 7 |
            ForEach-Object {
                Write-Host ("  {0,-30} {1,6} MB" -f $_.ProcessName, [math]::Round($_.WorkingSet/1MB, 1)) -ForegroundColor Gray
            }
    }

    Write-Host "`n=== КОНЕЦ ОТЧЁТА ===" -ForegroundColor Cyan
}

# Полный отчёт
Get-SystemSummary

# Без сети и процессов - быстро
Get-SystemSummary -SkipNetwork -SkipProcesses

Собираем всё в профиль PowerShell

Смысл всего этого — иметь инструменты под рукой в любой момент, не искать скрипты по папкам. Профиль PowerShell загружается при каждом открытии терминала. Добавь туда все функции.


# Открыть профиль для редактирования
notepad $PROFILE

# Если профиля нет - создать
if (-not (Test-Path $PROFILE)) {
    New-Item -ItemType File -Path $PROFILE -Force
}

В файл профиля добавь dot-sourcing или сами функции. Рекомендую вынести в отдельный файл:


# В $PROFILE добавь:
. "$HOME\Documents\PowerShell\MyFunctions.ps1"

Туда же — алиасы для быстрого вызова:


Set-Alias -Name ports       -Value Get-OpenPorts
Set-Alias -Name wholocksit  -Value Get-LockedFile
Set-Alias -Name allowping   -Value Enable-ICMPEcho
Set-Alias -Name diskcheck   -Value Get-DiskSpaceReport
Set-Alias -Name eventcheck  -Value Get-RecentEvents
Set-Alias -Name hostscan    -Value Test-HostsAlive
Set-Alias -Name svccheck    -Value Get-ServiceStatus
Set-Alias -Name bigfiles    -Value Get-TopDiskConsumers
Set-Alias -Name dnscheck    -Value Get-DNSCheck
Set-Alias -Name sysinfo     -Value Get-SystemSummary

После редактирования профиль перечитывается:


. $PROFILE

Диагностика рабочего дня: типовой сценарий

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    A["Утро. Что-то не работает."] --> B["sysinfo - общий осмотр"]
    B --> C{Диск забит?}
    C -->|Да| D["bigfiles - найти пожирателей"]
    C -->|Нет| E{Сервис упал?}
    E -->|Да| F["svccheck -AutoStartOnly -ShowOnlyStopped"]
    E -->|Нет| G{Сеть не отвечает?}
    G -->|Да| H["hostscan - проверить хосты"]
    H --> I["dnscheck - проверить DNS"]
    G -->|Нет| J{Ошибки в логах?}
    J -->|Да| K["eventcheck -HoursBack 24"]
    J -->|Нет| L["ports - проверить слушающие порты"]
    L --> M["Нашёл? Разобрался."]
    K --> M
    I --> M
    F --> M
    D --> M

    style A fill:#fef9c3,stroke:#eab308,stroke-width:2px,color:#713f12
    style M fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#15803d
    style D fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
    style F fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
    style H fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
    style I fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
    style K fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
    style L fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af

Troubleshooting: когда что-то пошло не так

Ошибка Причина Решение
File cannot be loaded because running scripts is disabled Execution Policy = Restricted Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Access to the path is denied Нет прав администратора Перезапусти PowerShell «от имени администратора»
Get-NetTCPConnection: The term is not recognized PowerShell 2.0 или Windows 7 Обновись до PS 5.1+. На старых системах используй netstat вручную.
Get-WinEvent: No events found Журнал пуст или период слишком мал Увеличь -HoursBack. Проверь, включён ли журнал: Get-WinEvent -ListLog Application
ForEach-Object -Parallel: The parameter 'Parallel' not found PowerShell 5.1 не поддерживает -Parallel Скрипт автоматически fallback на обычный цикл. Обнови до PS 7 для скорости.
New-NetFirewallRule: Access denied Нет прав на изменение фаервола Обязательно запускай Enable-ICMPEcho от администратора
Resolve-DnsName: DNS name does not exist Хост не резолвится через системный DNS Проверь через альтернативный DNS: -DNSServers "8.8.8.8"
Get-TopDiskConsumers завис Рекурсия на C:\ по миллиону файлов Указывай конкретную папку: -Path "C:\Logs"
Профиль не загружается Путь к файлу с пробелами, Execution Policy Проверь $PROFILE, убедись что файл существует и нет ошибок синтаксиса

Безопасность: что нельзя забывать

Принципы безопасности при работе с PowerShell
PowerShell — мощный инструмент. Именно поэтому злоумышленники его любят не меньше, чем администраторы. Несколько правил которые не стоит игнорировать.
  • Не храни учётные данные в скриптах открытым текстом. Используй Get-Credential или Windows Credential Manager.
  • Не давай всем скриптам ExecutionPolicy Unrestricted. RemoteSigned достаточно для большинства задач.
  • Enable-ICMPEcho открывает хост для пинга из любой сети (Profile Any). В продакшне уточни профиль: -Profile Domain или -Profile Private.
  • Логируй действия в продакшне. PowerShell умеет писать лог через Start-Transcript.
  • Скрипт Stop-Process -Force не спрашивает подтверждения. Проверь PID прежде чем убивать процесс.
  • Не запускай скрипты от администратора без необходимости. Принцип минимальных привилегий работает и в PowerShell.

# Логировать сессию в файл - полезно при работе с продакшн-серверами
Start-Transcript -Path "$HOME\ps_log_$(Get-Date -Format 'yyyyMMdd_HHmm').txt"
# ... работаешь ...
Stop-Transcript

Профилактика: как не чинить то, что сломается завтра

Скрипты написаны. Теперь сделай так, чтобы они работали на автопилоте, а не ждали когда ты вспомнишь запустить их вручную.

Создай задачу в планировщике для ежедневного отчёта о дисках и службах:


# Создать задачу - запускать каждое утро в 08:00
$action  = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument "-NonInteractive -File C:\Scripts\DailyCheck.ps1 -OutputPath C:\Reports\"
$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable

Register-ScheduledTask -TaskName "DailyServerCheck" `
    -Action $action -Trigger $trigger -Settings $settings `
    -RunLevel Highest -Description "Ежедневная диагностика"

Файл DailyCheck.ps1 — вызывает твои функции и пишет результат в файл или отправляет на почту:


# DailyCheck.ps1
param([string]$OutputPath = "C:\Reports\")

. "$HOME\Documents\PowerShell\MyFunctions.ps1"

$reportFile = Join-Path $OutputPath "report_$(Get-Date -Format 'yyyyMMdd').txt"

Start-Transcript -Path $reportFile

Get-SystemSummary
Get-ServiceStatus -AutoStartOnly -ShowOnlyStopped
Get-DiskSpaceReport -DriveLetter C

Stop-Transcript

# Отправить письмо если есть проблемы (опционально)
# Send-MailMessage -To "admin@corp.ru" -Subject "Daily Check" -Body (Get-Content $reportFile -Raw) -SmtpServer "mail.corp.ru"

Одна строка в crontab без комментария убивает весь отдел. Комментируй задачи планировщика — твой будущий я скажет спасибо.

Альтернативные решения

Задача PowerShell (эта статья) Альтернативы
Открытые порты Get-OpenPorts netstat, nmap, TCPView от Sysinternals
Блокировка файлов Get-LockedFile handle.exe, OpenedFilesView
Дисковое пространство Get-DiskSpaceReport + Get-TopDiskConsumers WinDirStat, TreeSize
Event Log Get-RecentEvents eventvwr.msc, MyEventViewer
Ping / сканирование Test-HostsAlive nmap, Angry IP Scanner
Полный мониторинг Get-SystemSummary Process Monitor, Zabbix

PowerShell выигрывает там, где нужна автоматизация, интеграция в пайплайн и работа без GUI. Для разовой визуальной диагностики — сторонние утилиты удобнее.

FAQ

Как запустить скрипт PowerShell если написано «запуск скриптов отключён»?

Это Execution Policy. Открой PowerShell от администратора и выполни:


Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

RemoteSigned разрешает запускать локальные скрипты без подписи, но требует подпись для скачанных из интернета. Это разумный компромисс между безопасностью и удобством.

Почему Get-OpenPorts не показывает UDP-порты?

Командлет Get-NetTCPConnection работает только с TCP. Для UDP используй отдельный запрос:


Get-NetUDPEndpoint | Select-Object LocalAddress, LocalPort, OwningProcess,
    @{N='Process';E={(Get-Process -Id $_.OwningProcess -EA SilentlyContinue).ProcessName}} |
    Sort-Object LocalPort | Format-Table -AutoSize

Как проверить, что профиль PowerShell загружается корректно?


# Посмотреть путь к профилю
$PROFILE

# Проверить, что файл существует
Test-Path $PROFILE

# Проверить на синтаксические ошибки перед загрузкой
. $PROFILE
# Если ошибок нет - тихо выполнится. Ошибки появятся в консоли.

Test-HostsAlive медленно работает на PS 5.1 — как ускорить?

На PS 5.1 нет -Parallel, поэтому хосты проверяются последовательно. Варианта два: обновиться до PowerShell 7, или использовать Jobs:


# Параллельная проверка через Jobs на PS 5.1
$jobs = $hosts | ForEach-Object {
    $h = $_
    Start-Job -ScriptBlock { Test-Connection -ComputerName $using:h -Count 1 -Quiet }
}
$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job

Можно ли эти скрипты запускать на удалённых серверах?

Да. Используй Invoke-Command:


# Запустить функцию на удалённом сервере
Invoke-Command -ComputerName "server01.corp" -ScriptBlock ${function:Get-DiskSpaceReport}

# Или передать функцию явно
$funcDef = ${function:Get-DiskSpaceReport}.ToString()
Invoke-Command -ComputerName "server01", "server02" -ScriptBlock {
    param($funcDef)
    Invoke-Expression "function Get-DiskSpaceReport { $funcDef }"
    Get-DiskSpaceReport
} -ArgumentList $funcDef

WinRM должен быть включён на целевых серверах: Enable-PSRemoting -Force.

Что делать если Event Log показывает тысячи ошибок?

Сначала сгруппируй по источнику — это покажет системный паттерн, а не случайный шум:


Get-WinEvent -LogName System -MaxEvents 1000 -ErrorAction SilentlyContinue |
    Where-Object { $_.Level -eq 2 } |
    Group-Object ProviderName |
    Sort-Object Count -Descending |
    Select-Object -First 10 Count, Name |
    Format-Table -AutoSize

Если одна служба генерирует 90% ошибок — начинай расследование с неё, а не с разбора каждого события.

Итог

Десять функций — это не коллекция скриптов ради коллекции. Это рабочий набор для конкретных задач: диагностика, поиск виновника, контроль состояния системы. Добавь всё в профиль, потрать полчаса один раз — и экономишь часы каждую неделю.

PowerShell — инструмент для тех кто хочет понять что происходит, а не просто нажать кнопку «починить». Разбирайся в том что запускаешь. Тогда и в 3 ночи будет понятно куда смотреть.

Если что-то не заработало
Пиши в комментарии — разберёмся. Укажи версию PowerShell, ОС и текст ошибки. Без этого диагностика превращается в гадание.

Оставайтесь на связи

Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.

Подписаться на IT-Аптеку →

Мы ВКонтакте

IT-Аптека — советы, новости и помощь рядом.

Вступить в группу ВКонтакте →
Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх