iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
IT Operation

系統工程師的 30 天自動化維運實戰:PowerShell × AD × Windows Server系列 第 13

Day 13|正式進入 Active Directory:用 PowerShell 查詢 User、Computer 與 OU

  • 分享至 

  • xImage
  •  

前 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

把今天主要內容整理起來:

=========================================

Active Directory Inventory

Day 13

=========================================

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

}

=========================================

User Report

=========================================

$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

=========================================

Computer Report

=========================================

$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

=========================================

OU Report

=========================================

$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 帳號稽核實戰。


上一篇
Day 12|Server 清單不要寫死:用 CSV 管理不同角色與巡檢設定
下一篇
Day 14|AD 帳號盤點:找出長期未登入與從未登入的 User
系列文
系統工程師的 30 天自動化維運實戰:PowerShell × AD × Windows Server14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言