Skip to content
This repository was archived by the owner on Feb 6, 2026. It is now read-only.

Commit 774ae2a

Browse files
Copilotjongio
andcommitted
Implement enhanced Deploy-TestResources.ps1 with multiple areas support
Co-authored-by: jongio <2163001+jongio@users.noreply.github.com>
1 parent 224d872 commit 774ae2a

3 files changed

Lines changed: 233 additions & 56 deletions

File tree

CONTRIBUTING.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -323,28 +323,39 @@ Before running live tests:
323323
- Deploy test resources:
324324

325325
```pwsh
326-
./eng/scripts/Deploy-TestResources.ps1 -Area Storage
326+
./eng/scripts/Deploy-TestResources.ps1 -Areas @('Storage')
327327
```
328328

329329
**Deploy-TestResources.ps1 Parameters:**
330330

331-
| Parameter | Type | Description |
332-
|---------------------|--------|--------------------------------------------------------------------------------------------------------------|
333-
| `Area` | string | REQUIRED. The service area to deploy test resources for (e.g., `Storage`, `KeyVault`). One area per run. |
334-
| `SubscriptionId` | string | Target subscription ID. If omitted, the current Azure context subscription (from `Get-AzContext`) is used. |
335-
| `ResourceGroupName` | string | Resource group name. Defaults to `{username}-mcp{hash(username)}`. |
336-
| `BaseName` | string | Base name prefix for resources. Defaults to `mcp{hash}`. |
337-
| `Unique` | switch | Use a unique GUID-based hash for this invocation instead of the stable username+subscription hash. |
338-
| `DeleteAfterHours` | int | Hours after which resources are tagged for deletion. Defaults to `12`. |
331+
| Parameter | Type | Description |
332+
|---------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
333+
| `Areas` | string[] | REQUIRED (ParameterSet: MultipleAreas). Array of service areas to deploy test resources for (e.g., `@('Storage', 'KeyVault')`). Areas are case-insensitive. |
334+
| `All` | switch | REQUIRED (ParameterSet: AllAreas). Deploy test resources for all available areas that have test-resources.bicep templates. |
335+
| `Area` | string | DEPRECATED (ParameterSet: SingleArea). Single service area to deploy test resources for. Use `-Areas @('AreaName')` instead. |
336+
| `SubscriptionId` | string | Target subscription ID. If omitted, the current Azure context subscription (from `Get-AzContext`) is used. |
337+
| `ResourceGroupName` | string | Resource group name. Defaults to `{username}-mcp{hash(username,subscription,area)}`. When deploying multiple areas, each gets a unique resource group. |
338+
| `BaseName` | string | Base name prefix for resources. Defaults to `mcp{hash}`. When deploying multiple areas, each gets a unique base name. |
339+
| `Unique` | switch | Use a unique GUID-based hash for this invocation instead of the stable username+subscription+area hash. Useful for parallel deployments. |
340+
| `DeleteAfterHours` | int | Hours after which resources are tagged for deletion. Defaults to `12`. |
339341

340342
Examples:
341343

342344
```pwsh
343345
# Deploy Storage test resources using current Azure context subscription
344-
./eng/scripts/Deploy-TestResources.ps1 -Area Storage
346+
./eng/scripts/Deploy-TestResources.ps1 -Areas @('Storage')
347+
348+
# Deploy test resources for multiple areas at once
349+
./eng/scripts/Deploy-TestResources.ps1 -Areas @('Storage', 'KeyVault', 'Cosmos')
350+
351+
# Deploy test resources for all available areas
352+
./eng/scripts/Deploy-TestResources.ps1 -All
345353
346354
# Deploy Key Vault test resources to a specific subscription and keep for one week
347-
./eng/scripts/Deploy-TestResources.ps1 -Area KeyVault -SubscriptionId <subId> -DeleteAfterHours 168 -Unique
355+
./eng/scripts/Deploy-TestResources.ps1 -Areas @('KeyVault') -SubscriptionId <subId> -DeleteAfterHours 168 -Unique
356+
357+
# Backward compatibility - single area (deprecated, shows warning)
358+
./eng/scripts/Deploy-TestResources.ps1 -Area Storage
348359
```
349360

350361
After deploying test resources, you should have a `.testsettings.json` file with your deployment information in the deployed areas' `/tests` directory.
@@ -358,7 +369,7 @@ Run live tests with:
358369
You can scope tests to specific areas:
359370

360371
```pwsh
361-
./eng/scripts/Test-Code.ps1 -TestType Live -Areas Storage, KeyVault
372+
./eng/scripts/Test-Code.ps1 -TestType Live -Areas @('Storage', 'KeyVault')
362373
```
363374

364375
### NPX Live Tests

docs/new-command.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ If your command interacts with Azure resources (storage accounts, databases, VMs
2626
-**MUST create** `areas/{area-name}/tests/test-resources-post.ps1` (required even if basic template)
2727
-**MUST include** RBAC role assignments for test application
2828
-**MUST validate** with `az bicep build --file areas/{area-name}/tests/test-resources.bicep`
29-
-**MUST test deployment** with `./eng/scripts/Deploy-TestResources.ps1 -Area {area-name}`
29+
-**MUST test deployment** with `./eng/scripts/Deploy-TestResources.ps1 -Areas @('{area-name}')`
3030

3131
### **Non-Azure Commands (No Test Infrastructure Needed)**
3232
If your command is a wrapper/utility (CLI tools, best practices, documentation):
@@ -1047,7 +1047,7 @@ Use the deployment script with your area:
10471047

10481048
```powershell
10491049
# Deploy test resources for your area
1050-
./eng/scripts/Deploy-TestResources.ps1 -Areas "{Area}"
1050+
./eng/scripts/Deploy-TestResources.ps1 -Areas @('{Area}')
10511051
10521052
# Run live tests
10531053
dotnet test --filter "Category=Live&Area={Area}"
@@ -1569,7 +1569,7 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
15691569
- **Cause**: Parameter constraints, resource naming conflicts, or invalid configurations
15701570
- **Solution**:
15711571
- Review deployment logs and error messages
1572-
- Use `./eng/scripts/Deploy-TestResources.ps1 -Area {area-name} -Debug` for verbose deployment logs including resource provider errors.
1572+
- Use `./eng/scripts/Deploy-TestResources.ps1 -Areas @('{area-name}') -Debug` for verbose deployment logs including resource provider errors.
15731573
15741574
### Live Test Project Configuration Issues
15751575
@@ -1713,7 +1713,7 @@ Before submitting:
17131713
- [ ] **Live test infrastructure created** (`test-resources.bicep` template in `areas/{area-name}/tests`)
17141714
- [ ] **Post-deployment script created** (`test-resources-post.ps1` in `areas/{area-name}/tests` - required even if basic template)
17151715
- [ ] **Bicep template validated** with `az bicep build --file areas/{area-name}/tests/test-resources.bicep`
1716-
- [ ] **Live test resource template tested** with `./eng/scripts/Deploy-TestResources.ps1 -Area {area-name}`
1716+
- [ ] **Live test resource template tested** with `./eng/scripts/Deploy-TestResources.ps1 -Areas @('{area-name}')`
17171717
- [ ] **RBAC permissions configured** for test application in Bicep template (use appropriate built-in roles)
17181718
- [ ] **Live test project configuration correct**:
17191719
- [ ] References `AzureMcp.Cli.csproj` (not just the area project)

eng/scripts/Deploy-TestResources.ps1

Lines changed: 206 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
param(
2-
[Parameter(Mandatory=$true)]
3-
[string]$Area,
2+
[Parameter(Mandatory=$true, ParameterSetName='SingleArea')]
3+
[string]$Area, # Deprecated - use -Areas instead
4+
5+
[Parameter(Mandatory=$true, ParameterSetName='MultipleAreas')]
6+
[string[]]$Areas,
7+
8+
[Parameter(Mandatory=$true, ParameterSetName='AllAreas')]
9+
[switch]$All,
10+
411
[string]$SubscriptionId,
512
[string]$ResourceGroupName,
613
[string]$BaseName,
@@ -11,6 +18,157 @@ param(
1118
$ErrorActionPreference = 'Stop'
1219
. "$PSScriptRoot/../common/scripts/common.ps1"
1320

21+
function Get-AvailableAreas {
22+
<#
23+
.SYNOPSIS
24+
Discovers all available areas that have test resources.
25+
#>
26+
$areasWithTestResources = @()
27+
$areasDir = "$RepoRoot/areas"
28+
29+
if (Test-Path $areasDir) {
30+
Get-ChildItem -Path $areasDir -Directory | ForEach-Object {
31+
$areaName = $_.Name.ToLower()
32+
$testResourcesPath = "$($_.FullName)/tests/test-resources.bicep"
33+
if (Test-Path $testResourcesPath) {
34+
$areasWithTestResources += $areaName
35+
}
36+
}
37+
}
38+
39+
return $areasWithTestResources | Sort-Object
40+
}
41+
42+
function Test-AreaExists {
43+
<#
44+
.SYNOPSIS
45+
Validates that the specified area exists and has test resources.
46+
#>
47+
param(
48+
[Parameter(Mandatory=$true)]
49+
[string]$AreaName
50+
)
51+
52+
$areaPath = "$RepoRoot/areas/$($AreaName.ToLower())"
53+
$testResourcesPath = "$areaPath/tests/test-resources.bicep"
54+
55+
if (!(Test-Path $areaPath)) {
56+
Write-Error "Area '$AreaName' does not exist at path '$areaPath'."
57+
return $false
58+
}
59+
60+
if (!(Test-Path $testResourcesPath)) {
61+
Write-Error "Area '$AreaName' does not have test resources. Expected bicep template at '$testResourcesPath'."
62+
return $false
63+
}
64+
65+
return $true
66+
}
67+
68+
function Get-AreasToProcess {
69+
<#
70+
.SYNOPSIS
71+
Determines which areas to process based on the provided parameters.
72+
#>
73+
param(
74+
[string]$Area,
75+
[string[]]$Areas,
76+
[switch]$All
77+
)
78+
79+
if ($Area) {
80+
# Backward compatibility - show deprecation warning
81+
Write-Warning "The -Area parameter is deprecated. Please use -Areas @('$Area') or -All instead."
82+
return @($Area.ToLower())
83+
}
84+
elseif ($All) {
85+
$availableAreas = Get-AvailableAreas
86+
if ($availableAreas.Count -eq 0) {
87+
Write-Error "No areas with test resources found."
88+
return @()
89+
}
90+
Write-Host "Deploying test resources for all available areas: $($availableAreas -join ', ')" -ForegroundColor Cyan
91+
return $availableAreas
92+
}
93+
else {
94+
# Multiple areas specified
95+
$processAreas = @()
96+
foreach ($areaName in $Areas) {
97+
$normalizedArea = $areaName.ToLower()
98+
if (Test-AreaExists -AreaName $normalizedArea) {
99+
$processAreas += $normalizedArea
100+
} else {
101+
return @() # Exit early on validation failure
102+
}
103+
}
104+
return $processAreas
105+
}
106+
}
107+
108+
function Deploy-AreaTestResources {
109+
<#
110+
.SYNOPSIS
111+
Deploys test resources for a specific area.
112+
#>
113+
param(
114+
[Parameter(Mandatory=$true)]
115+
[string]$AreaName,
116+
[string]$SubscriptionId,
117+
[string]$ResourceGroupName,
118+
[string]$BaseName,
119+
[int]$DeleteAfterHours,
120+
[switch]$Unique
121+
)
122+
123+
$testResourcesDirectory = Resolve-Path -Path "$RepoRoot/areas/$AreaName/tests" -ErrorAction SilentlyContinue
124+
$bicepPath = "$testResourcesDirectory/test-resources.bicep"
125+
126+
if(!(Test-Path -Path $bicepPath)) {
127+
Write-Error "Test resources bicep template '$bicepPath' does not exist."
128+
return $false
129+
}
130+
131+
# Base the user hash on the user's account ID, subscription, and area being deployed to
132+
if($Unique) {
133+
$hash = [guid]::NewGuid().ToString()
134+
} else {
135+
$hash = (New-StringHash $account.Id, $SubscriptionId, $AreaName)
136+
}
137+
138+
$suffix = $hash.ToLower().Substring(0, 8)
139+
140+
$areaBaseName = if($BaseName) { $BaseName } else { "mcp$($suffix)" }
141+
$areaResourceGroupName = if($ResourceGroupName) { $ResourceGroupName } else { "$username-mcp$($suffix)" }
142+
143+
Write-Host @"
144+
145+
=== Deploying Area: $($AreaName.ToUpper()) ===
146+
SubscriptionId: '$SubscriptionId'
147+
SubscriptionName: '$subscriptionName'
148+
ResourceGroupName: '$areaResourceGroupName'
149+
BaseName: '$areaBaseName'
150+
DeleteAfterHours: $DeleteAfterHours
151+
TestResourcesDirectory: '$testResourcesDirectory'
152+
"@ -ForegroundColor Green
153+
154+
try {
155+
./eng/common/TestResources/New-TestResources.ps1 `
156+
-SubscriptionId $SubscriptionId `
157+
-ResourceGroupName $areaResourceGroupName `
158+
-BaseName $areaBaseName `
159+
-TestResourcesDirectory $testResourcesDirectory `
160+
-DeleteAfterHours $DeleteAfterHours `
161+
-Force
162+
163+
Write-Host "✅ Successfully deployed test resources for area '$AreaName'" -ForegroundColor Green
164+
return $true
165+
}
166+
catch {
167+
Write-Error "❌ Failed to deploy test resources for area '$AreaName': $($_.Exception.Message)"
168+
return $false
169+
}
170+
}
171+
14172
function New-StringHash([string[]]$strings) {
15173
$string = $strings -join ' '
16174
$hash = [System.Security.Cryptography.SHA1]::Create()
@@ -19,13 +177,15 @@ function New-StringHash([string[]]$strings) {
19177
return [BitConverter]::ToString($hashBytes) -replace '-', ''
20178
}
21179

22-
$testResourcesDirectory = Resolve-Path -Path "$RepoRoot/areas/$($Area.ToLower())/tests" -ErrorAction SilentlyContinue
23-
$bicepPath = "$testResourcesDirectory/test-resources.bicep"
24-
if(!(Test-Path -Path $bicepPath)) {
25-
Write-Error "Test resources bicep template '$bicepPath' does not exist."
26-
return
180+
# Determine which areas to process
181+
$areasToProcess = Get-AreasToProcess -Area $Area -Areas $Areas -All:$All
182+
183+
if ($areasToProcess.Count -eq 0) {
184+
Write-Error "No valid areas to process."
185+
exit 1
27186
}
28187

188+
# Set up Azure context
29189
if($SubscriptionId) {
30190
Select-AzSubscription -Subscription $SubscriptionId | Out-Null
31191
$context = Get-AzContext
@@ -38,42 +198,48 @@ if($SubscriptionId) {
38198
$subscriptionName = $context.Subscription.Name
39199
$account = $context.Account
40200

41-
# Base the user hash on the user's account ID and subscription being deployed to
42-
if($Unique) {
43-
$hash = [guid]::NewGuid().ToString()
44-
} else {
45-
$hash = (New-StringHash $account.Id, $SubscriptionId, $Area)
46-
}
47-
48-
$suffix = $hash.ToLower().Substring(0, 8)
49-
50-
if(!$BaseName) {
51-
$BaseName = "mcp$($suffix)"
52-
}
53-
54-
if(!$ResourceGroupName) {
55-
$username = $account.Id.Split('@')[0].ToLower()
56-
$ResourceGroupName = "$username-mcp$($suffix)"
57-
}
201+
# Calculate username for default resource group naming
202+
$username = $account.Id.Split('@')[0].ToLower()
58203

59204
Push-Location $RepoRoot
60205
try {
61-
Write-Host @"
62-
Deploying:
63-
SubscriptionId: '$SubscriptionId'
64-
SubscriptionName: '$subscriptionName'
65-
ResourceGroupName: '$ResourceGroupName'
66-
BaseName: '$BaseName'
67-
DeleteAfterHours: $DeleteAfterHours
68-
TestResourcesDirectory: '$testResourcesDirectory'
69-
"@
70-
./eng/common/TestResources/New-TestResources.ps1 `
71-
-SubscriptionId $SubscriptionId `
72-
-ResourceGroupName $ResourceGroupName `
73-
-BaseName $BaseName `
74-
-TestResourcesDirectory $testResourcesDirectory `
75-
-DeleteAfterHours $DeleteAfterHours `
76-
-Force
206+
$successCount = 0
207+
$totalCount = $areasToProcess.Count
208+
209+
Write-Host "`n🚀 Starting deployment of test resources for $totalCount area(s)" -ForegroundColor Cyan
210+
211+
for ($i = 0; $i -lt $areasToProcess.Count; $i++) {
212+
$currentArea = $areasToProcess[$i]
213+
$progressPercent = [int](($i / $totalCount) * 100)
214+
215+
Write-Progress -Activity "Deploying Test Resources" -Status "Processing area $($i + 1) of ${totalCount}: $currentArea" -PercentComplete $progressPercent
216+
217+
$success = Deploy-AreaTestResources `
218+
-AreaName $currentArea `
219+
-SubscriptionId $SubscriptionId `
220+
-ResourceGroupName $ResourceGroupName `
221+
-BaseName $BaseName `
222+
-DeleteAfterHours $DeleteAfterHours `
223+
-Unique:$Unique
224+
225+
if ($success) {
226+
$successCount++
227+
}
228+
}
229+
230+
Write-Progress -Activity "Deploying Test Resources" -Completed
231+
232+
Write-Host "`n📊 Deployment Summary:" -ForegroundColor Cyan
233+
Write-Host " Total areas processed: $totalCount" -ForegroundColor White
234+
Write-Host " Successful deployments: $successCount" -ForegroundColor Green
235+
Write-Host " Failed deployments: $($totalCount - $successCount)" -ForegroundColor Red
236+
237+
if ($successCount -eq $totalCount) {
238+
Write-Host "`n🎉 All deployments completed successfully!" -ForegroundColor Green
239+
} else {
240+
Write-Host "`n⚠️ Some deployments failed. Check the output above for details." -ForegroundColor Yellow
241+
exit 1
242+
}
77243
}
78244
finally {
79245
Pop-Location

0 commit comments

Comments
 (0)