diff --git a/tools/Common.Netcore.Dependencies.Test.targets b/tools/Common.Netcore.Dependencies.Test.targets
index 35af6f388244..cadadff43711 100644
--- a/tools/Common.Netcore.Dependencies.Test.targets
+++ b/tools/Common.Netcore.Dependencies.Test.targets
@@ -10,7 +10,6 @@
-
@@ -40,7 +39,7 @@
-
+
diff --git a/tools/Gen2Master/MoveFromGeneration2Master.ps1 b/tools/Gen2Master/MoveFromGeneration2Master.ps1
index 90bfc6bdd013..5bae4cd8154f 100644
--- a/tools/Gen2Master/MoveFromGeneration2Master.ps1
+++ b/tools/Gen2Master/MoveFromGeneration2Master.ps1
@@ -142,9 +142,9 @@ Function Move-Generation2Master {
dotnet sln $slnFilePath add $_.FullName --solution-folder Accounts
}
}
- Get-ChildItem -Filter *.csproj -File -Path $DestPath -Recurse | ForEach-Object {
- dotnet sln $slnFilePath add $_.FullName
- }
+ }
+ Get-ChildItem -Filter *.csproj -File -Path $DestPath -Recurse | ForEach-Object {
+ dotnet sln $slnFilePath add $_.FullName
}
$Psd1Metadata.RequiredAssemblies = Unique-PathList $Psd1Metadata.RequiredAssemblies
diff --git a/tools/TestFx/EnvironmentSetupHelper.cs b/tools/TestFx/EnvironmentSetupHelper.cs
index ca175ef0ac2c..448aa2b7e66d 100644
--- a/tools/TestFx/EnvironmentSetupHelper.cs
+++ b/tools/TestFx/EnvironmentSetupHelper.cs
@@ -406,6 +406,7 @@ public void SetupAzureEnvironmentFromEnvironmentVariables(AzureModule mode)
environment.StorageEndpointSuffix = AzureEnvironmentConstants.AzureStorageEndpointSuffix;
environment.AzureKeyVaultDnsSuffix = AzureEnvironmentConstants.AzureKeyVaultDnsSuffix;
environment.AzureKeyVaultServiceEndpointResourceId = AzureEnvironmentConstants.AzureKeyVaultServiceEndpointResourceId;
+ environment.ExtendedProperties.SetProperty(AzureEnvironment.ExtendedEndpoint.MicrosoftGraphUrl, currentEnvironment.Endpoints.GraphUri.AbsoluteUri);
environment.ExtendedProperties.SetProperty(AzureEnvironment.ExtendedEndpoint.OperationalInsightsEndpoint, "https://api.loganalytics.io/v1");
environment.ExtendedProperties.SetProperty(AzureEnvironment.ExtendedEndpoint.OperationalInsightsEndpointResourceId, "https://api.loganalytics.io");
if (!AzureRmProfileProvider.Instance.GetProfile().EnvironmentTable.ContainsKey(TestEnvironmentName))
diff --git a/tools/TestFx/Mocks/MockClientFactory.cs b/tools/TestFx/Mocks/MockClientFactory.cs
index bd53fcfb2dc3..219711084f81 100644
--- a/tools/TestFx/Mocks/MockClientFactory.cs
+++ b/tools/TestFx/Mocks/MockClientFactory.cs
@@ -31,6 +31,8 @@
using Microsoft.Azure.Commands.TestFx.DelegatingHandlers;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using Microsoft.Azure.Test.HttpRecorder;
+using Microsoft.Azure.Commands.Common.MSGraph.Version1_0;
+
#if NETSTANDARD
using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core;
#endif
@@ -81,9 +83,16 @@ public TClient CreateArmClient(IAzureContext context, string endpoint)
public TClient CreateCustomArmClient(params object[] parameters) where TClient : Microsoft.Rest.ServiceClient
{
- if (!(_serviceClients.FirstOrDefault(o => o is TClient) is TClient client))
+ if (_serviceClients.FirstOrDefault(o => o is TClient) is not TClient client)
{
- client = _mockContext?.GetServiceClient();
+ if (typeof(TClient) == typeof(MicrosoftGraphClient))
+ {
+ client = _mockContext?.GetGraphServiceClient();
+ }
+ else
+ {
+ client = _mockContext?.GetServiceClient();
+ }
}
if (client == null)
diff --git a/tools/TestFx/Mocks/MockContext.cs b/tools/TestFx/Mocks/MockContext.cs
index e481e51e5aa6..d1784d72a333 100644
--- a/tools/TestFx/Mocks/MockContext.cs
+++ b/tools/TestFx/Mocks/MockContext.cs
@@ -99,6 +99,13 @@ public T GetServiceClient(bool internalBaseUri = false, params DelegatingHand
///
public T GetServiceClient(TestEnvironment currentEnvironment, bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class
{
+ if (!currentEnvironment.TokenInfo.ContainsKey(TokenAudience.Management))
+ {
+ throw new ArgumentNullException(
+ "currentEnvironment.TokenInfo[TokenAudience.Management]",
+ $"Unable to create Service Client because {nameof(T)} authentication token was not acquired during Login.");
+ }
+
return GetServiceClientWithCredentials(currentEnvironment, currentEnvironment.TokenInfo[TokenAudience.Management], internalBaseUri, handlers);
}
@@ -108,9 +115,7 @@ public T GetServiceClient(TestEnvironment currentEnvironment, bool internalBa
///
/// Delegating existingHandlers
///
- public T GetGraphServiceClient(
- bool internalBaseUri = false,
- params DelegatingHandler[] handlers) where T : class
+ public T GetGraphServiceClient(bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class
{
return GetGraphServiceClient(TestFxEnvironment, internalBaseUri, handlers);
}
@@ -121,52 +126,16 @@ public T GetGraphServiceClient(
///
/// Delegating existingHandlers
///
- public T GetGraphServiceClient(
- TestEnvironment currentEnvironment,
- bool internalBaseUri = false,
- params DelegatingHandler[] handlers) where T : class
+ public T GetGraphServiceClient(TestEnvironment currentEnvironment, bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class
{
if (!currentEnvironment.TokenInfo.ContainsKey(TokenAudience.Graph))
{
throw new ArgumentNullException(
"currentEnvironment.TokenInfo[TokenAudience.Graph]",
- "Unable to create Graph Management client because Graph authentication token was not acquired during Login.");
+ "Unable to create Graph Client because Graph authentication token was not acquired during Login.");
}
- return GetServiceClientWithCredentials(
- currentEnvironment,
- currentEnvironment.TokenInfo[TokenAudience.Graph],
- currentEnvironment.Endpoints.GraphUri,
- internalBaseUri,
- handlers);
- }
-
- ///
- /// Get a test environment using default options
- ///
- /// The type of the service client to return
- /// Credentials
- /// Delegating existingHandlers
- /// A Service client using credentials and base uri from the current environment
- public T GetServiceClientWithCredentials(object credentials, params DelegatingHandler[] handlers) where T : class
- {
- return GetServiceClientWithCredentials(TestFxEnvironment, credentials, handlers: handlers);
- }
-
- ///
- /// Get a test environment, allowing the test to customize the creation options
- ///
- ///
- /// Credentials
- /// Delegating existingHandlers
- ///
- public T GetServiceClientWithCredentials(
- TestEnvironment currentEnvironment,
- object credentials,
- bool internalBaseUri = false,
- params DelegatingHandler[] handlers) where T : class
- {
- return GetServiceClientWithCredentials(currentEnvironment, credentials, currentEnvironment.BaseUri, internalBaseUri, handlers);
+ return GetServiceClientWithCredentials(currentEnvironment, currentEnvironment.TokenInfo[TokenAudience.Graph], internalBaseUri, handlers);
}
///
@@ -177,21 +146,14 @@ public T GetServiceClientWithCredentials(
/// Base Uri
/// Delegating existingHandlers
///
- public T GetServiceClientWithCredentials(
- TestEnvironment currentEnvironment,
- object credentials,
- Uri baseUri,
- bool internalBaseUri = false,
- params DelegatingHandler[] handlers) where T : class
+ public T GetServiceClientWithCredentials(TestEnvironment currentEnvironment, object credentials, bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class
{
T client;
handlers = AddHandlers(currentEnvironment, handlers);
var constructors = typeof(T).GetConstructors(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
ConstructorInfo constructor = null;
- //We no longer use UseCustomUri function, rather check if BaseUri is notNull
- //UseCustomeUri use to return true when BaseUri was set to some value
- if ((currentEnvironment.BaseUri != null) && !internalBaseUri)
+ if (!internalBaseUri && currentEnvironment.BaseUri != null)
{
foreach (var c in constructors)
{
@@ -207,12 +169,11 @@ public T GetServiceClientWithCredentials(
}
if (constructor == null)
{
- throw new InvalidOperationException(
- "can't find constructor (uri, ServiceClientCredentials, DelegatingHandler[]) to create client");
+ throw new InvalidOperationException("Cannot find constructor (uri, ServiceClientCredentials, DelegatingHandler[]) to create service client");
}
client = constructor.Invoke(new object[]
{
- baseUri,
+ currentEnvironment.BaseUri,
credentials,
handlers
}) as T;
@@ -232,8 +193,7 @@ public T GetServiceClientWithCredentials(
}
if (constructor == null)
{
- throw new InvalidOperationException(
- "can't find constructor (ServiceClientCredentials, DelegatingHandler[]) to create client");
+ throw new InvalidOperationException("Cannot find constructor (ServiceClientCredentials, DelegatingHandler[]) to create service client");
}
client = constructor.Invoke(new object[]
{
@@ -262,15 +222,11 @@ private void SetLongRunningOperationTimeouts(T client) where T : class
if (HttpMockServer.Mode == HttpRecorderMode.Playback)
{
PropertyInfo retryTimeout = typeof(T).GetProperty("LongRunningOperationRetryTimeout");
- if (retryTimeout != null)
- {
- retryTimeout.SetValue(client, 0);
- }
+ retryTimeout?.SetValue(client, 0);
}
}
- public DelegatingHandler[] AddHandlers(TestEnvironment currentEnvironment,
- params DelegatingHandler[] existingHandlers)
+ public DelegatingHandler[] AddHandlers(TestEnvironment currentEnvironment, params DelegatingHandler[] existingHandlers)
{
HttpMockServer server;
diff --git a/tools/TestFx/TestEndpoints.cs b/tools/TestFx/TestEndpoints.cs
index c4513abd55a3..810b8665ba7d 100644
--- a/tools/TestFx/TestEndpoints.cs
+++ b/tools/TestFx/TestEndpoints.cs
@@ -127,7 +127,7 @@ internal TestEndpoints(TestEnvironmentName testEnvNames)
{
case TestEnvironmentName.Prod:
Name = TestEnvironmentName.Prod;
- GraphUri = new Uri("https://graph.windows.net/");
+ GraphUri = new Uri("https://graph.microsoft.com/");
AADAuthUri = new Uri("https://login.microsoftonline.com/");
IbizaPortalUri = new Uri("https://portal.azure.com/");
RdfePortalUri = new Uri("https://manage.windowsazure.com/");
diff --git a/tools/TestFx/TestEnvironment.cs b/tools/TestFx/TestEnvironment.cs
index 71b3766f6ca3..16db63a76324 100644
--- a/tools/TestFx/TestEnvironment.cs
+++ b/tools/TestFx/TestEnvironment.cs
@@ -13,8 +13,8 @@
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Test.HttpRecorder;
+using Microsoft.Identity.Client;
using Microsoft.Rest;
-using Microsoft.Rest.Azure.Authentication;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
@@ -32,35 +32,49 @@ public class TestEnvironment
{
private const string DummyTenantId = "395544B0-BF41-429D-921F-E1CA2252FCF4";
+ private const string MicrosoftLoginUrl = "https://login.microsoftonline.com";
+
+ private const string MicrosoftGraphUrl = "https://graph.microsoft.com";
+
///
/// Base Uri used by the Test Environment
///
- public Uri BaseUri { get; set; }
+ public Uri BaseUri { get; private set; }
///
/// Base Azure Graph Uri used by the Test Environment
///
- public Uri GraphUri { get; set; }
+ public Uri GraphUri { get; private set; }
///
- /// UserName used by the Test Environment
+ /// Tenant Id used by the Test Environment
///
- public string UserName { get; set; }
+ public string TenantId { get; private set; }
+
+ ///
+ /// Subscription Id used by the Test Environment
+ ///
+ public string SubscriptionId { get; private set; }
///
- /// Tenant used by the Test Environment
+ /// Service principal client id used by the Test Environment
///
- public string TenantId { get; set; }
+ public string ServicePrincipalClientId { get; private set; }
///
- /// Subscription Id used by the Test Environment
+ /// Service principal secret used by the Test Environment
+ ///
+ public string ServicePrincipalSecret { get; private set; }
+
+ ///
+ /// UserName used by the Test Environment
///
- public string SubscriptionId { get; set; }
+ public string UserName { get; set; }
///
/// Active TestEndpoint being used by the Test Environment
///
- public TestEndpoints Endpoints { get; set; }
+ public TestEndpoints Endpoints { get; private set; }
///
/// Holds default endpoints for all the supported environments
@@ -87,6 +101,8 @@ public TestEnvironment(string connectionString)
SubscriptionId = ConnectionString.GetValue(ConnectionStringKeys.SubscriptionIdKey);
TenantId = ConnectionString.GetValue(ConnectionStringKeys.TenantIdKey);
+ ServicePrincipalClientId = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalKey);
+ ServicePrincipalSecret = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalSecretKey);
UserName = ConnectionString.GetValue(ConnectionStringKeys.UserIdKey);
OptimizeRecordedFile = ConnectionString.GetValue(ConnectionStringKeys.OptimizeRecordedFileKey);
@@ -103,7 +119,7 @@ public TestEnvironment(string connectionString)
{
GraphUri = Endpoints.GraphUri;
}
- else if (!string.IsNullOrWhiteSpace(ConnectionString.GetValue(ConnectionStringKeys.GraphUriKey)))
+ else
{
GraphUri = new Uri(ConnectionString.GetValue(ConnectionStringKeys.GraphUriKey));
}
@@ -127,17 +143,14 @@ private void InitTestEndPoints()
private void LoadDefaultEnvironmentEndpoints()
{
- if (EnvEndpoints == null)
+ EnvEndpoints ??= new Dictionary()
{
- EnvEndpoints = new Dictionary()
- {
- { TestEnvironmentName.Prod, new TestEndpoints(TestEnvironmentName.Prod) },
- { TestEnvironmentName.Dogfood, new TestEndpoints(TestEnvironmentName.Dogfood) },
- { TestEnvironmentName.Next, new TestEndpoints(TestEnvironmentName.Next) },
- { TestEnvironmentName.Current, new TestEndpoints(TestEnvironmentName.Current) },
- { TestEnvironmentName.Custom, new TestEndpoints(TestEnvironmentName.Custom) }
- };
- }
+ { TestEnvironmentName.Prod, new TestEndpoints(TestEnvironmentName.Prod) },
+ { TestEnvironmentName.Dogfood, new TestEndpoints(TestEnvironmentName.Dogfood) },
+ { TestEnvironmentName.Next, new TestEndpoints(TestEnvironmentName.Next) },
+ { TestEnvironmentName.Current, new TestEndpoints(TestEnvironmentName.Current) },
+ { TestEnvironmentName.Custom, new TestEndpoints(TestEnvironmentName.Custom) }
+ };
}
private void InitTokenDictionary()
@@ -187,85 +200,46 @@ private void SetupHttpRecorderMode()
private void Login()
{
- string userPassword = ConnectionString.GetValue(ConnectionStringKeys.PasswordKey);
- string spnClientId = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalKey);
- string spnClientSecret = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalSecretKey);
- //We use this because when login silently using userTokenProvider, we need to provide a well known ClientId for an app that has delegating permissions.
- //All first party app have that permissions, so we use PowerShell app ClientId
-#if net452
- string PowerShellClientId = "1950a258-227b-4e31-a9cf-717495945fc2";
-#endif
- /*
- * Currently we prioritize login as below:
- * 1) ServicePrincipal/ServicePrincipal Secret Key
- * 2) UserName / Password combination
- * 3) Interactive Login (where user will be presented with prompt to login)
- */
-
- ActiveDirectoryServiceSettings aadServiceSettings = new ActiveDirectoryServiceSettings()
- {
- AuthenticationEndpoint = new Uri(Endpoints.AADAuthUri.ToString() + ConnectionString.GetValue(ConnectionStringKeys.TenantIdKey)),
- TokenAudience = Endpoints.AADTokenAudienceUri
- };
+ UpdateTokenInfo(TokenAudience.Management, new[] { "https://management.azure.com/.default" });
+ UpdateTokenInfo(TokenAudience.Graph, new[] { "https://graph.microsoft.com/.default" });
+ }
- ActiveDirectoryServiceSettings graphAADServiceSettings = new ActiveDirectoryServiceSettings()
- {
- AuthenticationEndpoint = new Uri(Endpoints.AADAuthUri.ToString() + ConnectionString.GetValue(ConnectionStringKeys.TenantIdKey)),
- TokenAudience = Endpoints.GraphTokenAudienceUri
- };
+ private void UpdateTokenInfo(TokenAudience tokenAudience, IEnumerable scopes)
+ {
+ var accessToken = GetServicePrincipalAccessToken(scopes);
+ TokenInfo[tokenAudience] = new TokenCredentials(accessToken);
+ }
- if ((!string.IsNullOrEmpty(spnClientId)) && (!string.IsNullOrEmpty(spnClientSecret)))
- {
- Task mgmAuthResult = Task.Run(async () => (TokenCredentials)await ApplicationTokenProvider.LoginSilentAsync(TenantId, spnClientId, spnClientSecret, aadServiceSettings).ConfigureAwait(false));
- TokenInfo[TokenAudience.Management] = mgmAuthResult.Result;
- UpdateTokenInfoWithGraphToken(graphAADServiceSettings, spnClientId: spnClientId, spnClientSecret: spnClientSecret);
- }
- else if ((!string.IsNullOrEmpty(UserName)) && (!string.IsNullOrEmpty(userPassword)))
- {
- //#if FullNetFx
-#if net452
- Task mgmAuthResult = Task.Run(async () => (TokenCredentials)await UserTokenProvider.LoginSilentAsync(PowerShellClientId, TenantId, UserName, userPassword, aadServiceSettings).ConfigureAwait(false));
- this.TokenInfo[TokenAudience.Management] = mgmAuthResult.Result;
- UpdateTokenInfoWithGraphToken(graphAADServiceSettings, userName: UserName, password: userPassword, psClientId: PowerShellClientId);
-#else
- throw new NotSupportedException("Username/Password login is supported only in NET452 and above projects");
-#endif
- }
- else
+ public string GetServicePrincipalAccessToken(IEnumerable scopes, string cloudInstanceUri = null)
+ {
+ if (string.IsNullOrWhiteSpace(cloudInstanceUri))
{
- //#if FullNetFx
-#if net452
- InteractiveLogin(TenantId, PowerShellClientId, aadServiceSettings, graphAADServiceSettings);
-#else
- throw new NotSupportedException("Interactive Login is supported only in NET452 and above projects");
-#endif
+ cloudInstanceUri = MicrosoftLoginUrl;
}
+
+ var spn = ConfidentialClientApplicationBuilder
+ .Create(ServicePrincipalClientId)
+ .WithClientSecret(ServicePrincipalSecret)
+ .WithAuthority(cloudInstanceUri, TenantId)
+ .Build();
+ var authResult = Task.Run(async () => await spn.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false));
+ return authResult.Result.AccessToken;
}
- private void UpdateTokenInfoWithGraphToken(ActiveDirectoryServiceSettings graphAADServiceSettings, string spnClientId = "", string spnClientSecret = "", string userName = "", string password = "", string psClientId = "")
+ public string GetServicePrincipalAccessToken(IEnumerable scopes, Uri authorityUri)
{
- Task graphAuthResult = null;
- try
- {
- if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password))
- {
- //#if FullNetFx
-#if net452
- graphAuthResult = Task.Run(async () => (TokenCredentials)await UserTokenProvider.LoginSilentAsync(psClientId, TenantId, userName, password, graphAADServiceSettings).ConfigureAwait(false));
-#endif
- }
- else if (!string.IsNullOrWhiteSpace(spnClientId) && !string.IsNullOrWhiteSpace(spnClientSecret))
- {
- graphAuthResult = Task.Run(async () => (TokenCredentials)await ApplicationTokenProvider.LoginSilentAsync(TenantId, spnClientId, spnClientSecret, graphAADServiceSettings).ConfigureAwait(false));
- }
-
- TokenInfo[TokenAudience.Graph] = graphAuthResult?.Result;
- }
- catch (Exception ex)
+ if (authorityUri == null)
{
- Debug.WriteLine($"Error while acquiring Graph Token: '{ex}'");
- // Not all accounts are registered to have access to Graph endpoints.
+ throw new ArgumentNullException(nameof(authorityUri));
}
+
+ var spn = ConfidentialClientApplicationBuilder
+ .Create(ServicePrincipalClientId)
+ .WithClientSecret(ServicePrincipalSecret)
+ .WithAuthority(authorityUri)
+ .Build();
+ var authResult = Task.Run(async () => await spn.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false));
+ return authResult.Result.AccessToken;
}
private void VerifyAuthTokens()
@@ -318,7 +292,7 @@ private void VerifySubscription()
// we then check if the retrieved subscription list has exactly 1 subscription, if yes we will just use that one.
if (string.IsNullOrEmpty(SubscriptionId))
{
- if (subscriptionList.Count() == 1)
+ if (subscriptionList.Count == 1)
{
ConnectionString.KeyValuePairs[ConnectionStringKeys.SubscriptionIdKey] = subscriptionList.First().SubscriptionId;
}