Commands reference
Every script lives in scripts/ and dot-sources scripts/Common.ps1. Run them from the repo
root (e.g. .\scripts\Get-UsedSkus.ps1 …).
Common parameters
Most scripts accept:
-Location <region>— e.g.norwayeast.-SubscriptionIds <guid[]>or-SubscriptionCsv <path>— omit both to scan all visible subscriptions. The CSV needs aSubIdcolumn (and optionallyName).-OutPath/-OutDir— where results are written (defaults tooutput/).
Common.ps1 exposes the shared helpers: Get-ScriptDir, Get-DefaultOutDir, Assert-AzLogin,
Resolve-Subscriptions (explicit ids, a CSV, or all visible), Get-ComputeSkus,
Resolve-SkuStatus (parses restrictions[] → Regional/Zone status).
Scenario → script map
| You need to… | Run | Key output |
|---|---|---|
| Discover which SKUs are actually in use (no guessing) | Get-UsedSkus.ps1 [-SubscriptionCsv …] [-Top N] |
used-skus-…csv (VM/VMSS/AKS counts + family + zones) + capacity-config.json |
| Complete sight: every family’s enablement + quota + in-use | Get-SkuCatalogue.ps1 [-SubscriptionCsv …] [-OnlyRelevant] |
sku-catalogue-…csv (per-family coverage + summed quota + InUse) + regional-totals-…csv (Total Regional & Spot vCPUs per sub) |
| Check if SKU(s) are regional + zonal enabled across subs | Scan-SkuEnablement.ps1 -Location <r> -Skus <list> [-SubscriptionCsv …] |
<sku>_reg = Enabled/BLOCKED, <sku>_zones = open logical zones |
| Resolve logical→physical zone mapping (per sub!) | Get-ZoneMappings.ps1 -Location <r> [-SubscriptionCsv …] |
logical1/2/3, Pattern |
| Validate quota / headroom per family | Get-QuotaUsage.ps1 -Location <r> -Families <list> |
<fam>_used/_limit/_avail + totals |
| Inventory AKS clusters tenant-wide | Get-AksInventory.ps1 [-Location <r>] |
name, sub, rg, region, state, k8s, nodePools, nodeCount, node SKUs |
| Inventory on-demand capacity reservations (the guaranteed-capacity construct) | Get-CapacityReservations.ps1 [-Location <r>] [-SubscriptionCsv …] |
capacity-reservations-…csv (per reservation: SKU, region, zone, reserved vs consumed, idle / over-allocated / at-capacity flags) |
AKS scale-headroom — can every node pool reach its autoscaler maxCount within current family quota? |
Get-AksScaleHeadroom.ps1 -Location <r> [-SubscriptionCsv …] |
aks-scale-headroom-detail-…csv (per pool) + …-rollup-…csv (per family: incremental vCPUs needed vs quota, CanFullyScale, ShortfallVCPUs) |
| Network quota — where are VNets / public IPs / NICs / LBs near their regional limit? | Get-NetworkQuota.ps1 [-Location <r>] [-AllLocations] [-SubscriptionCsv …] |
network-quota-…csv (per counter: used/limit/available/pctUsed, NearLimit/AtLimit, IsUnbounded, PerResourceScope) |
App Service quota — App Service Plan inventory + Microsoft.Web regional/per-plan usage, plus tier scale-out ceilings |
Get-AppServiceQuota.ps1 [-Location <r>] [-AllLocations] [-SubscriptionCsv …] |
appservice-quota-…csv (SubscriptionRegion/AppServicePlan/InventoryDerived rows: used/limit/available/pctUsed, NearLimit/AtLimit, PlanAtInstanceCeiling, IsTrueQuota, LimitBasis) |
| Storage quota — storage-account count vs regional limit (+ optional disk-capacity inventory) | Get-StorageQuota.ps1 [-Location <r>] [-AllLocations] [-IncludeDiskCapacityInventory] [-SubscriptionCsv …] |
storage-quota-…csv (StorageAccounts used/limit/available/pctUsed + Flag; informational ManagedDiskProvisionedGiB rows by SKU) |
| PaaS quota — Azure SQL true quota (subscription/region + per-server DTU) + Cosmos DB throughput inventory | Get-PaasQuota.ps1 [-Service AzureSQL\|CosmosDB\|All] [-Location <r>] [-IncludeCosmosThroughputInventory] [-SubscriptionCsv …] |
paas-quota-…csv (per metric: used/limit/available/pctUsed, Flag, IsInformational; SQL true quota vs Cosmos inventory) |
| Subscription / RG limits — live counts vs documented ARM structural limits (RGs, tags, resources/type, deployments, role assignments) | Get-SubscriptionLimits.ps1 [-IncludeTagDensity] [-IncludeDeploymentHistory] [-IncludeRoleAssignments] [-IncludeRegionInventory] [-SubscriptionCsv …] |
subscription-limits-…csv (per limit: used/limit/available/pctUsed, Flag, LimitSource, IsTrueQuota=False, DocReference) |
| Review database (PostgreSQL/MySQL) zone + HA resilience | Get-FlexServerZones.ps1 [-Location <r>] |
per-server SKU, tier, zone, HA mode, standby zone; single-zone flag |
| Sweep all zone-pinned resources for single-zone gaps | Get-ZonalResourceInventory.ps1 [-Location <r>] |
every resource with a zone, by type, with SingleZone flag |
| Complete overview: every resource type × sub × region | Get-ResourceInventory.ps1 [-SubscriptionIds …] |
resource-inventory-…csv (type, sub, region, count, zone-pinned count) |
| See which regions you run in + compare candidate regions | Get-RegionFootprint.ps1 [-EvaluateRegions <list>] [-SubscriptionCsv …] |
per-region resource/AKS counts + region-sku-comparison-…csv + region-quota-comparison-…csv |
| Score Spot allocation likelihood for SKUs in candidate regions/zones ⚠️ needs Compute Recommendations Role | Get-SpotPlacementScore.ps1 [-ConfigPath …] [-DesiredLocations <list>] [-DesiredCount N] |
spot-placement-…csv (High/Medium/Low per SKU×region×zone + IsQuotaAvailable, timestamped) |
| Draft a support ticket for Regional + Zonal enablement | New-EnablementRequest.ps1 -Location <r> -Skus <list> [-SubscriptionCsv …] |
enablement-request-…md (paste-ready) + findings CSV |
| Read shared quota-group pools: type + members + pooled limits | Get-QuotaGroups.ps1 [-Regions <list>] [-ManagementGroupId <id>] |
quota-groups-…csv (group type, member count, limits-set flag) + quota-group-members-…csv + quota-group-limits-…csv |
| Model a pooled-quota design (subscription Reader only) | Get-QuotaGroupPlan.ps1 [-HeadroomFactor 1.3] |
quota-group-plan-…csv (pooled used/limit/free + stranded headroom + suggested pool) + …-members-…csv (per-sub posture) |
| Turn a quota report into a deploy-ready design ⚠️ PS7 | New-QuotaGroupConfig.ps1 -SkeletonConfig <json> -QuotaReportCsv <csv> -OutputConfig <json> |
populated quota-groups config (auto-detects toolkit quota-usage CSV or external azure-quota-reports CSV) |
| Provision quota groups (the one opt-in write tool) ⚠️ PS7 | Deploy-QuotaGroups.ps1 -ConfigPath <json> [-Action <phase>] [-WhatIf] |
creates groups, adds members, requests pooled limits, allocates quota — idempotent, SupportsShouldProcess |
| Watch for an enablement to land | Watch-SkuEnablement.ps1 -SubscriptionIds … -Sku … [-WatchResourceIds <aksId>] |
rolling CSV log + error alerts |
| Render a visual HTML dashboard | New-CapacityDashboard.ps1 [-Location <r>] [-SecondaryRegion <r>] |
self-contained capacity-dashboard-…html |
| Try it offline — generate synthetic demo data (no Azure access) | New-DemoDataset.ps1 [-OutDir <path>] [-Company <name>] [-Seed <int>] |
a full set of fictional CSVs you can render with the dashboard |
| Produce a full status report | New-CapacityReport.ps1 [-ConfigPath capacity-config.json] -Location <r> [-SecondaryRegion <r> -IncludeAks -IncludeZonal -IncludeCatalogue -IncludeInventory -IncludeQuotaGroups -Dashboard -EnablementRequest -EvaluateRegions <list>] |
combined CSV + Markdown (+ HTML / request / region compare / zonal resilience / full SKU catalogue / inventory / quota groups) |
The orchestrator: New-CapacityReport.ps1
The recommended entry point. It runs the core scans, joins them into one CSV + a Markdown summary,
and optionally adds the dashboard, enablement request and the various -Include* panes.
| Parameter | Purpose |
|---|---|
-ConfigPath <path> |
Use a capacity-config.json (from Get-UsedSkus.ps1) for SKUs/families/subs. |
-Location <region> |
Home/primary region (default norwayeast). |
-SecondaryRegion <region> |
Your chosen failover/migration target; drives the tiered readiness cards. Auto-picked if omitted. |
-EvaluateRegions <list> |
Extra candidate regions to score. |
-IncludeAks |
Append tenant-wide AKS inventory. |
-IncludeZonal |
Append the zone-pinned resource sweep. |
-IncludeCatalogue |
Append the full SKU catalogue (complete sight). |
-IncludeInventory |
Append the every-resource overview. |
-IncludeQuotaGroups |
Read existing quota groups + model a pooled-quota design. |
-Dashboard |
Generate the HTML dashboard. |
-EnablementRequest |
Draft the enablement support ticket. |
Explicit -Skus / -Families / -SubscriptionIds always override the config file.
Raw command reference
Use these directly if you can’t or don’t want to run the scripts. All confirmed live with Reader.
SKU restrictions (regional + zonal blocks):
az rest --method get --url "https://management.azure.com/subscriptions/<SUB>/providers/Microsoft.Compute/skus?api-version=2021-07-01&\$filter=location eq 'norwayeast'"
Parse each SKU’s restrictions[]:
type == "Location"→ regional block (SKU not enabled in the region at all).type == "Zone"→restrictionInfo.zoneslists the blocked logical zones.locationInfo[0].zones= the logical zones the SKU is offered in.
Logical → physical zone mapping (differs per subscription!):
az rest --method get --url "https://management.azure.com/subscriptions/<SUB>/locations?api-version=2022-12-01"
# → value[?name=='norwayeast'].availabilityZoneMappings → {logicalZone, physicalZone}
# e.g. logical "1" → "norwayeast-az2"
Quota / usage per family:
az vm list-usage -l norwayeast -o table
# name.value e.g. standardBsv2Family ; currentValue / limit
Spot placement score (allocation-likelihood; needs the Compute Recommendations Role):
az rest --method post \
--url "https://management.azure.com/subscriptions/<SUB>/providers/Microsoft.Compute/locations/norwayeast/placementScores/spot/generate?api-version=2025-06-05" \
--headers "Content-Type=application/json" \
--body '{"availabilityZones":true,"desiredCount":10,"desiredLocations":["norwayeast"],"desiredSizes":[{"sku":"Standard_D4s_v5"}]}'
# → placementScores[]: { region, availabilityZone, sku, score (High/Medium/Low/DataNotFoundOrStale/RestrictedSkuNotAvailable), isQuotaAvailable }
AKS inventory (Resource Graph — paginate, project raw, aggregate locally):
az graph query --first 1000 -q "resources | where type =~ 'microsoft.containerservice/managedclusters' | project name, subscriptionId, resourceGroup, location, provState=tostring(properties.provisioningState), k8s=tostring(properties.kubernetesVersion), pools=tostring(properties.agentPoolProfiles) | order by subscriptionId asc"
# loop --skip 0,1000,… until rows >= .total_records
Quota groups (management-group scoped — use ARM REST, not az account management-group list):
az rest --method get --url "https://management.azure.com/providers/Microsoft.Management/managementGroups?api-version=2021-04-01"
# Pooled limits require a location filter:
az rest --method get --url "https://management.azure.com/providers/Microsoft.Management/managementGroups/<MG>/providers/Microsoft.Quota/groupQuotas/<GROUP>/resourceProviders/Microsoft.Compute/groupQuotaLimits?api-version=2023-06-01-preview&\$filter=location eq 'norwayeast'"
Activity-log error scan (watch a reconcile / allocation failure):
az monitor activity-log list --resource-id <RESOURCE_ID> --start-time 2026-06-19T00:00:00Z \
--query "[?level=='Error' || level=='Critical' || status.value=='Failed']"
See Troubleshooting & FAQ for the gotchas behind several of these.
Preview the dashboard offline (synthetic demo data)
Want to see what the dashboard looks like without an Azure tenant, login, or any real data?
New-DemoDataset.ps1 generates a complete, self-consistent fictional dataset (default
company “Zava Inc”) from a deterministic seed — it reads nothing from Azure and produces the
same CSV schemas the collector scripts do.
# 1) Generate the demo CSVs (default -> output/)
.\scripts\New-DemoDataset.ps1 -OutDir .\demo-output
# 2) Render the dashboard from them (no az login needed)
.\scripts\New-CapacityDashboard.ps1 -InputDir .\demo-output `
-Location norwayeast -SecondaryRegion swedencentral `
-Title "Zava Inc - Azure Capacity & Enablement" -OutPath .\demo-dashboard.html
The generated universe deliberately exercises a range of dashboard states: SKU enablement
blocks and availability-zone gaps, near-capacity quota (general-purpose + burstable families),
a GPU capacity crunch (NCads H100 v5), a regional vCPU ceiling under pressure, one actively
pooled quota group and one that is not, AKS clusters in Failed / Upgrading / Canceled
states, and zone-redundant HA flexible servers alongside single-zone ones.
| Parameter | Purpose |
|---|---|
-OutDir <path> |
Where to write the CSVs (default output/). |
-Company <name> |
Fictional company display name (default Zava Inc). |
-Prefix <token> |
Short token used in resource/subscription names (default Zava). |
-Location / -SecondaryRegion |
Primary / comparison regions to populate. |
-Seed <int> |
Reproducibility seed — same seed produces identical files. |
Because the data is invented, it is safe to commit, screenshot or share. (Real output/ data is
git-ignored.)