diff --git a/eng/.docsettings.yml b/eng/.docsettings.yml index f3dec6cf88378..a9a8846405fd3 100644 --- a/eng/.docsettings.yml +++ b/eng/.docsettings.yml @@ -9,6 +9,7 @@ omitted_paths: - samples/* - sdk/*/*.Management.*/* - sdk/*/*/perf/* + - sdk/*/*/integration/* - sdk/*/*/tests/Samples/* - sdk/*/*/tests/samples/* - sdk/*/*/samples/* diff --git a/sdk/identity/Azure.Identity/integration/WebApp/Controllers/TestController.cs b/sdk/identity/Azure.Identity/integration/WebApp/Controllers/TestController.cs new file mode 100644 index 0000000000000..0e792196f251a --- /dev/null +++ b/sdk/identity/Azure.Identity/integration/WebApp/Controllers/TestController.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using Azure.Core; +using Azure.Identity; +using Azure.Storage.Blobs; +using Microsoft.AspNetCore.Mvc; + +namespace WebApp.Controllers +{ + + [ApiController] + [Route("[controller]")] + public class TestController : ControllerBase + { + + [HttpGet(Name = "GetTest")] + public IActionResult Get() + { + string resourceId = Environment.GetEnvironmentVariable("IDENTITY_WEBAPP_USER_DEFINED_IDENTITY")!; + string account1 = Environment.GetEnvironmentVariable("IDENTITY_STORAGE_NAME_1")!; + string account2 = Environment.GetEnvironmentVariable("IDENTITY_STORAGE_NAME_2")!; + + var credential1 = new ManagedIdentityCredential(); + var credential2 = new ManagedIdentityCredential(new ResourceIdentifier(resourceId)); + var client1 = new BlobServiceClient(new Uri($"https://{account1}.blob.core.windows.net/"), credential1); + var client2 = new BlobServiceClient(new Uri($"https://{account2}.blob.core.windows.net/"), credential2); + try + { + var results = client1.GetBlobContainers().ToList(); + results = client2.GetBlobContainers().ToList(); + return Ok("Successfully acquired a token from ManagedIdentityCredential"); + } + catch (Exception ex) + { + return BadRequest(ex.ToString()); + } + } + } +} diff --git a/sdk/identity/Azure.Identity/integration/WebApp/Integration.Identity.WebApp.csproj b/sdk/identity/Azure.Identity/integration/WebApp/Integration.Identity.WebApp.csproj new file mode 100644 index 0000000000000..8460903024cfe --- /dev/null +++ b/sdk/identity/Azure.Identity/integration/WebApp/Integration.Identity.WebApp.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + enable + enable + + + + + + + + + + diff --git a/sdk/identity/Azure.Identity/integration/WebApp/Program.cs b/sdk/identity/Azure.Identity/integration/WebApp/Program.cs new file mode 100644 index 0000000000000..70c0b74c91a90 --- /dev/null +++ b/sdk/identity/Azure.Identity/integration/WebApp/Program.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/sdk/identity/Azure.Identity/integration/WebApp/appsettings.Development.json b/sdk/identity/Azure.Identity/integration/WebApp/appsettings.Development.json new file mode 100644 index 0000000000000..0c208ae9181e5 --- /dev/null +++ b/sdk/identity/Azure.Identity/integration/WebApp/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/sdk/identity/Azure.Identity/integration/WebApp/appsettings.json b/sdk/identity/Azure.Identity/integration/WebApp/appsettings.json new file mode 100644 index 0000000000000..10f68b8c8b4f7 --- /dev/null +++ b/sdk/identity/Azure.Identity/integration/WebApp/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/sdk/identity/Azure.Identity/integration/nuget.config b/sdk/identity/Azure.Identity/integration/nuget.config new file mode 100644 index 0000000000000..9ac17c067abd9 --- /dev/null +++ b/sdk/identity/Azure.Identity/integration/nuget.config @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/sdk/identity/Azure.Identity/tests/ClientSecretCredentialLiveTests.cs b/sdk/identity/Azure.Identity/tests/ClientSecretCredentialLiveTests.cs index a80a3df680ea4..7304410143d1b 100644 --- a/sdk/identity/Azure.Identity/tests/ClientSecretCredentialLiveTests.cs +++ b/sdk/identity/Azure.Identity/tests/ClientSecretCredentialLiveTests.cs @@ -7,6 +7,7 @@ using Azure.Core; using Azure.Core.TestFramework; using Microsoft.Identity.Client; +using Moq; using NUnit.Framework; namespace Azure.Identity.Tests diff --git a/sdk/identity/Azure.Identity/tests/CredentialTestHelpers.cs b/sdk/identity/Azure.Identity/tests/CredentialTestHelpers.cs index fc12b9afcb90b..1b748a5c3ac1b 100644 --- a/sdk/identity/Azure.Identity/tests/CredentialTestHelpers.cs +++ b/sdk/identity/Azure.Identity/tests/CredentialTestHelpers.cs @@ -82,12 +82,12 @@ public static TestFileSystemService CreateFileSystemForVisualStudioCode(Identity { var sb = new StringBuilder("{"); - if (testEnvironment.TestTenantId != default) + if (testEnvironment.IdentityTenantId != default) { - sb.AppendFormat("\"azure.tenant\": \"{0}\"", testEnvironment.TestTenantId); + sb.AppendFormat("\"azure.tenant\": \"{0}\"", testEnvironment.IdentityTenantId); } - if (testEnvironment.TestTenantId != default && cloudName != default) + if (testEnvironment.IdentityTenantId != default && cloudName != default) { sb.Append(','); } @@ -117,7 +117,7 @@ public static async ValueTask GetAuthenticationRecordAsync var username = testEnvironment.Username; var password = testEnvironment.Password; - var tenantId = testEnvironment.TestTenantId; + var tenantId = testEnvironment.IdentityTenantId; var result = await PublicClientApplicationBuilder.Create(clientId) .WithTenantId(tenantId) @@ -138,7 +138,7 @@ public static async Task GetRefreshTokenAsync(IdentityTestEnvironment te var clientId = "aebc6443-996d-45c2-90f0-388ff96faa56"; var username = testEnvironment.Username; var password = testEnvironment.Password; - var authorityUri = new Uri(new Uri(testEnvironment.AuthorityHostUrl), testEnvironment.TestTenantId).ToString(); + var authorityUri = new Uri(new Uri(testEnvironment.AuthorityHostUrl), testEnvironment.IdentityTenantId).ToString(); var client = PublicClientApplicationBuilder.Create(clientId) .WithAuthority(authorityUri) diff --git a/sdk/identity/Azure.Identity/tests/DefaultAzureCredentialLiveTests.cs b/sdk/identity/Azure.Identity/tests/DefaultAzureCredentialLiveTests.cs index 8ef5f8aca62d8..a89aaf08def5d 100644 --- a/sdk/identity/Azure.Identity/tests/DefaultAzureCredentialLiveTests.cs +++ b/sdk/identity/Azure.Identity/tests/DefaultAzureCredentialLiveTests.cs @@ -62,6 +62,7 @@ public async Task DefaultAzureCredential_UseVisualStudioCredential() [RecordedTest] [RunOnlyOnPlatforms(Windows = true, OSX = true, ContainerNames = new[] { "ubuntu_netcore_keyring" })] + [Ignore("https://github.com/Azure/azure-sdk-for-net/issues/27263")] public async Task DefaultAzureCredential_UseVisualStudioCodeCredential() { var options = InstrumentClientOptions(new DefaultAzureCredentialOptions @@ -73,7 +74,7 @@ public async Task DefaultAzureCredential_UseVisualStudioCodeCredential() ExcludeVisualStudioCredential = true, ExcludeAzureCliCredential = true, ExcludeVisualStudioCodeCredential = false, - VisualStudioCodeTenantId = TestEnvironment.TestTenantId + VisualStudioCodeTenantId = TestEnvironment.IdentityTenantId }); var cloudName = Guid.NewGuid().ToString(); @@ -102,6 +103,7 @@ public async Task DefaultAzureCredential_UseVisualStudioCodeCredential() [RecordedTest] [RunOnlyOnPlatforms(Windows = true, OSX = true, ContainerNames = new[] { "ubuntu_netcore_keyring" })] + [Ignore("https://github.com/Azure/azure-sdk-for-net/issues/27263")] public async Task DefaultAzureCredential_UseVisualStudioCodeCredential_ParallelCalls() { var options = InstrumentClientOptions(new DefaultAzureCredentialOptions @@ -112,7 +114,7 @@ public async Task DefaultAzureCredential_UseVisualStudioCodeCredential_ParallelC ExcludeManagedIdentityCredential = true, ExcludeAzureCliCredential = true, ExcludeVisualStudioCodeCredential = false, - VisualStudioCodeTenantId = TestEnvironment.TestTenantId + VisualStudioCodeTenantId = TestEnvironment.IdentityTenantId }); var cloudName = Guid.NewGuid().ToString(); @@ -149,7 +151,7 @@ public async Task DefaultAzureCredential_UseAzureCliCredential() ExcludeSharedTokenCacheCredential = true, ExcludeManagedIdentityCredential = true, ExcludeVisualStudioCodeCredential = false, - VisualStudioCodeTenantId = TestEnvironment.TestTenantId + VisualStudioCodeTenantId = TestEnvironment.IdentityTenantId }); var (expectedToken, expectedExpiresOn, processOutput) = CredentialTestHelpers.CreateTokenForAzureCli(); @@ -188,7 +190,7 @@ public async Task DefaultAzureCredential_UseAzureCliCredential_ParallelCalls() ExcludeSharedTokenCacheCredential = true, ExcludeManagedIdentityCredential = true, ExcludeVisualStudioCodeCredential = false, - VisualStudioCodeTenantId = TestEnvironment.TestTenantId + VisualStudioCodeTenantId = TestEnvironment.IdentityTenantId }); var (expectedToken, expectedExpiresOn, processOutput) = CredentialTestHelpers.CreateTokenForAzureCli(); diff --git a/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs b/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs index 671d49c9c899f..a760018dc9a4d 100644 --- a/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs +++ b/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs @@ -30,7 +30,6 @@ public class IdentityTestEnvironment : TestEnvironment public string UserAssignedVault => GetRecordedOptionalVariable("IDENTITYTEST_IMDSTEST_USERASSIGNEDVAULT"); public string TestPassword => GetOptionalVariable("AZURE_IDENTITY_TEST_PASSWORD") ?? "SANITIZED"; - public string TestTenantId => GetRecordedOptionalVariable("TENANT_ID") ?? GetRecordedVariable("AZURE_IDENTITY_TEST_TENANTID"); public string KeyvaultScope => GetRecordedOptionalVariable("AZURE_KEYVAULT_SCOPE") ?? "https://vault.azure.net/.default"; public string ServicePrincipalClientId => GetRecordedVariable("IDENTITY_SP_CLIENT_ID"); @@ -39,5 +38,6 @@ public class IdentityTestEnvironment : TestEnvironment public string ServicePrincipalCertificatePfxPath => GetOptionalVariable("IDENTITY_SP_CERT_PFX") ?? Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx"); public string ServicePrincipalCertificatePemPath => GetOptionalVariable("IDENTITY_SP_CERT_PEM") ?? Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pem"); public string ServicePrincipalSniCertificatePath => GetOptionalVariable("IDENTITY_SP_CERT_SNI") ?? Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx"); + public string IdentityTestWebName => GetRecordedVariable("IDENTITY_WEBAPP_NAME"); } } diff --git a/sdk/identity/Azure.Identity/tests/ManagedIdentityCredentialWebAppTests.cs b/sdk/identity/Azure.Identity/tests/ManagedIdentityCredentialWebAppTests.cs new file mode 100644 index 0000000000000..8a7d7ed4e3ed3 --- /dev/null +++ b/sdk/identity/Azure.Identity/tests/ManagedIdentityCredentialWebAppTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Net; +using System.Threading.Tasks; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.Core.TestFramework; +using NUnit.Framework; + +namespace Azure.Identity.Tests +{ + public class ManagedIdentityCredentialWebAppTests : IdentityRecordedTestBase + { + private HttpPipeline _pipeline; + private Uri _testEndpoint; + + public ManagedIdentityCredentialWebAppTests(bool isAsync) : base(isAsync) + { + } + + [SetUp] + public void Setup() { + var options = new TokenCredentialOptions(); + _testEndpoint = new Uri($"https://{TestEnvironment.IdentityTestWebName}.azurewebsites.net/test"); + _pipeline = HttpPipelineBuilder.Build(InstrumentClientOptions(options), Array.Empty(), Array.Empty(), new ResponseClassifier()); + } + + [RecordedTest] + [SyncOnly] + // This test leverages the test app found in Azure.Identity\integration\WebApp + // It validates that ManagedIdentityCredential can acquire a token in an actual Azure Web App environment + public async Task CallTestWebApp() + { + Request request = _pipeline.CreateRequest(); + request.Uri.Reset(_testEndpoint); + Response response = await _pipeline.SendRequestAsync(request, default); + + Assert.AreEqual((int)HttpStatusCode.OK, response.Status); + Assert.AreEqual("Successfully acquired a token from ManagedIdentityCredential", response.Content.ToString(), response.Content.ToString()); + } + } +} diff --git a/sdk/identity/Azure.Identity/tests/SessionRecords/ManagedIdentityCredentialWebAppTests/CallTestWebApp.json b/sdk/identity/Azure.Identity/tests/SessionRecords/ManagedIdentityCredentialWebAppTests/CallTestWebApp.json new file mode 100644 index 0000000000000..44e3adaebda69 --- /dev/null +++ b/sdk/identity/Azure.Identity/tests/SessionRecords/ManagedIdentityCredentialWebAppTests/CallTestWebApp.json @@ -0,0 +1,31 @@ +{ + "Entries": [ + { + "RequestUri": "https://chrissidentity-webapp.azurewebsites.net/test", + "RequestMethod": "GET", + "RequestHeaders": { + "User-Agent": "azsdk-net-Identity/1.9.0-alpha.20221205.1 (.NET 6.0.11; Microsoft Windows 10.0.22621)", + "x-ms-client-request-id": "da316510b79eb7bb601cb0177206c845", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "Content-Type": "text/plain; charset=utf-8", + "Date": "Mon, 05 Dec 2022 21:01:43 GMT", + "Server": "Microsoft-IIS/10.0", + "Set-Cookie": [ + "ARRAffinity=79e06db539acb57119e709978d2cf1da299e8341753d6f6345007fcab3f69bc5;Path=/;HttpOnly;Secure;Domain=chrissidentity-webapp.azurewebsites.net", + "ARRAffinitySameSite=79e06db539acb57119e709978d2cf1da299e8341753d6f6345007fcab3f69bc5;Path=/;HttpOnly;SameSite=None;Secure;Domain=chrissidentity-webapp.azurewebsites.net" + ], + "Transfer-Encoding": "chunked", + "X-Powered-By": "ASP.NET" + }, + "ResponseBody": "Successfully acquired a token from ManagedIdentityCredential" + } + ], + "Variables": { + "IDENTITY_WEBAPP_NAME": "chrissidentity-webapp", + "RandomSeed": "958608913" + } +} diff --git a/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialLiveTests.cs b/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialLiveTests.cs index cb29fc9b7b20f..31021d4609e63 100644 --- a/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialLiveTests.cs +++ b/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialLiveTests.cs @@ -11,6 +11,7 @@ namespace Azure.Identity.Tests { + [Ignore("https://github.com/Azure/azure-sdk-for-net/issues/27020")] public class VisualStudioCodeCredentialLiveTests : IdentityRecordedTestBase { private const string ExpectedServiceName = "VS Code Azure"; @@ -28,7 +29,7 @@ public async Task AuthenticateWithVscCredential() var fileSystem = CredentialTestHelpers.CreateFileSystemForVisualStudioCode(TestEnvironment, cloudName); using IDisposable fixture = await CredentialTestHelpers.CreateRefreshTokenFixtureAsync(TestEnvironment, Mode, ExpectedServiceName, cloudName); - var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.TestTenantId }); + var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.IdentityTenantId }); VisualStudioCodeCredential credential = InstrumentClient(new VisualStudioCodeCredential(options, default, default, fileSystem, default)); AccessToken token = await credential.GetTokenAsync(new TokenRequestContext(new[] { TestEnvironment.KeyvaultScope}), CancellationToken.None); Assert.IsNotNull(token.Token); @@ -41,7 +42,7 @@ public async Task AuthenticateWithVscCredential_NoSettingsFile() var fileSystemService = new TestFileSystemService { ReadAllHandler = s => throw new FileNotFoundException() }; var vscAdapter = new TestVscAdapter(ExpectedServiceName, "AzureCloud", refreshToken); - var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.TestTenantId }); + var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.IdentityTenantId }); VisualStudioCodeCredential credential = InstrumentClient(new VisualStudioCodeCredential(options, default, default, fileSystemService, vscAdapter)); AccessToken token = await credential.GetTokenAsync(new TokenRequestContext(new[] { TestEnvironment.KeyvaultScope}), CancellationToken.None); Assert.IsNotNull(token.Token); @@ -54,7 +55,7 @@ public async Task AuthenticateWithVscCredential_BrokenSettingsFile() var fileSystemService = new TestFileSystemService { ReadAllHandler = s => "{a,}" }; var vscAdapter = new TestVscAdapter(ExpectedServiceName, "AzureCloud", refreshToken); - var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.TestTenantId }); + var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.IdentityTenantId }); VisualStudioCodeCredential credential = InstrumentClient(new VisualStudioCodeCredential(options, default, default, fileSystemService, vscAdapter)); AccessToken token = await credential.GetTokenAsync(new TokenRequestContext(new[] {TestEnvironment.KeyvaultScope}), CancellationToken.None); Assert.IsNotNull(token.Token); @@ -67,7 +68,7 @@ public async Task AuthenticateWithVscCredential_EmptySettingsFile() var fileSystemService = CredentialTestHelpers.CreateFileSystemForVisualStudioCode(TestEnvironment); var vscAdapter = new TestVscAdapter(ExpectedServiceName, "AzureCloud", refreshToken); - var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.TestTenantId }); + var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.IdentityTenantId }); VisualStudioCodeCredential credential = InstrumentClient(new VisualStudioCodeCredential(options, default, default, fileSystemService, vscAdapter)); AccessToken token = await credential.GetTokenAsync(new TokenRequestContext(new[] {TestEnvironment.KeyvaultScope}), CancellationToken.None); @@ -97,7 +98,7 @@ public void AuthenticateWithVscCredential_NoVscInstalled() var cloudName = Guid.NewGuid().ToString(); var fileSystem = CredentialTestHelpers.CreateFileSystemForVisualStudioCode(TestEnvironment, cloudName); - var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.TestTenantId }); + var options = InstrumentClientOptions(new VisualStudioCodeCredentialOptions { TenantId = TestEnvironment.IdentityTenantId }); VisualStudioCodeCredential credential = InstrumentClient(new VisualStudioCodeCredential(options, default, default, fileSystem, default)); Assert.CatchAsync(async () => await credential.GetTokenAsync(new TokenRequestContext(new[] {TestEnvironment.KeyvaultScope}), CancellationToken.None)); @@ -106,7 +107,7 @@ public void AuthenticateWithVscCredential_NoVscInstalled() [Test] public void AuthenticateWithVscCredential_NoRefreshToken() { - var tenantId = TestEnvironment.TestTenantId; + var tenantId = TestEnvironment.IdentityTenantId; var vscAdapter = new TestVscAdapter(ExpectedServiceName, "AzureCloud", null); var fileSystem = CredentialTestHelpers.CreateFileSystemForVisualStudioCode(TestEnvironment); @@ -119,7 +120,7 @@ public void AuthenticateWithVscCredential_NoRefreshToken() [Test] public void AuthenticateWithVscCredential_AuthenticationCodeInsteadOfRefreshToken() { - var tenantId = TestEnvironment.TestTenantId; + var tenantId = TestEnvironment.IdentityTenantId; var fileSystemService = CredentialTestHelpers.CreateFileSystemForVisualStudioCode(TestEnvironment); var vscAdapter = new TestVscAdapter(ExpectedServiceName, "AzureCloud", "{}"); @@ -132,7 +133,7 @@ public void AuthenticateWithVscCredential_AuthenticationCodeInsteadOfRefreshToke [Test] public void AuthenticateWithVscCredential_InvalidRefreshToken() { - var tenantId = TestEnvironment.TestTenantId; + var tenantId = TestEnvironment.IdentityTenantId; var fileSystemService = CredentialTestHelpers.CreateFileSystemForVisualStudioCode(TestEnvironment); var vscAdapter = new TestVscAdapter(ExpectedServiceName, "AzureCloud", Guid.NewGuid().ToString()); diff --git a/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialTests.cs b/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialTests.cs index c5f969eed8852..0ac114ef00a9c 100644 --- a/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialTests.cs +++ b/sdk/identity/Azure.Identity/tests/VisualStudioCodeCredentialTests.cs @@ -12,6 +12,7 @@ namespace Azure.Identity.Tests { + [Ignore("https://github.com/Azure/azure-sdk-for-net/issues/27263")] public class VisualStudioCodeCredentialTests : CredentialTestBase { public VisualStudioCodeCredentialTests(bool isAsync) : base(isAsync) @@ -19,7 +20,7 @@ public VisualStudioCodeCredentialTests(bool isAsync) : base(isAsync) public override TokenCredential GetTokenCredential(TokenCredentialOptions options) { - using var env = new TestEnvVar(new Dictionary { { "TENANT_ID", TenantId } }); + using var env = new TestEnvVar(new Dictionary { { "IDENTITY_TENANT_ID", TenantId } }); var environment = new IdentityTestEnvironment(); var vscOptions = new VisualStudioCodeCredentialOptions { @@ -47,7 +48,7 @@ public void Setup() [NonParallelizable] public async Task AuthenticateWithVsCodeCredential([Values(null, TenantIdHint)] string tenantId, [Values(true)] bool allowMultiTenantAuthentication) { - using var env = new TestEnvVar(new Dictionary { { "TENANT_ID", TenantId } }); + using var env = new TestEnvVar(new Dictionary { { "IDENTITY_TENANT_ID", TenantId } }); var environment = new IdentityTestEnvironment(); var options = new VisualStudioCodeCredentialOptions { TenantId = environment.TenantId, AdditionallyAllowedTenants = { TenantIdHint }, Transport = new MockTransport() }; var context = new TokenRequestContext(new[] { Scope }, tenantId: tenantId); @@ -71,7 +72,7 @@ public override async Task VerifyAllowedTenantEnforcement(AllowedTenantsTestPara { Console.WriteLine(parameters.ToDebugString()); - using var env = new TestEnvVar(new Dictionary { { "TENANT_ID", TenantId } }); + using var env = new TestEnvVar(new Dictionary { { "IDENTITY_TENANT_ID", TenantId } }); var environment = new IdentityTestEnvironment(); var options = new VisualStudioCodeCredentialOptions { @@ -86,7 +87,7 @@ public override async Task VerifyAllowedTenantEnforcement(AllowedTenantsTestPara var msalClientMock = new MockMsalPublicClient(AuthenticationResultFactory.Create()); - var cred = InstrumentClient( + var cred = InstrumentClient( new VisualStudioCodeCredential( options, null, diff --git a/sdk/identity/test-resources-post.ps1 b/sdk/identity/test-resources-post.ps1 new file mode 100644 index 0000000000000..cc8637d9a4e3e --- /dev/null +++ b/sdk/identity/test-resources-post.ps1 @@ -0,0 +1,19 @@ +param ( + [hashtable] $DeploymentOutputs +) + +$webappRoot = "$PSScriptRoot/Azure.Identity/integration" | Resolve-Path +$workingFolder = $webappRoot; +if ($null -ne $Env:AGENT_WORKFOLDER) { + $workingFolder = $Env:AGENT_WORKFOLDER +} +az login --service-principal -u $DeploymentOutputs['IDENTITY_CLIENT_ID'] -p $DeploymentOutputs['IDENTITY_CLIENT_SECRET'] --tenant $DeploymentOutputs['IDENTITY_TENANT_ID'] +az account set --subscription $DeploymentOutputs['IDENTITY_SUBSCRIPTION_ID'] +dotnet publish "$webappRoot/WebApp/Integration.Identity.WebApp.csproj" -o "$workingFolder/Pub" /p:EnableSourceLink=false +Compress-Archive -Path "$workingFolder/Pub/*" -DestinationPath "$workingFolder/Pub/package.zip" -Force +az webapp deploy --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --name $DeploymentOutputs['IDENTITY_WEBAPP_NAME'] --src-path "$workingFolder/Pub/package.zip" +Remove-Item -Force -Recurse "$workingFolder/Pub" +if ($null -eq $Env:AGENT_WORKFOLDER) { + Remove-Item -Force -Recurse "$webappRoot/%AGENT_WORKFOLDER%" +} +az logout \ No newline at end of file diff --git a/sdk/identity/test-resources.bicep b/sdk/identity/test-resources.bicep new file mode 100644 index 0000000000000..b175d52a5e5ee --- /dev/null +++ b/sdk/identity/test-resources.bicep @@ -0,0 +1,133 @@ +@description('The client OID to grant access to test resources.') +param testApplicationOid string + +@minLength(6) +@maxLength(50) +@description('The base resource name.') +param baseName string = resourceGroup().name + +@description('The location of the resource. By default, this is the same as the resource group.') +param location string = resourceGroup().location + +//See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles +var blobContributor = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') //Storage Blob Data Contributor +var websiteContributor = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') //Website Contributor + +resource usermgdid 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: baseName + location: location +} + +resource blobRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: sa + name: guid(resourceGroup().id, blobContributor) + properties: { + principalId: web.identity.principalId + roleDefinitionId: blobContributor + principalType: 'ServicePrincipal' + } +} + +resource blobRole2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: sa2 + name: guid(resourceGroup().id, blobContributor, usermgdid.id) + properties: { + principalId: usermgdid.properties.principalId + roleDefinitionId: blobContributor + principalType: 'ServicePrincipal' + } +} + +resource webRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: web + name: guid(resourceGroup().id, websiteContributor) + properties: { + principalId: testApplicationOid + roleDefinitionId: websiteContributor + principalType: 'ServicePrincipal' + } +} + +resource sa 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: baseName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + } +} + +resource sa2 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: '${baseName}2' + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + } +} + +resource farm 'Microsoft.Web/serverfarms@2021-03-01' = { + name: '${baseName}_asp' + location: location + sku: { + name: 'F1' + tier: 'Free' + size: 'F1' + family: 'F' + capacity: 0 + } + properties: { } + kind: 'app' +} + +resource web 'Microsoft.Web/sites@2021-03-01' = { + name: '${baseName}-webapp' + location: location + kind: 'app' + identity: { + type: 'SystemAssigned, UserAssigned' + userAssignedIdentities: { + '${usermgdid.id}' : { } + } + } + properties: { + enabled: true + serverFarmId: farm.id + httpsOnly: true + keyVaultReferenceIdentity: 'SystemAssigned' + siteConfig: { + netFrameworkVersion: 'v6.0' + http20Enabled: true + minTlsVersion: '1.2' + appSettings: [ + { + name: 'AZURE_REGIONAL_AUTHORITY_NAME' + value: 'eastus' + } + { + name: 'IDENTITY_STORAGE_NAME_1' + value: sa.name + } + { + name: 'IDENTITY_STORAGE_NAME_2' + value: sa2.name + } + { + name: 'IDENTITY_WEBAPP_USER_DEFINED_IDENTITY' + value: usermgdid.id + } + ] + } + } +} + +output IDENTITY_WEBAPP_NAME string = web.name +output IDENTITY_WEBAPP_USER_DEFINED_IDENTITY string = usermgdid.id +output IDENTITY_STORAGE_NAME_1 string = sa.name +output IDENTITY_STORAGE_NAME_2 string = sa2.name diff --git a/sdk/identity/tests.yml b/sdk/identity/tests.yml index 764ca96da07b2..2a2bdfd0e908d 100644 --- a/sdk/identity/tests.yml +++ b/sdk/identity/tests.yml @@ -1,36 +1,12 @@ trigger: none -resources: - containers: - - container: 'ubuntu_netcore_keyring' - # See ./eng/containers/UbuntuNetCoreKeyring/Dockerfile for tool versions - image: 'azsdkengsys.azurecr.io/dotnet/ubuntu_netcore_keyring:1347171' - endpoint: 'azsdkengsys' - options: -ti --cap-add=IPC_LOCK - extends: template: ../../eng/pipelines/templates/stages/archetype-sdk-tests.yml parameters: TimeoutInMinutes: 120 - AdditionalMatrixConfigs: - - Name: identity_container - Path: sdk/identity/platform-matrix.json - Selection: sparse - GenerateContainerJobs: true ServiceDirectory: identity SupportedClouds: 'Public,UsGov,China,Canary' PreSteps: - - pwsh: Install-Module -Name Az -Scope CurrentUser -AllowClobber -Force -Verbose - displayName: Install Azure PowerShell module - condition: and(succeededOrFailed(), startsWith(variables['Agent.JobName'], 'ubuntu_keyring_container')) - - script: | - set -x - export $(dbus-launch) - gnome-keyring-daemon --start --daemonize --components=secrets - echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_SESSION_BUS_ADDRESS" - echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_PID]$DBUS_SESSION_BUS_PID" - echo "##vso[task.setvariable variable=GNOME_KEYRING_CONTROL]$GNOME_KEYRING_CONTROL" - condition: and(succeededOrFailed(), startsWith(variables['Agent.JobName'], 'ubuntu_keyring_container')) - pwsh: | [System.Convert]::FromBase64String($env:PFX_CONTENTS) | Set-Content -Path $(Agent.TempDirectory)/test.pfx -AsByteStream Set-Content -Path $(Agent.TempDirectory)/test.pem -Value $env:PEM_CONTENTS