ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

คำสั่ง PowerShell พื้นฐานสำหรับผู้ดูแล Windows Server
Home คำสั่ง PowerShell พื้นฐานสำหรับผู้ดูแล Windows Server

คำสั่ง PowerShell พื้นฐานสำหรับผู้ดูแล Windows Server

การคลิกผ่านหน้าจอทำงานได้ดีกับเครื่องเดียว แต่เมื่อต้องทำสิ่งเดียวกันกับยี่สิบเครื่อง หรือต้องทำซ้ำทุกเดือน PowerShell คือความต่างระหว่างงานสิบนาทีกับงานสองวัน

คู่มือนี้ไม่ใช่รายการคำสั่งเรียงตามตัวอักษร แต่เป็นชุดที่ผู้ดูแลเซิร์ฟเวอร์ใช้จริงในงานประจำวัน พร้อมหลักการที่ทำให้คุณหาคำสั่งที่ต้องการเองได้โดยไม่ต้องค้นอินเทอร์เน็ตทุกครั้ง

สิ่งที่ต้องเตรียม

  • Windows Server 2016 ขึ้นไป ซึ่งมี PowerShell 5.1 ติดตั้งมาให้แล้ว
  • สิทธิ์ Administrator สำหรับคำสั่งที่แก้ไขระบบ
$PSVersionTable
Get-ExecutionPolicy -List

ขั้นตอนที่ 1: เข้าใจโครงสร้างคำสั่ง

ทุกคำสั่งใน PowerShell ใช้รูปแบบ กริยา-นาม เสมอ เช่น Get-Service, Stop-Process, New-Item เมื่อรู้กริยาที่ใช้บ่อยก็เดาคำสั่งได้เอง

  • Get- ดูข้อมูล ไม่เปลี่ยนแปลงอะไร ปลอดภัยเสมอ
  • Set- แก้ไขสิ่งที่มีอยู่
  • New- สร้างใหม่
  • Remove- ลบ
  • Start- / Stop- / Restart- ควบคุมการทำงาน
  • Test- ตรวจสอบและคืนค่าจริงหรือเท็จ

สามคำสั่งนี้ทำให้คุณหาคำตอบเองได้ทุกเรื่อง

Get-Command *service*
Get-Help Get-Service -Examples
Get-Service | Get-Member

Get-Member คือคำสั่งที่มีค่าที่สุด มันบอกว่าผลลัพธ์ที่ได้มีคุณสมบัติอะไรให้เรียกใช้บ้าง ซึ่งนำไปกรองและเรียงต่อได้

ขั้นตอนที่ 2: จัดการบริการ

Get-Service | Where-Object Status -eq 'Running'
Get-Service -Name W3SVC, MSSQLSERVER
Restart-Service -Name W3SVC -Force
Set-Service -Name Spooler -StartupType Disabled

# บริการที่ตั้งให้เริ่มอัตโนมัติแต่ไม่ได้ทำงาน คือสิ่งที่ต้องตรวจก่อนเสมอ
Get-Service | Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running' }

ขั้นตอนที่ 3: ตรวจทรัพยากรเครื่อง

# สิบโปรเซสที่กินหน่วยความจำมากที่สุด
Get-Process | Sort-Object WS -Descending | Select-Object -First 10 Name, Id,
  @{n='RAM(MB)';e={[math]::Round($_.WS/1MB,1)}}, CPU

# พื้นที่ดิสก์
Get-Volume | Where-Object DriveLetter | Select-Object DriveLetter,
  @{n='Free(GB)';e={[math]::Round($_.SizeRemaining/1GB,1)}},
  @{n='Total(GB)';e={[math]::Round($_.Size/1GB,1)}},
  @{n='Free%';e={[math]::Round($_.SizeRemaining/$_.Size*100,0)}}

# หน่วยความจำและเวลาที่เครื่องทำงานต่อเนื่อง
Get-CimInstance Win32_OperatingSystem | Select-Object `
  @{n='RAM(GB)';e={[math]::Round($_.TotalVisibleMemorySize/1MB,1)}},
  @{n='Free(GB)';e={[math]::Round($_.FreePhysicalMemory/1MB,1)}},
  @{n='Uptime';e={(Get-Date) - $_.LastBootUpTime}}

ขั้นตอนที่ 4: อ่าน Event Log

# ข้อผิดพลาดของระบบใน 24 ชั่วโมงที่ผ่านมา
Get-WinEvent -FilterHashtable @{LogName='System'; Level=1,2; StartTime=(Get-Date).AddDays(-1)} |
  Select-Object TimeCreated, Id, ProviderName, Message -First 20

# การล็อกอินที่ล้มเหลว
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddDays(-1)} |
  Select-Object TimeCreated, @{n='User';e={$_.Properties[5].Value}},
                @{n='From';e={$_.Properties[19].Value}}

# เวลาที่เครื่องรีสตาร์ต
Get-WinEvent -FilterHashtable @{LogName='System'; Id=6005,6006,1074} -MaxEvents 20 |
  Select-Object TimeCreated, Id, Message

ใช้ -FilterHashtable เสมอแทนการดึงทั้งหมดมาแล้วค่อยกรองด้วย Where-Object เพราะการกรองที่ต้นทางเร็วกว่ามากบนเครื่องที่มี Log สะสมเป็นล้านรายการ

ขั้นตอนที่ 5: จัดการไฟล์

# หาไฟล์ใหญ่กว่า 500 MB
Get-ChildItem D:\ -Recurse -File -ErrorAction SilentlyContinue |
  Where-Object Length -gt 500MB |
  Sort-Object Length -Descending |
  Select-Object FullName, @{n='GB';e={[math]::Round($_.Length/1GB,2)}}

# ลบไฟล์เก่ากว่า 30 วัน
Get-ChildItem D:\Logs -File |
  Where-Object LastWriteTime -lt (Get-Date).AddDays(-30) |
  Remove-Item -WhatIf

# คัดลอกพร้อมสิทธิ์
robocopy D:\Source E:\Dest /E /COPYALL /R:1 /W:1 /LOG:C:\copy.log

-WhatIf คือสิ่งที่ควรใส่ทุกครั้งก่อนสั่งลบจริง มันแสดงว่าจะทำอะไรบ้างโดยไม่ทำจริง เมื่อมั่นใจแล้วค่อยถอดออก

ขั้นตอนที่ 6: จัดการผู้ใช้ในโดเมน

Get-ADUser -Filter * -Properties LastLogonDate |
  Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } |
  Select-Object Name, SamAccountName, LastLogonDate

# บัญชีที่ถูกล็อก
Search-ADAccount -LockedOut | Select-Object Name, SamAccountName
Unlock-ADAccount -Identity somchai

# บัญชีที่รหัสผ่านไม่มีวันหมดอายุ ซึ่งไม่ควรมีหลายรายการ
Get-ADUser -Filter { PasswordNeverExpires -eq $true } -Properties PasswordNeverExpires |
  Select-Object Name, SamAccountName

ขั้นตอนที่ 7: สั่งงานเครื่องอื่นจากระยะไกล

# เปิดใช้งานฝั่งเครื่องปลายทาง
Enable-PSRemoting -Force

# รันคำสั่งบนเครื่องเดียว
Invoke-Command -ComputerName WEB1 -ScriptBlock { Get-Service W3SVC }

# รันพร้อมกันหลายเครื่อง
$servers = 'WEB1','WEB2','DB1'
Invoke-Command -ComputerName $servers -ScriptBlock {
  [PSCustomObject]@{
    Name   = $env:COMPUTERNAME
    Uptime = (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
    FreeC  = [math]::Round((Get-Volume -DriveLetter C).SizeRemaining/1GB,1)
  }
} | Format-Table

# เปิดหน้าต่างโต้ตอบกับเครื่องเดียว
Enter-PSSession -ComputerName WEB1
Exit-PSSession

ขั้นตอนที่ 8: ส่งออกผลลัพธ์

Get-Service | Export-Csv C:\services.csv -NoTypeInformation -Encoding UTF8
Get-Process | ConvertTo-Json -Depth 2 | Out-File C:\proc.json -Encoding UTF8
Get-EventLog -LogName System -Newest 100 | ConvertTo-Html | Out-File C:\events.html

ใส่ -Encoding UTF8 เสมอเมื่อมีข้อความภาษาไทย ไม่เช่นนั้นไฟล์ที่ได้จะอ่านไม่ออก

ขั้นตอนที่ 9: เขียนสคริปต์และตั้งเวลา

notepad C:\Scripts\daily-check.ps1
$report = foreach ($s in 'WEB1','WEB2','DB1') {
  try {
    Invoke-Command -ComputerName $s -ErrorAction Stop -ScriptBlock {
      [PSCustomObject]@{
        Server = $env:COMPUTERNAME
        FreeC  = [math]::Round((Get-Volume -DriveLetter C).SizeRemaining/1GB,1)
        Status = 'OK'
      }
    }
  } catch {
    [PSCustomObject]@{ Server = $s; FreeC = $null; Status = "ติดต่อไม่ได้" }
  }
}

$report | Export-Csv "C:\Reports\check-$(Get-Date -f yyyyMMdd).csv" -NoTypeInformation -Encoding UTF8

ตั้งให้รันอัตโนมัติ

$action  = New-ScheduledTaskAction -Execute 'powershell.exe' `
             -Argument '-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\daily-check.ps1'
$trigger = New-ScheduledTaskTrigger -Daily -At 7am
Register-ScheduledTask -TaskName "Daily Server Check" -Action $action -Trigger $trigger `
  -User "SYSTEM" -RunLevel Highest

ปัญหาที่พบบ่อย

รันสคริปต์ไม่ได้ ขึ้นว่าถูกปิดกั้น

นโยบายการรันสคริปต์ปิดอยู่ ตรวจด้วย Get-ExecutionPolicy -List สำหรับเซิร์ฟเวอร์ที่ใช้งานจริง ตั้งเป็น RemoteSigned ซึ่งยอมให้รันสคริปต์ที่เขียนเองแต่บังคับให้สคริปต์ที่ดาวน์โหลดมาต้องมีลายเซ็น

Set-ExecutionPolicy RemoteSigned -Scope LocalMachine

Invoke-Command เชื่อมต่อไม่ได้

ตรวจว่าเปิด PSRemoting แล้วที่ปลายทาง และพอร์ต 5985 เปิดอยู่ ทดสอบด้วย Test-WSMan -ComputerName WEB1 และ Test-NetConnection WEB1 -Port 5985

ภาษาไทยเป็นเครื่องหมายคำถาม

ตั้งการเข้ารหัสของหน้าต่างด้วย [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 และใส่ -Encoding UTF8 ตอนส่งออกไฟล์

สคริปต์รันด้วยมือได้แต่ตั้งเวลาแล้วไม่ทำงาน

Task ที่รันด้วย SYSTEM ไม่มีไดรฟ์ที่แมปไว้และไม่มีโปรไฟล์ผู้ใช้ ให้ใช้พาธเต็มแบบ UNC เช่น \\fs01\share แทนอักษรไดรฟ์ และใส่ -NoProfile ในพารามิเตอร์

ต้องการให้ทีมงานช่วยวางระบบอัตโนมัติสำหรับงานประจำ ติดต่อ THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security