PowerShell — Quản trị Windows bằng dòng lệnh
PowerShell là công cụ dòng lệnh và scripting mạnh mẽ nhất của Microsoft. Từ Windows Server 2012 trở đi, PowerShell là cách chính để quản trị — hầu hết tác vụ GUI đều có cmdlet tương đương.
PowerShell vs CMD
PowerShell Core vs Windows PowerShell
- Windows PowerShell 5.1: built-in Windows, dùng .NET Framework
- PowerShell 7+ (Core): cross-platform, open source, dùng .NET 6/7/8 — khuyến nghị cài thêm
Cấu trúc Cmdlet
PowerShell dùng cú pháp Verb-Noun nhất quán:
Get-Process # Lấy thông tin process
Stop-Service # Dừng service
New-Item # Tạo file/folder mới
Remove-Item # Xoá
Set-Content # Ghi nội dung vào file
# Cấu trúc đầy đủ
Verb-Noun -Parameter Value
# Ví dụ
Get-Process -Name "nginx"
Stop-Service -Name "wuauserv" -Force
New-Item -Path "C:\logs" -ItemType Directory
Verb phổ biến: Get, Set, New, Remove, Start, Stop, Restart, Test, Invoke, Export, Import
Điều hướng và File System
# Điều hướng (giống Linux)
Get-Location # pwd — vị trí hiện tại
Set-Location C:\Users # cd
Set-Location .. # cd ..
Set-Location ~ # về home
# Alias ngắn gọn
pwd # Get-Location
cd # Set-Location
ls # Get-ChildItem
dir # Get-ChildItem
cls # Clear-Host
cat # Get-Content
# Liệt kê file
Get-ChildItem # ls / dir
Get-ChildItem -Force # bao gồm hidden files
Get-ChildItem -Recurse # đệ quy
Get-ChildItem -Filter "*.log" # filter theo pattern
Get-ChildItem C:\inetpub\logs -Recurse -Filter "*.log"
# File operations
New-Item -Path "C:\temp\test.txt" -ItemType File
New-Item -Path "C:\mydir" -ItemType Directory
Copy-Item C:\src\file.txt C:\dst\
Move-Item C:\old.txt C:\new.txt
Remove-Item C:\temp\*.tmp -Force
Remove-Item C:\mydir -Recurse -Force
# Đọc nội dung file
Get-Content C:\Windows\System32\drivers\etc\hosts
Get-Content app.log -Tail 50 # tail -n 50
Get-Content app.log -Wait # tail -f (follow)
Quản lý Process và Service
# === Process ===
Get-Process # ps aux
Get-Process -Name "chrome" # tìm theo tên
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 # top 10 CPU
Stop-Process -Name "notepad" # kill theo tên
Stop-Process -Id 1234 # kill theo PID
Stop-Process -Name "app" -Force # force kill
# Xem chi tiết process
Get-Process -Name "nginx" | Select-Object Name, Id, CPU, WorkingSet
Get-Process | Where-Object {$_.CPU -gt 10} | Format-Table Name, Id, CPU
# === Service ===
Get-Service # liệt kê tất cả service
Get-Service -Name "wuauserv" # Windows Update service
Get-Service | Where-Object {$_.Status -eq "Running"} # chỉ running
Start-Service -Name "nginx"
Stop-Service -Name "nginx"
Restart-Service -Name "nginx"
Set-Service -Name "nginx" -StartupType Automatic # auto start
# Tạo service mới
New-Service -Name "MyApp" `
-BinaryPathName "C:\apps\myapp.exe" `
-DisplayName "My Application" `
-StartupType Automatic
Pipeline — Sức mạnh của PowerShell
PowerShell pipeline truyền objects (không phải text) giữa các cmdlet:
# Lấy process → sort → lấy top 5
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5
# Tìm service stopped → start lại
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Start-Service
# Lấy file log cũ hơn 30 ngày → xoá
Get-ChildItem C:\logs -Filter "*.log" |
Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} |
Remove-Item -Force
# Export ra CSV
Get-Process | Select-Object Name, CPU, WorkingSet |
Export-Csv C:\reports\processes.csv -NoTypeInformation
# Đếm số lượng
Get-Service | Where-Object {$_.Status -eq "Running"} | Measure-Object
Variables và Scripting
# Variables
$name = "DevOps"
$port = 8080
$servers = @("web01", "web02", "web03") # array
$config = @{ # hashtable
Host = "localhost"
Port = 5432
DB = "myapp"
}
echo $name
echo $config["Host"]
echo $servers[0]
# String interpolation
$env = "production"
Write-Host "Deploying to $env"
Write-Host "Port: $($port + 1)"
# Special variables
$PSVersionTable # PowerShell version info
$env:USERNAME # username
$env:COMPUTERNAME # hostname
$env:PATH # PATH variable
$env:TEMP # temp directory
$HOME # user home
$PWD # current directory
$? # last command success (True/False)
$LASTEXITCODE # last exit code (số)
Điều kiện và vòng lặp
# if/else
$diskUsage = 85
if ($diskUsage -gt 90) {
Write-Warning "Disk critical!"
} elseif ($diskUsage -gt 80) {
Write-Warning "Disk high: $diskUsage%"
} else {
Write-Host "Disk OK: $diskUsage%"
}
# Toán tử so sánh: -eq, -ne, -lt, -gt, -le, -ge
# Chuỗi: -like "*.log", -match "regex", -contains
# foreach
$servers = @("web01", "web02", "web03")
foreach ($server in $servers) {
$result = Test-Connection -ComputerName $server -Count 1 -Quiet
if ($result) {
Write-Host "$server : OK" -ForegroundColor Green
} else {
Write-Host "$server : FAIL" -ForegroundColor Red
}
}
# for
for ($i = 0; $i -lt 5; $i++) {
Write-Host "Attempt $($i+1)"
}
# while
$retries = 0
while ($retries -lt 3) {
try {
# thử kết nối
break
} catch {
$retries++
Start-Sleep -Seconds 5
}
}
Functions
function Get-DiskUsage {
param(
[string]$Drive = "C:",
[int]$WarnThreshold = 80
)
$disk = Get-PSDrive $Drive.TrimEnd(":")
$usedPercent = [math]::Round(($disk.Used / ($disk.Used + $disk.Free)) * 100, 1)
if ($usedPercent -gt $WarnThreshold) {
Write-Warning "Drive $Drive : ${usedPercent}% used"
} else {
Write-Host "Drive $Drive : ${usedPercent}% used" -ForegroundColor Green
}
return $usedPercent
}
# Gọi
Get-DiskUsage -Drive "C:" -WarnThreshold 85
Quản lý Mạng
# Xem network interface
Get-NetIPAddress # tất cả IP
Get-NetIPAddress -AddressFamily IPv4
Get-NetAdapter # network adapters
Get-NetRoute # routing table
# Cấu hình IP tĩnh
New-NetIPAddress -InterfaceAlias "Ethernet" `
-IPAddress "192.168.1.100" `
-PrefixLength 24 `
-DefaultGateway "192.168.1.1"
# DNS
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses "8.8.8.8","8.8.4.4"
Resolve-DnsName google.com # nslookup
Test-NetConnection google.com -Port 443 # test-port
# Firewall
Get-NetFirewallRule | Where-Object {$_.Enabled -eq "True"}
New-NetFirewallRule -DisplayName "Allow HTTP" -Direction Inbound `
-Protocol TCP -LocalPort 80 -Action Allow
PowerShell Remoting — Quản trị từ xa
# Bật WinRM (chạy trên server target)
Enable-PSRemoting -Force
# Từ máy admin — chạy lệnh trên remote server
Invoke-Command -ComputerName "server01" -ScriptBlock {
Get-Service | Where-Object {$_.Status -eq "Stopped"}
}
# Mở interactive session
Enter-PSSession -ComputerName "server01"
# Giờ mọi lệnh chạy trên server01
Exit-PSSession
# Chạy script trên nhiều server cùng lúc
$servers = @("web01", "web02", "web03")
Invoke-Command -ComputerName $servers -ScriptBlock {
Restart-Service -Name "myapp"
Get-Service "myapp" | Select-Object Name, Status
}
Xem Log và Event
# Event Viewer từ CLI
Get-EventLog -LogName System -Newest 50
Get-EventLog -LogName Application -EntryType Error -Newest 20
Get-EventLog -LogName Security -Source "Microsoft-Windows-Security-Auditing" -Newest 30
# Windows Event Log (mới hơn)
Get-WinEvent -LogName "System" -MaxEvents 50
Get-WinEvent -LogName "Application" -MaxEvents 20 |
Where-Object {$_.LevelDisplayName -eq "Error"}
# Lọc theo thời gian
Get-WinEvent -LogName "System" -FilterHashtable @{
LogName = "System"
StartTime = (Get-Date).AddHours(-1)
Level = 2 # Error
}
# Xem log cụ thể
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4625 # Failed logon
} -MaxEvents 100
Script thực tế: Health Check Windows
#!/usr/bin/env pwsh
# health_check.ps1 — Kiểm tra sức khoẻ Windows Server
$warnings = @()
$errors = @()
function Write-Status {
param($Label, $Value, $Status)
$color = switch ($Status) {
"OK" { "Green" }
"WARN" { "Yellow" }
"FAIL" { "Red" }
}
Write-Host ("[{0}] {1}: {2}" -f $Status, $Label, $Value) -ForegroundColor $color
}
Write-Host "=== Health Check: $env:COMPUTERNAME — $(Get-Date) ===" -ForegroundColor Cyan
# CPU
$cpu = (Get-WmiObject Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average
if ($cpu -lt 80) { Write-Status "CPU" "$cpu%" "OK" }
elseif ($cpu -lt 90) { Write-Status "CPU" "$cpu%" "WARN"; $warnings += "CPU $cpu%" }
else { Write-Status "CPU" "$cpu%" "FAIL"; $errors += "CPU $cpu%" }
# Memory
$os = Get-WmiObject Win32_OperatingSystem
$memUsed = [math]::Round((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100, 1)
if ($memUsed -lt 80) { Write-Status "Memory" "$memUsed%" "OK" }
elseif ($memUsed -lt 90) { Write-Status "Memory" "$memUsed%" "WARN"; $warnings += "Memory $memUsed%" }
else { Write-Status "Memory" "$memUsed%" "FAIL"; $errors += "Memory $memUsed%" }
# Disk
$disks = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Used -gt 0 }
foreach ($disk in $disks) {
$usedPct = [math]::Round(($disk.Used / ($disk.Used + $disk.Free)) * 100, 1)
$label = "Disk $($disk.Name):"
if ($usedPct -lt 80) { Write-Status $label "$usedPct%" "OK" }
elseif ($usedPct -lt 90) { Write-Status $label "$usedPct%" "WARN"; $warnings += "$label $usedPct%" }
else { Write-Status $label "$usedPct%" "FAIL"; $errors += "$label $usedPct%" }
}
# Services
$criticalServices = @("W3SVC", "MSSQLSERVER", "wuauserv")
foreach ($svc in $criticalServices) {
$service = Get-Service -Name $svc -ErrorAction SilentlyContinue
if ($service -and $service.Status -eq "Running") {
Write-Status "Service $svc" "Running" "OK"
} elseif ($service) {
Write-Status "Service $svc" $service.Status "FAIL"
$errors += "Service $svc stopped"
}
}
Write-Host "================================"
if ($errors.Count -gt 0) {
Write-Host "ERRORS: $($errors -join ', ')" -ForegroundColor Red
exit 1
} elseif ($warnings.Count -gt 0) {
Write-Host "WARNINGS: $($warnings -join ', ')" -ForegroundColor Yellow
exit 0
} else {
Write-Host "All checks passed." -ForegroundColor Green
exit 0
}