Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync eng/common directory with azure-sdk-tools for PR 2248 #25408

Merged
merged 5 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 91 additions & 73 deletions eng/common/TestResources/New-TestResources.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ param (
[switch] $OutFile
)

. $PSScriptRoot/SubConfig-Helpers.ps1

# By default stop for any error.
if (!$PSBoundParameters.ContainsKey('ErrorAction')) {
$ErrorActionPreference = 'Stop'
Expand Down Expand Up @@ -126,7 +128,7 @@ function LoadCloudConfig([string] $env)
function MergeHashes([hashtable] $source, [psvariable] $dest)
{
foreach ($key in $source.Keys) {
if ($dest.Value.ContainsKey($key) -and $dest.Value[$key] -ne $source[$key]) {
if ($dest.Value.Contains($key) -and $dest.Value[$key] -ne $source[$key]) {
Write-Warning ("Overwriting '$($dest.Name).$($key)' with value '$($dest.Value[$key])' " +
"to new value '$($source[$key])'")
}
Expand Down Expand Up @@ -155,6 +157,93 @@ function BuildBicepFile([System.IO.FileSystemInfo] $file)
return $templateFilePath
}

function BuildDeploymentOutputs([string]$serviceDirectoryPrefix, [object]$azContext, [object]$deployment) {
# Add default values
$deploymentOutputs = [Ordered]@{
"${serviceDirectoryPrefix}CLIENT_ID" = $TestApplicationId;
"${serviceDirectoryPrefix}CLIENT_SECRET" = $TestApplicationSecret;
"${serviceDirectoryPrefix}TENANT_ID" = $azContext.Tenant.Id;
"${serviceDirectoryPrefix}SUBSCRIPTION_ID" = $azContext.Subscription.Id;
"${serviceDirectoryPrefix}RESOURCE_GROUP" = $resourceGroup.ResourceGroupName;
"${serviceDirectoryPrefix}LOCATION" = $resourceGroup.Location;
"${serviceDirectoryPrefix}ENVIRONMENT" = $azContext.Environment.Name;
"${serviceDirectoryPrefix}AZURE_AUTHORITY_HOST" = $azContext.Environment.ActiveDirectoryAuthority;
"${serviceDirectoryPrefix}RESOURCE_MANAGER_URL" = $azContext.Environment.ResourceManagerUrl;
"${serviceDirectoryPrefix}SERVICE_MANAGEMENT_URL" = $azContext.Environment.ServiceManagementUrl;
}

MergeHashes $EnvironmentVariables $(Get-Variable deploymentOutputs)

foreach ($key in $deployment.Outputs.Keys) {
$variable = $deployment.Outputs[$key]

# Work around bug that makes the first few characters of environment variables be lowercase.
$key = $key.ToUpperInvariant()

if ($variable.Type -eq 'String' -or $variable.Type -eq 'SecureString') {
$deploymentOutputs[$key] = $variable.Value
}
}

return $deploymentOutputs
}

function SetDeploymentOutputs([string]$serviceName, [object]$azContext, [object]$deployment, [object]$templateFile) {
$serviceDirectoryPrefix = $serviceName.ToUpperInvariant() + "_"
$deploymentOutputs = BuildDeploymentOutputs $serviceDirectoryPrefix $azContext $deployment

if ($OutFile) {
if (!$IsWindows) {
Write-Host 'File option is supported only on Windows'
}

$outputFile = "$($templateFile.originalFilePath).env"

$environmentText = $deploymentOutputs | ConvertTo-Json;
$bytes = [System.Text.Encoding]::UTF8.GetBytes($environmentText)
$protectedBytes = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)

Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force

Write-Host "Test environment settings`n $environmentText`nstored into encrypted $outputFile"
} else {
if (!$CI) {
# Write an extra new line to isolate the environment variables for easy reading.
Log "Persist the following environment variables based on your detected shell ($shell):`n"
}

# Marking values as secret by allowed keys below is not sufficient, as there may be outputs set in the ARM/bicep
# file that re-mark those values as secret (since all user-provided deployment outputs are treated as secret by default).
# This variable supports a second check on not marking previously allowed keys/values as secret.
$notSecretValues = @()
foreach ($key in $deploymentOutputs.Keys) {
$value = $deploymentOutputs[$key]
$EnvironmentVariables[$key] = $value

if ($CI) {
if (ShouldMarkValueAsSecret $serviceDirectoryPrefix $key $value $notSecretValues) {
# Treat all ARM template output variables as secrets since "SecureString" variables do not set values.
# In order to mask secrets but set environment variables for any given ARM template, we set variables twice as shown below.
Write-Host "##vso[task.setvariable variable=_$key;issecret=true;]$value"
Write-Host "Setting variable as secret '$key': $value"
} else {
Write-Host "Setting variable '$key': $value"
$notSecretValues += $value
}
Write-Host "##vso[task.setvariable variable=$key;]$value"
} else {
Write-Host ($shellExportFormat -f $key, $value)
}
}

if ($key) {
# Isolate the environment variables for easy reading.
Write-Host "`n"
$key = $null
}
}
}

# Support actions to invoke on exit.
$exitActions = @({
if ($exitActions.Count -gt 1) {
Expand Down Expand Up @@ -580,78 +669,7 @@ try {
Write-Verbose "Successfully deployed template '$($templateFile.jsonFilePath)' to resource group '$($resourceGroup.ResourceGroupName)'"
}

$serviceDirectoryPrefix = $serviceName.ToUpperInvariant() + "_"

# Add default values
$deploymentOutputs = @{
"$($serviceDirectoryPrefix)CLIENT_ID" = $TestApplicationId;
"$($serviceDirectoryPrefix)CLIENT_SECRET" = $TestApplicationSecret;
"$($serviceDirectoryPrefix)TENANT_ID" = $context.Tenant.Id;
"$($serviceDirectoryPrefix)SUBSCRIPTION_ID" = $context.Subscription.Id;
"$($serviceDirectoryPrefix)RESOURCE_GROUP" = $resourceGroup.ResourceGroupName;
"$($serviceDirectoryPrefix)LOCATION" = $resourceGroup.Location;
"$($serviceDirectoryPrefix)ENVIRONMENT" = $context.Environment.Name;
"$($serviceDirectoryPrefix)AZURE_AUTHORITY_HOST" = $context.Environment.ActiveDirectoryAuthority;
"$($serviceDirectoryPrefix)RESOURCE_MANAGER_URL" = $context.Environment.ResourceManagerUrl;
"$($serviceDirectoryPrefix)SERVICE_MANAGEMENT_URL" = $context.Environment.ServiceManagementUrl;
"$($serviceDirectoryPrefix)STORAGE_ENDPOINT_SUFFIX" = $context.Environment.StorageEndpointSuffix;
}

MergeHashes $EnvironmentVariables $(Get-Variable deploymentOutputs)

foreach ($key in $deployment.Outputs.Keys) {
$variable = $deployment.Outputs[$key]

# Work around bug that makes the first few characters of environment variables be lowercase.
$key = $key.ToUpperInvariant()

if ($variable.Type -eq 'String' -or $variable.Type -eq 'SecureString') {
$deploymentOutputs[$key] = $variable.Value
}
}

if ($OutFile) {
if (!$IsWindows) {
Write-Host 'File option is supported only on Windows'
}

$outputFile = "$($templateFile.originalFilePath).env"

$environmentText = $deploymentOutputs | ConvertTo-Json;
$bytes = [System.Text.Encoding]::UTF8.GetBytes($environmentText)
$protectedBytes = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)

Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force

Write-Host "Test environment settings`n $environmentText`nstored into encrypted $outputFile"
} else {

if (!$CI) {
# Write an extra new line to isolate the environment variables for easy reading.
Log "Persist the following environment variables based on your detected shell ($shell):`n"
}

foreach ($key in $deploymentOutputs.Keys) {
$value = $deploymentOutputs[$key]
$EnvironmentVariables[$key] = $value

if ($CI) {
# Treat all ARM template output variables as secrets since "SecureString" variables do not set values.
# In order to mask secrets but set environment variables for any given ARM template, we set variables twice as shown below.
Write-Host "Setting variable '$key': ***"
Write-Host "##vso[task.setvariable variable=_$key;issecret=true;]$($value)"
Write-Host "##vso[task.setvariable variable=$key;]$($value)"
} else {
Write-Host ($shellExportFormat -f $key, $value)
}
}

if ($key) {
# Isolate the environment variables for easy reading.
Write-Host "`n"
$key = $null
}
}
SetDeploymentOutputs $serviceName $context $deployment $templateFile

$postDeploymentScript = $templateFile.originalFilePath | Split-Path | Join-Path -ChildPath 'test-resources-post.ps1'
if (Test-Path $postDeploymentScript) {
Expand Down
93 changes: 93 additions & 0 deletions eng/common/TestResources/SubConfig-Helpers.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
function ShouldMarkValueAsSecret([string]$serviceDirectoryPrefix, [string]$key, [string]$value, [array]$allowedValues = @())
{
$logOutputNonSecret = @(
# Environment Variables
"RESOURCEGROUP_NAME",
# Deployment Outputs
"CLIENT_ID",
"TENANT_ID",
"SUBSCRIPTION_ID",
"RESOURCE_GROUP",
"LOCATION",
"ENVIRONMENT",
"AUTHORITY_HOST",
"RESOURCE_MANAGER_URL",
"SERVICE_MANAGEMENT_URL",
"ENDPOINT_SUFFIX",
# This is used in many places and is harder to extract from the base subscription config, so hardcode it for now.
"STORAGE_ENDPOINT_SUFFIX",
# Parameters
"Environment",
"SubscriptionId",
"TenantId",
"TestApplicationId",
"TestApplicationOid",
"ProvisionerApplicationId"
)

$suffix1 = $key -replace $serviceDirectoryPrefix, ""
$suffix2 = $key -replace "AZURE_", ""
$variants = @($key, $suffix1, $suffix2)
if ($variants | Where-Object { $logOutputNonSecret -contains $_ }) {
return $false
}

if ($allowedValues -contains $value) {
return $false
}

return $true
}

function SetSubscriptionConfiguration([object]$subscriptionConfiguration)
{
foreach($pair in $subscriptionConfiguration.GetEnumerator()) {
if ($pair.Value -is [Hashtable]) {
foreach($nestedPair in $pair.Value.GetEnumerator()) {
# Mark values as secret so we don't print json blobs containing secrets in the logs.
# Prepend underscore to the variable name, so we can still access the variable names via environment
# variables if they get set subsequently.
if (ShouldMarkValueAsSecret "AZURE_" $nestedPair.Name $nestedPair.Value) {
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
}
}
} else {
if (ShouldMarkValueAsSecret "AZURE_" $pair.Name $pair.Value) {
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
}
}
}

Write-Host ($subscriptionConfiguration | ConvertTo-Json)
$serialized = $subscriptionConfiguration | ConvertTo-Json -Compress
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
}

function UpdateSubscriptionConfiguration([object]$subscriptionConfigurationBase, [object]$subscriptionConfiguration)
{
foreach ($pair in $subscriptionConfiguration.GetEnumerator()) {
if ($pair.Value -is [Hashtable]) {
if (!$subscriptionConfigurationBase.ContainsKey($pair.Name)) {
$subscriptionConfigurationBase[$pair.Name] = @{}
}
foreach($nestedPair in $pair.Value.GetEnumerator()) {
# Mark values as secret so we don't print json blobs containing secrets in the logs.
# Prepend underscore to the variable name, so we can still access the variable names via environment
# variables if they get set subsequently.
if (ShouldMarkValueAsSecret "AZURE_" $nestedPair.Name $nestedPair.Value) {
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
}
$subscriptionConfigurationBase[$pair.Name][$nestedPair.Name] = $nestedPair.Value
}
} else {
if (ShouldMarkValueAsSecret "AZURE_" $pair.Name $pair.Value) {
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
}
$subscriptionConfigurationBase[$pair.Name] = $pair.Value
}
}

$serialized = $subscriptionConfigurationBase | ConvertTo-Json -Compress
Write-Host ($subscriptionConfigurationBase | ConvertTo-Json)
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
}
45 changes: 6 additions & 39 deletions eng/common/TestResources/build-test-resource-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,8 @@ steps:
${{ parameters.SubscriptionConfiguration }}
'@ | ConvertFrom-Json -AsHashtable

foreach($pair in $config.GetEnumerator()) {
if ($pair.Value -is [Hashtable]) {
foreach($nestedPair in $pair.Value.GetEnumerator()) {
# Mark values as secret so we don't print json blobs containing secrets in the logs.
# Prepend underscore to the variable name, so we can still access the variable names via environment
# variables if they get set subsequently.
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
}
} else {
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
}
}

Write-Host ($config | ConvertTo-Json)
$serialized = $config | ConvertTo-Json -Compress
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
. ./eng/common/TestResources/SubConfig-Helpers.ps1
SetSubscriptionConfiguration $config
displayName: Initialize SubscriptionConfiguration variable

- ${{ if parameters.SubscriptionConfigurations }}:
Expand All @@ -39,33 +25,14 @@ steps:

- ${{ each config in parameters.SubscriptionConfigurations }}:
- pwsh: |
$config = @'
$configBase = @'
$(SubscriptionConfiguration)
'@ | ConvertFrom-Json -AsHashtable
$addToConfig = @'
$config = @'
${{ config }}
'@ | ConvertFrom-Json -AsHashtable

foreach ($pair in $addToConfig.GetEnumerator()) {
if ($pair.Value -is [Hashtable]) {
if (!$config.ContainsKey($pair.Name)) {
$config[$pair.Name] = @{}
}
foreach($nestedPair in $pair.Value.GetEnumerator()) {
# Mark values as secret so we don't print json blobs containing secrets in the logs.
# Prepend underscore to the variable name, so we can still access the variable names via environment
# variables if they get set subsequently.
Write-Host "##vso[task.setvariable variable=_$($nestedPair.Name);issecret=true;]$($nestedPair.Value)"
$config[$pair.Name][$nestedPair.Name] = $nestedPair.Value
}
} else {
Write-Host "##vso[task.setvariable variable=_$($pair.Name);issecret=true;]$($pair.Value)"
$config[$pair.Name] = $pair.Value
}
}

$serialized = $config | ConvertTo-Json -Compress
Write-Host ($config | ConvertTo-Json)
Write-Host "##vso[task.setvariable variable=SubscriptionConfiguration;]$serialized"
. ./eng/common/TestResources/SubConfig-Helpers.ps1
UpdateSubscriptionConfiguration $configBase $config

displayName: Merge Test Resource Configurations