Windows AD PowerShell(三):计算机

6630 字
33 分钟
Windows AD PowerShell(三):计算机

这是 Windows AD PowerShell 学习记录的第三节,主要整理计算机对象管理、网络与 DNS 检查、安全通道、加域及自动化脚本。

三、计算机#

3.1 查询计算机的基本信息#

查询域中的所有计算机对象:

Terminal window
# 查询所有计算机
Get-ADComputer -Filter *
DistinguishedName : CN=QYNET-CORE01,OU=Domain Controllers,DC=qy,DC=net
DNSHostName : QYNET-CORE01.qy.net
Enabled : True
Name : QYNET-CORE01
ObjectClass : computer
SamAccountName : QYNET-CORE01$
DistinguishedName : CN=QYNET-EXCHANGE,OU=Computer,OU=Operation,OU=Organization,DC=qy,DC=net
DNSHostName : QYNET-EXCHANGE.qy.net
Enabled : True
Name : QYNET-EXCHANGE
ObjectClass : computer
SamAccountName : QYNET-EXCHANGE$
# 当前实验域共查询到 6 个计算机对象,其余对象为:
# QYNET-CORE02、QYNET-WEBUI、QYNET-PROXY、QYNET-USER01

计算机的 SamAccountName 通常以 $ 结尾,这是计算机账户与普通用户账户之间一个很直观的区别。

只显示日常排查中常用的字段:

Terminal window
Get-ADComputer -Filter * `
-Properties OperatingSystem,OperatingSystemVersion,LastLogonDate |
Select-Object Name,Enabled,DNSHostName,OperatingSystem,OperatingSystemVersion,LastLogonDate
Name Enabled DNSHostName OperatingSystem OperatingSystemVersion LastLogonDate
---- ------- ----------- --------------- ---------------------- -------------
QYNET-WEBUI True QYNET-WEBUI.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/10 15:57:35
QYNET-EXCHANGE True QYNET-EXCHANGE.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/9 09:23:55
QYNET-PROXY True QYNET-PROXY.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/7 14:05:45
QYNET-USER01 True QYNET-USER01.qy.net Windows 10 企业版 2016 长期服务版 10.0 (14393) 2026/9/18 13:42:29
QYNET-CORE02 True QYNET-CORE02.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/17 11:18:25
QYNET-CORE01 True QYNET-CORE01.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/14 13:34:01

IPv4Address 是根据 DNS 解析得到的便利属性,不是判断设备在线状态的可靠依据。DNS 中存在旧记录时,它仍可能返回地址。

3.2 查询单台计算机的详细属性#

Terminal window
# 查询某台计算机的全部属性
Get-ADComputer -Identity QYNET-CORE01 -Properties *
CanonicalName : qy.net/Domain Controllers/QYNET-CORE01
CN : QYNET-CORE01
Created : 2026/9/4 12:42:17
Description :
DistinguishedName : CN=QYNET-CORE01,OU=Domain Controllers,DC=qy,DC=net
DNSHostName : QYNET-CORE01.qy.net
Enabled : True
LastLogonDate : 2026/9/14 13:34:01
Name : QYNET-CORE01
ObjectClass : computer
OperatingSystem : Windows Server 2016 Datacenter Evaluation
OperatingSystemVersion : 10.0 (14393)
PasswordLastSet : 2026/9/4 12:42:33
SamAccountName : QYNET-CORE01$

-Properties * 会返回大量属性。实际排查时最好只获取需要的属性,减少输出噪声:

Terminal window
Get-ADComputer -Identity QYNET-CORE01 `
-Properties Description,OperatingSystem,OperatingSystemVersion,LastLogonDate,PasswordLastSet |
Select-Object Name,Enabled,Description,OperatingSystem,OperatingSystemVersion,LastLogonDate,PasswordLastSet
Name : QYNET-CORE01
Enabled : True
Description :
OperatingSystem : Windows Server 2016 Datacenter Evaluation
OperatingSystemVersion : 10.0 (14393)
LastLogonDate : 2026/9/14 13:34:01
PasswordLastSet : 2026/9/4 12:42:33

常见计算机属性如下:

属性含义
Name计算机对象名称
SamAccountName计算机账户登录名,通常以 $ 结尾
DNSHostName计算机的完整 DNS 名称
EnabledAD 中的计算机账户是否启用
OperatingSystem设备上报的操作系统名称
OperatingSystemVersion设备上报的操作系统版本
LastLogonDate由 lastLogonTimestamp 转换而来的易读时间
PasswordLastSet计算机账户密码上次修改时间
Description管理员填写的设备说明
ManagedBy负责管理该计算机对象的用户或组
DistinguishedName计算机对象在 AD 中的完整路径

3.3 查询长期未登录的计算机#

根据 LastLogonTimeStamp 粗略筛选 90 天未登录的计算机:

Terminal window
$days = (Get-Date).AddDays(-90)
Get-ADComputer -Filter * -Properties LastLogonTimeStamp |
Where-Object {
-not $_.LastLogonTimeStamp -or
$_.LastLogonTimeStamp -lt $days.ToFileTime()
} |
Select-Object Name,
@{Name="LastLogon";Expression={
if ($_.LastLogonTimeStamp) {
[datetime]::FromFileTime($_.LastLogonTimeStamp)
}
}}
# 当前 qy.net 中没有超过 90 天未登录的计算机,因此无输出

空白的 LastLogon 通常表示该属性从未写入,可能是预创建但未入域的对象,也可能是从未成功登录域的设备,不能直接断定设备已经报废。

也可以使用专门的账户状态查询命令:

Terminal window
Search-ADAccount `
-AccountInactive `
-TimeSpan 90.00:00:00 `
-ComputersOnly |
Select-Object Name,Enabled,LastLogonDate,DistinguishedName
# 当前 qy.net 中没有符合条件的计算机账户,因此无输出

⚠️ LastLogonTimeStamp 的坑:该属性不是每次登录都立即复制,默认设计下可能相差约 9~14 天。它适合筛选 90 天、180 天这类长期不活跃设备,不能用于判断“这台计算机今天是否登录过”。

如果必须取得较准确的最后登录时间,需要向每台 DC 查询不复制的 LastLogon,然后取最大值:

Terminal window
$computerName = "QYNET-WEBUI"
$lastLogons = Get-ADDomainController -Filter * |
ForEach-Object {
$computer = Get-ADComputer `
-Identity $computerName `
-Server $_.HostName `
-Properties LastLogon
[pscustomobject]@{
DomainController = $_.HostName
LastLogon = if ($computer.LastLogon -gt 0) {
[datetime]::FromFileTime($computer.LastLogon)
}
}
}
$lastLogons | Sort-Object LastLogon -Descending
DomainController LastLogon
---------------- -------------
QYNET-CORE01.qy.net 2026/9/10 15:57:35
QYNET-CORE02.qy.net 2026/9/10 15:57:35
$lastLogons |
Sort-Object LastLogon -Descending |
Select-Object -First 1
DomainController LastLogon
---------------- -------------
QYNET-CORE01.qy.net 2026/9/10 15:57:35

这种方式查询量较大,只适合针对少量关键设备精确排查,不建议频繁扫描全域所有计算机。

3.4 统计操作系统分布#

Terminal window
Get-ADComputer -Filter * -Properties OperatingSystem |
Group-Object OperatingSystem |
Sort-Object Count -Descending
Count Name Group
----- ---- -----
5 Windows Server 2016 Datacenter Evaluation {CN=QYNET-WEBUI,...}
1 Windows 10 企业版 2016 长期服务版 {CN=QYNET-USER01,...}

如果只需要适合报表的名称和数量,可以进一步整理:

Terminal window
Get-ADComputer -Filter * -Properties OperatingSystem |
Group-Object {
if ($_.OperatingSystem) { $_.OperatingSystem } else { "<未填写>" }
} |
Sort-Object Count -Descending |
Select-Object Count,Name
Count Name
----- ----
5 Windows Server 2016 Datacenter Evaluation
1 Windows 10 企业版 2016 长期服务版

查询仍在使用旧系统的设备:

Terminal window
Get-ADComputer `
-Filter 'OperatingSystem -eq "Windows Server 2016 Datacenter Evaluation"' `
-Properties OperatingSystem,OperatingSystemVersion,LastLogonDate |
Select-Object Name,OperatingSystem,OperatingSystemVersion,LastLogonDate
Name OperatingSystem OperatingSystemVersion LastLogonDate
---- --------------- ---------------------- -------------
QYNET-WEBUI Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/10 15:57:35
QYNET-EXCHANGE Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/9 09:23:55
QYNET-PROXY Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/7 14:05:45
QYNET-CORE02 Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/17 11:18:25
QYNET-CORE01 Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/14 13:34:01

OperatingSystem 与 OperatingSystemVersion 依赖设备写入 AD 的信息,不能替代资产管理或补丁管理系统,但很适合快速粗筛。

3.5 修改、启用与禁用计算机账户#

以下使用专门的实验对象 PC-LAB-01 演示写操作,避免直接修改域控制器或业务服务器。修改描述、位置和管理人:

Terminal window
Set-ADComputer `
-Identity PC-LAB-01 `
-Description "实验室测试计算机" `
-Location "Shanghai-Lab-A" `
-ManagedBy "CN=浅月,OU=User,OU=Admin,OU=Organization,DC=qy,DC=net"
# 执行成功时默认无输出,重新查询验证
Get-ADComputer -Identity PC-LAB-01 `
-Properties Description,Location,ManagedBy |
Select-Object Name,Description,Location,ManagedBy
Name : PC-LAB-01
Description : 实验室测试计算机
Location : Shanghai-Lab-A
ManagedBy : CN=浅月,OU=User,OU=Admin,OU=Organization,DC=qy,DC=net

禁用计算机账户前先预演:

Terminal window
Disable-ADAccount -Identity PC-LAB-01 -WhatIf
What if: Performing the operation "Set" on target "CN=PC-LAB-01,OU=Lab,OU=Computer,OU=Organization,DC=qy,DC=net".

确认后执行并验证:

Terminal window
Disable-ADAccount -Identity PC-LAB-01
# 执行成功时默认无输出
Get-ADComputer -Identity PC-LAB-01 |
Select-Object Name,Enabled
Name Enabled
---- -------
PC-LAB-01 False

重新启用计算机账户:

Terminal window
Enable-ADAccount -Identity PC-LAB-01
# 执行成功时默认无输出
Get-ADComputer -Identity PC-LAB-01 |
Select-Object Name,Enabled
Name Enabled
---- -------
PC-LAB-01 True

禁用 AD 计算机账户不会远程关闭设备,但可能导致设备后续无法正常使用域身份访问资源,因此仍应先确认业务影响。

3.6 创建与移动计算机对象#

预先创建计算机对象:

Terminal window
New-ADComputer `
-Name "PC-LAB-01" `
-SamAccountName "PC-LAB-01$" `
-DNSHostName "PC-LAB-01.qy.net" `
-Path "OU=Computer,OU=Organization,DC=qy,DC=net" `
-Description "实验室测试计算机" `
-Enabled $true `
-PassThru
DistinguishedName : CN=PC-LAB-01,OU=Computer,OU=Organization,DC=qy,DC=net
DNSHostName : PC-LAB-01.qy.net
Enabled : True
Name : PC-LAB-01
ObjectClass : computer
SamAccountName : PC-LAB-01$

移动计算机前先使用 -WhatIf:

Terminal window
Get-ADComputer -Identity PC-LAB-01 |
Move-ADObject `
-TargetPath "OU=Lab,OU=Computer,OU=Organization,DC=qy,DC=net" `
-WhatIf
What if: Performing the operation "Move" on target "CN=PC-LAB-01,OU=Computer,OU=Organization,DC=qy,DC=net".

确认目标 OU 正确后移动并验证:

Terminal window
Get-ADComputer -Identity PC-LAB-01 |
Move-ADObject `
-TargetPath "OU=Lab,OU=Computer,OU=Organization,DC=qy,DC=net"
# 执行成功时默认无输出
Get-ADComputer -Identity PC-LAB-01 |
Select-Object Name,DistinguishedName
Name DistinguishedName
---- -----------------
PC-LAB-01 CN=PC-LAB-01,OU=Lab,OU=Computer,OU=Organization,DC=qy,DC=net

3.7 排查计算机是否在线以及 DNS 是否正常#

当 AD 中存在计算机对象,但远程管理或访问失败时,应先区分“AD 对象存在”和“设备当前在线”这两件事。

Terminal window
# 检查 ICMP 连通性
Test-Connection -ComputerName QYNET-CORE01 -Count 2
Destination Address Latency BufferSize Status
----------- ------- ------- ---------- ------
QYNET-CORE01 10.0.0.10 1 32 Success
QYNET-CORE01 10.0.0.10 1 32 Success

设备可能禁用了 ICMP,因此 Ping 失败不一定表示离线。可以继续检查 DNS:

Terminal window
Resolve-DnsName QYNET-CORE01.qy.net
Name Type TTL Section IPAddress
---- ---- --- ------- ---------
QYNET-CORE01.qy.net A 1200 Answer 10.0.0.10

检查常见远程管理端口:

Terminal window
# SMB
Test-NetConnection -ComputerName QYNET-CORE01 -Port 445
ComputerName : QYNET-CORE01
RemoteAddress : 10.0.0.10
RemotePort : 445
InterfaceAlias : Ethernet
TcpTestSucceeded : True
# WinRM HTTP
Test-NetConnection -ComputerName QYNET-CORE01 -Port 5985
ComputerName : QYNET-CORE01
RemoteAddress : 10.0.0.10
RemotePort : 5985
InterfaceAlias : Ethernet
TcpTestSucceeded : True

常见判断如下:

现象可能原因
AD 中能查到,DNS 查不到DNS 记录缺失、过期或设备从未成功注册
DNS 能解析,Ping 不通防火墙阻止 ICMP、路由问题或设备离线
Ping 正常,445 不通SMB 服务未启用或防火墙阻止
5985 不通WinRM 未配置、服务未运行或防火墙阻止
解析到错误 IPDNS 陈旧记录或重复注册

3.8 排查“此工作站和主域间的信任关系失败”#

该错误通常表示成员计算机本地保存的计算机账户密码,与 AD 中的计算机账户密码不一致。应在发生问题的成员计算机上,以本地管理员身份打开提升权限的 PowerShell。

首先测试安全通道:

Terminal window
Test-ComputerSecureChannel -Verbose
VERBOSE: The secure channel between the local computer and the domain qy.net is broken.
False

如果返回 False,尝试直接修复:

Terminal window
$credential = Get-Credential "QY\Administrator"
Test-ComputerSecureChannel `
-Repair `
-Server "QYNET-CORE01.qy.net" `
-Credential $credential `
-Verbose
PowerShell credential request
Enter your credentials.
Password for user QY\Administrator: ********
VERBOSE: The secure channel between the local computer and the domain qy.net has been repaired.
True

修复后再次确认:

Terminal window
Test-ComputerSecureChannel -Server "QYNET-CORE01.qy.net"
True

如果仍然失败,可以重置本机的计算机账户密码:

Terminal window
$credential = Get-Credential "QY\Administrator"
Reset-ComputerMachinePassword `
-Server "QYNET-CORE01.qy.net" `
-Credential $credential `
-Verbose
VERBOSE: Performing the operation "Reset-ComputerMachinePassword" on target "PC-LAB-01".
True
Restart-Computer -Force

Test-ComputerSecureChannel 主要用于域成员计算机。在域控制器上使用它可能产生误导性结果;域控制器的安全通道问题应结合 netdom、nltest、复制状态和事件日志进行排查。

3.9 计算机入域、退域与重新入域#

在待入域计算机上,以管理员身份打开 PowerShell:

Terminal window
$credential = Get-Credential "QY\Administrator"
Add-Computer `
-DomainName "qy.net" `
-OUPath "OU=Computer,OU=Organization,DC=qy,DC=net" `
-Credential $credential `
-PassThru `
-Verbose
HasSucceeded ComputerName
------------ ------------
True PC-LAB-01
# 入域成功后重启
Restart-Computer

如果入域时报“找不到域”或“指定的域不存在”,先检查客户端是否使用 AD DNS:

Terminal window
Get-DnsClientServerAddress -AddressFamily IPv4 |
Where-Object ServerAddresses |
Select-Object InterfaceAlias,ServerAddresses
InterfaceAlias ServerAddresses
-------------- ---------------
Ethernet {10.0.0.10}
Resolve-DnsName -Type SRV "_ldap._tcp.dc._msdcs.qy.net"
Name Type TTL Section NameTarget Port Priority Weight
---- ---- --- ------- ---------- ---- -------- ------
_ldap._tcp.dc._msdcs.qy.net SRV 600 Answer QYNET-CORE01.qy.net 389 0 100

让当前计算机退出域时,使用的是 Remove-Computer:

Terminal window
$credential = Get-Credential "QY\Administrator"
Remove-Computer `
-UnjoinDomainCredential $credential `
-WorkgroupName "WORKGROUP" `
-PassThru `
-Verbose `
-Restart
HasSucceeded ComputerName
------------ ------------
True PC-LAB-01

Remove-ADComputer 与 Remove-Computer 不是一回事:

命令执行位置作用
Remove-ADComputer管理端或 DC删除 AD 中的计算机对象,不会让在线设备自动退出域
Remove-Computer待处理的本机或远程目标让 Windows 设备退出域并加入工作组

域信任修复失败时,重新入域应作为靠后的方案,因为它需要重启,也可能影响证书、服务账户、计划任务和依赖原计算机身份的权限。

3.10 安全清理长期离线的计算机对象#

不建议把“90 天未登录”的结果直接传给 Remove-ADComputer。更稳妥的流程是:生成报告、人工确认、禁用、移动到隔离 OU、观察一段时间,最后再删除。

第一步,生成候选列表:

Terminal window
$staleComputers = Search-ADAccount `
-AccountInactive `
-TimeSpan 90.00:00:00 `
-ComputersOnly |
Where-Object Enabled -eq $true
$staleComputers |
Select-Object Name,Enabled,LastLogonDate,DistinguishedName
Name Enabled LastLogonDate DistinguishedName
---- ------- ------------- -----------------
PC-OLD-01 True 2026/4/11 08:42:30 CN=PC-OLD-01,OU=Computer,OU=Organization,DC=qy,DC=net
PC-STOCK-02 True CN=PC-STOCK-02,OU=Computer,OU=Organization,DC=qy,DC=net

第二步,导出报告供资产负责人确认:

Terminal window
$staleComputers |
Select-Object Name,Enabled,LastLogonDate,DistinguishedName |
Export-Csv `
-Path "C:\Temp\Stale-Computers.csv" `
-NoTypeInformation `
-Encoding UTF8
# Export-Csv 成功时默认无输出
Get-Item "C:\Temp\Stale-Computers.csv" |
Select-Object Name,Length,LastWriteTime
Name Length LastWriteTime
---- ------ -------------
Stale-Computers.csv 1248 2026/9/20 16:12:08

第三步,先预演禁用操作:

Terminal window
$staleComputers | Disable-ADAccount -WhatIf
What if: Performing the operation "Set" on target "CN=PC-OLD-01,OU=Computer,OU=Organization,DC=qy,DC=net".
What if: Performing the operation "Set" on target "CN=PC-STOCK-02,OU=Computer,OU=Organization,DC=qy,DC=net".

人工确认后禁用,并移动到隔离 OU:

Terminal window
$quarantineOU = "OU=Disabled Computers,OU=Organization,DC=qy,DC=net"
$staleComputers | ForEach-Object {
Disable-ADAccount -Identity $_
Move-ADObject -Identity $_.DistinguishedName -TargetPath $quarantineOU
}
# 成功时默认无输出,重新查询验证
Get-ADComputer `
-SearchBase $quarantineOU `
-Filter * |
Select-Object Name,Enabled,DistinguishedName
Name Enabled DistinguishedName
---- ------- -----------------
PC-OLD-01 False CN=PC-OLD-01,OU=Disabled Computers,OU=Organization,DC=qy,DC=net
PC-STOCK-02 False CN=PC-STOCK-02,OU=Disabled Computers,OU=Organization,DC=qy,DC=net

观察期结束并完成审批后,再预演删除:

Terminal window
Get-ADComputer `
-SearchBase $quarantineOU `
-Filter 'Enabled -eq $false' |
Remove-ADComputer -WhatIf
What if: Performing the operation "Remove" on target "CN=PC-OLD-01,OU=Disabled Computers,OU=Organization,DC=qy,DC=net".
What if: Performing the operation "Remove" on target "CN=PC-STOCK-02,OU=Disabled Computers,OU=Organization,DC=qy,DC=net".

即使设备长期未登录,也应额外核对以下情况:

  1. 设备是否属于库存机、冷备机或灾备设备。
  2. 是否是只在特定时间启动的业务服务器。
  3. 是否存在网络隔离,导致登录时间长期没有更新。
  4. 设备是否已经改名,而旧对象仍留在 AD 中。
  5. 资产、监控、DNS、DHCP、证书和配置管理系统中是否仍有记录。

3.11 常见计算机故障与 PowerShell 排查顺序#

故障现象优先检查
AD 中找不到计算机Get-ADComputer、查询所在 OU、确认名称是否改变
AD 中存在但无法连接Resolve-DnsName、Test-Connection、Test-NetConnection
入域时提示找不到域客户端 DNS、域控 SRV 记录、时间同步、网络端口
登录时提示域信任失败Test-ComputerSecureChannel、计算机账户密码、安全通道
远程 PowerShell 无法连接5985/5986 端口、WinRM 服务、防火墙和权限
加入正确 OU 后策略仍未应用gpupdate /force、gpresult、OU 路径和 GPO 链接
同名设备无法重新入域检查旧计算机对象,确认后重置或删除旧对象

以“设备能入域,但没有应用预期 GPO”为例:

Terminal window
# 在管理端确认计算机所在 OU
Get-ADComputer -Identity PC-LAB-01 |
Select-Object Name,DistinguishedName
Name DistinguishedName
---- -----------------
PC-LAB-01 CN=PC-LAB-01,OU=Lab,OU=Computer,OU=Organization,DC=qy,DC=net
# 在目标计算机上刷新组策略
gpupdate /force
Updating policy...
Computer Policy update has completed successfully.
User Policy update has completed successfully.
# 查看计算机实际应用的策略
gpresult /Scope Computer /R
COMPUTER SETTINGS
-----------------
CN=PC-LAB-01,OU=Lab,OU=Computer,OU=Organization,DC=qy,DC=net
Applied Group Policy Objects
-----------------------------
Default Domain Policy
Lab Computer Policy

计算机故障排查时,建议始终区分四个层面:AD 中的计算机对象、DNS 与网络连通性、设备本地状态,以及域控制器之间的复制状态。对象存在不代表设备在线,设备在线也不代表安全通道和组策略一定正常。 +

3.12 使用 BAT 向导配置网络并加入域#

如果需要重复部署实验环境中的成员计算机,可以把“修改计算机名、设置静态 IP、配置 DNS、加入指定 OU 和重启”组合成一个交互式 BAT 向导。

⚠️ 请在待加域计算机本地,以管理员身份运行。 脚本会删除所选网卡现有的 IPv4 地址和默认路由,远程执行可能立即中断连接。正式使用前还应核对 VLAN、网关、DNS、IP 地址占用情况,以及目标 OU 是否正确。

这个示例具有以下保护措施:

  1. 计算机名、网卡、IP、网关、DNS 和 OU 选择都要输入两次。
  2. 修改前集中显示全部配置,只有输入大写 JOIN 才继续。
  3. 域名固定为 qy.net,目标 OU 只能从实验环境中已有的两个 OU 选择。
  4. 域密码通过 Get-Credential 输入,不会以明文写入脚本。
  5. 修改前把原网络配置备份到 %TEMP%,便于失败后人工恢复。
  6. 加域成功后等待 15 秒再重启,可使用 shutdown /a 取消倒计时。

将下面内容保存为 Join-QyDomain.bat,然后右键选择“以管理员身份运行”:

Terminal window
@echo off
rem ================================================================
rem qy.net 域加入教学脚本
rem
rem BAT 负责:检查管理员权限、启动内嵌 PowerShell、接收退出代码。
rem PowerShell 负责:参数输入与校验、网络配置、改名、加域和重启。
rem
rem 为什么采用这种组合?
rem 传统 BAT 不擅长处理网卡对象、IP 校验和安全凭据;PowerShell
rem 对这些 Windows 管理任务有原生命令,而 BAT 便于直接双击启动。
rem ================================================================
rem EnableExtensions 启用现代 CMD 扩展语法;setlocal 防止变量污染父 CMD。
setlocal EnableExtensions
rem 切换到 UTF-8 代码页,尽量避免中文提示乱码。
chcp 65001 >nul
title qy.net 域加入向导
rem fltmc 需要管理员权限。这里只利用其退出代码判断是否为提升会话,
rem 不会修改筛选器或系统配置。
fltmc >nul 2>&1
if errorlevel 1 (
echo [错误] 请右键此脚本,选择“以管理员身份运行”。
echo.
pause
exit /b 1
)
rem %~f0 表示当前 BAT 自身的绝对路径。
rem PowerShell 稍后会重新读取这个文件,并执行标记行之后的脚本内容。
set "JOIN_SCRIPT_PATH=%~f0"
rem 强制调用 Windows PowerShell 5.1,因为 Add-Computer 与传统 AD/RSAT
rem 环境的兼容性最好。-NoProfile 可避免个人 Profile 影响脚本行为。
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command ^
"$lines = Get-Content -LiteralPath $env:JOIN_SCRIPT_PATH;" ^
"$marker = [Array]::IndexOf($lines, '# POWERSHELL-BEGIN');" ^
"if ($marker -lt 0) { throw '找不到内嵌 PowerShell 脚本。' };" ^
"$code = $lines[($marker + 1)..($lines.Length - 1)] -join [Environment]::NewLine;" ^
"& ([ScriptBlock]::Create($code))"
rem 保存 PowerShell 返回的退出代码:0 表示正常完成,非 0 表示失败。
set "JOIN_EXIT_CODE=%ERRORLEVEL%"
echo.
if not "%JOIN_EXIT_CODE%"=="0" (
echo [失败] 加域流程没有完成,退出代码:%JOIN_EXIT_CODE%
) else (
echo [完成] 加域命令已提交。
)
echo.
pause
exit /b %JOIN_EXIT_CODE%
# POWERSHELL-BEGIN
<#
从这一行开始是内嵌 PowerShell。
CMD 在前面已经 exit /b,因此不会把以下内容当作 BAT 命令执行。
#>
# 让未处理的 PowerShell 错误直接进入 catch,防止失败后继续执行。
$ErrorActionPreference = 'Stop'
# 固定环境参数:域名、两个真实存在的目标 OU、默认加域账户。
# 密码不会写入文件,稍后由 Get-Credential 安全读取。
$domainName = 'qy.net'
$adminComputerOU = 'OU=Computer,OU=Admin,OU=Organization,DC=qy,DC=net'
$operationComputerOU = 'OU=Computer,OU=Operation,OU=Organization,DC=qy,DC=net'
$defaultDomainUser = 'QY\Administrator'
<#
.SYNOPSIS
要求用户把同一个值输入两次。
.DESCRIPTION
只有两次内容完全相同才返回结果,用来降低计算机名、IP、DNS 等
关键配置因输入错误而造成的风险。
.PARAMETER Label
显示给用户看的字段名称。
.PARAMETER AllowEmpty
允许两次都直接回车。当前仅用于可选的备用 DNS。
#>
function Read-ConfirmedValue {
param(
[Parameter(Mandatory)]
[string]$Label,
[switch]$AllowEmpty
)
while ($true) {
$first = (Read-Host "请输入$Label").Trim()
if (-not $AllowEmpty -and [string]::IsNullOrWhiteSpace($first)) {
Write-Host "[提示] $Label 不能为空。" -ForegroundColor Yellow
continue
}
$second = (Read-Host "请再次输入$Label进行确认").Trim()
if ($first -ceq $second) {
return $first
}
Write-Host "[提示] 两次输入不一致,请重新输入。" -ForegroundColor Yellow
}
}
<#
.SYNOPSIS
检查文本是不是有效的 IPv4 地址。
.DESCRIPTION
IPAddress.TryParse 可以判断地址格式;AddressFamily 再排除 IPv6,
从而确保后面的 New-NetIPAddress 收到的是 IPv4。
#>
function Test-IPv4Address {
param([string]$Address)
$parsed = $null
return [System.Net.IPAddress]::TryParse($Address, [ref]$parsed) -and
$parsed.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork
}
<#
.SYNOPSIS
使用统一格式显示错误并终止脚本。
.DESCRIPTION
exit 1 会把失败状态返回给外层 BAT,外层据此显示“流程没有完成”。
#>
function Stop-WithMessage {
param([string]$Message)
Write-Host "[错误] $Message" -ForegroundColor Red
exit 1
}
# -------------------------- 初始化检查 --------------------------
# 清屏并显示脚本用途及远程连接中断风险。
Clear-Host
Write-Host '==============================================' -ForegroundColor Cyan
Write-Host ' qy.net 域加入向导' -ForegroundColor Cyan
Write-Host '==============================================' -ForegroundColor Cyan
Write-Host
Write-Host '请在待加域计算机本地运行此脚本。' -ForegroundColor Yellow
Write-Host '网络配置修改后,远程连接可能会立即中断。' -ForegroundColor Yellow
Write-Host
# Win32_ComputerSystem.PartOfDomain 可以判断当前设备是否已经入域。
# 已入域设备涉及改名、退域或修复信任关系,不应走“首次加域”流程。
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
if ($computerSystem.PartOfDomain) {
Stop-WithMessage "当前计算机已经加入域 $($computerSystem.Domain)。本脚本只用于首次加域。"
}
# 列出没有被禁用的网卡,让用户复制正确的 Name 作为 InterfaceAlias。
Write-Host '当前可用网络适配器:' -ForegroundColor Cyan
Get-NetAdapter |
Where-Object Status -ne 'Disabled' |
Select-Object Name,InterfaceDescription,Status,LinkSpeed |
Format-Table -AutoSize
# -------------------------- 收集参数 ----------------------------
# 每一项均要求输入两次。静态 IP 必须同时具备前缀长度与默认网关。
$computerName = Read-ConfirmedValue -Label '新计算机名(建议不超过 15 个字符)'
$interfaceAlias = Read-ConfirmedValue -Label '网络适配器名称(例如 Ethernet)'
$ipAddress = Read-ConfirmedValue -Label '静态 IPv4 地址'
$prefixLengthText = Read-ConfirmedValue -Label '子网前缀长度(例如 24)'
$defaultGateway = Read-ConfirmedValue -Label '默认网关'
$dnsServer1 = Read-ConfirmedValue -Label '首选 DNS 服务器'
$dnsServer2 = Read-ConfirmedValue -Label '备用 DNS 服务器(没有则两次都直接回车)' -AllowEmpty
# 根据设备用途选择 OU,避免终端和服务器套用错误的 GPO。
Write-Host
Write-Host '请选择计算机对象的目标 OU:' -ForegroundColor Cyan
Write-Host " 1. 管理终端:$adminComputerOU"
Write-Host " 2. 业务服务器:$operationComputerOU"
$targetOU = $null
while (-not $targetOU) {
$ouChoice = Read-ConfirmedValue -Label '目标 OU 编号(1 或 2)'
switch ($ouChoice) {
'1' { $targetOU = $adminComputerOU }
'2' { $targetOU = $operationComputerOU }
default {
Write-Host '[提示] 只能输入 1 或 2。' -ForegroundColor Yellow
}
}
}
# -------------------------- 校验参数 ----------------------------
# NetBIOS 兼容名称建议不超过 15 个字符,并避免非法字符和首尾连字符。
if ($computerName.Length -gt 15 -or
$computerName -notmatch '^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$') {
Stop-WithMessage '计算机名必须为 1~15 个字符,只能包含字母、数字和连字符,且不能以连字符开头或结尾。'
}
# Get-NetAdapter 不仅校验名称,还把网卡对象保存下来供后续步骤使用。
try {
$adapter = Get-NetAdapter -Name $interfaceAlias -ErrorAction Stop
} catch {
Stop-WithMessage "找不到网络适配器 '$interfaceAlias'。"
}
# 依次验证静态 IP、前缀长度、网关和 DNS,任何一项错误都立即终止。
if (-not (Test-IPv4Address $ipAddress)) {
Stop-WithMessage "静态 IP '$ipAddress' 不是有效的 IPv4 地址。"
}
$prefixLength = 0
if (-not [int]::TryParse($prefixLengthText, [ref]$prefixLength) -or
$prefixLength -lt 1 -or $prefixLength -gt 32) {
Stop-WithMessage '子网前缀长度必须是 1~32 之间的整数。'
}
if (-not (Test-IPv4Address $defaultGateway)) {
Stop-WithMessage "默认网关 '$defaultGateway' 不是有效的 IPv4 地址。"
}
if (-not (Test-IPv4Address $dnsServer1)) {
Stop-WithMessage "首选 DNS '$dnsServer1' 不是有效的 IPv4 地址。"
}
if ($dnsServer2 -and -not (Test-IPv4Address $dnsServer2)) {
Stop-WithMessage "备用 DNS '$dnsServer2' 不是有效的 IPv4 地址。"
}
# Set-DnsClientServerAddress 接收数组,因此把首选和可选备用 DNS 合并。
$dnsServers = @($dnsServer1)
if ($dnsServer2) {
$dnsServers += $dnsServer2
}
# -------------------------- 最终确认 ----------------------------
# 在发生任何修改前集中展示最终配置。
Write-Host
Write-Host '================ 最终配置确认 ================' -ForegroundColor Cyan
Write-Host ("当前计算机名: {0}" -f $env:COMPUTERNAME)
Write-Host ("新计算机名: {0}" -f $computerName)
Write-Host ("网络适配器: {0}" -f $adapter.Name)
Write-Host ("静态 IP: {0}/{1}" -f $ipAddress,$prefixLength)
Write-Host ("默认网关: {0}" -f $defaultGateway)
Write-Host ("DNS 服务器: {0}" -f ($dnsServers -join ', '))
Write-Host ("目标域: {0}" -f $domainName)
Write-Host ("目标 OU: {0}" -f $targetOU)
Write-Host '================================================' -ForegroundColor Cyan
Write-Host
# 要求输入完整单词 JOIN,避免误按 Y 或 Enter 就开始修改网络。
$confirmation = Read-Host '确认无误后请输入 JOIN;输入其他内容将取消'
if ($confirmation -cne 'JOIN') {
Write-Host '[取消] 未修改任何配置。' -ForegroundColor Yellow
exit 0
}
# -------------------------- 安全预检查 --------------------------
# Ping 只能发现“明确正在响应”的地址,不能百分百证明地址未占用;
# 因此通过这里只是最低限度的防冲突检查,正式环境还应核对 DHCP/IPAM。
Write-Host
Write-Host '[预检查] 检查目标 IP 是否已经响应……' -ForegroundColor Cyan
if (Test-Connection -ComputerName $ipAddress -Count 1 -Quiet -ErrorAction SilentlyContinue) {
Stop-WithMessage "目标 IP $ipAddress 当前能够响应。为避免 IP 冲突,流程已经停止。"
}
# AD 加域依赖 _ldap._tcp.dc._msdcs.<domain> SRV 记录。
# 如果指定 DNS 无法解析它,提前停止比改完网络后再失败更安全。
Write-Host '[预检查] 使用指定 DNS 查询 qy.net 域控 SRV 记录……' -ForegroundColor Cyan
try {
$srvRecords = Resolve-DnsName `
-Name '_ldap._tcp.dc._msdcs.qy.net' `
-Type SRV `
-Server $dnsServer1 `
-DnsOnly `
-ErrorAction Stop
$srvRecords |
Where-Object Type -eq 'SRV' |
Select-Object NameTarget,Port,Priority,Weight |
Format-Table -AutoSize
} catch {
Stop-WithMessage "指定 DNS 无法解析 qy.net 域控 SRV 记录:$($_.Exception.Message)"
}
# -------------------------- 获取凭据 ----------------------------
# Get-Credential 返回 PSCredential,密码以 SecureString 保存,
# 不会作为明文变量或命令行参数出现在脚本中。
$domainUserInput = Read-Host "请输入加域账户(直接回车使用 $defaultDomainUser)"
$domainUser = if ([string]::IsNullOrWhiteSpace($domainUserInput)) {
$defaultDomainUser
} else {
$domainUserInput.Trim()
}
$credential = Get-Credential `
-UserName $domainUser `
-Message '请输入用于加入 qy.net 域的凭据'
if (-not $credential) {
Stop-WithMessage '没有取得域凭据。'
}
# -------------------------- 备份现状 ----------------------------
# 修改网络前,把 ipconfig、IP 配置和 DNS 配置写入临时文件。
# 若加域失败,这份记录可用于人工核对和恢复;脚本不会盲目自动回滚。
$backupPath = Join-Path `
$env:TEMP `
("qy-domain-join-network-{0}.txt" -f (Get-Date -Format 'yyyyMMdd-HHmmss'))
Write-Host "[备份] 当前网络配置将保存到 $backupPath" -ForegroundColor Cyan
@(
'===== ipconfig /all ====='
(ipconfig /all | Out-String)
'===== Get-NetIPConfiguration ====='
(Get-NetIPConfiguration | Format-List * | Out-String)
'===== Get-DnsClientServerAddress ====='
(Get-DnsClientServerAddress | Format-List * | Out-String)
) | Set-Content -LiteralPath $backupPath -Encoding UTF8
# -------------------------- 应用配置 ----------------------------
# 整个修改过程放在 try/catch 中。发生异常时会进入 catch、显示错误,
# 并把非零退出代码返回给外层 BAT。
try {
Write-Host '[1/4] 正在配置静态 IP……' -ForegroundColor Cyan
# 删除所选网卡已有的 IPv4 地址,防止 DHCP 地址与新静态地址并存。
Get-NetIPAddress `
-InterfaceAlias $adapter.Name `
-AddressFamily IPv4 `
-ErrorAction SilentlyContinue |
Where-Object IPAddress -ne '127.0.0.1' |
Remove-NetIPAddress -Confirm:$false -ErrorAction SilentlyContinue
# 删除旧的 IPv4 默认路由,避免添加新网关时发生路由冲突。
Get-NetRoute `
-InterfaceAlias $adapter.Name `
-AddressFamily IPv4 `
-ErrorAction SilentlyContinue |
Where-Object DestinationPrefix -eq '0.0.0.0/0' |
Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue
# 关闭该网卡的 IPv4 DHCP,然后写入新的静态地址、前缀和网关。
Set-NetIPInterface `
-InterfaceAlias $adapter.Name `
-AddressFamily IPv4 `
-Dhcp Disabled
New-NetIPAddress `
-InterfaceAlias $adapter.Name `
-IPAddress $ipAddress `
-PrefixLength $prefixLength `
-DefaultGateway $defaultGateway | Out-Null
# AD 客户端必须使用能够解析域控 SRV 记录的内部 DNS。
Write-Host '[2/4] 正在配置 DNS……' -ForegroundColor Cyan
Set-DnsClientServerAddress `
-InterfaceAlias $adapter.Name `
-ServerAddresses $dnsServers
# 清除旧 DNS 缓存,避免后续验证继续命中修改前的缓存结果。
Clear-DnsClientCache
# 使用已经应用到网卡的新 DNS 再验证一次域控 SRV 记录。
Write-Host '[3/4] 正在验证域 DNS……' -ForegroundColor Cyan
Resolve-DnsName `
-Name '_ldap._tcp.dc._msdcs.qy.net' `
-Type SRV `
-DnsOnly `
-ErrorAction Stop | Out-Null
# Add-Computer 的 -NewName 可以把“改名”和“加域”合并到同一次重启中;
# -OUPath 决定计算机对象创建在哪个 OU,-PassThru 返回执行结果。
Write-Host '[4/4] 正在修改计算机名并加入 qy.net……' -ForegroundColor Cyan
$joinResult = Add-Computer `
-DomainName $domainName `
-NewName $computerName `
-OUPath $targetOU `
-Credential $credential `
-PassThru `
-Verbose `
-ErrorAction Stop
$joinResult | Format-Table HasSucceeded,ComputerName -AutoSize
if (-not $joinResult.HasSucceeded) {
throw 'Add-Computer 没有返回成功状态。'
}
} catch {
# 网络可能已经发生变化,所以这里只报告错误和备份路径,
# 不自动套用旧配置,避免错误回滚进一步中断网络。
Write-Host
Write-Host '[失败] 网络配置或加域过程中发生错误:' -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host "修改前的网络配置保存在:$backupPath" -ForegroundColor Yellow
Write-Host '请先检查当前网络配置和连通性,再决定是否恢复。' -ForegroundColor Yellow
exit 1
}
# -------------------------- 完成与重启 --------------------------
# 改名和加域都要在重启后完全生效。15 秒倒计时给管理员留下取消机会。
Write-Host
Write-Host '[成功] 计算机名和加域请求已提交。' -ForegroundColor Green
Write-Host '计算机将在 15 秒后重启;如需取消,请立即运行 shutdown /a。' -ForegroundColor Yellow
shutdown.exe /r /t 15 /c "计算机已加入 qy.net,正在重启以完成配置。"
exit 0

这是一份用于学习和实验环境的示例脚本。目前只进行了静态语法检查,没有在真实客户端上执行加域。第一次使用时,建议先在可通过虚拟机控制台恢复网络的测试机上验证。

脚本采用 BAT 与 PowerShell 混合写法:文件开头由 CMD 检查管理员权限并启动 PowerShell;# POWERSHELL-BEGIN 后面的内容由 PowerShell 读取并执行。这样既保留了双击 BAT 的便利,也能使用 PowerShell 的网络管理、输入校验和安全凭据功能。


上一篇:Windows AD PowerShell(二):组与成员

下一篇:Windows AD PowerShell(四):域控与 FSMO

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Windows AD PowerShell(三):计算机
https://springtsuki.xucjno.com/posts/springtsuki/notes/windowsad/powershell/920-computer/
作者
SpringTsuki
发布于
2026-09-20
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
SpringTsuki
你好,我是浅月。
公告
浅月的开发小窝!欢迎你来到这里!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
56
分类
9
标签
42
总字数
188,888
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录