Windows AD PowerShell(三):计算机
这是 Windows AD PowerShell 学习记录的第三节,主要整理计算机对象管理、网络与 DNS 检查、安全通道、加域及自动化脚本。
三、计算机
3.1 查询计算机的基本信息
查询域中的所有计算机对象:
# 查询所有计算机Get-ADComputer -Filter *
DistinguishedName : CN=QYNET-CORE01,OU=Domain Controllers,DC=qy,DC=netDNSHostName : QYNET-CORE01.qy.netEnabled : TrueName : QYNET-CORE01ObjectClass : computerSamAccountName : QYNET-CORE01$
DistinguishedName : CN=QYNET-EXCHANGE,OU=Computer,OU=Operation,OU=Organization,DC=qy,DC=netDNSHostName : QYNET-EXCHANGE.qy.netEnabled : TrueName : QYNET-EXCHANGEObjectClass : computerSamAccountName : QYNET-EXCHANGE$
# 当前实验域共查询到 6 个计算机对象,其余对象为:# QYNET-CORE02、QYNET-WEBUI、QYNET-PROXY、QYNET-USER01计算机的 SamAccountName 通常以 $ 结尾,这是计算机账户与普通用户账户之间一个很直观的区别。
只显示日常排查中常用的字段:
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:35QYNET-EXCHANGE True QYNET-EXCHANGE.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/9 09:23:55QYNET-PROXY True QYNET-PROXY.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/7 14:05:45QYNET-USER01 True QYNET-USER01.qy.net Windows 10 企业版 2016 长期服务版 10.0 (14393) 2026/9/18 13:42:29QYNET-CORE02 True QYNET-CORE02.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/17 11:18:25QYNET-CORE01 True QYNET-CORE01.qy.net Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/14 13:34:01IPv4Address 是根据 DNS 解析得到的便利属性,不是判断设备在线状态的可靠依据。DNS 中存在旧记录时,它仍可能返回地址。
3.2 查询单台计算机的详细属性
# 查询某台计算机的全部属性Get-ADComputer -Identity QYNET-CORE01 -Properties *
CanonicalName : qy.net/Domain Controllers/QYNET-CORE01CN : QYNET-CORE01Created : 2026/9/4 12:42:17Description :DistinguishedName : CN=QYNET-CORE01,OU=Domain Controllers,DC=qy,DC=netDNSHostName : QYNET-CORE01.qy.netEnabled : TrueLastLogonDate : 2026/9/14 13:34:01Name : QYNET-CORE01ObjectClass : computerOperatingSystem : Windows Server 2016 Datacenter EvaluationOperatingSystemVersion : 10.0 (14393)PasswordLastSet : 2026/9/4 12:42:33SamAccountName : QYNET-CORE01$-Properties * 会返回大量属性。实际排查时最好只获取需要的属性,减少输出噪声:
Get-ADComputer -Identity QYNET-CORE01 ` -Properties Description,OperatingSystem,OperatingSystemVersion,LastLogonDate,PasswordLastSet | Select-Object Name,Enabled,Description,OperatingSystem,OperatingSystemVersion,LastLogonDate,PasswordLastSet
Name : QYNET-CORE01Enabled : TrueDescription :OperatingSystem : Windows Server 2016 Datacenter EvaluationOperatingSystemVersion : 10.0 (14393)LastLogonDate : 2026/9/14 13:34:01PasswordLastSet : 2026/9/4 12:42:33常见计算机属性如下:
| 属性 | 含义 |
|---|---|
| Name | 计算机对象名称 |
| SamAccountName | 计算机账户登录名,通常以 $ 结尾 |
| DNSHostName | 计算机的完整 DNS 名称 |
| Enabled | AD 中的计算机账户是否启用 |
| OperatingSystem | 设备上报的操作系统名称 |
| OperatingSystemVersion | 设备上报的操作系统版本 |
| LastLogonDate | 由 lastLogonTimestamp 转换而来的易读时间 |
| PasswordLastSet | 计算机账户密码上次修改时间 |
| Description | 管理员填写的设备说明 |
| ManagedBy | 负责管理该计算机对象的用户或组 |
| DistinguishedName | 计算机对象在 AD 中的完整路径 |
3.3 查询长期未登录的计算机
根据 LastLogonTimeStamp 粗略筛选 90 天未登录的计算机:
$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 通常表示该属性从未写入,可能是预创建但未入域的对象,也可能是从未成功登录域的设备,不能直接断定设备已经报废。
也可以使用专门的账户状态查询命令:
Search-ADAccount ` -AccountInactive ` -TimeSpan 90.00:00:00 ` -ComputersOnly | Select-Object Name,Enabled,LastLogonDate,DistinguishedName
# 当前 qy.net 中没有符合条件的计算机账户,因此无输出⚠️
LastLogonTimeStamp的坑:该属性不是每次登录都立即复制,默认设计下可能相差约 9~14 天。它适合筛选 90 天、180 天这类长期不活跃设备,不能用于判断“这台计算机今天是否登录过”。
如果必须取得较准确的最后登录时间,需要向每台 DC 查询不复制的 LastLogon,然后取最大值:
$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:35QYNET-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 统计操作系统分布
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,...}如果只需要适合报表的名称和数量,可以进一步整理:
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 长期服务版查询仍在使用旧系统的设备:
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:35QYNET-EXCHANGE Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/9 09:23:55QYNET-PROXY Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/7 14:05:45QYNET-CORE02 Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/17 11:18:25QYNET-CORE01 Windows Server 2016 Datacenter Evaluation 10.0 (14393) 2026/9/14 13:34:01OperatingSystem 与 OperatingSystemVersion 依赖设备写入 AD 的信息,不能替代资产管理或补丁管理系统,但很适合快速粗筛。
3.5 修改、启用与禁用计算机账户
以下使用专门的实验对象 PC-LAB-01 演示写操作,避免直接修改域控制器或业务服务器。修改描述、位置和管理人:
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-01Description : 实验室测试计算机Location : Shanghai-Lab-AManagedBy : CN=浅月,OU=User,OU=Admin,OU=Organization,DC=qy,DC=net禁用计算机账户前先预演:
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".确认后执行并验证:
Disable-ADAccount -Identity PC-LAB-01
# 执行成功时默认无输出Get-ADComputer -Identity PC-LAB-01 | Select-Object Name,Enabled
Name Enabled---- -------PC-LAB-01 False重新启用计算机账户:
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 创建与移动计算机对象
预先创建计算机对象:
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=netDNSHostName : PC-LAB-01.qy.netEnabled : TrueName : PC-LAB-01ObjectClass : computerSamAccountName : PC-LAB-01$移动计算机前先使用 -WhatIf:
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 正确后移动并验证:
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=net3.7 排查计算机是否在线以及 DNS 是否正常
当 AD 中存在计算机对象,但远程管理或访问失败时,应先区分“AD 对象存在”和“设备当前在线”这两件事。
# 检查 ICMP 连通性Test-Connection -ComputerName QYNET-CORE01 -Count 2
Destination Address Latency BufferSize Status----------- ------- ------- ---------- ------QYNET-CORE01 10.0.0.10 1 32 SuccessQYNET-CORE01 10.0.0.10 1 32 Success设备可能禁用了 ICMP,因此 Ping 失败不一定表示离线。可以继续检查 DNS:
Resolve-DnsName QYNET-CORE01.qy.net
Name Type TTL Section IPAddress---- ---- --- ------- ---------QYNET-CORE01.qy.net A 1200 Answer 10.0.0.10检查常见远程管理端口:
# SMBTest-NetConnection -ComputerName QYNET-CORE01 -Port 445
ComputerName : QYNET-CORE01RemoteAddress : 10.0.0.10RemotePort : 445InterfaceAlias : EthernetTcpTestSucceeded : True
# WinRM HTTPTest-NetConnection -ComputerName QYNET-CORE01 -Port 5985
ComputerName : QYNET-CORE01RemoteAddress : 10.0.0.10RemotePort : 5985InterfaceAlias : EthernetTcpTestSucceeded : True常见判断如下:
| 现象 | 可能原因 |
|---|---|
| AD 中能查到,DNS 查不到 | DNS 记录缺失、过期或设备从未成功注册 |
| DNS 能解析,Ping 不通 | 防火墙阻止 ICMP、路由问题或设备离线 |
| Ping 正常,445 不通 | SMB 服务未启用或防火墙阻止 |
| 5985 不通 | WinRM 未配置、服务未运行或防火墙阻止 |
| 解析到错误 IP | DNS 陈旧记录或重复注册 |
3.8 排查“此工作站和主域间的信任关系失败”
该错误通常表示成员计算机本地保存的计算机账户密码,与 AD 中的计算机账户密码不一致。应在发生问题的成员计算机上,以本地管理员身份打开提升权限的 PowerShell。
首先测试安全通道:
Test-ComputerSecureChannel -Verbose
VERBOSE: The secure channel between the local computer and the domain qy.net is broken.False如果返回 False,尝试直接修复:
$credential = Get-Credential "QY\Administrator"Test-ComputerSecureChannel ` -Repair ` -Server "QYNET-CORE01.qy.net" ` -Credential $credential ` -Verbose
PowerShell credential requestEnter your credentials.Password for user QY\Administrator: ********
VERBOSE: The secure channel between the local computer and the domain qy.net has been repaired.True修复后再次确认:
Test-ComputerSecureChannel -Server "QYNET-CORE01.qy.net"
True如果仍然失败,可以重置本机的计算机账户密码:
$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 -ForceTest-ComputerSecureChannel 主要用于域成员计算机。在域控制器上使用它可能产生误导性结果;域控制器的安全通道问题应结合 netdom、nltest、复制状态和事件日志进行排查。
3.9 计算机入域、退域与重新入域
在待入域计算机上,以管理员身份打开 PowerShell:
$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:
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:
$credential = Get-Credential "QY\Administrator"
Remove-Computer ` -UnjoinDomainCredential $credential ` -WorkgroupName "WORKGROUP" ` -PassThru ` -Verbose ` -Restart
HasSucceeded ComputerName------------ ------------ True PC-LAB-01Remove-ADComputer 与 Remove-Computer 不是一回事:
| 命令 | 执行位置 | 作用 |
|---|---|---|
| Remove-ADComputer | 管理端或 DC | 删除 AD 中的计算机对象,不会让在线设备自动退出域 |
| Remove-Computer | 待处理的本机或远程目标 | 让 Windows 设备退出域并加入工作组 |
域信任修复失败时,重新入域应作为靠后的方案,因为它需要重启,也可能影响证书、服务账户、计划任务和依赖原计算机身份的权限。
3.10 安全清理长期离线的计算机对象
不建议把“90 天未登录”的结果直接传给 Remove-ADComputer。更稳妥的流程是:生成报告、人工确认、禁用、移动到隔离 OU、观察一段时间,最后再删除。
第一步,生成候选列表:
$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=netPC-STOCK-02 True CN=PC-STOCK-02,OU=Computer,OU=Organization,DC=qy,DC=net第二步,导出报告供资产负责人确认:
$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第三步,先预演禁用操作:
$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:
$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=netPC-STOCK-02 False CN=PC-STOCK-02,OU=Disabled Computers,OU=Organization,DC=qy,DC=net观察期结束并完成审批后,再预演删除:
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".即使设备长期未登录,也应额外核对以下情况:
- 设备是否属于库存机、冷备机或灾备设备。
- 是否是只在特定时间启动的业务服务器。
- 是否存在网络隔离,导致登录时间长期没有更新。
- 设备是否已经改名,而旧对象仍留在 AD 中。
- 资产、监控、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”为例:
# 在管理端确认计算机所在 OUGet-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 是否正确。
这个示例具有以下保护措施:
- 计算机名、网卡、IP、网关、DNS 和 OU 选择都要输入两次。
- 修改前集中显示全部配置,只有输入大写
JOIN才继续。 - 域名固定为
qy.net,目标 OU 只能从实验环境中已有的两个 OU 选择。 - 域密码通过
Get-Credential输入,不会以明文写入脚本。 - 修改前把原网络配置备份到
%TEMP%,便于失败后人工恢复。 - 加域成功后等待 15 秒再重启,可使用
shutdown /a取消倒计时。
将下面内容保存为 Join-QyDomain.bat,然后右键选择“以管理员身份运行”:
@echo off
rem ================================================================rem qy.net 域加入教学脚本remrem BAT 负责:检查管理员权限、启动内嵌 PowerShell、接收退出代码。rem PowerShell 负责:参数输入与校验、网络配置、改名、加域和重启。remrem 为什么采用这种组合?rem 传统 BAT 不擅长处理网卡对象、IP 校验和安全凭据;PowerShellrem 对这些 Windows 管理任务有原生命令,而 BAT 便于直接双击启动。rem ================================================================
rem EnableExtensions 启用现代 CMD 扩展语法;setlocal 防止变量污染父 CMD。setlocal EnableExtensions
rem 切换到 UTF-8 代码页,尽量避免中文提示乱码。chcp 65001 >nultitle qy.net 域加入向导
rem fltmc 需要管理员权限。这里只利用其退出代码判断是否为提升会话,rem 不会修改筛选器或系统配置。fltmc >nul 2>&1if 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/RSATrem 环境的兼容性最好。-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.pauseexit /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 地址。
.DESCRIPTIONIPAddress.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使用统一格式显示错误并终止脚本。
.DESCRIPTIONexit 1 会把失败状态返回给外层 BAT,外层据此显示“流程没有完成”。#>function Stop-WithMessage { param([string]$Message)
Write-Host "[错误] $Message" -ForegroundColor Red exit 1}
# -------------------------- 初始化检查 --------------------------
# 清屏并显示脚本用途及远程连接中断风险。Clear-HostWrite-Host '==============================================' -ForegroundColor CyanWrite-Host ' qy.net 域加入向导' -ForegroundColor CyanWrite-Host '==============================================' -ForegroundColor CyanWrite-HostWrite-Host '请在待加域计算机本地运行此脚本。' -ForegroundColor YellowWrite-Host '网络配置修改后,远程连接可能会立即中断。' -ForegroundColor YellowWrite-Host
# Win32_ComputerSystem.PartOfDomain 可以判断当前设备是否已经入域。# 已入域设备涉及改名、退域或修复信任关系,不应走“首次加域”流程。$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystemif ($computerSystem.PartOfDomain) { Stop-WithMessage "当前计算机已经加入域 $($computerSystem.Domain)。本脚本只用于首次加域。"}
# 列出没有被禁用的网卡,让用户复制正确的 Name 作为 InterfaceAlias。Write-Host '当前可用网络适配器:' -ForegroundColor CyanGet-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-HostWrite-Host '请选择计算机对象的目标 OU:' -ForegroundColor CyanWrite-Host " 1. 管理终端:$adminComputerOU"Write-Host " 2. 业务服务器:$operationComputerOU"
$targetOU = $nullwhile (-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 = 0if (-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-HostWrite-Host '================ 最终配置确认 ================' -ForegroundColor CyanWrite-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 CyanWrite-Host
# 要求输入完整单词 JOIN,避免误按 Y 或 Enter 就开始修改网络。$confirmation = Read-Host '确认无误后请输入 JOIN;输入其他内容将取消'if ($confirmation -cne 'JOIN') { Write-Host '[取消] 未修改任何配置。' -ForegroundColor Yellow exit 0}
# -------------------------- 安全预检查 --------------------------
# Ping 只能发现“明确正在响应”的地址,不能百分百证明地址未占用;# 因此通过这里只是最低限度的防冲突检查,正式环境还应核对 DHCP/IPAM。Write-HostWrite-Host '[预检查] 检查目标 IP 是否已经响应……' -ForegroundColor Cyanif (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 Cyantry { $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-HostWrite-Host '[成功] 计算机名和加域请求已提交。' -ForegroundColor GreenWrite-Host '计算机将在 15 秒后重启;如需取消,请立即运行 shutdown /a。' -ForegroundColor Yellowshutdown.exe /r /t 15 /c "计算机已加入 qy.net,正在重启以完成配置。"exit 0这是一份用于学习和实验环境的示例脚本。目前只进行了静态语法检查,没有在真实客户端上执行加域。第一次使用时,建议先在可通过虚拟机控制台恢复网络的测试机上验证。
脚本采用 BAT 与 PowerShell 混合写法:文件开头由 CMD 检查管理员权限并启动 PowerShell;# POWERSHELL-BEGIN 后面的内容由 PowerShell 读取并执行。这样既保留了双击 BAT 的便利,也能使用 PowerShell 的网络管理、输入校验和安全凭据功能。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!













