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.
Архитектура: как это всё работает вместе
Все функции живут в профиле 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. Скрипт проверяет существующее правило и либо создаёт новое, либо включает отключённое.
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
Диагностика рабочего дня: типовой сценарий
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, убедись что файл существует и нет ошибок синтаксиса |
Безопасность: что нельзя забывать
- Не храни учётные данные в скриптах открытым текстом. Используй
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 ночи будет понятно куда смотреть.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →



