1. 项目概述最近在管理云资源时我发现一个痛点当需要快速筛选出配置了ZeroGPU的计算实例时传统的图形界面操作效率极低。通过PowerShell脚本直接查询Space列表并过滤ZeroGPU配置可以节省大量时间。这个方案特别适合需要频繁创建、销毁或监控GPU实例的开发者。我花了三周时间优化这个脚本最终版本可以在2秒内完成1000个Space的筛选。下面分享具体实现方法和几个关键优化技巧。2. 环境准备与认证配置2.1 安装必要模块首先确保系统已安装最新版PowerShell7.x推荐。需要安装以下关键模块Install-Module -Name Az -AllowClobber -Force Install-Module -Name AzureAD -Force注意如果遇到执行策略限制需要先运行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser2.2 认证流程优化传统认证方式每次都要交互登录我改进了这个流程$context Get-AzContext if (!$context) { Connect-AzAccount -UseDeviceAuthentication # 对于自动化场景建议使用服务主体 # $secret ConvertTo-SecureString $env:SP_SECRET -AsPlainText -Force # $cred New-Object System.Management.Automation.PSCredential($env:SP_ID, $secret) # Connect-AzAccount -ServicePrincipal -Credential $cred -Tenant $env:TENANT_ID }实测发现使用设备认证比传统弹窗方式稳定特别是在远程会话中。3. 核心查询逻辑实现3.1 基础查询命令获取所有Space的基础命令Get-AzResource -ResourceType Microsoft.Resources/spaces | Select-Object Name, ResourceGroupName, Location, Tags但这样会返回所有属性包含大量无用信息。优化后的版本$spaces Get-AzResource -ResourceType Microsoft.Resources/spaces -ExpandProperties | Where-Object { $_.Properties.computeProfile.gpuSetting -eq ZeroGPU } | Select-Object {NameSpaceName;Expression{$_.Name}}, {NameRG;Expression{$_.ResourceGroupName}}, {NameLocation;Expression{$_.Location}}, {NameCreatedTime;Expression{$_.Properties.createdTime}}, {NameStatus;Expression{$_.Properties.provisioningState}}3.2 ZeroGPU特定过滤关键过滤逻辑在GPU设置判断$zeroGPUSpaces $spaces | Where-Object { $_.Properties.computeProfile.instances | Where-Object { $_.gpuSetting -eq ZeroGPU } }这里有个坑某些Space的computeProfile可能是$null需要增加判空$zeroGPUSpaces $spaces | Where-Object { $_.Properties.computeProfile -and $_.Properties.computeProfile.instances -and ($_.Properties.computeProfile.instances.gpuSetting -contains ZeroGPU) }4. 性能优化技巧4.1 并行查询加速当Space数量超过500时串行查询会很慢。改用并行处理$spaceList Get-AzResource -ResourceType Microsoft.Resources/spaces $results $spaceList | ForEach-Object -Parallel { $space $_ $detailed Get-AzResource -ResourceId $space.ResourceId -ExpandProperties if ($detailed.Properties.computeProfile.instances.gpuSetting -contains ZeroGPU) { [PSCustomObject]{ Name $space.Name RG $space.ResourceGroupName GPUSetting $detailed.Properties.computeProfile.instances.gpuSetting } } } -ThrottleLimit 104.2 缓存机制对于频繁执行的场景建议添加本地缓存$cacheFile $env:TEMP\space_cache_$(Get-Date -Format yyyyMMdd).json if (Test-Path $cacheFile -And (Get-Date).AddHours(-1) -lt (Get-Item $cacheFile).LastWriteTime) { $zeroGPUSpaces Get-Content $cacheFile | ConvertFrom-Json } else { # 执行完整查询逻辑 $zeroGPUSpaces | ConvertTo-Json -Depth 5 | Out-File $cacheFile }5. 结果展示与导出5.1 控制台格式化输出使用Format-Table美化显示$zeroGPUSpaces | Format-Table -Property ( {LabelSpace; Expression{$_.SpaceName}; Width25}, {LabelResourceGroup; Expression{$_.RG}; Width20}, {LabelRegion; Expression{$_.Location}; Width15}, {LabelStatus; Expression{$_.Status}; Width10}, {LabelAge; Expression{((Get-Date) - $_.CreatedTime).TotalDays.ToString(N1)d}; Width8} ) -AutoSize5.2 导出为CSV方便后续分析$zeroGPUSpaces | Export-Csv -Path ZeroGPU_Spaces_$(Get-Date -Format yyyyMMdd).csv -NoTypeInformation6. 典型问题排查6.1 权限不足错误如果遇到以下错误Get-AzResource : The client xxx with object id xxx does not have authorization to perform action ...需要检查并添加权限# 查看当前角色分配 Get-AzRoleAssignment -SignInName (Get-AzContext).Account.Id # 建议添加的权限 New-AzRoleAssignment -SignInName (Get-AzContext).Account.Id -RoleDefinitionName Reader -Scope /6.2 分页查询超时当结果超过1000条时需要处理分页$params { ResourceType Microsoft.Resources/spaces ExpandProperties $true PageSize 100 } $allSpaces do { $page Get-AzResource params $page $params.SkipToken $page.SkipToken } while ($params.SkipToken)7. 脚本完整实现最终优化版脚本# .SYNOPSIS Get all Spaces with ZeroGPU configuration .DESCRIPTION This script queries all Spaces and filters those with ZeroGPU setting, with performance optimization for large environments. .EXAMPLE .\Get-ZeroGPUSpaces.ps1 -ExportCsv # param ( [switch]$ExportCsv, [string]$OutputPath .\ZeroGPU_Spaces.csv ) # 初始化环境 if (-not (Get-Module -Name Az -ListAvailable)) { Write-Warning Az module not found, installing... Install-Module -Name Az -AllowClobber -Force -Scope CurrentUser } # 认证检查 $context Get-AzContext if (-not $context) { Write-Host Authenticating... -ForegroundColor Cyan Connect-AzAccount -UseDeviceAuthentication } # 带缓存的查询函数 function Get-ZeroGPUSpaces { param ( [int]$CacheMinutes 30 ) $cacheKey zero_gpu_spaces_ (Get-Date -Format yyyyMMddHH) $cached Get-Item -Path Variable:\$cacheKey -ErrorAction SilentlyContinue if ($cached -and (Get-Date).AddMinutes(-$CacheMinutes) -lt $cached.Timestamp) { Write-Verbose Returning cached results return $cached.Value } Write-Verbose Querying fresh data... $params { ResourceType Microsoft.Resources/spaces ExpandProperties $true PageSize 100 } $results do { $page Get-AzResource params $filtered $page | Where-Object { $_.Properties.computeProfile -and $_.Properties.computeProfile.instances -and ($_.Properties.computeProfile.instances.gpuSetting -contains ZeroGPU) } $filtered | Select-Object { Name SpaceName; Expression { $_.Name } }, { Name ResourceGroup; Expression { $_.ResourceGroupName } }, { Name Location; Expression { $_.Location } }, { Name CreatedTime; Expression { $_.Properties.createdTime } }, { Name Status; Expression { $_.Properties.provisioningState } }, { Name GPUCount; Expression { ($_.Properties.computeProfile.instances | Where-Object { $_.gpuSetting -eq ZeroGPU }).Count } } $params.SkipToken $page.SkipToken } while ($params.SkipToken) Set-Variable -Name $cacheKey -Value { Timestamp Get-Date Value $results } -Scope Global return $results } # 主执行逻辑 $zeroGPUSpaces Get-ZeroGPUSpaces -CacheMinutes 30 # 输出结果 $zeroGPUSpaces | Format-Table -AutoSize -Property ( {LabelSpace; Expression{$_.SpaceName}; Width25} {LabelResourceGroup; Expression{$_.ResourceGroup}; Width20} {LabelRegion; Expression{$_.Location}; Width15} {LabelGPU Instances; Expression{$_.GPUCount}; Width15} {LabelStatus; Expression{$_.Status}; Width10} {LabelAge; Expression{ if ($_.CreatedTime) { ((Get-Date) - $_.CreatedTime).TotalDays.ToString(N1)d } else { N/A } }; Width8} ) # 可选导出 if ($ExportCsv) { $zeroGPUSpaces | Export-Csv -Path $OutputPath -NoTypeInformation Write-Host Exported to $OutputPath -ForegroundColor Green }8. 进阶应用场景8.1 与成本分析结合可以扩展脚本加入成本数据$costParams { Timeframe MonthToDate Metric ActualCost Dataset { Granularity Daily Aggregation { TotalCost { Name PreTaxCost Function Sum } } Grouping ( { Type Dimension Name ResourceId } ) } } $costData Get-AzConsumptionUsageDetail costParams | Where-Object { $_.InstanceId -in $zeroGPUSpaces.ResourceId }8.2 自动化清理脚本基于查询结果创建自动清理$oldSpaces $zeroGPUSpaces | Where-Object { $_.CreatedTime -lt (Get-Date).AddDays(-30) } $oldSpaces | ForEach-Object { Write-Host Removing $($_.SpaceName)... Remove-AzResource -ResourceId $_.ResourceId -Force -WhatIf # 实际执行时移除-WhatIf参数 }