Intune Win32 应用检测规则批量导出:Graph API 实操指南
做 Win32 LOB 应用的自动化维护已经有几年时间之前每次需要核对检测规则我都习惯打开 Intune 控制台那几条应用详情慢慢翻直到有次审计要求把几百个应用的检测规则全部导出成表格我才下定决心走 Microsoft Graph API 这条路。这篇文章就把我踩过的坑和验证过的接口细节完整记录下来从 OAuth 认证、接口字段、批量拉取到解析落地一条龙给你捋清楚。如果你也在写 Intune 运维脚本或者准备做 Win32 应用合规审计这篇可以直接抄作业。1. Win32 LOB 应用检测规则的定位与方案选型1.1 检测规则到底是做什么的Win32 LOB 应用在 Intune 里属于“业务线应用”本质上就是把传统的.exe、.msi安装包通过IntuneWin32App包装工具重新封装然后推送到 Windows 设备上。相比 MSI 或 Store 应用Win32 应用的部署方式更加自由安装脚本可以自定义但自由换来的是不确定性你怎么知道安装到底成功没有这时候就轮到检测规则出场。它是一条安装在客户端执行完后的“验证证据”Intune 会根据检测结果决定这台设备是否需要重新安装应用或者是否标记为安装失败。检测规则通常有四种形态文件系统检测指定一个文件或文件夹路径存在即认为安装成功注册表检测检查某个注册表键值或数据MSI 产品代码检测根据已安装产品的 ProductCode 和版本号判断PowerShell 脚本检测执行脚本返回特定退出码和输出来判断检测规则看起来只是部署配置里的一个小模块但它直接关系到一个应用的“成功安装”状态。规则写得太松应用明明坏了却显示已安装规则写得太严应用装好了又反复提示失败。所以无论是做部署优化还是做合规审计将这些规则从控制台里面捞出来分析是刚需。1.2 为什么选择 Graph API 而不是控制台理论上 Intune 控制台也能看到检测规则但那只适合单点查看。真实场景里往往存在三个问题第一是批量审计需求。团队需要定期检查所有 Win32 应用的检测规则是否满足规范比如必须包含文件检测或脚本检测不允许只用模糊的注册表规则。像这种跨应用、跨租户的核对工作控制台手动点击根本不现实。第二是版本对比与变更管理。Win32 应用每次内容更新都会产生新的内容版本有时候检测规则也随版本变化。用控制台看你很难快速对比哪个版本改了规则、改了什么字段而 Graph API 返回的结构化 JSON 可以拿来做差异对比。第三是自动化集成。如果你们团队有 CMDB 或监控平台想把这些检测规则同步过去作为配置基线那必须走 API。控制台既没有导出接口也没有查询接口。所以选择 Graph API 不是因为它“看起来比较潮”而是因为它是 Intune 数据模型的唯一编程入口结构稳定、权限可控、数据完整。1.3 我们到底要拉哪些数据在动手之前先明确一下目标对象。Win32 检测规则在 Graph API 中存在的位置略微让人困惑因为单看mobileApps接口你可能会摸不到头脑。我们需要关注两类数据实体win32LobApp表示 Win32 业务线应用本体包含displayName、installExperience、rules等属性rules集合应用下挂载的规则数组其中ruleType为detection的条目就是检测规则另外如果真的需要追溯完整内容版本还要关心contentVersions、committedContainedApps这些关联实体。不过日常拿检测规则核心操作区域就集中在win32LobApp.rules上。2. 做足准备工作理清接口对象与字段结构2.1 认识 win32LobApp 主体对象通过 Graph API 获取应用信息的基础路径是GET https://graph.microsoft.com/beta/deviceAppManagement/mobileApps这个接口返回的是一个mobileApp派生类集合里面混着 Win32 LOB 应用、MSI 应用、Store 应用等。当我们想精确筛选出 Win32 应用时可以借助 OData 的isOf运算符GET https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?$filterisof(microsoft.graph.win32LobApp)一个典型的win32LobApp响应长这样{ odata.type: #microsoft.graph.win32LobApp, id: a1b2c3d4-e5f6-7890-abcd-ef1234567890, displayName: 企业内部工具客户端, description: 内部业务线工具, publisher: IT 部门, installExperience: { runAsAccount: system, deviceRestartBehavior: allow }, rules: [ { ruleType: detection, detection: { odata.type: #microsoft.graph.win32LobAppFileSystemDetection, path: C:\\Program Files\\InternalTool, fileOrFolderName: InternalTool.exe, check32BitOn64System: true, operationType: exists, operator: notConfigured } } ], requirementRules: [], installCommandLine: InternalToolSetup.exe /S, uninstallCommandLine: msiexec /x {GUID} }注意其中rules数组是我们要解析的重头戏。每个规则对象都包含ruleType以及一个特定类型的detection子对象实际字段根据检测类型而变化。这个嵌套结构是初学者最容易卡壳的地方因为如果不往下展开detection节点你看到的仅仅是ruleType一个字段。2.2 四种检测规则的字段对比根据我的实践总结Graph API 返回的检测规则主要有四种odata.type整理成表方便查阅检测类型odata.type关键字段适用场景文件系统#microsoft.graph.win32LobAppFileSystemDetectionpath、fileOrFolderName、check32BitOn64System、operationType、operator最常用验证主程序文件是否存在注册表#microsoft.graph.win32LobAppRegistryDetectionregistryKeyPath、registryValueName、detectionType、operator验证注册表配置写入情况MSI 产品代码#microsoft.graph.win32LobAppProductCodeDetectionproductCode、productVersion、operator适用于 MSI 封装类应用精确可靠PowerShell 脚本#microsoft.graph.win32LobAppPowerShellScriptDetectionscriptContent、enforceSignatureCheck、runAs32Bit、runAsAccount自定义逻辑复杂灵活但排查难度高这里有几个字段值得单独解释。check32BitOn64System是文件检测特有的开关用来决定是否同时在 64 位系统上检查 32 位程序文件夹的常见重定向路径比如C:\Program Files (x86)。如果你封装的是老牌 32 位软件这个字段忘了开检测很容易误判。operationType和operator是配套存在的。operationType定义检测行为比如exists、notExists、fileOrFolderSize、fileOrFolderVersion等operator则定义比较方式比如equal、greaterThan、notConfigured。响应里经常看到operator为notConfigured这是正常的一般表示该检测不涉及数值比较。PowerShell 脚本检测的scriptContent在 Graph 响应中可能是 Base64 编码后的脚本内容。你在控制台里看到的是明文脚本但 API 返回时会做编码处理解析的时候需要先解码再使用。2.3 规则的嵌套结构细节很多人第一眼看到rules数组会下意识以为里面每个对象就是一个“完整规则”但实际它的结构是“规则外壳 具体类型内容”的组合。举个例子一条文件检测规则完整展开是这样的{ ruleType: detection, detection: { odata.type: #microsoft.graph.win32LobAppFileSystemDetection, path: C:\\Program Files\\Example, fileOrFolderName: example.exe, operationType: exists } }外壳ruleType只有两个可选值detection和requirement。我们在文章里只关心detection但代码里建议还是加一层判断避免把要求规则也当成检测规则处理。如果你拿到的响应结构跟我上面展示的略有差异也不要惊慌。Graph API 在不同版本v1.0 和 beta以及不同租户配置下部分字段的嵌套层级会有细微区别尤其是老租户上创建的应用。我的经验是先把原始 JSON 原样打印一份肉眼确认它的实际结构再写解析逻辑。这比照着文档硬套靠谱得多。3. 实操过程从认证到批量导出检测规则3.1 创建应用注册并配置 API 权限要用 Graph API 读取 Intune 数据第一件事是在 Azure AD 里创建一个“应用注册”。这一步是整个流程里最容易出错但也最固定的环节。登录 Azure 门户进入“应用注册”页面新建应用方式选择“仅此目录中的帐户”。创建完成后记录三个核心信息应用程序客户端ID目录租户ID客户端密码需要在“证书和密码”里新建然后进入“API 权限”添加 Microsoft Graph 的应用程序权限DeviceManagementApps.Read.All。这里必须强调不要给委派权限因为我们是无人值守的后台脚本适合用客户端凭证模式。授权类型选择应用程序权限后务必点击“为 X 授予管理员同意”否则调用时会一直报Forbidden。这个权限听起来只与应用有关但实际读取mobileApps下面所有应用类型都靠它。如果你后面还要拉设备状态那可以再加DeviceManagementManagedDevices.Read.All只读检测规则的话一个权限足够了。3.2 获取访问令牌的完整流程这里我用 Python 的requests库写一个最朴素的认证获取逻辑方便没有现成 SDK 的团队直接复制import requests import json tenant_id 你的租户ID client_id 你的应用ID client_secret 你的客户端密码 token_url fhttps://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token data { grant_type: client_credentials, client_id: client_id, client_secret: client_secret, scope: https://graph.microsoft.com/.default } resp requests.post(token_url, datadata) token_resp resp.json() if access_token not in token_resp: print(获取令牌失败:) print(json.dumps(token_resp, indent2, ensure_asciiFalse)) exit(1) headers { Authorization: fBearer {token_resp[access_token]}, Content-Type: application/json } print(令牌获取成功剩余有效分钟数:, round(token_resp.get(expires_in, 0) / 60, 1))这里的scope固定写成https://graph.microsoft.com/.default即可它表示使用你在应用注册里配置过的所有静态权限。令牌有效期大约一小时脚本跑批任务时通常不需要做令牌刷新但如果是长期运行的服务最好加上刷新逻辑。3.3 拉取 Win32 应用并解析检测规则拿到令牌之后用下面的逻辑拉取并解析graph_url https://graph.microsoft.com/beta/deviceAppManagement/mobileApps params { $filter: isof(microsoft.graph.win32LobApp), $top: 50 } all_apps [] while graph_url: resp requests.get(graph_url, headersheaders, paramsparams if graph_url.startswith(https://graph) else None) data resp.json() if value not in data: print(接口返回异常:) print(json.dumps(data, indent2, ensure_asciiFalse)) break all_apps.extend(data[value]) # 这里要特别注意分页 graph_url data.get(odata.nextLink, ) params None print(f命中 Win32 应用总数: {len(all_apps)})注意分页逻辑。Graph API 返回结果超过一页时响应里会出现odata.nextLink它已经带了完整的后续链接直接对它继续 GET 即可。我见过很多新手在第二页开始报错就是因为用原来的params去拼接结果丢掉了skiptoken。接着解析检测规则output [] for app in all_apps: app_name app.get(displayName, ) app_id app.get(id, ) rules app.get(rules, []) has_detection False for rule in rules: if rule.get(ruleType) ! detection: continue detection rule.get(detection, rule) odata_type detection.get(odata.type, ) record { 应用ID: app_id, 应用名称: app_name, 检测类型: odata_type, } if FileSystem in odata_type: record[路径] detection.get(path, ) record[文件/文件夹名] detection.get(fileOrFolderName, ) record[检测行为] detection.get(operationType, ) elif Registry in odata_type: record[注册表路径] detection.get(registryKeyPath, ) record[值名称] detection.get(registryValueName, ) record[检测方式] detection.get(detectionType, ) elif ProductCode in odata_type: record[产品代码] detection.get(productCode, ) record[产品版本] detection.get(productVersion, ) elif PowerShellScript in odata_type: record[脚本内容(Base64)] detection.get(scriptContent, ) record[强制签名校验] detection.get(enforceSignatureCheck, ) output.append(record) has_detection True if not has_detection: output.append({ 应用ID: app_id, 应用名称: app_name, 检测类型: 无检测规则, }) print(解析完成共生成 {} 条记录.format(len(output)))这段代码兼容了“检测规则直接嵌在 rule 里”和“检测规则在 rule.detection 里”两种常见结构。我特意写了detection rule.get(detection, rule)这个兜底逻辑就是因为真实租户返回结构有差异少一个detection层级很常见。3.4 批量导出 CSV 并验证结果拿到output列表后导出 CSV 就很简单了import csv csv_columns [ 应用ID, 应用名称, 检测类型, 路径, 文件/文件夹名, 检测行为, 注册表路径, 值名称, 检测方式, 产品代码, 产品版本, 脚本内容(Base64), 强制签名校验 ] with open(win32_detection_rules.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnamescsv_columns) writer.writeheader() writer.writerows(output) print(导出完成请检查 win32_detection_rules.csv)这里的编码我特意用了utf-8-sig否则 CSV 用 Excel 打开时中文会乱码。输出后建议抽几个应用回控制台人工核对一次。我每次写完这类脚本都会先用displayName过滤一个小范围试跑确认字段映射没问题才放全量跑。3.5 用 Graph SDK 快速验证的替代方案如果你不想自己处理分页和 JSON 结构也可以先用官方 SDK 做个快速验证。PowerShell 的 Microsoft Graph SDK 命令大概是这样的Connect-MgGraph -Scopes DeviceManagementApps.Read.All Get-MgDeviceAppManagementMobileApp -All | Where-Object { $_.AdditionalProperties.odata.type -eq #microsoft.graph.win32LobApp } | Select-Object DisplayName, IdGet-MgDeviceAppManagementMobileApp返回的是Microsoft.Graph.PowerShell.Models.MicrosoftGraphMobileApp对象检测规则等自定义扩展字段需要从AdditionalProperties里取把它转成 JSON 再查rules即可。这个方法适合临时验证跑批效率还是 Python 脚本更高。4. 常见问题与排错技巧实录4.1 directory picker failed 错误与文件夹选择器问题在做 Win32 应用打包或者调试脚本时我遇到过几次directory picker failed: directory picker failed: win32 folder dialog worker这样的报错。这个错误的字面意思是 Windows 文件夹选择对话框工作进程异常通常出现在从工具界面上点击“浏览”选择源文件目录时目录会被拒绝选中或者对话框直接闪退。根据几次现场排查原因集中在三方面旧版 Win32 文件夹对话框组件与新版 Windows 图形栈兼容性问题尤其发生在使用老式SHBrowseForFolder接口的工具中路径过长、包含特殊字符导致对话框 worker 处理超时比如路径结尾带空格或者包含#、这类字符系统资源异常explorer 进程或系统索引服务异常导致对话框无法正常回调解决办法有三个层级。最简单的就是重启explorer.exe或者注销重登一次让对话框外壳组件重新启动。如果问题还复现就不要在工控界面上浏览目录直接在地址栏粘贴完整路径比如C:\Intune\Packages\MyApp。最后如果路径里确实有特殊字符最稳妥的办法是先把目录复制到不含特殊字符的路径下比如C:\PackageSource\下再选择。这个问题跟 Graph API 本身没有直接关系但它是 Win32 应用打包与自动化流程里很典型的“外围坑”单独列出来提醒大家遇到别慌不是你的代码问题。4.2 检测规则返回为空的三种原因这个是我在群里被问过最多的问题明明控制台里检测规则写得清清楚楚API 拉出来rules却是空的。排查过后无非三种情况第一种是过滤条件太严格。有的租户把 Win32 应用建成了managedAndroidLobApp或其他派生类型或者封装方式特殊导致isof(microsoft.graph.win32LobApp)匹配不到。这种情况建议先不加$filter全量拉一遍看返回的所有odata.type都有哪些。第二种是解析层级遗漏。前面说过检测规则可能在rule.detection里也可能直接平铺在rule顶层。如果你只取detection字段而实际接口把字段直接放在rule上就会漏掉绝大多数规则。第三种是内容版本尚未提交。有些应用创建后还没有完成内容上传和提交rules集合里可能暂时查不到完整的检测数据结构。这时候需要确认应用状态是否为“已就绪”或“可用”等待内容版本提交完成后再拉取。4.3 分页和筛选条件让脚本偶尔抽风我在脚本里见过几个典型的坑。首先是$top与isof组合使用后某些租户返回的odata.nextLink会丢失类型过滤导致第二页混杂其他类型应用。解决办法是在循环里对odata.type做二次判断不要完全依赖第一页的过滤条件。其次是$filter无法直接用startswith(rules/...)这种嵌套路径去做深度过滤Graph API 对rules属性并不支持 OData 深度筛选。所以实际策略只能是“全量拉取 本地过滤”数量少的时候还好如果租户里 Win32 应用上千建议按$top100分页拉取避免单次响应体太大导致网络超时。最后是beta和v1.0的数据一致性。mobileApps这个接口在 v1.0 也能用但部分字段比如installExperience、rules在 v1.0 里可能不完整。我一般固定用 beta 端点做读取分析因为字段更全。4.4 检测规则更新后的生效延迟另一个值得注意的现象是你用 Graph API 修改了一条检测规则PATCH 请求当下读取可能还是旧值需要等一段时间。这是因为 Intune 后台存在异步内容发布流程特别是 Win32 应用的内容版本需要重新提交和同步。同步延时的长短没有官方明确承诺我的经验是 30 秒到 5 分钟不等。如果你在做自动化变更后的立刻回读验证建议在 PATCH 和 GET 之间加一个重试等待逻辑反复验证直到数据一致。不要把“立即回读”写进强校验里否则你的 CI/CD 流水线会偶发失败一段时间后你会发现日志里全是这种伪报错。另外注意Win32 应用的检测规则修改并不会自动触发应用“重新部署”只会影响后续的安装状态判定。如果需要让已安装的设备重新评估你需要通过 Graph API 触发设备同步或者让用户在 Intune 公司门户里点击检查。5. 个人操作体会与后续扩展建议把检测规则从 Graph API 里拉出来只是第一步我更建议你在这一步之上再构建一个小型分析工具把检测规则的结构化数据沉淀下来。比如输出一个规则覆盖率报告哪些应用还没有检测规则哪些应用只是简单文件存在检测哪些应用用了复杂脚本检测但没开启签名校验。这比单纯导出 CSV 更有价值能直接指导企业应用基线整改。我个人在实操中最喜欢的做法是给每条检测规则打一个“严格度”标签文件存在检测为低严格度注册表加版本为中等严格度MSI 产品代码或脚本检测为高严格度。然后每周跑一次统计把“低严格度而且没有版本验证”的应用清单发给相关负责人作为优化排期依据。这套玩法不需要额外系统一个 Python 脚本加一个定时任务就能跑起来。最后再分享一个经验不要迷信任何文档里的返回结构长这样就在生产环境直接跑数。Graph API 的 Intune 模型迭代不算慢不同区域租户的行为也可能有细微差异。接到手的数据先看作“线索”打印几份原始响应确认后在写解析十分钟的确认时间能帮你省下后面大把排查功夫。毕竟这年头最贵的不是代码而是被垃圾数据误导后浪费掉的那一夜。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →