Skip to content

Commit 875f10f

Browse files
committed
Moving masking helpers to module
1 parent ac0909b commit 875f10f

5 files changed

Lines changed: 206 additions & 116 deletions

File tree

tests/integration/all-resource-types/Deploy-SourceApim.ps1

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,31 @@ param(
4848

4949
[switch]$Destroy,
5050

51-
[ValidateSet('trace', 'debug', 'info', 'warn', 'error', 'silent')]
52-
[string]$LogLevel = 'info'
51+
[ValidateSet('Info', 'Verbose', 'Debug')]
52+
[string]$LogLevel = 'Info'
5353
)
5454

5555
$ErrorActionPreference = 'Stop'
56-
$maskingHelpers = Join-Path $PSScriptRoot 'Masking.Helpers.ps1'
57-
. $maskingHelpers
56+
$VerbosePreference = if ($LogLevel -in @('Verbose', 'Debug')) { 'Continue' } else { 'SilentlyContinue' }
57+
$DebugPreference = if ($LogLevel -eq 'Debug') { 'Continue' } else { 'SilentlyContinue' }
58+
Import-Module (Join-Path $PSScriptRoot 'MaskingHelpers.psm1') -Force
59+
60+
# Map this script's LogLevel (Info/Verbose/Debug) to the apiops CLI log level
61+
# values used in the printed example command.
62+
function Get-ApiopsLogLevelLocal([string]$ScriptLogLevel) {
63+
switch ($ScriptLogLevel) {
64+
'Verbose' { return 'warn' }
65+
'Debug' { return 'debug' }
66+
default { return 'info' }
67+
}
68+
}
69+
$apiopsLogLevel = Get-ApiopsLogLevelLocal -ScriptLogLevel $LogLevel
5870

5971
# ---------------------------------------------------------------------------
6072
# Destroy path
6173
# ---------------------------------------------------------------------------
6274
if ($Destroy) {
63-
Write-Host "🗑️ Deleting resource group '$(Mask-Identifier -Value $ResourceGroupName)'..." -ForegroundColor Yellow
75+
Write-Host "🗑️ Deleting resource group '$(Protect-Identifier -Value $ResourceGroupName)'..." -ForegroundColor Yellow
6476
az group delete --name $ResourceGroupName --yes --no-wait
6577
Write-Host "✅ Deletion initiated (async). Resource group will be removed shortly." -ForegroundColor Green
6678
exit 0
@@ -83,7 +95,7 @@ if (-not $account) {
8395
}
8496

8597
$subscriptionId = $account.id
86-
Write-Host " Subscription: $($account.name) ($(Mask-SubscriptionId -Value $subscriptionId))" -ForegroundColor Gray
98+
Write-Host " Subscription: $($account.name) ($(Protect-SubscriptionId -Value $subscriptionId))" -ForegroundColor Gray
8799

88100
# Register required resource providers
89101
Write-Host "📋 Registering required resource providers..." -ForegroundColor Cyan
@@ -129,7 +141,7 @@ if (-not $allRegistered) {
129141
Write-Host " ✅ Resource providers ready" -ForegroundColor Green
130142

131143
# Create resource group if needed
132-
Write-Host "📦 Ensuring resource group '$(Mask-Identifier -Value $ResourceGroupName)' exists in '$Location'..." -ForegroundColor Cyan
144+
Write-Host "📦 Ensuring resource group '$(Protect-Identifier -Value $ResourceGroupName)' exists in '$Location'..." -ForegroundColor Cyan
133145
az group create --name $ResourceGroupName --location $Location --output none
134146

135147
# Deploy Bicep template
@@ -141,12 +153,28 @@ Write-Host ""
141153

142154
$deploymentName = "source-apim-$(Get-Date -Format 'yyyyMMddHHmmss')"
143155

144-
$result = az deployment group create `
145-
--resource-group $ResourceGroupName `
146-
--name $deploymentName `
147-
--template-file $bicepFile `
148-
--parameters skuName=$SkuName location=$Location publisherEmail=$PublisherEmail `
149-
--output json | ConvertFrom-Json
156+
$azVerbosity = @()
157+
switch ($LogLevel) {
158+
'Verbose' { $azVerbosity = @('--verbose') }
159+
'Debug' { $azVerbosity = @('--debug') }
160+
}
161+
162+
$azReplacements = @{
163+
$subscriptionId = Protect-SubscriptionId -Value $subscriptionId
164+
$ResourceGroupName = Protect-Identifier -Value $ResourceGroupName
165+
}
166+
167+
$raw = Invoke-MaskedAzCommand -Replacements $azReplacements -Command {
168+
az deployment group create `
169+
--resource-group $ResourceGroupName `
170+
--name $deploymentName `
171+
--template-file $bicepFile `
172+
--parameters skuName=$SkuName location=$Location publisherEmail=$PublisherEmail `
173+
--output json `
174+
@azVerbosity
175+
}
176+
177+
$result = $raw | ConvertFrom-Json
150178

151179
if ($LASTEXITCODE -ne 0) {
152180
Write-Error "Deployment failed. Check the Azure portal for details."
@@ -163,11 +191,11 @@ Write-Host ""
163191
Write-Host "APIOps CLI extract command:" -ForegroundColor Cyan
164192
Write-Host ""
165193
Write-Host " npx apiops extract \"
166-
Write-Host " --subscription-id $(Mask-SubscriptionId -Value $outputs.subscriptionId.value) \"
167-
Write-Host " --resource-group $(Mask-Identifier -Value $outputs.resourceGroupName.value) \"
194+
Write-Host " --subscription-id $(Protect-SubscriptionId -Value $outputs.subscriptionId.value) \"
195+
Write-Host " --resource-group $(Protect-Identifier -Value $outputs.resourceGroupName.value) \"
168196
Write-Host " --service-name $($outputs.apimServiceName.value) \"
169197
Write-Host " --output-dir ./extracted \"
170-
Write-Host " --log-level $LogLevel"
198+
Write-Host " --log-level $apiopsLogLevel"
171199
Write-Host ""
172200
Write-Host "Gateway URL: $($outputs.gatewayUrl.value)" -ForegroundColor Gray
173201
Write-Host "Workspace deployed: $($outputs.workspaceDeployed.value)" -ForegroundColor Gray

tests/integration/all-resource-types/Deploy-TargetApim.ps1

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,36 @@ param(
3030
[string]$Location = 'eastus2',
3131

3232
[ValidateSet('Developer', 'Premium', 'StandardV2', 'PremiumV2')]
33-
[string]$SkuName = 'StandardV2'
33+
[string]$SkuName = 'StandardV2',
34+
35+
[ValidateSet('Info', 'Verbose', 'Debug')]
36+
[string]$LogLevel = 'Info'
3437
)
3538

3639
$ErrorActionPreference = 'Stop'
37-
$maskingHelpers = Join-Path $PSScriptRoot 'Masking.Helpers.ps1'
38-
. $maskingHelpers
40+
$VerbosePreference = if ($LogLevel -in @('Verbose', 'Debug')) { 'Continue' } else { 'SilentlyContinue' }
41+
$DebugPreference = if ($LogLevel -eq 'Debug') { 'Continue' } else { 'SilentlyContinue' }
42+
Import-Module (Join-Path $PSScriptRoot 'MaskingHelpers.psm1') -Force
3943

4044
$bicepFile = Join-Path $PSScriptRoot 'target-apim.bicep'
4145

4246
if (-not (Test-Path $bicepFile)) {
4347
Write-Error "Bicep file not found at: $bicepFile"
4448
}
4549

50+
# Verify az CLI authentication and capture subscription id for masked logging
51+
$account = az account show --output json 2>$null | ConvertFrom-Json
52+
if (-not $account) {
53+
Write-Error "Not logged in to Azure CLI. Run 'az login' first."
54+
}
55+
$subscriptionId = $account.id
56+
4657
Write-Host "Starting target APIM deployment..."
47-
Write-Host "Resource Group: $(Mask-Identifier -Value $ResourceGroupName)"
48-
Write-Host "SKU: $SkuName"
49-
Write-Host "Location: $Location"
58+
Write-Host "Subscription: $($account.name) ($(Protect-SubscriptionId -Value $subscriptionId))"
59+
Write-Host "Resource Group: $(Protect-Identifier -Value $ResourceGroupName)"
60+
Write-Host "SKU: $SkuName"
61+
Write-Host "Location: $Location"
62+
Write-Host "Log Level: $LogLevel"
5063

5164
Write-Host "Creating resource group..."
5265
az group create --name $ResourceGroupName --location $Location --output none
@@ -55,12 +68,26 @@ if ($LASTEXITCODE -ne 0) {
5568
}
5669

5770
Write-Host "Deploying target-apim.bicep (this takes 30-45 minutes)..."
58-
$raw = az deployment group create `
59-
--resource-group $ResourceGroupName `
60-
--name "target-apim-$(Get-Date -Format 'yyyyMMddHHmmss')" `
61-
--template-file $bicepFile `
62-
--parameters skuName=$SkuName location=$Location publisherEmail=$PublisherEmail `
63-
--output json
71+
$azVerbosity = @()
72+
switch ($LogLevel) {
73+
'Verbose' { $azVerbosity = @('--verbose') }
74+
'Debug' { $azVerbosity = @('--debug') }
75+
}
76+
77+
$azReplacements = @{
78+
$subscriptionId = Protect-SubscriptionId -Value $subscriptionId
79+
$ResourceGroupName = Protect-Identifier -Value $ResourceGroupName
80+
}
81+
82+
$raw = Invoke-MaskedAzCommand -Replacements $azReplacements -Command {
83+
az deployment group create `
84+
--resource-group $ResourceGroupName `
85+
--name "target-apim-$(Get-Date -Format 'yyyyMMddHHmmss')" `
86+
--template-file $bicepFile `
87+
--parameters skuName=$SkuName location=$Location publisherEmail=$PublisherEmail `
88+
--output json `
89+
@azVerbosity
90+
}
6491

6592
if ($LASTEXITCODE -ne 0) {
6693
throw "Target APIM deployment failed"

tests/integration/all-resource-types/Masking.Helpers.ps1

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# MaskingHelpers — secret-redaction utilities for the round-trip integration
2+
# test scripts. Imported via:
3+
#
4+
# Import-Module (Join-Path $PSScriptRoot 'MaskingHelpers.psm1') -Force
5+
#
6+
# All exported functions use approved PowerShell verbs.
7+
8+
# Module-scoped flag. Flip to $false locally if you need to see raw values
9+
# while debugging — never commit that change.
10+
$script:EnableMasking = $true
11+
12+
function Protect-Identifier {
13+
param(
14+
[string]$Value,
15+
[int]$Prefix = 6,
16+
[int]$Suffix = 4
17+
)
18+
19+
if (-not $script:EnableMasking) {
20+
return $Value
21+
}
22+
23+
if ([string]::IsNullOrWhiteSpace($Value)) {
24+
return '<empty>'
25+
}
26+
27+
if ($Value.Length -le ($Prefix + $Suffix)) {
28+
return ('*' * $Value.Length)
29+
}
30+
31+
return "{0}...{1}" -f $Value.Substring(0, $Prefix), $Value.Substring($Value.Length - $Suffix)
32+
}
33+
34+
function Protect-SubscriptionId {
35+
param([string]$Value)
36+
return Protect-Identifier -Value $Value -Prefix 8 -Suffix 4
37+
}
38+
39+
function Protect-LogLine {
40+
param(
41+
[string]$Line,
42+
[hashtable]$Replacements
43+
)
44+
45+
if (-not $script:EnableMasking -or [string]::IsNullOrEmpty($Line) -or -not $Replacements) {
46+
return $Line
47+
}
48+
49+
$protectedLine = $Line
50+
foreach ($entry in $Replacements.GetEnumerator()) {
51+
if ([string]::IsNullOrEmpty($entry.Key) -or [string]::IsNullOrEmpty($entry.Value)) {
52+
continue
53+
}
54+
55+
$protectedLine = $protectedLine.Replace($entry.Key, $entry.Value)
56+
}
57+
58+
return $protectedLine
59+
}
60+
61+
function Invoke-MaskedApiopsCommand {
62+
param(
63+
[scriptblock]$Command,
64+
[hashtable]$Replacements
65+
)
66+
67+
& $Command 2>&1 | ForEach-Object {
68+
$message = $_.ToString()
69+
Write-Host (Protect-LogLine -Line $message -Replacements $Replacements)
70+
}
71+
72+
return $LASTEXITCODE
73+
}
74+
75+
# Invoke a native command (typically `az`) capturing its stdout for return while
76+
# streaming stderr through Protect-LogLine so verbose/debug output cannot leak
77+
# secrets (subscription id, resource group name, etc.). Returns stdout as a
78+
# single string. Sets/preserves $LASTEXITCODE for the caller.
79+
function Invoke-MaskedAzCommand {
80+
param(
81+
[scriptblock]$Command,
82+
[hashtable]$Replacements
83+
)
84+
85+
$stdoutLines = New-Object System.Collections.Generic.List[string]
86+
87+
& $Command 2>&1 | ForEach-Object {
88+
if ($_ -is [System.Management.Automation.ErrorRecord]) {
89+
$message = $_.Exception.Message
90+
if ([string]::IsNullOrEmpty($message)) { $message = $_.ToString() }
91+
Write-Host (Protect-LogLine -Line $message -Replacements $Replacements)
92+
} else {
93+
$stdoutLines.Add([string]$_)
94+
}
95+
}
96+
97+
return ($stdoutLines -join "`n")
98+
}
99+
100+
Export-ModuleMember -Function `
101+
Protect-Identifier, `
102+
Protect-SubscriptionId, `
103+
Protect-LogLine, `
104+
Invoke-MaskedApiopsCommand, `
105+
Invoke-MaskedAzCommand

0 commit comments

Comments
 (0)