前 12 天,我們的重點都放在 Windows Server 與 PowerShell 自動化。
到目前為止,我們已經碰過:
Object
Pipeline
CSV
Function
Error Handling
PowerShell Remoting
Server Health Check
Configuration
從 Day 13 開始,正式進入這個系列另外一個核心:
Active Directory
這一篇我不打算一開始就做:
批次建立帳號
批次停用帳號
刪除 Computer Object
大量修改 AD
因為只要開始碰 AD,操作風險就比一般系統查詢高很多。
所以第一步我認為應該先做到:
先學會安全地查資料。
今天主要會使用:
Get-ADUser
Get-ADComputer
Get-ADOrganizationalUnit
並把前面學過的:
Object
Pipeline
Where-Object
Select-Object
Export-Csv
正式套用到 Active Directory。
為什麼 AD 很適合用 PowerShell?
如果今天只找一個帳號:
tedliu
用:
Active Directory Users and Computers
搜尋一下其實很快。
但如果需求變成:
幫我找出所有停用帳號。
或:
幫我列出某個 OU 裡面所有 User。
甚至:
幫我整理 1000 個 User 的 Enabled、LastLogonDate、Department 與 OU。
這時候繼續用 GUI,就會開始很痛苦。
GUI 比較適合:
單一帳號
單一電腦
臨時確認
人工修改
PowerShell 比較適合:
大量查詢
條件篩選
資料整理
稽核
報表
批次處理
所以今天我們先從最安全的:
Read Only
開始。
開始前:確認 Active Directory Module
要使用:
Get-ADUser
目前執行 PowerShell 的 Windows 必須有:
Active Directory PowerShell Module
Windows Server 如果已經安裝 AD DS 管理工具,通常會有。
Windows 10 / 11 管理電腦則通常需要 RSAT。
先確認:
Get-Module -ListAvailable ActiveDirectory
如果有看到:
ModuleType Version Name
Manifest ... ActiveDirectory
代表模組存在。
接著:
Import-Module ActiveDirectory
如果沒有 Error,就可以開始。
直接確認 Cmdlet
也可以:
Get-Command Get-ADUser
如果正常,會看到類似:
CommandType Name Version Source
Cmdlet Get-ADUser ... ActiveDirectory
或者一次看看有哪些 AD 指令:
Get-Command -Module ActiveDirectory
會看到非常多:
Get-ADUser
Get-ADComputer
Get-ADGroup
Get-ADGroupMember
Get-ADOrganizationalUnit
Set-ADUser
Disable-ADAccount
Enable-ADAccount
...
今天先不要看到:
Set-ADUser
Remove-ADUser
Disable-ADAccount
就開始測。
Day 13 先專注:
Get
第一個 AD Query:Get-ADUser
先查一個指定帳號。
例如:
Get-ADUser -Identity tedliu
如果帳號存在,可能看到:
DistinguishedName : CN=Ted Liu,OU=IT,DC=contoso,DC=com
Enabled : True
GivenName : Ted
Name : Ted Liu
ObjectClass : user
SamAccountName : tedliu
SID : ...
Surname : Liu
UserPrincipalName : tedliu@contoso.com
這裡已經出現很多重要資料。
例如:
Enabled
SamAccountName
UserPrincipalName
DistinguishedName
Identity 可以放什麼?
Get-ADUser -Identity 常見可以用:
SamAccountName
DistinguishedName
GUID
SID
日常最常用的還是:
Get-ADUser -Identity tedliu
也就是直接用帳號名稱查。
不知道帳號完整名稱怎麼辦?
假設只知道姓名裡面有:
Ted
可以使用:
Get-ADUser -Filter 'Name -like "Ted"'
可能得到:
DistinguishedName
Enabled
GivenName
Name
ObjectClass
SamAccountName
SID
Surname
UserPrincipalName
如果只想看:
Name
SamAccountName
Enabled
就接:
Get-ADUser -Filter 'Name -like "Ted"' |
Select-Object Name, SamAccountName, Enabled
這就是前面學的 Pipeline。
-Filter * 是什麼?
如果輸入:
Get-ADUser -Filter *
意思就是:
查詢全部 User。
這個指令在 Lab 裡可能沒什麼問題。
但 Production Domain 如果有:
幾千
幾萬
甚至更多 User
就不要隨便把所有資料全部抓回來再慢慢看。
更好的方式是:
先縮小範圍。
例如:
Get-ADUser `
-Filter 'Enabled -eq $true'
或者指定 OU。
後面會做到。
為什麼 Get-ADUser 預設看不到 LastLogonDate?
這是剛接觸 AD PowerShell 很容易碰到的地方。
例如:
Get-ADUser -Identity tedliu
可能看不到:
LastLogonDate
Department
Description
PasswordLastSet
Created
不是 AD 沒有這些資訊。
而是 Get-ADUser 為了效率,不會預設把所有 Property 全部抓回來。
這跟 Day 3 講的概念完全一樣:
畫面上沒有顯示,不代表 Object 沒有更多資料。
使用 -Properties
如果我要看:
LastLogonDate
可以:
Get-ADUser -Identity tedliu
-Properties LastLogonDate
再:
Get-ADUser -Identity tedliu
-Properties LastLogonDate |
Select-Object `
Name,
SamAccountName,
Enabled,
LastLogonDate
結果:
Name SamAccountName Enabled LastLogonDate
Ted Liu tedliu True 2026/09/20 09:31
一次取得多個 Property
例如:
Get-ADUser -Identity tedliu
-Properties `
LastLogonDate,
Department,
Description,
PasswordLastSet
再:
Get-ADUser -Identity tedliu
-Properties LastLogonDate, Department, Description, PasswordLastSet | Select-Object
Name,
SamAccountName,
Enabled,
Department,
Description,
LastLogonDate,
PasswordLastSet
這就開始比較像帳號盤點。
可以直接 -Properties * 嗎?
可以。
例如:
Get-ADUser -Identity tedliu
-Properties *
會看到很多資料。
Lab 或 troubleshooting 時很好用。
但如果要查:
1000 個 User
我不建議:
Get-ADUser -Filter * -Properties *
因為很多 Property 根本用不到。
比較好的習慣是:
我要什麼
↓
就查什麼
例如:
-Properties LastLogonDate, Department
而不是所有東西都抓。
查詢所有 Enabled User
例如:
Get-ADUser `
-Filter 'Enabled -eq $true'
接著:
Get-ADUser -Filter 'Enabled -eq $true' | Select-Object
Name,
SamAccountName,
Enabled
結果可能:
Name SamAccountName Enabled
User A usera True
User B userb True
User C userc True
反過來找 Disabled:
Get-ADUser -Filter 'Enabled -eq $false' | Select-Object
Name,
SamAccountName,
Enabled
這已經是一個非常常用的 AD 稽核 Query。
查某個 Department
假設 AD 有維護:
Department
可以:
Get-ADUser -Filter 'Department -eq "IT"'
-Properties Department |
Select-Object `
Name,
SamAccountName,
Department
或者:
Get-ADUser -Filter 'Department -like "*IT*"'
-Properties Department
這裡開始可以看到:
AD PowerShell 本質上還是在查 Object。
DistinguishedName 是什麼?
前面一直看到:
CN=Ted Liu,OU=IT,OU=Taipei,DC=contoso,DC=com
這就是:
Distinguished Name
DN
可以簡單理解成:
這個 Object 在 Active Directory 裡的完整位置。
例如:
CN=Ted Liu
代表物件本身。
OU=IT
代表在 IT OU。
OU=Taipei
代表再上一層是 Taipei。
DC=contoso
DC=com
就是 Domain。
所以:
CN=Ted Liu,
OU=IT,
OU=Taipei,
DC=contoso,
DC=com
可以想像成:
contoso.com
└── Taipei
└── IT
└── Ted Liu
後面查 OU 時會很常看到 DN。
開始查 Computer:Get-ADComputer
除了 User,企業 IT 很常處理:
Computer Object
例如:
Get-ADComputer -Identity PC001
可能看到:
DistinguishedName
DNSHostName
Enabled
Name
ObjectClass
SamAccountName
SID
如果只想看:
Get-ADComputer -Identity PC001 | Select-Object
Name,
DNSHostName,
Enabled
查全部 Computer
可以:
Get-ADComputer -Filter *
再整理:
Get-ADComputer -Filter * | Select-Object
Name,
DNSHostName,
Enabled
例如:
Name DNSHostName Enabled
PC001 PC001.contoso.com True
PC002 PC002.contoso.com True
OLDPC01 OLDPC01.contoso.com False
這個結果就可以拿來做:
Computer Object Inventory
Computer 也可以查 LastLogonDate
例如:
Get-ADComputer -Filter *
-Properties LastLogonDate |
Select-Object `
Name,
Enabled,
LastLogonDate
可能:
Name Enabled LastLogonDate
PC001 True 2026/09/21 08:31
PC002 True 2026/09/20 17:42
OLDPC01 False 2025/10/03 11:22
看到這種資料,後面其實就可以開始做:
長期未登入 Computer Object 盤點。
但今天先不急。
後面的 Day 再正式做 Inactive。
查詢 OU:Get-ADOrganizationalUnit
接著看 OU。
Get-ADOrganizationalUnit -Filter *
可能看到:
City
Country
DistinguishedName
LinkedGroupPolicyObjects
Name
ObjectClass
ObjectGUID
PostalCode
State
StreetAddress
整理:
Get-ADOrganizationalUnit -Filter * | Select-Object
Name,
DistinguishedName
例如:
Name DistinguishedName
Taipei OU=Taipei,DC=contoso,DC=com
IT OU=IT,OU=Taipei,DC=contoso,DC=com
HR OU=HR,OU=Taipei,DC=contoso,DC=com
Sales OU=Sales,OU=Taipei,DC=contoso,DC=com
這對之後限定查詢範圍非常重要。
SearchBase:不要每次都查整個 Domain
假設我只想查:
Taipei
└── IT
這個 OU 裡面的 User。
先知道 DN:
OU=IT,OU=Taipei,DC=contoso,DC=com
就可以:
Get-ADUser -Filter *
-SearchBase "OU=IT,OU=Taipei,DC=contoso,DC=com"
這樣 PowerShell 不需要查整個 Domain。
而是限定:
只看 IT OU。
再:
Get-ADUser -Filter *
-SearchBase "OU=IT,OU=Taipei,DC=contoso,DC=com" |
Select-Object `
Name,
SamAccountName,
Enabled
這就是非常實用的 OU Query。
SearchScope 又是什麼?
假設架構:
Taipei
└── IT
├── User
├── Admin
└── ServiceAccount
我們指定:
-SearchBase "OU=IT,..."
但問題是:
要不要連下面的子 OU 一起查?
這時候可以使用:
-SearchScope
常見:
Base
OneLevel
Subtree
Base
只看 SearchBase 本身。
OneLevel
只查這一層。
Subtree
連下面所有子 OU 一起查。
例如:
Get-ADUser -Filter *
-SearchBase "OU=IT,OU=Taipei,DC=contoso,DC=com" `
-SearchScope Subtree
會包含:
IT
├── User
├── Admin
└── ServiceAccount
底下的 User。
查某個 OU 有多少 User
這時前面學過的:
Measure-Object
又可以拿出來。
例如:
Get-ADUser -Filter *
-SearchBase "OU=IT,OU=Taipei,DC=contoso,DC=com" |
Measure-Object
或者:
(
Get-ADUser -Filter *
-SearchBase "OU=IT,OU=Taipei,DC=contoso,DC=com"
).Count
假設結果:
184
代表這個查詢範圍裡找到 184 個 User。
用 Group-Object 統計 Department
Day 9 我們用過:
Group-Object
整理 Event Log。
AD 一樣可以。
例如:
Get-ADUser -Filter *
-Properties Department |
Group-Object Department |
Sort-Object Count -Descending
可能看到:
Count Name
120 Sales
85 IT
60 Finance
42 HR
是不是又回到前面的資料處理概念?
Data Source 變了
但:
Pipeline 思考方式沒變
查沒有填 Department 的 User
例如:
Get-ADUser -Filter *
-Properties Department |
Where-Object {
[string]::IsNullOrWhiteSpace(
$_.Department
)
} |
Select-Object `
Name,
SamAccountName,
Department
這類 Query 其實很適合做 AD Data Quality Check。
例如:
Department 空白
Mail 空白
EmployeeID 空白
Description 空白
後面都可以慢慢自動化。
把 AD User 做成 CSV
到了這裡當然也要接 Day 4。
例如產生:
AD_User_Report.csv
可以:
$ReportFolder = "C:\Temp"
if (-not (Test-Path $ReportFolder)) {
New-Item `
-Path $ReportFolder `
-ItemType Directory |
Out-Null
}
$Date = Get-Date -Format "yyyyMMdd"
$ReportFile =
"$ReportFolder\AD_User_Report_$Date.csv"
接著:
Get-ADUser -Filter *
-Properties Department, LastLogonDate, PasswordLastSet | Select-Object
Name,
SamAccountName,
Enabled,
Department,
LastLogonDate,
PasswordLastSet,
DistinguishedName |
Export-Csv -Path $ReportFile
-NoTypeInformation `
-Encoding UTF8
就完成第一份:
AD User Inventory Report
Computer 也產生一份
例如:
$ComputerReport =
"$ReportFolder\AD_Computer_Report_$Date.csv"
然後:
Get-ADComputer -Filter *
-Properties LastLogonDate, OperatingSystem, OperatingSystemVersion | Select-Object
Name,
DNSHostName,
Enabled,
OperatingSystem,
OperatingSystemVersion,
LastLogonDate,
DistinguishedName |
Export-Csv -Path $ComputerReport
-NoTypeInformation `
-Encoding UTF8
最後:
C:\Temp
│
├── AD_User_Report_20260921.csv
└── AD_Computer_Report_20260921.csv
不要把「查不到」直接當作帳號不存在
例如:
Get-ADUser -Identity user01
出錯。
第一個想法可能是:
user01 不存在。
但還有其他可能性:
目前連不到 DC
DNS 有問題
AD Web Services 有問題
權限問題
Identity 打錯
查錯 Domain
所以正式 Script 還是要用前面學過的:
try
catch
例如:
try {
$User = Get-ADUser `
-Identity "user01" `
-ErrorAction Stop
Write-Host "User Found"
}
catch {
Write-Host "Query failed:"
Write-Host $_.Exception.Message
}
不要看到 Error 就直接下結論。
指定 Domain Controller
大部分情況:
Get-ADUser
會自動找適合的 DC。
但 troubleshooting 或特定需求時,可以指定:
-Server
例如:
Get-ADUser -Identity tedliu
-Server DC01.contoso.com
這對排查 AD 複寫問題很有用。
例如:
DC01 查得到
DC02 查不到
那事情可能就不只是:
帳號不存在。
而是可以往:
Replication
DNS
DC 狀態
去查。
User 的 LastLogonDate 要注意
今天我們開始碰:
LastLogonDate
但這邊先埋一個很重要的提醒。
如果未來要做:
「超過 180 天沒登入就停用帳號」
不能只看到一個登入時間欄位,就立刻拿來做破壞性操作。
AD 裡跟登入時間相關的屬性不只一種,例如:
lastLogon
lastLogonTimestamp
LastLogonDate
它們用途、更新方式與精準度並不完全相同。
所以今天:
LastLogonDate
只先拿來:
查詢與盤點。
Day 14 / Day 15 做 Inactive Account 時,再把這件事情拆清楚。
Read Only 是 AD 自動化很重要的第一步
我很建議在任何 AD Automation 裡面,都先做一個:
Report Only
模式。
例如今天想找:
180 天沒登入的帳號
第一版應該:
Query
↓
產生 CSV
↓
人工 Review
而不是:
Query
↓
Disable-ADAccount
尤其 AD 裡可能存在:
Service Account
Shared Account
System Account
Application Account
Break Glass Account
單純符合某個條件,不代表可以直接停用。
所以一開始:
Query first, Action later.
這個原則後面會一直沿用。
今天做一支簡單 AD Inventory Script
把今天主要內容整理起來:
Import-Module ActiveDirectory
$ReportFolder = "C:\Temp\ADReports"
$Date = Get-Date -Format "yyyyMMdd"
if (-not (Test-Path $ReportFolder)) {
New-Item `
-Path $ReportFolder `
-ItemType Directory |
Out-Null
}
$Users = Get-ADUser -Filter *
-Properties `
Department,
Description,
LastLogonDate,
PasswordLastSet
$UserReport = $Users |
Select-Object `
Name,
SamAccountName,
Enabled,
Department,
Description,
LastLogonDate,
PasswordLastSet,
DistinguishedName
$UserReport |
Export-Csv -Path "$ReportFolder\AD_Users_$Date.csv"
-NoTypeInformation `
-Encoding UTF8
$Computers = Get-ADComputer -Filter *
-Properties `
LastLogonDate,
OperatingSystem,
OperatingSystemVersion
$ComputerReport = $Computers |
Select-Object `
Name,
DNSHostName,
Enabled,
OperatingSystem,
OperatingSystemVersion,
LastLogonDate,
DistinguishedName
$ComputerReport |
Export-Csv -Path "$ReportFolder\AD_Computers_$Date.csv"
-NoTypeInformation `
-Encoding UTF8
$OUReport =
Get-ADOrganizationalUnit -Filter * | Select-Object
Name,
DistinguishedName
$OUReport |
Export-Csv -Path "$ReportFolder\AD_OUs_$Date.csv"
-NoTypeInformation `
-Encoding UTF8
Write-Host ""
Write-Host "AD Inventory Completed"
Write-Host ""
Write-Host "User Report:"
Write-Host "$ReportFolder\AD_Users_$Date.csv"
Write-Host ""
Write-Host "Computer Report:"
Write-Host "$ReportFolder\AD_Computers_$Date.csv"
Write-Host ""
Write-Host "OU Report:"
Write-Host "$ReportFolder\AD_OUs_$Date.csv"
執行後:
C:\Temp\ADReports
│
├── AD_Users_20260921.csv
├── AD_Computers_20260921.csv
└── AD_OUs_20260921.csv
我們就完成了第一套:
Active Directory Inventory
今天其實把前 12 天全部帶進 AD 了
你會發現今天看起來開始學 AD。
但 PowerShell 的操作方式幾乎沒有重新開始。
以前:
Get-Service
現在:
Get-ADUser
以前:
Get-CimInstance
現在:
Get-ADComputer
後面還是一樣:
Get
↓
Object
↓
Filter
↓
Select
↓
Sort
↓
Group
↓
Export
這就是前面一直花時間理解 Object 與 Pipeline 的原因。
資料來源不同,但操作思維可以重複使用。
Day 13 小結
今天正式進入 Active Directory。
主要認識:
Import-Module ActiveDirectory
Get-ADUser
Get-ADComputer
Get-ADOrganizationalUnit
以及兩個很重要的查詢參數:
-Properties
-SearchBase
現在已經可以回答一些很實際的問題:
這個 User 存不存在?
帳號 Enabled 嗎?
最後登入時間?
在哪個 OU?
這台 Computer Object 存不存在?
Computer Enabled 嗎?
最後登入時間?
Domain 裡有哪些 OU?
某個 OU 裡有多少 User?
而且可以直接:
AD
↓
PowerShell
↓
Object
↓
CSV
產生盤點資料。
但今天最重要的不是:
Get-ADUser 怎麼背。
而是開始建立 AD Automation 的第一個安全原則:
先查詢、先產報表、先確認,再做修改。
Day 14 預告
Day 14|AD 帳號盤點:找出長期未登入與從未登入的 User
今天我們已經取得:
Name
SamAccountName
Enabled
LastLogonDate
DistinguishedName
所以下一步就可以開始做真正很常見的 AD 稽核需求:
哪些帳號已經很久沒有登入?
Day 14 會處理:
最後登入早於指定日期
從來沒有登入過
Enabled 仍然是 True
排除特定 OU
排除 Service Account
產生 Review CSV
並開始釐清:
lastLogon
lastLogonTimestamp
LastLogonDate
到底有什麼差別。
最後做出第一份:
Inactive AD Account Review Report
而且我們會維持一個原則:
找出可疑帳號
↓
產生清單
↓
人工確認
不是
找到
↓
直接停用
Day 14 開始,AD PowerShell 就會真正進入企業 IT 帳號稽核實戰。