From dbfd3f68dd95465eb58e1f4ca52068e9685e0c6e Mon Sep 17 00:00:00 2001 From: Vincent Dai <23257217+vidai-msft@users.noreply.github.com> Date: Wed, 18 Jan 2023 13:59:25 +0800 Subject: [PATCH] Updated script to build TestFx environment as well as the document (#20592) * Updated script to build TestFx environment as well as the document * Update documentation/testing-docs/using-azure-test-framework.md Co-authored-by: Yeming Liu <11371776+isra-fel@users.noreply.github.com> Co-authored-by: Yeming Liu <11371776+isra-fel@users.noreply.github.com> --- .../using-azure-test-framework.md | 201 ++++--- tools/Modules/TestFx-Tasks.psd1 | 170 +++--- tools/Modules/TestFx-Tasks.psm1 | 536 ++++++++---------- tools/TestFx/EnvironmentSetupHelper.cs | 125 ++-- tools/TestFx/Live/LiveTestUtility.psm1 | 2 +- 5 files changed, 491 insertions(+), 543 deletions(-) diff --git a/documentation/testing-docs/using-azure-test-framework.md b/documentation/testing-docs/using-azure-test-framework.md index 5b3e13eb3c0a..45c4350663c8 100644 --- a/documentation/testing-docs/using-azure-test-framework.md +++ b/documentation/testing-docs/using-azure-test-framework.md @@ -1,34 +1,40 @@ -# Using Microsoft.Rest.ClientRuntime.Azure.TestFramework # - -- [Getting Started](#getting-started) -- [Acquiring TestFramework](#acquiring-testframework) -- [Setup prior to Record/Playback tests](#setup-prior-to-record-or-playback-of-tests) - - [New-TestCredential](#new-testcredential) - - [Create New Service Principal](#create-new-service-principal) - - [Use Existing Service Principal](#use-existing-service-principal) - - [Set-TestEnvironment](#set-testenvironment) - - [Existing Service Principal](#existing-service-principal) - - [Manually Set Environment Variables](#manually-set-environment-variables) - - [Environment Variables](#environment-variables) - - [Playback Test](#playback-test) - - [Record Test with Interactive login using OrgId](#record-test-with-interactive-login-using-orgid) - - [Record Test with ServicePrincipal](#record-test-with-serviceprincipal) -- [Record/Playback tests](#record-or-playback-tests) -- [Change Test Environment settings at run-time](#change-test-environment-settings-at-run-time) -- [Troubleshooting](#troubleshooting) -- [Supported Key Value pairs in ConnectionString](#supported-key-value-pairs-in-connectionstring) -- [Environment Variable Reference](#supported-environment-in-test-framework) +# Using Azure PowerShell Test Framework + +- [Using Microsoft.Rest.ClientRuntime.Azure.TestFramework](#using-microsoftrestclientruntimeazuretestframework) + - [Getting Started](#getting-started) + - [Azure PowerShell Test Framework](#azure-powershell-test-framework) + - [Setup prior to Record or Playback of tests](#setup-prior-to-record-or-playback-of-tests) + - [Run Command Set-TestFxEnvironment to Build Connection String (Recommended)](#run-command-set-testfxenvironment-to-build-connection-string-recommended) + - [Create New Service Principal](#create-new-service-principal) + - [Use Existing Service Principal](#use-existing-service-principal) + - [Manually Set Environment Variables to Build Connection String](#manually-set-environment-variables-to-build-connection-string) + - [Environment Variables](#environment-variables) + - [Record Test with service principal](#record-test-with-service-principal) + - [Playback Test](#playback-test) + - [JSON Config File V.S. Environment Variables](#json-config-file-vs-environment-variables) + - [Record or Playback Tests](#record-or-playback-tests) + - [Change Test Environment settings at run-time](#change-test-environment-settings-at-run-time) + - [Once you set your connection string, you can add or update key/value settings](#once-you-set-your-connection-string-you-can-add-or-update-keyvalue-settings) + - [Note:](#note) + - [Troubleshooting](#troubleshooting) + - [Issue: exceptions in Microsoft.Azure.Test.HttpRecorder](#issue-exceptions-in-microsoftazuretesthttprecorder) + - [Supported Environments in Test Framework](#supported-environments-in-test-framework) + - [Default Environments and associated Uri](#default-environments-and-associated-uri) + - [Environment = Prod](#environment--prod) + - [Environment = Dogfood](#environment--dogfood) + - [Environment = Next](#environment--next) + - [Environment = Current](#environment--current) + - [Environment = Custom](#environment--custom) ## Getting Started -- Install the latest Az.Resources from the [PSGallery](https://www.powershellgallery.com/) into Windows PowerShell - - Run Windows PowerShell as administrator and execute following command - - `Install-Module -Name Az.Resources -Scope AllUsers -AllowClobber` -- Double click `.\tools\PS-VSPrompt` shortcut - - This starts the VS Developer command prompt in PowerShell inside the `azure-powershell/tools` directory -- Import the `Repo-Tasks` module that helps to perform basic repository tasks - - Run the command `Import-Module .\Repo-Tasks.psd1` -## Azure PowerShell TestFramework +- Install the latest `Az.Resources` from the [PSGallery](https://www.powershellgallery.com/) into PowerShell + - Run PowerShell as administrator and execute the following command + - `Install-Module -Name Az.Resources -Scope AllUsers -AllowClobber -Force` +- Import the `TestFx-Tasks` module that helps to configure the settings + - Run the command `Import-Module ./tools/Modules/TestFx-Tasks.psd1` + +## Azure PowerShell Test Framework Azure PowerShell repo now has its own test framework located under `tools\TestFx`, which supports recording all the HTTP requests from behind Azure PowerShell cmdlets and then playing them back. @@ -36,99 +42,112 @@ The target framework of test is .Net Core 3.1, please ensure .Net runtime Micros ## Setup prior to Record or Playback of tests -In order to Record/Playback a test, you need to setup a connection string that consists of various key/value pairs that provide information to the test environment. +In order to Record/Playback a test, test framework needs to setup a connection string that consists of various key/value pairs that provide necessary information. + +You can choose either option to configure the settings: -You have three options to set up the connection string: -- Run the [`New-TestCredential` cmdlet](#new-testcredential) (recommended for PowerShell development) -- Run the [`Set-TestEnvironment` cmdlet](#set-testenvironment) -- [Manually set the environment variables](#manually-set-environment-variables). +- Run the [`Set-TestFxEnvironment` cmdlet](#run-command-set-testfxenvironment-to-build-connection-string) (Recommended) +- [Manually set the environment variables](#manually-set-environment-variables-to-build-connection-string) -### New-TestCredential +### Run Command Set-TestFxEnvironment to Build Connection String (Recommended) -This cmdlet, located in the [`Repo-Tasks` module](/tools/Repo-Tasks.psd1), which pulls in the [`TestFx-Tasks` module](/tools/Modules/TestFx-Tasks.psd1) and [`Build-Tasks` module](/tools/Modules/Build-Tasks.psd1), will allow you to create a credentials file (located in `C:/Users/\/.azure/testcredentials.json`) that will be used to set the environment variable when scenario tests are run. This credentials file will be used in all future sessions unless it is deleted or the environment variables are manually set. This cmdlet is not currently available for .NET SDK development. +This cmdlet will allow you to create a credentials file (located in `C:/Users//.Azure/testcredentials.json`) that will be used to set the connection string when scenario tests are run. This credentials file will be used in all future sessions unless it is deleted. #### Create New Service Principal -Using a service principal is the preferred option for recording tests because it works with both .NET Framework and .NET Core. In order to create a new service principal, run this command with an unused service principal display name: +Using a service principal is the preferred option for recording tests because it works with both .NET Framework and .NET Core. +In order to create a new service principal, run the following command with an unused service principal display name: ```powershell -New-TestCredential -ServicePrincipalDisplayName "ScenarioTestCredentials" -SubscriptionId ` - -TenantId -RecordMode "Record" +Set-TestFxEnvironment -ServicePrincipalDisplayName -SubscriptionId -TenantId -RecordMode "Record" ``` -This command will create a new service principal, set the correct role assignment for this service principal based upon the subscription provided, and place the service principal id and automatically generated secret into the credentials file. +This command will first create a new service principal. And then set the `Contributor` role assignment for this service principal based upon the subscription provided. After that, it will place the service principal application id and automatically generated secret into the credentials file. -Alternatively, to create a service principal, follow the [Azure AD guide to create a Application Service Principal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#create-an-active-directory-application). The application type should be `Web app / API` and the sign-on URL value is irrelevant (you can set any value). +If the display name of the service principal already exists, it will prompt if you would like to create a new one with the same name. +If the answer is "Y", the new generated application id and the secret will be saved. + +Alternatively, if you prefer creating a service principal by yourself from Azure portal, follow the [Azure AD guide to create a Application Service Principal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#create-an-active-directory-application). #### Use Existing Service Principal -If you would like to use an existing service principal, run this command with an existing service principal display name and secret: +If you would like to use an existing service principal, run the following command with an existing service principal application id and secret: ```powershell -New-TestCredential -ServicePrincipalDisplayName "Existing Service Principal" -ServicePrincipalSecret ` -"testpassword" -SubscriptionId -TenantId -RecordMode "Record" +Set-TestFxEnvironment -ServicePrincipalId -ServicePrincipalSecret -SubscriptionId -TenantId -RecordMode "Record" ``` -### Set-TestEnvironment - -This cmdlet, located in the [`Repo-Tasks` module](/tools/Repo-Tasks.psd1), will directly set the environment variable for the session. +For existing service principal, this command will respect your own settings and won't assign the `Contributor` role automatically. -#### Existing Service Principal - -This is the preferred option for recording tests because it works with both .NET Framework and .NET Core. - -```powershell -Set-TestEnvironment -ServicePrincipalId -ServicePrincipalSecret ` -"testpassword" -SubscriptionId -TenantId -RecordMode "Record" -``` + -### Manually Set Environment Variables +### Manually Set Environment Variables to Build Connection String #### Environment Variables `TEST_CSM_ORGID_AUTHENTICATION` -* This is the connection string that determines how to connect to Azure. This includes both your authentication and the Azure environment to connect to. -`AZURE_TEST_MODE` -* This specifies whether the test framework will `Record` test sessions or `Playback` previously recorded test sessions. +* This determines how to connect to Azure. It includes both your authentication and the Azure environment information. -#### Playback Test - -The default test mode is `Playback` mode, so setting up the connection string is not required. You can optionally set environment variables: +`AZURE_TEST_MODE` -``` -TEST_CSM_ORGID_AUTHENTICATION= -AZURE_TEST_MODE=Playback -``` +* This specifies whether the test framework will `Record` test sessions or `Playback` previously recorded test sessions. -#### Record Test with service principal +#### Record Tests -After the service principal is created, you will need to give it access to Azure resources. This can be done with the following PowerShell command, with the [Service Principal Application ID](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-application-id-and-authentication-key) (this is a guid, not the display name of the service principal) substituted in for `{clientId}`. +After the service principal is created, you will need to give it access to Azure resources. This can be done with the following PowerShell command. The argument for this command is the application id (See [Service Principal Application ID](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-application-id-and-authentication-key)) ```powershell -New-AzRoleAssignment -ServicePrincipalName {clientId} -RoleDefinitionName Contributor +New-AzRoleAssignment -ApplicationId -Scope "/subscriptions/" -RoleDefinitionName Contributor ``` -To use this option, set the following environment variable before starting Visual Studio. The following values are substituted into the below connection string: +To use this option, set the following environment variable before starting Visual Studio. The following values are substituted into the below environment variable: + +`ClientId` -`clientId` * The [Service Principal Application ID](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-application-id-and-authentication-key) -`clientSecret` -* A [Service Principal Authentication Key](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-application-id-and-authentication-key) +`ClientSecret` -`tenantId` -* The [AAD Tenant ID](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-tenant-id) +* The [Service Principal Authentication Key](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-application-id-and-authentication-key) ``` -TEST_CSM_ORGID_AUTHENTICATION=SubscriptionId={SubId};ServicePrincipal={clientId};ServicePrincipalSecret={clientSecret};TenantId={tenantId};Environment={env};HttpRecorderMode=Record; +TEST_CSM_ORGID_AUTHENTICATION=Environment=Prod;SubscriptionId=;TenantId=;ServicePrincipal=;ServicePrincipalSecret=;HttpRecorderMode=Record; AZURE_TEST_MODE=Record ``` +#### Playback Tests + +The default test mode is `Playback`, so setting up the `AZURE_TEST_MODE` is not required. You can optionally set environment variables: + +``` +TEST_CSM_ORGID_AUTHENTICATION=Environment=Prod;SubscriptionId=;TenantId=;ServicePrincipal=;ServicePrincipalSecret=;HttpRecorderMode=Playback; +AZURE_TEST_MODE=Playback +``` + +## JSON Config File V.S. Environment Variables + +Opting for config file is the recommended way to build connection string because any changes you make will take effect immediately without having to restart Visual Studio. However, updating the environment variables is different. It requires rebooting Visual Studio before it can read the updated values. So following is the steps how Test Framework detects the settings. + +- If JSON config file exists + - It will be used to build the connection string. Anything set in the environment variables will be ignored +- If JSON config file does not exist + - Test framework will first retrieve the environment variable `TEST_CSM_ORGID_AUTHENTICATION` and use its value to build the connection string except for the test mode (Record/Playback). + - Then test framework will try to get the value of the environment variable `AZURE_TEST_MODE` + - If `AZURE_TEST_MODE` is set, its value will be used as the test mode + - Otherwise, the property named `HttpRecorderMode` configured in `TEST_CSM_ORGID_AUTHENTICATION` will be used + - If the property `HttpRecorderMode` is also not set, `Playback` will be applied as the default value + +If you are not sure the settings on your machine, please run command `Get-TestFxEnvironment`. It will consolidate above steps and display the ultimate result. + ## Record or Playback Tests - [Run the tests](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/azure-powershell-developer-guide.md#recordingrunning-tests) and make sure that you got a generated `.json` file that matches the test name under the `SessionRecords` folder in the test project. -- To assure that the records work fine, delete the connection string (default mode is Playback mode) OR change HttpRecorderMode within the connection string to "Playback" and run the tests +- If you want to switch from Record to Playback or from Playback to Record, consider below steps. + - If you opt for JSON config file, update the value of the property `HttpRecorderMode` in the JSON. + - If you opt for environment variables + - If you have `AZURE_TEST_MODE` set, update the value of this variable + - Otherwise, update the value of the property `HttpRecorderMode` defined in the variable `TEST_CSM_ORGID_AUTHENTICATION` ## Change Test Environment settings at run-time @@ -146,6 +165,7 @@ TestEnvironment.Endpoints.GraphUri = new Uri("https://newGraphUri.windows.net"); ``` ### Note: + Changing the above properties at run-time has the potential to hard code few things in your tests. Best practice would be to use these properties to change values at run-time from immediate window at run-time and avoid hard-coding certain values. ## Troubleshooting @@ -160,21 +180,21 @@ Ensure that the `HttpRecorderMode` in the `TEST_CSM_ORGID_AUTHENTICATION` enviro ##### Environment = Prod - AADAuthUri = "https://login.microsoftonline.com" - GalleryUri = "https://gallery.azure.com/" - GraphUri = "https://graph.windows.net/" - IbizaPortalUri = "https://portal.azure.com/" - RdfePortalUri = "http://go.microsoft.com/fwlink/?LinkId=254433" - ResourceManagementUri = "https://management.azure.com/" - ServiceManagementUri = "https://management.core.windows.net" - AADTokenAudienceUri = "https://management.core.windows.net" - GraphTokenAudienceUri = "https://graph.windows.net/" - DataLakeStoreServiceUri = "https://azuredatalakestore.net" - DataLakeAnalyticsJobAndCatalogServiceUri = "https://azuredatalakeanalytics.net" + AADAuthUri = "https://login.microsoftonline.com" + GalleryUri = "https://gallery.azure.com/" + GraphUri = "https://graph.windows.net/" + IbizaPortalUri = "https://portal.azure.com/" + RdfePortalUri = "http://go.microsoft.com/fwlink/?LinkId=254433" + ResourceManagementUri = "https://management.azure.com/" + ServiceManagementUri = "https://management.core.windows.net" + AADTokenAudienceUri = "https://management.core.windows.net" + GraphTokenAudienceUri = "https://graph.windows.net/" + DataLakeStoreServiceUri = "https://azuredatalakestore.net" + DataLakeAnalyticsJobAndCatalogServiceUri = "https://azuredatalakeanalytics.net" ##### Environment = Dogfood - AADAuthUri = "https://login.windows-ppe.net"; + AADAuthUri = "https://login.windows-ppe.net"; GalleryUri = "https://df.gallery.azure-test.net/"; GraphUri = "https://graph.ppe.windows.net/"; IbizaPortalUri = "http://df.onecloud.azure-test.net"; @@ -188,7 +208,7 @@ Ensure that the `HttpRecorderMode` in the `TEST_CSM_ORGID_AUTHENTICATION` enviro ##### Environment = Next - AADAuthUri = "https://login.windows-ppe.net" + AADAuthUri = "https://login.windows-ppe.net" GalleryUri = "https://next.gallery.azure-test.net/" GraphUri = "https://graph.ppe.windows.net/" IbizaPortalUri = "http://next.onecloud.azure-test.net" @@ -202,7 +222,7 @@ Ensure that the `HttpRecorderMode` in the `TEST_CSM_ORGID_AUTHENTICATION` enviro ##### Environment = Current - AADAuthUri = "https://login.windows-ppe.net" + AADAuthUri = "https://login.windows-ppe.net" GalleryUri = "https://df.gallery.azure-test.net/" GraphUri = "https://graph.ppe.windows.net/" IbizaPortalUri = "http://df.onecloud.azure-test.net" @@ -215,6 +235,7 @@ Ensure that the `HttpRecorderMode` in the `TEST_CSM_ORGID_AUTHENTICATION` enviro DataLakeAnalyticsJoAbndCatalogServiceUri = "https://konaaccountdogfood.net" ##### Environment = Custom + When specified, test framework expect all Uri's to be provided by the user as part of the connection string. What is also supported is as below (connections string example) diff --git a/tools/Modules/TestFx-Tasks.psd1 b/tools/Modules/TestFx-Tasks.psd1 index a48360232ffa..ee26de85d0f8 100644 --- a/tools/Modules/TestFx-Tasks.psd1 +++ b/tools/Modules/TestFx-Tasks.psd1 @@ -1,126 +1,130 @@ -# -# Module manifest for module 'TestFx-Tasks' -# -# Generated by: ShahAbhijeet -# -# Generated on: 11/18/2016 -# +# ---------------------------------------------------------------------------------- +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- @{ -# Script module or binary module file associated with this manifest. -RootModule = 'TestFx-Tasks.psm1' + # Script module or binary module file associated with this manifest. + RootModule = 'TestFx-Tasks.psm1' -# Version number of this module. -ModuleVersion = '1.0' + # Version number of this module. + ModuleVersion = '1.0' -# Supported PSEditions -# CompatiblePSEditions = @() + # Supported PSEditions + # CompatiblePSEditions = @() -# ID used to uniquely identify this module -GUID = '676f988a-1745-4263-bb86-5887d6b1f9f3' + # ID used to uniquely identify this module + GUID = '676f988a-1745-4263-bb86-5887d6b1f9f3' -# Author of this module -Author = 'ShahAbhijeet' + # Author of this module + Author = 'Azure PowerShell' -# Company or vendor of this module -CompanyName = 'Microsoft' + # Company or vendor of this module + CompanyName = 'Microsoft Corporation' -# Copyright statement for this module -Copyright = '(c) 2016 ShahAbhijeet. All rights reserved.' + # Copyright statement for this module + Copyright = 'Microsoft Corporation. All rights reserved.' -# Description of the functionality provided by this module -# Description = '' + # Description of the functionality provided by this module + # Description = '' -# Minimum version of the Windows PowerShell engine required by this module -# PowerShellVersion = '' + # Minimum version of the Windows PowerShell engine required by this module + # PowerShellVersion = '' -# Name of the Windows PowerShell host required by this module -# PowerShellHostName = '' + # Name of the Windows PowerShell host required by this module + # PowerShellHostName = '' -# Minimum version of the Windows PowerShell host required by this module -# PowerShellHostVersion = '' + # Minimum version of the Windows PowerShell host required by this module + # PowerShellHostVersion = '' -# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# DotNetFrameworkVersion = '' + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' -# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# CLRVersion = '' + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # CLRVersion = '' -# Processor architecture (None, X86, Amd64) required by this module -# ProcessorArchitecture = '' + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' -# Modules that must be imported into the global environment prior to importing this module -RequiredModules = @(@{ModuleName = 'Az.Resources'; ModuleVersion = '1.0.0'; }) + # Modules that must be imported into the global environment prior to importing this module + RequiredModules = @(@{ModuleName = 'Az.Resources'; ModuleVersion = '1.0.0'; }) -# Assemblies that must be loaded prior to importing this module -# RequiredAssemblies = @() + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -# ScriptsToProcess = @() + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() -# Format files (.ps1xml) to be loaded when importing this module -# FormatsToProcess = @() + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() -# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'Set-TestEnvironment', 'Remove-ServicePrincipal', 'New-ServicePrincipal', 'Set-SPNRole', 'New-TestCredential' + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = 'Get-TestFxEnvironment', 'Set-TestFxEnvironment' -# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. -CmdletsToExport = @() + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() -# Variables to export from this module -# VariablesToExport = @() + # Variables to export from this module + # VariablesToExport = @() -# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = @() + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() -# DSC resources to export from this module -# DscResourcesToExport = @() + # DSC resources to export from this module + # DscResourcesToExport = @() -# List of all modules packaged with this module -# ModuleList = @() + # List of all modules packaged with this module + # ModuleList = @() -# List of all files packaged with this module -# FileList = @() + # List of all files packaged with this module + # FileList = @() -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ - PSData = @{ + PSData = @{ - # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() - # A URL to the license for this module. - # LicenseUri = '' + # A URL to the license for this module. + # LicenseUri = '' - # A URL to the main website for this project. - # ProjectUri = '' + # A URL to the main website for this project. + # ProjectUri = '' - # A URL to an icon representing this module. - # IconUri = '' + # A URL to an icon representing this module. + # IconUri = '' - # ReleaseNotes of this module - # ReleaseNotes = '' + # ReleaseNotes of this module + # ReleaseNotes = '' - # External dependent modules of this module - # ExternalModuleDependencies = '' + # External dependent modules of this module + # ExternalModuleDependencies = '' - } # End of PSData hashtable + } # End of PSData hashtable -} # End of PrivateData hashtable + } # End of PrivateData hashtable -# HelpInfo URI of this module -# HelpInfoURI = '' + # HelpInfo URI of this module + # HelpInfoURI = '' -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' } - diff --git a/tools/Modules/TestFx-Tasks.psm1 b/tools/Modules/TestFx-Tasks.psm1 index 55654655fde9..a6a8ffc5ce8a 100644 --- a/tools/Modules/TestFx-Tasks.psm1 +++ b/tools/Modules/TestFx-Tasks.psm1 @@ -1,360 +1,290 @@ -Function New-TestCredential -{ - [CmdletBinding( - SupportsShouldProcess=$true - )] +$script:TestFxEnvDirectory = Join-Path -Path $env:USERPROFILE -ChildPath ".Azure" +$script:TestFxEnvFileName = "testcredentials.json" +$script:TestFxEnvConnectionStringKey = "TEST_CSM_ORGID_AUTHENTICATION" +$script:TestFxEnvTestModeKey = "AZURE_TEST_MODE" +$script:TestFxEnvExtraPropKeys = @( + "AADAuthUri", + "AADTokenAudienceUri", + "DataLakeAnalyticsJobAndCatalogServiceUri", + "DataLakeStoreServiceUri", + "GalleryUri", + "GraphTokenAudienceUri", + "GraphUri", + "IbizaPortalUri", + "RdfePortalUri", + "ResourceManagementUri", + "ServiceManagementUri" +) + +function Set-TestFxEnvironment { + [CmdletBinding(DefaultParameterSetName = "NewServicePrincipal")] param( + [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string]$ServicePrincipalDisplayName, + [guid] $SubscriptionId, - [Parameter(Mandatory=$true, HelpMessage = "SubscriptionId you would like to use")] + [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string]$SubscriptionId, + [guid] $TenantId, - [Parameter(Mandatory=$true, HelpMessage='AADTenant/TenantId you would like to use')] + [Parameter(Mandatory, ParameterSetName = "NewServicePrincipal")] [ValidateNotNullOrEmpty()] - [string]$TenantId, + [string] $ServicePrincipalDisplayName, - [Parameter(Mandatory=$true, HelpMessage = "SubscriptionId you would like to use")] - [ValidateSet("Playback", "Record", "None")] - [string]$RecordMode, + [Parameter(Mandatory, ParameterSetName = "ExistingServicePrincipal")] + [ValidateNotNullOrEmpty()] + [guid] $ServicePrincipalId, - [Parameter(Mandatory=$false, HelpMessage='ServicePrincipal Secret/ClientId Secret you would like to use (required for existing Service Principal)')] + [Parameter(Mandatory, ParameterSetName = "ExistingServicePrincipal")] [ValidateNotNullOrEmpty()] - [securestring]$ServicePrincipalSecret, + [string] $ServicePrincipalSecret, + + [Parameter(Mandatory)] + [ValidateSet("Playback", "Record")] + [string] $RecorderMode, - [Parameter(Mandatory=$false, HelpMessage="Environment you would like to run in")] + [Parameter()] [ValidateSet("Prod", "Dogfood", "Current", "Next", "Custom")] - [string]$TargetEnvironment='Prod', - - [Parameter(Mandatory=$false)] - [string]$ResourceManagementUri, - - [Parameter(Mandatory=$false)] - [string]$GraphUri, - - [Parameter(Mandatory=$false)] - [string]$AADAuthUri, - - [Parameter(Mandatory=$false)] - [string]$AADTokenAudienceUri, - - [Parameter(Mandatory=$false)] - [string]$GraphTokenAudienceUri, - - [Parameter(Mandatory=$false)] - [string]$IbizaPortalUri, - - [Parameter(Mandatory=$false)] - [string]$ServiceManagementUri, - - [Parameter(Mandatory=$false)] - [string]$RdfePortalUri, - - [Parameter(Mandatory=$false)] - [string]$GalleryUri, - - [Parameter(Mandatory=$false)] - [string]$DataLakeStoreServiceUri, - - [Parameter(Mandatory=$false)] - [string]$DataLakeAnalyticsJobAndCatalogServiceUri, - - [Parameter(Mandatory=$false)] - [switch]$Force - ) + [string] $TargetEnvironment = "Prod", - [hashtable]$credentials = @{} - $credentials.SubscriptionId = $SubscriptionId - $credentials.HttpRecorderMode = $RecordMode - $credentials.Environment = $TargetEnvironment - - if ([string]::IsNullOrEmpty($ServicePrincipalDisplayName) -eq $false) { - $existingServicePrincipal = Get-AzADServicePrincipal -SearchString $ServicePrincipalDisplayName | Where-Object {$_.DisplayName -eq $ServicePrincipalDisplayName} - if ($existingServicePrincipal -eq $null -and ($Force -or $PSCmdlet.ShouldContinue("ServicePrincipal `"" + $ServicePrincipalDisplayName + "`" does not exist, would you like to create a new ServicePrincipal with this name?", "Create ServicePrincipal?"))) - { - if (![string]::IsNullOrEmpty($ServicePrincipalSecret)) - { - Write-Warning "Service Principal secrets are randomly generated, so provided secret value will not be used during creation." - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $AADAuthUri, - if ($TargetEnvironment -ne 'Prod') - { - throw "To create a new Service Principal you must be in Prod. Please run again with `$TargetEnvironment set to 'Prod'" - } - $Scope = "/subscriptions/" + $SubscriptionId - $NewServicePrincipal = New-AzADServicePrincipal -DisplayName $ServicePrincipalDisplayName - Write-Host "New ServicePrincipal created: " $NewServicePrincipal.ApplicationId - - $NewRole = Get-AzRoleAssignment -ObjectId $NewServicePrincipal.Id -RoleDefinitionName Contributor -ErrorAction SilentlyContinue - $Retries = 0; - While (($NewRole.RoleDefinitionName -ne 'Contributor') -and ($Retries -le 6)) - { - # Sleep here for a few seconds to allow the service principal application to become active (should only take a couple of seconds normally) - Start-Sleep 5 - New-AzRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $NewServicePrincipal.ApplicationId -Scope $Scope | Write-Verbose -ErrorAction SilentlyContinue - $NewRole = Get-AzRoleAssignment -ObjectId $NewServicePrincipal.Id -ErrorAction SilentlyContinue - $Retries++; - } - - $credentials.ServicePrincipal = $NewServicePrincipal.ApplicationId - $ServicePrincipalSecret = $NewServicePrincipal.Secret - $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ServicePrincipalSecret) - $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - $credentials.ServicePrincipalSecret = $UnsecurePassword - } - - else - { - if ([string]::IsNullOrEmpty($ServicePrincipalSecret)) - { - throw "Service Principal secret required for existing Service Principal." - } - $credentials.ServicePrincipal = $existingServicePrincipal.ApplicationId - $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ServicePrincipalSecret) - $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - $credentials.ServicePrincipalSecret = $UnsecurePassword - } - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $AADTokenAudienceUri, - if ([string]::IsNullOrEmpty($TenantId) -eq $false) { - $credentials.TenantId = $TenantId - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $DataLakeAnalyticsJobAndCatalogServiceUri, - if ([string]::IsNullOrEmpty($ResourceManagementUri) -eq $false) { - $credentials.ResourceManagementUri = $ResourceManagementUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $DataLakeStoreServiceUri, - if ([string]::IsNullOrEmpty($GraphUri) -eq $false) { - $credentials.GraphUri = $GraphUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $GalleryUri, - if ([string]::IsNullOrEmpty($AADAuthUri) -eq $false) { - $credentials.AADAuthUri = $AADAuthUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $GraphTokenAudienceUri, - if ([string]::IsNullOrEmpty($AADTokenAudienceUri) -eq $false) { - $credentials.AADTokenAudienceUri = $AADTokenAudienceUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $GraphUri, - if ([string]::IsNullOrEmpty($GraphTokenAudienceUri) -eq $false) { - $credentials.GraphTokenAudienceUri = $GraphTokenAudienceUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $IbizaPortalUri, - if ([string]::IsNullOrEmpty($IbizaPortalUri) -eq $false) { - $credentials.IbizaPortalUri = $IbizaPortalUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $RdfePortalUri, - if ([string]::IsNullOrEmpty($ServiceManagementUri) -eq $false) { - $credentials.ServiceManagementUri = $ServiceManagementUri - } + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $ResourceManagementUri, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $ServiceManagementUri, + + [Parameter()] + [switch] $Force + ) - if ([string]::IsNullOrEmpty($RdfePortalUri) -eq $false) { - $credentials.RdfePortalUri = $RdfePortalUri + $azContext = Get-AzContext + $currentSubscriptionId = $azContext.Subscription.Id + $currentTenantId = $azContext.Tenant.Id + + if (!$Force.IsPresent) { + if ($SubscriptionId -ne $currentSubscriptionId) { + Write-Warning "The passed in argument SubscriptionId does not match with the current Azure context." + } + if ($TenantId -ne $currentTenantId) { + Write-Warning "The passed in argument TenantId does not match with the current Azure context." + } } - if ([string]::IsNullOrEmpty($GalleryUri) -eq $false) { - $credentials.GalleryUri = $GalleryUri + switch ($PSCmdlet.ParameterSetName) { + "NewServicePrincipal" { + $sp = New-TestFxServicePrincipal -SubscriptionId $SubscriptionId -ServicePrincipalDisplayName $ServicePrincipalDisplayName -Force:$Force + $spAppId = $sp.AppId + $spSecret = $sp.PasswordCredentials.SecretText + } + "ExistingServicePrincipal" { + $sp = Get-AzADServicePrincipal -ApplicationId $ServicePrincipalId + if ($null -eq $sp) { + throw "The service principal `"$ServicePrincipalId`" does not exist. Please verify or create a new one." + } + + $spAppId = $ServicePrincipalId + $spSecret = $ServicePrincipalSecret + } } - if ([string]::IsNullOrEmpty($DataLakeStoreServiceUri) -eq $false) { - $credentials.DataLakeStoreServiceUri = $DataLakeStoreServiceUri + $testFxEnvProps = [PSCustomObject]@{ + Environment = $TargetEnvironment + SubscriptionId = $SubscriptionId + TenantId = $TenantId + ServicePrincipal = $spAppId + ServicePrincipalSecret = $spSecret + HttpRecorderMode = $RecorderMode } - if ([string]::IsNullOrEmpty($DataLakeAnalyticsJobAndCatalogServiceUri) -eq $false) { - $credentials.DataLakeAnalyticsJobAndCatalogServiceUri = $DataLakeAnalyticsJobAndCatalogServiceUri + $script:testFxEnvExtraPropKeys | ForEach-Object { + if ($PSBoundParameters.ContainsKey($_)) { + $testFxEnvProps | Add-Member -NotePropertyName $_ -NotePropertyValue $PSBoundParameters[$_] + } } - $credentialsJson = $credentials | ConvertTo-Json - $directoryPath = $Env:USERPROFILE + "\.azure" - if (!(Test-Path $directoryPath) -and ($Force -or $PSCmdlet.ShouldContinue("Do you want to create directory: " + $directoryPath + " which will contain your credentials file?", "Create directory?"))) { - New-Item -ItemType Directory -Path $directoryPath + if (!(Test-Path -LiteralPath $script:TestFxEnvDirectory -PathType Container)) { + New-Item -Path $script:TestFxEnvDirectory -ItemType Directory -Force } - $filePath = $Env:USERPROFILE + "\.azure\testcredentials.json" - $credentialsJson | Out-File $filePath - Write-Host "" - Write-Host "Created credential file:" $filePath - + $testFxEnvProps + $testFxEnvFile = Join-Path -Path $script:TestFxEnvDirectory -ChildPath $script:TestFxEnvFileName + $testFxEnvProps | ConvertTo-Json | Out-File -LiteralPath $testFxEnvFile -Force } -Function Set-TestEnvironment -{ -<# -.SYNOPSIS -This cmdlet helps you to setup Test Environment for running tests -In order to successfully run a test, you will need SubscriptionId, TenantId -This cmdlet will only prompt you for Subscription and Tenant information, rest all other parameters are optional - -#> - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, HelpMessage='ServicePrincipal/ClientId you would like to use')] - [ValidateNotNullOrEmpty()] - [string]$ServicePrincipalId, - [Parameter(Mandatory=$true, HelpMessage='ServicePrincipal Secret/ClientId Secret you would like to use')] +function New-TestFxServicePrincipal { + [CmdletBinding(SupportsShouldProcess)] + param( [ValidateNotNullOrEmpty()] - [string]$ServicePrincipalSecret, + [string] $ServicePrincipalDisplayName, - [Parameter(Mandatory=$true, HelpMessage = "SubscriptionId you would like to use")] + [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string]$SubscriptionId, + [guid] $SubscriptionId, - [Parameter(Mandatory=$true, HelpMessage='AADTenant/TenantId you would like to use')] - [ValidateNotNullOrEmpty()] - [string]$TenantId, - - [Parameter(Mandatory=$true, HelpMessage = "Would you like to record or playback your tests?")] - [ValidateSet("Playback", "Record", "None")] - [string]$RecordMode='Playback', - - [ValidateSet("Prod", "Dogfood", "Current", "Next")] - [string]$TargetEnvironment='Prod', - - [string]$ResourceManagementUri, - [string]$GraphUri, - [string]$AADAuthUri, - [string]$AADTokenAudienceUri, - [string]$GraphTokenAudienceUri, - [string]$IbizaPortalUri, - [string]$ServiceManagementUri, - [string]$RdfePortalUri, - [string]$GalleryUri, - [string]$DataLakeStoreServiceUri, - [string]$DataLakeAnalyticsJobAndCatalogServiceUri + [Parameter()] + [switch] $Force ) - $formattedConnStr = [string]::Format("SubscriptionId={0};HttpRecorderMode={1};Environment={2}", $SubscriptionId, $RecordMode, $TargetEnvironment) + $sp = Get-AzADServicePrincipal -DisplayName $ServicePrincipalDisplayName - if([string]::IsNullOrEmpty($TenantId) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";TenantId={0}"), $TenantId) + if (($null -ne $sp) -and !$Force.IsPresent) { + $continue = $PSCmdlet.ShouldContinue("The service principal `"$ServicePrincipalDisplayName`" already exists. Would you like to create a new service principal with the same name?", "Create Service Principal?") + if (!$continue) { + throw "The service principal `"$ServicePrincipalDisplayName`" already exists. Pass in the Client Id and Client Secret to re-use it." + } } - if([string]::IsNullOrEmpty($ServicePrincipalId) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";ServicePrincipal={0}"), $ServicePrincipalId) - } + $sp = Invoke-TestFxCommand -Command "New-AzADServicePrincipal -DisplayName $ServicePrincipalDisplayName" + Start-Sleep -Seconds 10 + Set-TestFxServicePrincipalPermission -SubscriptionId $SubscriptionId -ServicePrincipalObjectId $sp.Id - if([string]::IsNullOrEmpty($ServicePrincipalSecret) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";ServicePrincipalSecret={0}"), $ServicePrincipalSecret) - } + return $sp +} - #Uris - if([string]::IsNullOrEmpty($ResourceManagementUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";ResourceManagementUri={0}"), $ResourceManagementUri) - } - - if([string]::IsNullOrEmpty($GraphUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";GraphUri={0}"), $GraphUri) - } - - if([string]::IsNullOrEmpty($AADAuthUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";AADAuthUri={0}"), $AADAuthUri) - } - - if([string]::IsNullOrEmpty($AADTokenAudienceUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";AADTokenAudienceUri={0}"), $AADTokenAudienceUri) - } - - if([string]::IsNullOrEmpty($GraphTokenAudienceUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";GraphTokenAudienceUri={0}"), $GraphTokenAudienceUri) - } - - if([string]::IsNullOrEmpty($IbizaPortalUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";IbizaPortalUri={0}"), $IbizaPortalUri) - } - - if([string]::IsNullOrEmpty($ServiceManagementUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";ServiceManagementUri={0}"), $ServiceManagementUri) - } - - if([string]::IsNullOrEmpty($RdfePortalUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";RdfePortalUri={0}"), $RdfePortalUri) - } - - if([string]::IsNullOrEmpty($GalleryUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";GalleryUri={0}"), $GalleryUri) - } - - if([string]::IsNullOrEmpty($DataLakeStoreServiceUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";DataLakeStoreServiceUri={0}"), $DataLakeStoreServiceUri) - } - - if([string]::IsNullOrEmpty($DataLakeAnalyticsJobAndCatalogServiceUri) -eq $false) - { - $formattedConnStr = [string]::Format([string]::Concat($formattedConnStr, ";DataLakeAnalyticsJobAndCatalogServiceUri={0}"), $DataLakeAnalyticsJobAndCatalogServiceUri) - } +function Set-TestFxServicePrincipalPermission { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [guid] $SubscriptionId, - Write-Host "Below connection string is ready to be set" - Print-ConnectionString $SubscriptionId $TenantId $ServicePrincipal $ServicePrincipalSecret $RecordMode $TargetEnvironment - - #Set connection string to Environment variable - $env:TEST_CSM_ORGID_AUTHENTICATION=$formattedConnStr - $env:AZURE_TEST_MODE=$RecordMode - Write-Host "" - - # Retrieve the environment variable - Write-Host "" - Write-Host "Below connection string was set. Please open your service's solution in Visual Studio and run your tests from the Test Explorer." -ForegroundColor Green - [Environment]::GetEnvironmentVariable($envVariableName) - Write-Host "" - - Write-Host "If your needs demand you to set connection string differently, for all the supported Key/Value pairs in connection string" - Write-Host "Please visit https://github.com/Azure/azure-powershell/blob/master/documentation/testing-docs/using-azure-test-framework.md" -ForegroundColor Yellow -} + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [guid] $ServicePrincipalObjectId + ) -Function Print-ConnectionString([string]$uid, [string]$subId, [string]$aadTenant, [string]$spn, [string]$spnSecret, [string]$recordMode, [string]$targetEnvironment) -{ - if([string]::IsNullOrEmpty($subId) -eq $false) - { - Write-Host "SubscriptionId=" -ForegroundColor Green -NoNewline - Write-Host $subId";" -NoNewline + $scope = "/subscriptions/$SubscriptionId" + $roleName = "Contributor" + try { + $spRoleAssg = Get-AzRoleAssignment -ObjectId $ServicePrincipalObjectId -Scope $scope -RoleDefinitionName $roleName -ErrorAction Stop + if ($null -eq $spRoleAssg) { + Invoke-TestFxCommand -Command "New-AzRoleAssignment -ObjectId $ServicePrincipalObjectId -RoleDefinitionName $roleName -Scope $scope | Out-Null" + } } - - if([string]::IsNullOrEmpty($aadTenant) -eq $false) - { - Write-Host "TenantId=" -ForegroundColor Green -NoNewline - Write-Host $aadTenant";" -NoNewline + catch { + throw "Exception occurred when retrieving the role assignment for service principal with error message $($_.Exception.Message)." } +} - if([string]::IsNullOrEmpty($spn) -eq $false) - { - Write-Host "ServicePrincipal=" -ForegroundColor Green -NoNewline - Write-Host $spn";" -NoNewline - } +function Get-TestFxEnvironment { + [CmdletBinding()] + param () - if([string]::IsNullOrEmpty($spnSecret) -eq $false) - { - Write-Host "ServicePrincipalSecret=" -ForegroundColor Green -NoNewline - Write-Host $spnSecret";" -NoNewline - } + $testFxEnvFile = Join-Path -Path $script:TestFxEnvDirectory -ChildPath $script:TestFxEnvFileName + if (Test-Path -LiteralPath $testFxEnvFile -PathType Leaf) { + Write-Verbose "Config file was found for TestFx environment." + try { + $testFxEnvProps = Get-Content -LiteralPath $testFxEnvFile | Out-String | ConvertFrom-Json + } + catch { + Write-Error "Exception occurred when trying to parse the JSON file with error message `"$($_.Exception.Message)`"." + } - if([string]::IsNullOrEmpty($recordMode) -eq $false) - { - Write-Host "HttpRecorderMode=" -ForegroundColor Green -NoNewline - Write-Host $recordMode";" -NoNewline - } + $testFxEnvProps + return + } + + $testFxEnvConnStr = [Environment]::GetEnvironmentVariable($script:TestFxEnvConnectionStringKey) + if ($null -ne $testFxEnvConnStr) { + Write-Verbose "Environment variable was found for TestFx environment." + $testFxEnvProps = [PSCustomObject]@{} + $testFxEnvVars = $testFxEnvConnStr -split ";" + $testFxEnvVars | ForEach-Object { + if (![string]::IsNullOrWhiteSpace($_)) { + $testFxEnvProp = $_ -split "=" + $testFxEnvPropKey = $testFxEnvProp[0] + $testFxEnvPropValue = $testFxEnvProp[1] + $testFxEnvProps | Add-Member -NotePropertyName $testFxEnvPropKey -NotePropertyValue $testFxEnvPropValue + } + } + + $testFxEnvTestMode = [Environment]::GetEnvironmentVariable($script:TestFxEnvTestModeKey) + if ($null -ne $testFxEnvTestMode) { + if ($null -eq $testFxEnvProps.HttpRecorderMode) { + $testFxEnvProps | Add-Member -NotePropertyName HttpRecorderMode -NotePropertyValue $testFxEnvTestMode + } + else { + $testFxEnvProps.HttpRecorderMode = $testFxEnvTestMode + } + } - if([string]::IsNullOrEmpty($targetEnvironment) -eq $false) - { - Write-Host "Environment=" -ForegroundColor Green -NoNewline - Write-Host $targetEnvironment";" -NoNewline + $testFxEnvProps + return } - Write-Host "" + Write-Warning "TestFx environment has not been setup. Please run command Set-TestFxEnvironment." } -export-modulemember -Function Set-TestEnvironment -export-modulemember -Function New-TestCredential +function Invoke-TestFxCommand { + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string] $Command + ) + + $cmdRetryCount = 0 + + do { + try { + Write-Verbose "Start to execute the command `"$Command`"." + $cmdResult = Invoke-Expression -Command $Command -ErrorAction Stop + Write-Verbose "Successfully executed the command `"$Command`"." + $cmdResult + break + } + catch { + $cmdErrorMessage = $_.Exception.Message + if ($cmdRetryCount -le 3) { + Write-Warning "Error occurred when executing the command `"$Command`" with error message `"$cmdErrorMessage`"." + Write-Warning "Will retry automatically in 5 seconds." + Write-Host + + Start-Sleep -Seconds 5 + $cmdRetryCount++ + Write-Warning "Retrying #$cmdRetryCount to execute the command `"$Command`"." + } + else { + throw "Failed to execute the command `"$Command`" after retrying for 3 times with error message `"$cmdErrorMessage`"." + } + } + } + while ($true) +} diff --git a/tools/TestFx/EnvironmentSetupHelper.cs b/tools/TestFx/EnvironmentSetupHelper.cs index 3fa839439702..79a0dd5f2b61 100644 --- a/tools/TestFx/EnvironmentSetupHelper.cs +++ b/tools/TestFx/EnvironmentSetupHelper.cs @@ -15,6 +15,7 @@ using Microsoft.Azure.Commands.Common.Authentication; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.Azure.Commands.Common.Authentication.Properties; using Microsoft.Azure.Commands.ScenarioTest; using Microsoft.Azure.Commands.TestFx.Mocks; using Microsoft.Azure.ServiceManagement.Common.Models; @@ -136,7 +137,7 @@ public EnvironmentSetupHelper() // Set RunningMocked TestMockSupport.RunningMocked = HttpMockServer.GetCurrentMode() == HttpRecorderMode.Playback; - if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".azure", "testcredentials.json"))) + if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), Resources.AzureDirectoryName, "testcredentials.json"))) { SetEnvironmentVariableFromCredentialFile(); } @@ -294,7 +295,7 @@ public void SetupEnvironment(AzureModule mode) public void SetEnvironmentVariableFromCredentialFile() { - var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".azure", "testcredentials.json"); + var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), Resources.AzureDirectoryName, "testcredentials.json"); Dictionary testSettings; using (StreamReader r = new StreamReader(filePath)) { @@ -302,84 +303,77 @@ public void SetEnvironmentVariableFromCredentialFile() testSettings = JsonUtilities.DeserializeJson(json); } - if (Environment.GetEnvironmentVariable(ConnectionStringKeys.TestCSMOrgIdConnectionStringKey) == null) - { - StringBuilder formattedConnectionString = new StringBuilder(); - formattedConnectionString.Append($"Environment={testSettings["Environment"]};SubscriptionId={testSettings["SubscriptionId"]};TenantId={testSettings["TenantId"]};HttpRecorderMode={testSettings["HttpRecorderMode"]};"); - - if (testSettings.ContainsKey("UserId")) - { - formattedConnectionString.Append($"UserId={testSettings["UserId"]};"); - } - - if (testSettings.ContainsKey("ServicePrincipal")) - { - formattedConnectionString.Append($"ServicePrincipal={testSettings["ServicePrincipal"]};"); - formattedConnectionString.Append($"ServicePrincipalSecret={testSettings["ServicePrincipalSecret"]};"); - } + StringBuilder formattedConnectionString = new StringBuilder(); + formattedConnectionString.Append($"Environment={testSettings["Environment"]};SubscriptionId={testSettings["SubscriptionId"]};TenantId={testSettings["TenantId"]};HttpRecorderMode={testSettings["HttpRecorderMode"]};"); - if (testSettings.ContainsKey("ResourceManagementUri")) - { - formattedConnectionString.Append($"ResourceManagementUri={testSettings["ResourceManagementUri"]};"); - } + if (testSettings.ContainsKey("UserId")) + { + formattedConnectionString.Append($"UserId={testSettings["UserId"]};"); + } - if (testSettings.ContainsKey("ServiceManagementUri")) - { - formattedConnectionString.Append($"ServiceManagementUri={testSettings["ServiceManagementUri"]};"); - } + if (testSettings.ContainsKey("ServicePrincipal")) + { + formattedConnectionString.Append($"ServicePrincipal={testSettings["ServicePrincipal"]};"); + formattedConnectionString.Append($"ServicePrincipalSecret={testSettings["ServicePrincipalSecret"]};"); + } - if (testSettings.ContainsKey("AADAuthUri")) - { - formattedConnectionString.Append($"AADAuthUri={testSettings["AADAuthUri"]};"); - } + if (testSettings.ContainsKey("ResourceManagementUri")) + { + formattedConnectionString.Append($"ResourceManagementUri={testSettings["ResourceManagementUri"]};"); + } - if (testSettings.ContainsKey("GraphUri")) - { - formattedConnectionString.Append($"GraphUri={testSettings["GraphUri"]};"); - } + if (testSettings.ContainsKey("ServiceManagementUri")) + { + formattedConnectionString.Append($"ServiceManagementUri={testSettings["ServiceManagementUri"]};"); + } - if (testSettings.ContainsKey("AADTokenAudienceUri")) - { - formattedConnectionString.Append($"AADTokenAudienceUri={testSettings["AADTokenAudienceUri"]};"); - } + if (testSettings.ContainsKey("AADAuthUri")) + { + formattedConnectionString.Append($"AADAuthUri={testSettings["AADAuthUri"]};"); + } - if (testSettings.ContainsKey($"GraphTokenAudienceUri")) - { - formattedConnectionString.Append($"GraphTokenAudienceUri={testSettings["GraphTokenAudienceUri"]};"); - } + if (testSettings.ContainsKey("GraphUri")) + { + formattedConnectionString.Append($"GraphUri={testSettings["GraphUri"]};"); + } - if (testSettings.ContainsKey("IbizaPortalUri")) - { - formattedConnectionString.Append($"IbizaPortalUri={testSettings["IbizaPortalUri"]};"); - } + if (testSettings.ContainsKey("AADTokenAudienceUri")) + { + formattedConnectionString.Append($"AADTokenAudienceUri={testSettings["AADTokenAudienceUri"]};"); + } - if (testSettings.ContainsKey("RdfePortalUri")) - { - formattedConnectionString.Append($"RdfePortalUri={testSettings["RdfePortalUri"]};"); - } + if (testSettings.ContainsKey($"GraphTokenAudienceUri")) + { + formattedConnectionString.Append($"GraphTokenAudienceUri={testSettings["GraphTokenAudienceUri"]};"); + } - if (testSettings.ContainsKey("GalleryUri")) - { - formattedConnectionString.Append($"GalleryUri={testSettings["GalleryUri"]};"); - } + if (testSettings.ContainsKey("IbizaPortalUri")) + { + formattedConnectionString.Append($"IbizaPortalUri={testSettings["IbizaPortalUri"]};"); + } - if (testSettings.ContainsKey("DataLakeStoreServiceUri")) - { - formattedConnectionString.Append($"DataLakeStoreServiceUri={testSettings["DataLakeStoreServiceUri"]};"); - } + if (testSettings.ContainsKey("RdfePortalUri")) + { + formattedConnectionString.Append($"RdfePortalUri={testSettings["RdfePortalUri"]};"); + } - if (testSettings.ContainsKey("DataLakeAnalyticsJobAndCatalogServiceUri")) - { - formattedConnectionString.Append($"DataLakeAnalyticsJobAndCatalogServiceUri={testSettings["DataLakeAnalyticsJobAndCatalogServiceUri"]};"); - } + if (testSettings.ContainsKey("GalleryUri")) + { + formattedConnectionString.Append($"GalleryUri={testSettings["GalleryUri"]};"); + } - Environment.SetEnvironmentVariable(ConnectionStringKeys.TestCSMOrgIdConnectionStringKey, formattedConnectionString.ToString()); + if (testSettings.ContainsKey("DataLakeStoreServiceUri")) + { + formattedConnectionString.Append($"DataLakeStoreServiceUri={testSettings["DataLakeStoreServiceUri"]};"); } - if (Environment.GetEnvironmentVariable(ConnectionStringKeys.AZURE_TEST_MODE_ENVKEY) == null) + if (testSettings.ContainsKey("DataLakeAnalyticsJobAndCatalogServiceUri")) { - Environment.SetEnvironmentVariable(ConnectionStringKeys.AZURE_TEST_MODE_ENVKEY, testSettings["HttpRecorderMode"].ToString()); + formattedConnectionString.Append($"DataLakeAnalyticsJobAndCatalogServiceUri={testSettings["DataLakeAnalyticsJobAndCatalogServiceUri"]};"); } + + Environment.SetEnvironmentVariable(ConnectionStringKeys.TestCSMOrgIdConnectionStringKey, formattedConnectionString.ToString()); + Environment.SetEnvironmentVariable(ConnectionStringKeys.AZURE_TEST_MODE_ENVKEY, testSettings["HttpRecorderMode"].ToString()); } public void SetupAzureEnvironmentFromEnvironmentVariables(AzureModule mode) @@ -586,8 +580,7 @@ private void SetupPowerShellModules(System.Management.Automation.PowerShell powe powershell.AddScript("$VerbosePreference='Continue'"); powershell.AddScript("$DebugPreference='Continue'"); powershell.AddScript("$ErrorActionPreference='Stop'"); - powershell.AddScript("Write-Debug \"AZURE_TEST_MODE = $($env:AZURE_TEST_MODE)\""); - powershell.AddScript("Write-Debug \"TEST_HTTPMOCK_OUTPUT = $($env:TEST_HTTPMOCK_OUTPUT)\""); + powershell.AddScript("Write-Debug \"AZURE_TEST_MODE = $env:AZURE_TEST_MODE\""); } } } diff --git a/tools/TestFx/Live/LiveTestUtility.psm1 b/tools/TestFx/Live/LiveTestUtility.psm1 index f5678132f3fe..eb7937f66e18 100644 --- a/tools/TestFx/Live/LiveTestUtility.psm1 +++ b/tools/TestFx/Live/LiveTestUtility.psm1 @@ -156,7 +156,7 @@ function Invoke-LiveTestCommand { $cmdErrorMessage = $_.Exception.Message if ($cmdRetryCount -le $script:CommandMaxRetryCount) { Write-Warning "Error occurred when executing the command '$Command' with error message '$cmdErrorMessage'." - Write-Warning "Live test will retry automatically in $script:CommandMaxRetryCount seconds." + Write-Warning "Live test will retry automatically in $script:CommandDelay seconds." Write-Host Start-Sleep -Seconds $script:CommandDelay