Skip to content

Commit 06c44de

Browse files
azure-sdkheaths
andauthored
Sync eng/common directory with azure-sdk-tools for PR 10579 (Azure#45269)
* Support writing .env files from Test Resources If a language repo opts into it *and* if a `test-resources.bicep` file exists and lints clean of writing secrets *and* if the `.env` file is gitignore'd, write a `.env` file next to `test-resources.bicep`. * Resolve PR feedback * Pass -Force for . hidden files on non-Windows --------- Co-authored-by: Heath Stewart <[email protected]>
1 parent 845f2c7 commit 06c44de

File tree

6 files changed

+98
-31
lines changed

6 files changed

+98
-31
lines changed

eng/common/TestResources/New-TestResources.ps1

+19-10
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ param (
122122
$NewTestResourcesRemainingArguments
123123
)
124124

125+
. (Join-Path $PSScriptRoot .. scripts common.ps1)
125126
. (Join-Path $PSScriptRoot .. scripts Helpers Resource-Helpers.ps1)
126127
. $PSScriptRoot/TestResources-Helpers.ps1
127128
. $PSScriptRoot/SubConfig-Helpers.ps1
@@ -203,11 +204,14 @@ try {
203204
$PSBoundParameters['BaseName'] = $BaseName
204205

205206
# Try detecting repos that support OutFile and defaulting to it
206-
if (!$CI -and !$PSBoundParameters.ContainsKey('OutFile') -and $IsWindows) {
207+
if (!$CI -and !$PSBoundParameters.ContainsKey('OutFile')) {
207208
# TODO: find a better way to detect the language
208-
if (Test-Path "$repositoryRoot/eng/service.proj") {
209+
if ($IsWindows -and $Language -eq 'dotnet') {
209210
$OutFile = $true
210-
Log "Detected .NET repository. Defaulting OutFile to true. Test environment settings would be stored into the file so you don't need to set environment variables manually."
211+
Log "Detected .NET repository. Defaulting OutFile to true. Test environment settings will be stored into a file so you don't need to set environment variables manually."
212+
} elseif ($SupportsTestResourcesDotenv) {
213+
$OutFile = $true
214+
Log "Repository supports reading .env files. Defaulting OutFile to true. Test environment settings may be stored in a .env file so they are read by tests automatically."
211215
}
212216
}
213217

@@ -342,10 +346,10 @@ try {
342346
if ($context.Account.Type -eq 'User') {
343347
# Support corp tenant and TME tenant user id lookups
344348
$user = Get-AzADUser -Mail $context.Account.Id
345-
if ($user -eq $null -or !$user.Id) {
349+
if ($null -eq $user -or !$user.Id) {
346350
$user = Get-AzADUser -UserPrincipalName $context.Account.Id
347351
}
348-
if ($user -eq $null -or !$user.Id) {
352+
if ($null -eq $user -or !$user.Id) {
349353
throw "Failed to find entra object ID for the current user"
350354
}
351355
$ProvisionerApplicationOid = $user.Id
@@ -419,10 +423,10 @@ try {
419423

420424
# Support corp tenant and TME tenant user id lookups
421425
$userAccount = (Get-AzADUser -Mail (Get-AzContext).Account.Id)
422-
if ($userAccount -eq $null -or !$userAccount.Id) {
426+
if ($null -eq $userAccount -or !$userAccount.Id) {
423427
$userAccount = (Get-AzADUser -UserPrincipalName (Get-AzContext).Account)
424428
}
425-
if ($userAccount -eq $null -or !$userAccount.Id) {
429+
if ($null -eq $userAccount -or !$userAccount.Id) {
426430
throw "Failed to find entra object ID for the current user"
427431
}
428432
$TestApplicationOid = $userAccount.Id
@@ -860,14 +864,19 @@ Force creation of resources instead of being prompted.
860864
861865
.PARAMETER OutFile
862866
Save test environment settings into a .env file next to test resources template.
863-
The contents of the file are protected via the .NET Data Protection API (DPAPI).
864-
This is supported only on Windows. The environment file is scoped to the current
865-
service directory.
866867
868+
On Windows in the Azure/azure-sdk-for-net repository,
869+
the contents of the file are protected via the .NET Data Protection API (DPAPI).
870+
The environment file is scoped to the current service directory.
867871
The environment file will be named for the test resources template that it was
868872
generated for. For ARM templates, it will be test-resources.json.env. For
869873
Bicep templates, test-resources.bicep.env.
870874
875+
If `$SupportsTestResourcesDotenv=$true` in language repos' `LanguageSettings.ps1`,
876+
and if `.env` files are gitignore'd, and if a service directory's `test-resources.bicep`
877+
file does not expose secrets based on `bicep lint`, a `.env` file is written next to
878+
`test-resources.bicep` that can be loaded by a test harness to be used for recording tests.
879+
871880
.PARAMETER SuppressVsoCommands
872881
By default, the -CI parameter will print out secrets to logs with Azure Pipelines log
873882
commands that cause them to be redacted. For CI environments that don't support this (like

eng/common/TestResources/New-TestResources.ps1.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -588,17 +588,19 @@ Accept wildcard characters: False
588588
589589
### -OutFile
590590
Save test environment settings into a .env file next to test resources template.
591-
The contents of the file are protected via the .NET Data Protection API (DPAPI).
592-
This is supported only on Windows.
593-
The environment file is scoped to the current
594-
service directory.
595591
592+
On Windows in the Azure/azure-sdk-for-net repository,
593+
the contents of the file are protected via the .NET Data Protection API (DPAPI).
594+
The environment file is scoped to the current service directory.
596595
The environment file will be named for the test resources template that it was
597-
generated for.
598-
For ARM templates, it will be test-resources.json.env.
599-
For
596+
generated for. For ARM templates, it will be test-resources.json.env. For
600597
Bicep templates, test-resources.bicep.env.
601598
599+
If `$SupportsTestResourcesDotenv=$true` in language repos' `LanguageSettings.ps1`,
600+
and if `.env` files are gitignore'd, and if a service directory's `test-resources.bicep`
601+
file does not expose secrets based on `bicep lint`, a `.env` file is written next to
602+
`test-resources.bicep` that can be loaded by a test harness to be used for recording tests.
603+
602604
```yaml
603605
Type: SwitchParameter
604606
Parameter Sets: (All)

eng/common/TestResources/README.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Live Test Resource Management
22

33
Running and recording live tests often requires first creating some resources
4-
in Azure. Service directories that include a `test-resources.json` or `test-resources.bicep`
4+
in Azure. Service directories that include a `test-resources.json` or `test-resources.bicep`
55
file require running [New-TestResources.ps1][] to create these resources and output
66
environment variables you must set.
77

@@ -19,8 +19,8 @@ scenarios as well as on hosted agents for continuous integration testing.
1919
## On the Desktop
2020

2121
To set up your Azure account to run live tests, you'll need to log into Azure,
22-
and create the resources defined in your `test-resources.json` or `test-resources.bicep`
23-
template as shown in the following example using Azure Key Vault. The script will create
22+
and create the resources defined in your `test-resources.json` or `test-resources.bicep`
23+
template as shown in the following example using Azure Key Vault. The script will create
2424
a service principal automatically, or you may create a service principal that can be reused
2525
subsequently.
2626

@@ -34,12 +34,16 @@ Connect-AzAccount -Subscription 'YOUR SUBSCRIPTION ID'
3434
eng\common\TestResources\New-TestResources.ps1 keyvault
3535
```
3636

37-
The `OutFile` switch will be set by default if you are running this for a .NET project on Windows.
38-
This will save test environment settings into a `test-resources.json.env` file next to `test-resources.json`
37+
The `OutFile` switch will be set by default if you are running this for a .NET project on Windows.
38+
This will save test environment settings into a `test-resources.json.env` file next to `test-resources.json`
3939
or a `test-resources.bicep.env` file next to `test-resources.bicep`. The file is protected via DPAPI.
4040
The environment file would be scoped to the current repository directory and avoids the need to
4141
set environment variables or restart your IDE to recognize them.
4242

43+
It will also be set by default for other repositories and on other platforms if your `assets.json`
44+
file contains `"Dotenv": true`. It must be in your `.gitignore` file;
45+
otherwise, an error is returned and no file is generated.
46+
4347
Along with some log messages, this will output environment variables based on
4448
your current shell like in the following example:
4549

eng/common/TestResources/Remove-TestResources.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ if ($ServiceDirectory) {
235235

236236
# Make sure environment files from New-TestResources -OutFile are removed.
237237
Get-ChildItem -Path $root -Filter "$ResourceType-resources.json.env" -Recurse | Remove-Item -Force:$Force
238+
Get-ChildItem -Path $root -Filter ".env" -Recurse -Force | Remove-Item -Force
238239
}
239240

240241
$verifyDeleteScript = {

eng/common/TestResources/TestResources-Helpers.ps1

+57-9
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,33 @@ function BuildBicepFile([System.IO.FileSystemInfo] $file) {
149149
return $templateFilePath
150150
}
151151

152+
function LintBicepFile([string] $path) {
153+
if (!(Get-Command bicep -ErrorAction Ignore)) {
154+
Write-Error "A bicep file was found at '$path' but the Azure Bicep CLI is not installed. See https://aka.ms/bicep-install"
155+
throw
156+
}
157+
158+
# Work around lack of config file override: https://github.com/Azure/bicep/issues/5013
159+
$output = bicep lint "$path" 2>&1
160+
if ($LASTEXITCODE) {
161+
Write-Error "Failed linting bicep file '$path'"
162+
throw
163+
}
164+
165+
$clean = $true
166+
foreach ($line in $output) {
167+
$line = $line.ToString()
168+
169+
# See https://learn.microsoft.com/azure/azure-resource-manager/bicep/bicep-config-linter for lints.
170+
if ($line.Contains('outputs-should-not-contain-secrets')) {
171+
$clean = $false
172+
}
173+
Write-Warning $line
174+
}
175+
176+
$clean
177+
}
178+
152179
function BuildDeploymentOutputs([string]$serviceName, [object]$azContext, [object]$deployment, [hashtable]$environmentVariables) {
153180
$serviceDirectoryPrefix = BuildServiceDirectoryPrefix $serviceName
154181
# Add default values
@@ -203,19 +230,40 @@ function SetDeploymentOutputs(
203230
$deploymentOutputs = BuildDeploymentOutputs $serviceName $azContext $deployment $deploymentEnvironmentVariables
204231

205232
if ($OutFile) {
206-
if (!$IsWindows) {
207-
Write-Host 'File option is supported only on Windows'
208-
}
233+
if ($IsWindows -and $Language -eq 'dotnet') {
234+
$outputFile = "$($templateFile.originalFilePath).env"
235+
236+
$environmentText = $deploymentOutputs | ConvertTo-Json;
237+
$bytes = [System.Text.Encoding]::UTF8.GetBytes($environmentText)
238+
$protectedBytes = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
209239

210-
$outputFile = "$($templateFile.originalFilePath).env"
240+
Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force
211241

212-
$environmentText = $deploymentOutputs | ConvertTo-Json;
213-
$bytes = [System.Text.Encoding]::UTF8.GetBytes($environmentText)
214-
$protectedBytes = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
242+
Write-Host "Test environment settings`n$environmentText`nstored into encrypted $outputFile"
243+
}
244+
elseif ($templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep")) {
245+
$bicepTemplateFile = $templateFile.originalFilePath
215246

216-
Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force
247+
# Make sure the file would not write secrets to .env file.
248+
if (!(LintBicepFile $bicepTemplateFile)) {
249+
Write-Error "$bicepTemplateFile may write secrets. No file written."
250+
}
251+
$outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env'
217252

218-
Write-Host "Test environment settings`n $environmentText`nstored into encrypted $outputFile"
253+
# Make sure the file would be ignored.
254+
git check-ignore -- "$outputFile" > $null
255+
if ($?) {
256+
$environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) {
257+
"$($kv.Key)=`"$($kv.Value)`""
258+
}
259+
260+
Set-Content $outputFile -Value $environmentText -Force
261+
Write-Host "Test environment settings`n$environmentText`nstored in $outputFile"
262+
}
263+
else {
264+
Write-Error "$outputFile is not ignored by .gitignore. No file written."
265+
}
266+
}
219267
}
220268
else {
221269
if (!$CI) {

eng/common/scripts/common.ps1

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ $PackageRepository = "Unknown"
2424
$packagePattern = "Unknown"
2525
$MetadataUri = "Unknown"
2626

27+
# Whether the language repo supports automatically loading .env file generated from TestResources scripts.
28+
$SupportsTestResourcesDotenv = $false
29+
2730
# Import common language settings
2831
$EngScriptsLanguageSettings = Join-path $EngScriptsDir "Language-Settings.ps1"
2932
if (Test-Path $EngScriptsLanguageSettings) {

0 commit comments

Comments
 (0)