Node+PowerShell导出AD用户数据 如何替代硬编码实现Excel清单
AD域用户信息自动化导出程序优化方案
针对现有程序的硬编码、格式化两个核心问题,直接按以下方案改造即可:
一、消除组判断硬编码
原有逐组写if判断的逻辑完全可以废弃,改用「配置映射+通用过滤」的模式实现:
- 单独维护一个组配置对象,仅需要标记两类规则:需要自定义显示名称的组、需要忽略不统计的组,其余所有未在配置中出现的AD组会自动保留原名纳入统计,不需要提前枚举所有组。
- 把PowerShell返回结果的解析逻辑抽成通用函数,删掉原有冗余的数组拷贝、截断逻辑,直接过滤空行和PowerShell默认输出的表头行即可。
- 组匹配时不需要逐组写
includes判断,直接遍历用户所属组列表,按配置规则转换后输出有效组数组即可。
核心改造代码(替换ADPropertiesFunction.mjs对应部分):
// 组规则配置,仅需维护此处即可,新增规则无需修改业务逻辑 const GROUP_RULES = { // 格式:AD组原名: 显示名,显示名为空字符串代表忽略该组不统计 "bitbucket-users": "Bitbucket User", "confluence-users": "Confluence User", "LoggedInViaOidcSso": "", "jira-software-users": "Jira User", "jira-servicedesk-users": "Jira Service Desk User", "Group Policy Creator Owners": "Group Policy Creator Owners", "Domain Admins": "Domain Admins", "Enterprise Admins": "Enterprise Admins", "Schema Admins": "Schema Admins", "Administrators": "Administrators", "Guests": "Guest", "Denied RODC Password Replication Group": "", "confluence-administrators": "Confluence Administrator", "bitbucket-system-administrators": "Bitbucket System Administrator", "jira-system-administrators": "Jira System Administrator" } // 通用解析PowerShell命令返回结果 const parsePsResult = (rawBuffer) => { return rawBuffer.toString() .split(/\r?\n/) .map(line => line.trim()) .filter(line => line && !line.startsWith("Name") && !line.startsWith("----")) } const searchUsers = () => { console.log("Loading AD user list..."); const rawUserResult = execSync("./searchUsers.ps1", { shell: "powershell.exe" }) return parsePsResult(rawUserResult) } const getUserProperties = (username) => { const rawUserInfo = execSync(`Get-ADUser ${username} -Properties DistinguishedName | Select-Object Name,DistinguishedName`, {shell: "powershell.exe"}) // 解析用户DN属性 const userLines = parsePsResult(rawUserInfo) const dn = userLines.find(line => line.startsWith("DC=")) // 获取用户所属有效组 const rawGroupResult = execSync(`./FindADUserGroup.ps1 ${username}`, { shell: "powershell.exe" }) const allGroups = parsePsResult(rawGroupResult) const validGroups = allGroups .map(g => GROUP_RULES.hasOwnProperty(g) ? GROUP_RULES[g] : g) .filter(g => g) return { name: username, DistinguishedName: dn, groups: validGroups } }
二、实现Excel勾选矩阵格式输出
原有每个用户占3行、所有组塞单个单元格、每写一个用户开关一次Excel的逻辑性能极差,改造为一次性写入的矩阵格式:
- 先拉取所有用户的信息和所属组,遍历所有用户的组列表,去重得到全量组集合,作为Excel的动态表头,不需要提前固定列。
- Excel固定前两列为
Name、DistinguishedName,从第三列开始依次排列所有收集到的组。 - 每个用户占单独一行,对应组列位置如果用户属于该组填
1,否则填0。 - 整个写入过程只打开一次Excel文件,写完所有数据后统一保存关闭,同时释放COM对象,避免后台残留Excel进程。
改造后的PowerShell写入脚本(替换原有ADPropertiesFunction.ps1):
param( [string]$ConfigJson ) $config = $ConfigJson | ConvertFrom-Json Import-Module ActiveDirectory $ExcelObj = New-Object -ComObject Excel.Application $ExcelObj.Visible = $false $ExcelWorkBook = $ExcelObj.Workbooks.Open($config.ExcelPath) $ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("Sheet1") $ExcelWorkSheet.Activate() # 写入表头 $ExcelWorkSheet.Cells.Item(1, 1) = "Name" $ExcelWorkSheet.Cells.Item(1, 2) = "DistinguishedName" for ($i = 0; $i -lt $config.AllGroupColumns.Count; $i++) { $ExcelWorkSheet.Cells.Item(1, $i + 3) = $config.AllGroupColumns[$i] } # 写入用户数据 for ($rowIdx = 0; $rowIdx -lt $config.UserListData.Count; $rowIdx++) { $currentRow = $rowIdx + 2 $user = $config.UserListData[$rowIdx] $ExcelWorkSheet.Cells.Item($currentRow, 1) = $user.name $ExcelWorkSheet.Cells.Item($currentRow, 2) = $user.DistinguishedName for ($colIdx = 0; $colIdx -lt $config.AllGroupColumns.Count; $colIdx++) { $cellValue = if ($user.groups -contains $config.AllGroupColumns[$colIdx]) { 1 } else { 0 } $ExcelWorkSheet.Cells.Item($currentRow, $colIdx + 3) = $cellValue } } # 保存清理 $ExcelObj.DisplayAlerts = $false $ExcelWorkBook.Save() $ExcelWorkBook.Close($true) $ExcelObj.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelObj) | Out-Null
对应的JS侧主逻辑调整为:
const main = () => { const userList = searchUsers() const allUserData = [] const allGroups = new Set() // 拉取所有用户数据,收集全量组 for (const username of userList) { const userProp = getUserProperties(username) allUserData.push(userProp) userProp.groups.forEach(g => allGroups.add(g)) } const groupColumns = Array.from(allGroups) // 一次性把所有数据传给PowerShell写入Excel const psParam = JSON.stringify({ ExcelPath: "C:\\Users\\Administrator\\Downloads\\ADPropertiesVer1\\testADpropertiesVer1.xlsx", AllGroupColumns: groupColumns, UserListData: allUserData }) execSync(`./ADPropertiesFunction.ps1 '${psParam}'`, {shell: "powershell.exe"}) console.log(`Export completed, total ${allUserData.length} users, ${groupColumns.length} groups.`) } main()
改造后优势
- 维护成本极低:后续新增AD组、调整组显示名、设置忽略组,仅需修改
GROUP_RULES配置即可,不需要动任何业务判断逻辑。 - 性能提升明显:从原来每个用户调用3次PowerShell、开关1次Excel,改为全流程仅调用N+1次PowerShell、开关1次Excel,百级用户量下导出速度从分钟级降到秒级。
- 自动适配场景:不需要提前枚举所有AD组,程序会自动收集扫描到的所有组生成列,适配不同域环境的组差异,不需要针对部署环境改代码。
- 无后台残留:脚本最后增加了COM对象释放逻辑,不会出现导出完成后后台残留大量Excel进程占内存的问题。
内容的提问来源于stack exchange,提问作者ActiveDirectoryAdmin101
相关产品推荐
相关产品推荐

