Skip to content

Commit e6bea2a

Browse files
azure-sdkbenbp
andauthored
Sync eng/common directory with azure-sdk-tools for PR 2248 (Azure#21814)
* Exclude certain live test deployment outputs from being marked as log secrets * debug * Update subscription configuration merge jobs to use secret handler * Rename subscription config helper function script * Fix variable name reference in scope Co-authored-by: Ben Broderick Phillips <[email protected]>
1 parent 93b62ec commit e6bea2a

File tree

3 files changed

+190
-112
lines changed

3 files changed

+190
-112
lines changed

eng/common/TestResources/New-TestResources.ps1

+91-73
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ param (
7979
[switch] $OutFile
8080
)
8181

82+
. $PSScriptRoot/SubConfig-Helpers.ps1
83+
8284
# By default stop for any error.
8385
if (!$PSBoundParameters.ContainsKey('ErrorAction')) {
8486
$ErrorActionPreference = 'Stop'
@@ -126,7 +128,7 @@ function LoadCloudConfig([string] $env)
126128
function MergeHashes([hashtable] $source, [psvariable] $dest)
127129
{
128130
foreach ($key in $source.Keys) {
129-
if ($dest.Value.ContainsKey($key) -and $dest.Value[$key] -ne $source[$key]) {
131+
if ($dest.Value.Contains($key) -and $dest.Value[$key] -ne $source[$key]) {
130132
Write-Warning ("Overwriting '$($dest.Name).$($key)' with value '$($dest.Value[$key])' " +
131133
"to new value '$($source[$key])'")
132134
}
@@ -155,6 +157,93 @@ function BuildBicepFile([System.IO.FileSystemInfo] $file)
155157
return $templateFilePath
156158
}
157159

160+
function BuildDeploymentOutputs([string]$serviceDirectoryPrefix, [object]$azContext, [object]$deployment) {
161+
# Add default values
162+
$deploymentOutputs = [Ordered]@{
163+
"${serviceDirectoryPrefix}CLIENT_ID" = $TestApplicationId;
164+
"${serviceDirectoryPrefix}CLIENT_SECRET" = $TestApplicationSecret;
165+
"${serviceDirectoryPrefix}TENANT_ID" = $azContext.Tenant.Id;
166+
"${serviceDirectoryPrefix}SUBSCRIPTION_ID" = $azContext.Subscription.Id;
167+
"${serviceDirectoryPrefix}RESOURCE_GROUP" = $resourceGroup.ResourceGroupName;
168+
"${serviceDirectoryPrefix}LOCATION" = $resourceGroup.Location;
169+
"${serviceDirectoryPrefix}ENVIRONMENT" = $azContext.Environment.Name;
170+
"${serviceDirectoryPrefix}AZURE_AUTHORITY_HOST" = $azContext.Environment.ActiveDirectoryAuthority;
171+
"${serviceDirectoryPrefix}RESOURCE_MANAGER_URL" = $azContext.Environment.ResourceManagerUrl;
172+
"${serviceDirectoryPrefix}SERVICE_MANAGEMENT_URL" = $azContext.Environment.ServiceManagementUrl;
173+
}
174+
175+
MergeHashes $EnvironmentVariables $(Get-Variable deploymentOutputs)
176+
177+
foreach ($key in $deployment.Outputs.Keys) {
178+
$variable = $deployment.Outputs[$key]
179+
180+
# Work around bug that makes the first few characters of environment variables be lowercase.
181+
$key = $key.ToUpperInvariant()
182+
183+
if ($variable.Type -eq 'String' -or $variable.Type -eq 'SecureString') {
184+
$deploymentOutputs[$key] = $variable.Value
185+
}
186+
}
187+
188+
return $deploymentOutputs
189+
}
190+
191+
function SetDeploymentOutputs([string]$serviceName, [object]$azContext, [object]$deployment, [object]$templateFile) {
192+
$serviceDirectoryPrefix = $serviceName.ToUpperInvariant() + "_"
193+
$deploymentOutputs = BuildDeploymentOutputs $serviceDirectoryPrefix $azContext $deployment
194+
195+
if ($OutFile) {
196+
if (!$IsWindows) {
197+
Write-Host 'File option is supported only on Windows'
198+
}
199+
200+
$outputFile = "$($templateFile.originalFilePath).env"
201+
202+
$environmentText = $deploymentOutputs | ConvertTo-Json;
203+
$bytes = [System.Text.Encoding]::UTF8.GetBytes($environmentText)
204+
$protectedBytes = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
205+
206+
Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force
207+
208+
Write-Host "Test environment settings`n $environmentText`nstored into encrypted $outputFile"
209+
} else {
210+
if (!$CI) {
211+
# Write an extra new line to isolate the environment variables for easy reading.
212+
Log "Persist the following environment variables based on your detected shell ($shell):`n"
213+
}
214+
215+
# Marking values as secret by allowed keys below is not sufficient, as there may be outputs set in the ARM/bicep
216+
# file that re-mark those values as secret (since all user-provided deployment outputs are treated as secret by default).
217+
# This variable supports a second check on not marking previously allowed keys/values as secret.
218+
$notSecretValues = @()
219+
foreach ($key in $deploymentOutputs.Keys) {
220+
$value = $deploymentOutputs[$key]
221+
$EnvironmentVariables[$key] = $value
222+
223+
if ($CI) {
224+
if (ShouldMarkValueAsSecret $serviceDirectoryPrefix $key $value $notSecretValues) {
225+
# Treat all ARM template output variables as secrets since "SecureString" variables do not set values.
226+
# In order to mask secrets but set environment variables for any given ARM template, we set variables twice as shown below.
227+
Write-Host "##vso[task.setvariable variable=_$key;issecret=true;]$value"
228+
Write-Host "Setting variable as secret '$key': $value"
229+
} else {
230+
Write-Host "Setting variable '$key': $value"
231+
$notSecretValues += $value
232+
}
233+
Write-Host "##vso[task.setvariable variable=$key;]$value"
234+
} else {
235+
Write-Host ($shellExportFormat -f $key, $value)
236+
}
237+
}
238+
239+
if ($key) {
240+
# Isolate the environment variables for easy reading.
241+
Write-Host "`n"
242+
$key = $null
243+
}
244+
}
245+
}
246+
158247
# Support actions to invoke on exit.
159248
$exitActions = @({
160249
if ($exitActions.Count -gt 1) {
@@ -580,78 +669,7 @@ try {
580669
Write-Verbose "Successfully deployed template '$($templateFile.jsonFilePath)' to resource group '$($resourceGroup.ResourceGroupName)'"
581670
}
582671

583-
$serviceDirectoryPrefix = $serviceName.ToUpperInvariant() + "_"
584-
585-
# Add default values
586-
$deploymentOutputs = @{
587-
"$($serviceDirectoryPrefix)CLIENT_ID" = $TestApplicationId;
588-
"$($serviceDirectoryPrefix)CLIENT_SECRET" = $TestApplicationSecret;
589-
"$($serviceDirectoryPrefix)TENANT_ID" = $context.Tenant.Id;
590-
"$($serviceDirectoryPrefix)SUBSCRIPTION_ID" = $context.Subscription.Id;
591-
"$($serviceDirectoryPrefix)RESOURCE_GROUP" = $resourceGroup.ResourceGroupName;
592-
"$($serviceDirectoryPrefix)LOCATION" = $resourceGroup.Location;
593-
"$($serviceDirectoryPrefix)ENVIRONMENT" = $context.Environment.Name;
594-
"$($serviceDirectoryPrefix)AZURE_AUTHORITY_HOST" = $context.Environment.ActiveDirectoryAuthority;
595-
"$($serviceDirectoryPrefix)RESOURCE_MANAGER_URL" = $context.Environment.ResourceManagerUrl;
596-
"$($serviceDirectoryPrefix)SERVICE_MANAGEMENT_URL" = $context.Environment.ServiceManagementUrl;
597-
"$($serviceDirectoryPrefix)STORAGE_ENDPOINT_SUFFIX" = $context.Environment.StorageEndpointSuffix;
598-
}
599-
600-
MergeHashes $EnvironmentVariables $(Get-Variable deploymentOutputs)
601-
602-
foreach ($key in $deployment.Outputs.Keys) {
603-
$variable = $deployment.Outputs[$key]
604-
605-
# Work around bug that makes the first few characters of environment variables be lowercase.
606-
$key = $key.ToUpperInvariant()
607-
608-
if ($variable.Type -eq 'String' -or $variable.Type -eq 'SecureString') {
609-
$deploymentOutputs[$key] = $variable.Value
610-
}
611-
}
612-
613-
if ($OutFile) {
614-
if (!$IsWindows) {
615-
Write-Host 'File option is supported only on Windows'
616-
}
617-
618-
$outputFile = "$($templateFile.originalFilePath).env"
619-
620-
$environmentText = $deploymentOutputs | ConvertTo-Json;
621-
$bytes = [System.Text.Encoding]::UTF8.GetBytes($environmentText)
622-
$protectedBytes = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
623-
624-
Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force
625-
626-
Write-Host "Test environment settings`n $environmentText`nstored into encrypted $outputFile"
627-
} else {
628-
629-
if (!$CI) {
630-
# Write an extra new line to isolate the environment variables for easy reading.
631-
Log "Persist the following environment variables based on your detected shell ($shell):`n"
632-
}
633-
634-
foreach ($key in $deploymentOutputs.Keys) {
635-
$value = $deploymentOutputs[$key]
636-
$EnvironmentVariables[$key] = $value
637-
638-
if ($CI) {
639-
# Treat all ARM template output variables as secrets since "SecureString" variables do not set values.
640-
# In order to mask secrets but set environment variables for any given ARM template, we set variables twice as shown below.
641-
Write-Host "Setting variable '$key': ***"
642-
Write-Host "##vso[task.setvariable variable=_$key;issecret=true;]$($value)"
643-
Write-Host "##vso[task.setvariable variable=$key;]$($value)"
644-
} else {
645-
Write-Host ($shellExportFormat -f $key, $value)
646-
}
647-
}
648-
649-
if ($key) {
650-
# Isolate the environment variables for easy reading.
651-
Write-Host "`n"
652-
$key = $null
653-
}
654-
}
672+
SetDeploymentOutputs $serviceName $context $deployment $templateFile
655673

656674
$postDeploymentScript = $templateFile.originalFilePath | Split-Path | Join-Path -ChildPath 'test-resources-post.ps1'
657675
if (Test-Path $postDeploymentScript) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
function ShouldMarkValueAsSecret([string]$serviceDirectoryPrefix, [string]$key, [string]$value, [array]$allowedValues = @())
2+
{
3+
$logOutputNonSecret = @(
4+
# Environment Variables
5+
"RESOURCEGROUP_NAME",
6+
# Deployment Outputs
7+
"CLIENT_ID",
8+
"TENANT_ID",
9+
"SUBSCRIPTION_ID",
10+
"RESOURCE_GROUP",
11+
"LOCATION",
12+
"ENVIRONMENT",
13+
"AUTHORITY_HOST",
14+
"RESOURCE_MANAGER_URL",
15+
"SERVICE_MANAGEMENT_URL",
16+
"ENDPOINT_SUFFIX",
17+
# This is used in many places and is harder to extract from the base subscription config, so hardcode it for now.
18+
"STORAGE_ENDPOINT_SUFFIX",
19+
# Parameters
20+
"Environment",
21+
"SubscriptionId",
22+
"TenantId",
23+
"TestApplicationId",
24+
"TestApplicationOid",
25+
"ProvisionerApplicationId"
26+
)
27+
28+
$suffix1 = $key -replace $serviceDirectoryPrefix, ""
29+
$suffix2 = $key -replace "AZURE_", ""
30+
$variants = @($key, $suffix1, $suffix2)
31+
if ($variants | Where-Object { $logOutputNonSecret -contains $_ }) {
32+
return $false
33+
}
34+
35+
if ($allowedValues -contains $value) {
36+
return $false
37+
}
38+
39+
return $true
40+
}
41+
42+
function SetSubscriptionConfiguration([object]$subscriptionConfiguration)
43+
{
44+
foreach($pair in $subscriptionConfiguration.GetEnumerator()) {
45+
if ($pair.Value -is [Hashtable]) {
46+
foreach($nestedPair in $pair.Value.GetEnumerator()) {
47+
# Mark values as secret so we don't print json blobs containing secrets in the logs.
48+
# Prepend underscore to the variable name, so we can still access the variable names via environment
49+
# variables if they get set subsequently.
50+
if (ShouldMarkValueAsSecret "AZURE_" $nestedPair.Name $nestedPair.Value) {
51+
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
52+
}
53+
}
54+
} else {
55+
if (ShouldMarkValueAsSecret "AZURE_" $pair.Name $pair.Value) {
56+
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
57+
}
58+
}
59+
}
60+
61+
Write-Host ($subscriptionConfiguration | ConvertTo-Json)
62+
$serialized = $subscriptionConfiguration | ConvertTo-Json -Compress
63+
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
64+
}
65+
66+
function UpdateSubscriptionConfiguration([object]$subscriptionConfigurationBase, [object]$subscriptionConfiguration)
67+
{
68+
foreach ($pair in $subscriptionConfiguration.GetEnumerator()) {
69+
if ($pair.Value -is [Hashtable]) {
70+
if (!$subscriptionConfigurationBase.ContainsKey($pair.Name)) {
71+
$subscriptionConfigurationBase[$pair.Name] = @{}
72+
}
73+
foreach($nestedPair in $pair.Value.GetEnumerator()) {
74+
# Mark values as secret so we don't print json blobs containing secrets in the logs.
75+
# Prepend underscore to the variable name, so we can still access the variable names via environment
76+
# variables if they get set subsequently.
77+
if (ShouldMarkValueAsSecret "AZURE_" $nestedPair.Name $nestedPair.Value) {
78+
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
79+
}
80+
$subscriptionConfigurationBase[$pair.Name][$nestedPair.Name] = $nestedPair.Value
81+
}
82+
} else {
83+
if (ShouldMarkValueAsSecret "AZURE_" $pair.Name $pair.Value) {
84+
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
85+
}
86+
$subscriptionConfigurationBase[$pair.Name] = $pair.Value
87+
}
88+
}
89+
90+
$serialized = $subscriptionConfigurationBase | ConvertTo-Json -Compress
91+
Write-Host ($subscriptionConfigurationBase | ConvertTo-Json)
92+
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
93+
}

eng/common/TestResources/build-test-resource-config.yml

+6-39
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,8 @@ steps:
1313
${{ parameters.SubscriptionConfiguration }}
1414
'@ | ConvertFrom-Json -AsHashtable
1515
16-
foreach($pair in $config.GetEnumerator()) {
17-
if ($pair.Value -is [Hashtable]) {
18-
foreach($nestedPair in $pair.Value.GetEnumerator()) {
19-
# Mark values as secret so we don't print json blobs containing secrets in the logs.
20-
# Prepend underscore to the variable name, so we can still access the variable names via environment
21-
# variables if they get set subsequently.
22-
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
23-
}
24-
} else {
25-
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
26-
}
27-
}
28-
29-
Write-Host ($config | ConvertTo-Json)
30-
$serialized = $config | ConvertTo-Json -Compress
31-
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
16+
. ./eng/common/TestResources/SubConfig-Helpers.ps1
17+
SetSubscriptionConfiguration $config
3218
displayName: Initialize SubscriptionConfiguration variable
3319
3420
- ${{ if parameters.SubscriptionConfigurations }}:
@@ -39,33 +25,14 @@ steps:
3925
4026
- ${{ each config in parameters.SubscriptionConfigurations }}:
4127
- pwsh: |
42-
$config = @'
28+
$configBase = @'
4329
$(SubscriptionConfiguration)
4430
'@ | ConvertFrom-Json -AsHashtable
45-
$addToConfig = @'
31+
$config = @'
4632
${{ config }}
4733
'@ | ConvertFrom-Json -AsHashtable
4834
49-
foreach ($pair in $addToConfig.GetEnumerator()) {
50-
if ($pair.Value -is [Hashtable]) {
51-
if (!$config.ContainsKey($pair.Name)) {
52-
$config[$pair.Name] = @{}
53-
}
54-
foreach($nestedPair in $pair.Value.GetEnumerator()) {
55-
# Mark values as secret so we don't print json blobs containing secrets in the logs.
56-
# Prepend underscore to the variable name, so we can still access the variable names via environment
57-
# variables if they get set subsequently.
58-
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
59-
$config[$pair.Name][$nestedPair.Name] = $nestedPair.Value
60-
}
61-
} else {
62-
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
63-
$config[$pair.Name] = $pair.Value
64-
}
65-
}
66-
67-
$serialized = $config | ConvertTo-Json -Compress
68-
Write-Host ($config | ConvertTo-Json)
69-
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
35+
. ./eng/common/TestResources/SubConfig-Helpers.ps1
36+
UpdateSubscriptionConfiguration $configBase $config
7037
7138
displayName: Merge Test Resource Configurations

0 commit comments

Comments
 (0)