GalloTheFourth-RG/aperture-data-collector
GitHub: GalloTheFourth-RG/aperture-data-collector
一款面向Azure虚拟桌面环境的只读数据收集工具,将ARM资源清单、监控指标和日志分析查询结果打包为可离线分析的JSON压缩包。
Stars: 0 | Forks: 0
# Aperture 数据收集器
[](LICENSE)


从您的 AVD 部署中收集 ARM 资源清单、Azure Monitor 指标和 Log Analytics (KQL) 查询结果,并将其导出为可移植的**收集包** —— 一个包含 JSON 文件的 ZIP 压缩包,您可以将其输入到任何工具中。
**不进行分析、不评分、不含专有逻辑。** 只有原始数据,完全透明。
## ⚡ 快速安装
克隆仓库:
```
git clone https://github.com/GalloTheFourth-RG/aperture-data-collector.git
cd avd-data-collector
```
或者从 GitHub 下载 ZIP:**Code** → **Download ZIP** → 解压到文件夹。
## 🚀 快速开始
```
# 模拟运行 — 预览将收集的内容 (无数据离开 Azure)
.\Collect-ApertureData.ps1 `
-TenantId "your-tenant-id" `
-SubscriptionIds @("your-sub-id") `
-DryRun
# 完整收集 — ARM + metrics + KQL
.\Collect-ApertureData.ps1 `
-TenantId "your-tenant-id" `
-SubscriptionIds @("your-sub-id") `
-LogAnalyticsWorkspaceResourceIds @(
"/subscriptions//resourceGroups//providers/Microsoft.OperationalInsights/workspaces/"
)
# 包含所有扩展数据的完整收集 (cost, network, storage, images, 等)
.\Collect-ApertureData.ps1 `
-TenantId "your-tenant-id" `
-SubscriptionIds @("your-sub-id") `
-LogAnalyticsWorkspaceResourceIds @("/subscriptions/.../workspaces/") `
-IncludeAllExtended `
-IncludeReservedInstances
```
输出:`Aperture-CollectionPack-YYYYMMDD-HHMMSS.zip`
## 📋 收集内容
| 类别 | 数据 | API 来源 |
|----------|------|-----------|
| **Host Pools** | 配置、负载均衡、RDP 设置 | `Get-AzWvdHostPool` |
| **Session Hosts** | 状态、代理版本、健康状况、活跃会话 | `Get-AzWvdSessionHost` |
| **Virtual Machines** | 大小、OS、区域、磁盘、NIC、安全配置文件、扩展 | `Get-AzVM` |
| **VM Scale Sets** | VMSS 配置 + 单个实例详情 | `Get-AzVmss` |
| **Application Groups** | 应用组类型、主机池分配 | `Get-AzWvdApplicationGroup` |
| **Scaling Plans** | 自动缩放定义、计划、池分配 | ARM API |
| **Metrics** | 每台 VM 的 CPU、内存、磁盘 IOPS(可配置回溯) | `Get-AzMetric` |
| **Log Analytics** | 36 个 KQL 查询 — 连接、错误、配置文件、Shortpath、代理健康状况 | `Invoke-AzOperationalInsightsQuery` |
| **Capacity Reservations** | CRG 利用率、已分配与已使用容量 | ARM REST API |
| **Quota Usage** | 每个区域的 vCPU 配额(当前/限制) | `Get-AzVMUsage` |
| **Reserved Instances** | RI 订单、SKU、期限、到期时间、利用率 | `Az.Reservations` |
| **Cost Data** ⁺ | 每台 VM 和基础设施成本(过去 30 天) | Cost Management API |
| **Network Topology** ⁺ | 子网、VNet、NSG 规则、私有端点、NAT Gateway | `Az.Network` + ARM |
| **Image Analysis** ⁺ | Gallery 映像版本、市场新鲜度 | ARM API |
| **Storage** ⁺ | FSLogix 存储账户、文件共享、容量 | `Az.Storage` |
| **Orphaned Resources** ⁺ | 未挂载磁盘、未使用 NIC、未关联 PIP | ARM |
| **Diagnostics/Alerts** ⁺ | 诊断设置、警报规则、活动日志 | ARM REST API |
| **Governance** ⁺ | 策略分配、资源标签 | ARM REST API |
## 🔒 安全与隐私
本节记录了 Aperture 数据收集器的安全状况,供信息安全、合规性和风险团队审查。
### 安全保证
| 保证 | 详情 |
|-----------|--------|
| **只读** | 每个 API 调用都是 `GET` 或只读 cmdlet(`Get-AzVM`、`Get-AzWvdHostPool` 等)。脚本绝不创建、修改或删除任何 Azure 资源。 |
| **无出站数据传输** | 所有收集的数据仅写入本地文件系统。脚本不会调用外部服务、遥测端点或第三方 API。 |
| **无凭证存储** | 脚本不存储、缓存或导出任何 Azure 凭证、令牌或密钥。身份验证完全由 `Az.Accounts` 模块的现有会话处理。 |
| **透明输出** | 所有输出均为纯 JSON —— 在共享前完全可检查、可过滤和可编辑。 |
| **输出中无可执行代码** | 收集包 ZIP 仅包含 JSON 数据文件和元数据清单。没有脚本、二进制文件或可执行内容。 |
| **已签名且可审计** | 脚本是开源的 (MIT)。您的安全团队可以在执行前检查每一行代码。 |
### 脚本访问的内容
| Azure 资源 | 访问类型 | 所需角色 | 用途 |
|---------------|-------------|---------------|---------|
| Subscriptions | 读取 | Reader | 枚举 AVD 资源 |
| Host pools, session hosts, app groups | 读取 | Reader | AVD 清单 |
| Virtual machines, NICs, disks | 读取 | Reader | VM 配置和大小 |
| Azure Monitor metrics | 读取 | Reader | CPU、内存、磁盘性能 |
| Log Analytics workspaces | 查询 | Log Analytics Reader | 会话、连接和错误遥测 |
| Cost Management API | 读取 | Cost Management Reader | 每 VM 成本数据(仅限 opt-in) |
| Network resources | 读取 | Reader | 子网、NSG、VNet 拓扑(仅限 opt-in) |
| Storage accounts | 读取 | Reader | FSLogix 共享分析(仅限 opt-in) |
| Reserved Instances | 读取 | Reservations Reader | RI 利用率(仅限 opt-in) |
### 未收集的内容
脚本**不**访问或收集:
- 密码、密钥、证书或 Key Vault 内容
- 文件共享内容、用户文件或配置文件数据
- 应用程序数据或数据库内容
- Azure AD/Entra ID 用户属性(除了用于会话关联的 UPN)
- 网络流量或数据包捕获
- OS 级配置(注册表、本地策略、组策略)
- 来自本地或非 Azure 系统的任何数据
### 网络行为
脚本仅使用您现有的经过身份验证的会话与 Azure 管理平面 API(`management.azure.com`、`api.loganalytics.io`)通信。它不会:
- 打开任何监听端口
- 向非 Azure 域发起 DNS 查询
- 建立到任何非 Microsoft Azure 拥有的 IP 或域的出站连接
- 使用 WebSockets、SignalR 或持久连接
- 在运行时下载任何外部内容或依赖项
### PII 脱敏
添加 `-ScrubPII` 以在将所有可识别数据写入磁盘**之前**对其进行匿名化处理:
| 数据类别 | 脱敏前示例 | 脱敏后示例 |
|--------------|---------------|---------------|
| VM names | `avd-prod-vm-001` | `Host-3F7C` |
| Host pool names | `HP-Finance-US` | `Pool-D4E5` |
| Usernames (UPN) | `jsmith@contoso.com` | `User-A1B2` |
| Subscription IDs | `12345678-abcd-...` | `Sub-F6A1` |
| Resource groups | `rg-avd-prod` | `RG-B2C3` |
| IP addresses | `10.0.1.50` | `IP-7D8E` |
| ARM resource IDs | `/subscriptions/12345.../rg-prod/...` | `/subscriptions/Sub-F6A1/resourceGroups/RG-B2C3/...` |
| Storage accounts | `stfslogixprod01` | `Storage-9A2B` |
| Subnet names | `snet-avd-prod` | `Subnet-C3D4` |
- 使用带有每次运行随机盐值的 **SHA256 哈希**
- 同一实体在一次运行中始终映射到相同的匿名 ID(保留关联性)
- 不同的运行产生不同的 ID(无跨运行可链接性)
- 脱敏在任何文件写入之前在内存中发生 —— 原始数据永不接触磁盘
```
.\Collect-ApertureData.ps1 `
-TenantId "your-tenant-id" `
-SubscriptionIds @("your-sub-id") `
-LogAnalyticsWorkspaceResourceIds @("/subscriptions/.../workspaces/your-ws") `
-ScrubPII
```
### 共享前检查
输出 ZIP 仅包含纯 JSON 文件。在共享之前:
1. 解压收集包
2. 在文本编辑器或 VS Code 中打开任意 JSON 文件
3. 搜索您不愿意共享的任何字符串
4. 删除或编辑特定文件,然后重新压缩
### HIPAA 与医疗环境
对于受 HIPAA 约束的医疗机构:
- 收集器**不**访问、处理或存储受保护健康信息 (PHI)
- 来自 Log Analytics 的会话遥测仅包含 UPN 和连接元数据 —— 无临床数据
- 使用 `-ScrubPII` 在数据离开环境之前对所有 UPN 字段进行匿名化
- 输出包含基础设施配置和性能指标 —— 无患者数据、医疗记录或临床应用数据
- 脚本在管理员工作站上运行,不与临床系统、EHR 数据库或医疗设备交互
- 收集工作可由您的内部团队执行,并在与外部顾问共享前进行审查
## 📦 要求
| 要求 | 详情 |
|-------------|---------|
| **PowerShell** | 7+ (`pwsh.exe`,而非 `powershell.exe`) |
| **Az Modules** | `Az.Accounts`, `Az.Compute`, `Az.DesktopVirtualization`, `Az.Monitor`, `Az.OperationalInsights`, `Az.Resources` |
| **Optional Modules** | `Az.Network` (网络拓扑), `Az.Storage` (存储分析), `Az.Reservations` (RI 收集) |
| **Azure RBAC** | AVD 订阅上的 **Reader** + 工作区上的 **Log Analytics Reader** |
如果您尚未安装 Az 模块,请安装:
```
Install-Module Az.Accounts, Az.Compute, Az.DesktopVirtualization, Az.Monitor, Az.OperationalInsights, Az.Resources -Scope CurrentUser
```
## ⚙️ 参数
### 必需
| 参数 | 描述 |
|-----------|-------------|
| `-TenantId` | Azure AD / Entra ID 租户 ID |
| `-SubscriptionIds` | 包含 AVD 资源的订阅 ID 数组 |
### 推荐
| 参数 | 描述 |
|-----------|-------------|
| `-LogAnalyticsWorkspaceResourceIds` | 用于 KQL 查询的工作区资源 ID |
### 收集控制
| 参数 | 默认值 | 描述 |
|-----------|---------|-------------|
| `-SkipAzureMonitorMetrics` | `$false` | 跳过 CPU/内存/磁盘指标收集 |
| `-SkipLogAnalyticsQueries` | `$false` | 跳过所有 KQL 查询 |
| `-MetricsLookbackDays` | `7` | 指标历史天数 (1–30) |
| `-MetricsTimeGrainMinutes` | `15` | 聚合间隔 (5/15/30/60 分钟) |
| `-IncludeCapacityReservations` | `$false` | 收集容量预留组数据 |
| `-IncludeQuotaUsage` | `$false` | 收集每个区域的 vCPU 配额数据 |
| `-IncludeReservedInstances` | `$false` | 收集 Azure Reserved Instances(需要 Az.Reservations) |
| `-ScrubPII` | `$false` | 在导出前对所有可识别数据进行匿名化 |
### 扩展收集 (v1.1.0)
使用 `-IncludeAllExtended` 一次启用所有这些功能,或单独选择:
| 参数 | 描述 |
|-----------|-------------|
| `-IncludeCostData` | Azure Cost Management 每台 VM 和基础设施成本(30 天) |
| `-IncludeNetworkTopology` | VNet/子网分析、NSG 规则、私有端点、NAT Gateway |
| `-IncludeImageAnalysis` | Compute Gallery 映像版本、市场映像新鲜度 |
| `-IncludeStorageAnalysis` | FSLogix 存储账户、文件共享容量和配额 |
| `-IncludeOrphanedResources` | 未挂载磁盘、未使用 NIC、未关联公共 IP |
| `-IncludeDiagnosticSettings` | 主机池诊断日志转发配置 |
| `-IncludeAlertRules` | Azure Monitor 指标警报和计划查询规则 |
| `-IncludeActivityLog` | 每个 AVD 资源组的活动日志条目(过去 7 天) |
| `-IncludePolicyAssignments` | Azure Policy 分配和合规状态 |
| `-IncludeResourceTags` | 从 VM、主机池、存储账户提取标签 |
### 事件窗口
事件窗口功能允许您收集**第二组集中的 Azure Monitor 指标和 KQL 查询结果**,涵盖特定的过去停机或性能事件。这与您的基线数据(默认为 7 天回溯)并存,因此您的顾问可以将正常状态下的性能与事件期间的数据进行并排比较。
**何时使用:** 用户在已知时间窗口内报告了延迟、断开连接或登录失败,并且您需要针对性的数据进行根因分析。
| 参数 | 描述 |
|-----------|-------------|
| `-IncludeIncidentWindow` | 为特定事件期间收集第二组指标和 KQL 查询 |
| `-IncidentWindowStart` | 事件窗口开始时间(日期时间)。默认:14 天前 |
| `-IncidentWindowEnd` | 事件窗口结束时间(日期时间)。默认:现在 |
事件窗口生成一个单独的 `metrics-incident.json` 文件和带有 incident 前缀的 KQL 结果(连接、峰值并发配置文件加载时间、错误、连接质量),这些将在证据包的 **Incident Analysis** 选项卡中与基线数据一起分析。
### 操作
| 参数 | 默认值 | 描述 |
|-----------|---------|-------------|
| `-DryRun` | `$false` | 预览收集范围而不实际运行 |
| `-SkipDisclaimer` | `$false` | 跳过交互式免责声明提示 |
| `-OutputPath` | `.` | 输出收集包的目录 |
## 📊 输出
收集器生成一个包含 JSON 数据文件的 ZIP:
```
Aperture-CollectionPack-20260225-120000/
├── collection-metadata.json # Schema version, parameters, counts
├── host-pools.json # Host pool configurations
├── session-hosts.json # Session host status & health
├── virtual-machines.json # Full VM inventory
├── metrics-baseline.json # Azure Monitor metric datapoints
├── metrics-incident.json # Incident window metrics (if requested)
├── la-results.json # All KQL query results
├── diagnostic-readiness.json # Diagnostic data availability per table group
├── scaling-plans.json # Autoscale plan definitions
├── scaling-plan-assignments.json # Plan-to-pool assignments
├── scaling-plan-schedules.json # Schedule details per plan
├── app-groups.json # Application groups
├── vmss.json # VM Scale Set configurations
├── vmss-instances.json # VMSS instance details
├── capacity-reservation-groups.json # CRG utilization (if requested)
├── quota-usage.json # vCPU quota (if requested)
├── reserved-instances.json # Reserved Instance data (if requested)
├── actual-cost-data.json # Per-VM daily costs (extended)
├── vm-actual-monthly-cost.json # VM monthly cost lookup (extended)
├── infra-cost-data.json # Infrastructure costs per RG (extended)
├── cost-access.json # Cost API access status (extended)
├── subnet-analysis.json # Subnet details + NSG coverage (extended)
├── vnet-analysis.json # VNet DNS, peering, topology (extended)
├── private-endpoint-findings.json # Private endpoint status (extended)
├── nsg-rule-findings.json # Risky NSG inbound rules (extended)
├── orphaned-resources.json # Unattached disks, unused NICs (extended)
├── fslogix-storage-analysis.json # Storage account analysis (extended)
├── fslogix-shares.json # File share details (extended)
├── diagnostic-settings.json # Host pool diagnostic config (extended)
├── alert-rules.json # Azure Monitor alerts (extended)
├── activity-log.json # Activity log entries (extended)
├── policy-assignments.json # Azure Policy assignments (extended)
├── gallery-analysis.json # Compute Gallery images (extended)
├── gallery-image-details.json # Gallery image version details (extended)
├── marketplace-image-details.json # Marketplace image data (extended)
└── resource-tags.json # Resource tags (extended)
```
## 🔍 KQL 查询 (36)
所有查询位于 `queries/` 中,可以自定义。类别:
| 类别 | 捕获内容 |
|----------|-----------------|
| **Connections** | 摘要、成功率、登录时间、会话持续时间、峰值和每小时并发 |
| **Errors** | 错误分类、连接失败、断开原因 |
| **Disconnects** | 按主机、热力图、CPU 相关性、重连循环 |
| **Network** | RTT/带宽质量、跨区域、Shortpath 使用/有效性/传输 |
| **Performance** | 进程 CPU/内存消耗、每台主机的 CPU 百分位 |
| **Profiles** | FSLogix 配置文件加载性能、检查点登录分解 |
| **Agent Health** | RD Agent 状态、版本分布、健康检查结果 |
| **Autoscale** | 缩放活动、每个主机池的详细评估 |
| **Environment** | 客户端 OS、身份加入类型、表发现 |
| **Transport** | 多链路传输协商和分布 |
## 🔗 离线分析工作流
收集包 ZIP 专为离线、断开连接的分析而设计 —— 收集后不需要 Azure 凭证:
1. **收集** —— 针对客户的 Azure 环境运行收集器
2. **共享** —— 将 ZIP 发送给分析师(共享前使用 `-ScrubPII` 进行匿名化)
3. **分析** —— 分析师使用其评估工具导入 ZIP —— 无需 Azure 访问权限
这种分离实现了:
- **委托收集** —— 拥有 Azure 访问权限的人运行收集器;分析师离线工作
- **隐私控制** —— PII 脱敏确保敏感数据永不离开客户的控制
- **可重复性** —— 使用更新的工具或不同的参数重新分析相同的数据
- **归档** —— 保留收集包以便跨项目进行历史比较
## ⏱️ 运行时间估算
Azure Virtual Desktop 支持每个主机池最多 **10,000 个会话主机**,每个订阅每个区域最多 **25,000 个 VM**([Azure 服务限制](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits))。收集器旨在处理这些规模的环境。
| 环境规模 | VMs | 预计时间 |
|-----------------|-----|----------------|
| Small | ~50 | 3–5 分钟 |
| Medium | ~200 | 8–15 分钟 |
| Large | ~500 | 15–25 分钟 |
| Very Large | ~1,500 | 30–60 分钟 |
| Enterprise | ~3,000 | 60–90 分钟 |
| Large Enterprise | 5,000+ | 90–150+ 分钟 |
**影响运行时间的因素:**
| 收集阶段 | 随...增长 | 影响 |
|-----------------|-------------|--------|
| ARM resources (host pools, VMs, NICs) | 资源组 | 快 —— 按 RG 批量获取 |
| Azure Monitor metrics | VM 数量 | **主要时间驱动因素** —— 按 VM 并行处理 |
| Log Analytics (36 KQL queries) | 工作区数量 | 中等 —— 按工作区并行化 |
| Extended collection (costs, network, storage, orphans, diagnostics, alerts) | 订阅范围 | 启用时增加 5–15 分钟 |
**大型环境提示:**
- 使用 `-SkipAzureMonitorMetrics` 进行仅清单运行(无论大小约 2–5 分钟)
- 调整 `-MetricsParallel`(默认 15)和 `-KqlParallel`(默认 5)以适应对限制敏感的租户
- 减少 `-MetricsLookbackDays`(默认 7)以缩短指标窗口
- 使用 `-MetricsTimeGrainMinutes 60`(默认 15)以更粗的粒度换取更快的收集速度
- 对于 5,000+ VM 的环境,考虑在非高峰时段收集以避免 API 限制
## 📁 项目结构
```
avd-data-collector/
├── Collect-ApertureData.ps1 # Main collector script (source)
├── build.ps1 # Build script (embeds KQL → dist/)
├── dist/ # Built distributable (self-contained)
│ └── Collect-ApertureData.ps1
├── queries/ # 36 KQL query files (customizable)
│ ├── kqlTableDiscovery.kql
│ ├── kqlWvdConnections.kql
│ ├── kqlConnectionErrors.kql
│ └── ...
├── docs/ # Documentation
│ ├── QUERIES.md
│ ├── SCHEMA.md
│ └── USER-MANUAL.md
├── tools/ # Development utilities
├── examples/ # Usage examples
├── LICENSE # MIT License
├── CHANGELOG.md
└── CONTRIBUTING.md
```
## 📜 许可证
[MIT](LICENSE) —— 随意使用。
## 🤝 贡献
参见 [CONTRIBUTING.md](CONTRIBUTING.md)。欢迎提交针对新 KQL 查询、错误修复和文档的 PR。
标签:ARM 资源, AVD, Awesome, AZ-140, Azure Monitor, Azure 虚拟桌面, Homebrew安装, IT 运维, JSON 导出, KQL 查询, Libemu, Log Analytics, Microsoft Azure, OpenCanary, PowerShell 脚本, VDI 诊断, 云桌面运维, 会话主机, 库存收集, 数据采集器, 离线分析, 租户配置, 系统管理, 资源监控, 远程桌面服务