diff --git a/eng/pipelines/notifications.yml b/eng/pipelines/notifications.yml
index 028d4af6528..572200b096f 100644
--- a/eng/pipelines/notifications.yml
+++ b/eng/pipelines/notifications.yml
@@ -54,23 +54,20 @@ stages:
arguments: 'install --global --add-source "$(DotNetDevOpsFeed)" --version "$(NotificationsCreatorVersion)" "Azure.Sdk.Tools.NotificationConfiguration"'
workingDirectory: '$(Agent.BuildDirectory)'
- - pwsh: |
- notification-creator `
- --organization $(Organization) `
- --project $(Project) `
- --path-prefix "\$(PathPrefix)" `
- --token-variable-name DEVOPS_TOKEN `
- --aad-app-id-var OPENSOURCE_AAD_APP_ID `
- --aad-app-secret-var OPENSOURCE_AAD_APP_SECRET `
- --aad-tenant-var OPENSOURCE_AAD_TENANT_ID `
- --selection-strategy Scheduled `
- $(AdditionalParameters)
+ - task: AzureCLI@2
displayName: 'Run Team/Notification Creator'
+ inputs:
+ azureSubscription: 'opensource-api-connection'
+ scriptType: pscore
+ scriptLocation: inlineScript
+ inlineScript:
+ notification-creator `
+ --organization $(Organization) `
+ --project $(Project) `
+ --path-prefix "\$(PathPrefix)" `
+ --selection-strategy Scheduled `
+ $(AdditionalParameters)
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_MULTILEVEL_LOOKUP: 0
- DEVOPS_TOKEN: $(azure-sdk-notification-tools-pat)
- OPENSOURCE_AAD_APP_ID: $(opensource-aad-app-id)
- OPENSOURCE_AAD_APP_SECRET: $(opensource-aad-secret)
- OPENSOURCE_AAD_TENANT_ID: $(opensource-aad-tenant-id)
diff --git a/eng/pipelines/pipeline-owners-extraction.yml b/eng/pipelines/pipeline-owners-extraction.yml
index 16b63891ab3..bbe6e5621e5 100644
--- a/eng/pipelines/pipeline-owners-extraction.yml
+++ b/eng/pipelines/pipeline-owners-extraction.yml
@@ -49,7 +49,7 @@ stages:
- task: AzureCLI@2
displayName: Run Pipeline Owners Extractor
inputs:
- azureSubscription: 'Azure SDK Engineering System'
+ azureSubscription: 'opensource-api-connection'
scriptType: pscore
scriptLocation: inlineScript
inlineScript: pipeline-owners-extractor --output "$(OutputPath)"
diff --git a/tools/codeowners-utils/Azure.Sdk.Tools.CodeownersUtils/Utils/FileHelpers.cs b/tools/codeowners-utils/Azure.Sdk.Tools.CodeownersUtils/Utils/FileHelpers.cs
index 5109598f437..66eb754e117 100644
--- a/tools/codeowners-utils/Azure.Sdk.Tools.CodeownersUtils/Utils/FileHelpers.cs
+++ b/tools/codeowners-utils/Azure.Sdk.Tools.CodeownersUtils/Utils/FileHelpers.cs
@@ -57,9 +57,10 @@ private static string GetUrlContents(string url)
using HttpClient client = new HttpClient();
while (attempts <= maxRetries)
{
+ HttpResponseMessage response = null;
try
{
- HttpResponseMessage response = client.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
+ response = client.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
if (response.StatusCode == HttpStatusCode.OK)
{
// This writeline is probably unnecessary but good to have if there are previous attempts that failed
@@ -76,17 +77,22 @@ private static string GetUrlContents(string url)
// HttpRequestException means the request failed due to an underlying issue such as network connectivity,
// DNS failure, server certificate validation or timeout.
Console.WriteLine($"GetUrlContents attempt number {attempts}. HttpRequestException trying to fetch {url}. Exception message = {httpReqEx.Message}");
- if (attempts == maxRetries)
- {
- // At this point the retries have been exhausted, let this rethrow
- throw;
- }
}
- System.Threading.Thread.Sleep(delayTimeInMs);
+
+ // Skip retries on a NotFound response
+ if (response?.StatusCode == HttpStatusCode.NotFound)
+ {
+ break;
+ }
+
+ if (attempts < maxRetries)
+ {
+ System.Threading.Thread.Sleep(delayTimeInMs);
+ }
attempts++;
}
// This will only get hit if the final retry is non-OK status code
- throw new FileLoadException($"Unable to fetch {url} after {maxRetries}. See above for status codes for each attempt.");
+ throw new FileLoadException($"Unable to fetch {url} after {attempts} attempts. See above for status codes for each attempt.");
}
}
}
diff --git a/tools/identity-resolution/Helpers/GitHubToAADConverter.cs b/tools/identity-resolution/Helpers/GitHubToAADConverter.cs
index 6b8698a49b3..9e8129492a1 100644
--- a/tools/identity-resolution/Helpers/GitHubToAADConverter.cs
+++ b/tools/identity-resolution/Helpers/GitHubToAADConverter.cs
@@ -1,96 +1,107 @@
using System;
-using System.Net;
+using System.Collections.Generic;
+using System.Linq;
using System.Net.Http;
+using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
-using Azure.Identity;
using Microsoft.Extensions.Logging;
using Models.OpenSourcePortal;
using Newtonsoft.Json;
namespace Azure.Sdk.Tools.NotificationConfiguration.Helpers
{
+ ///
+ /// Utility class for converting GitHub usernames to AAD user principal names.
+ ///
+ ///
+ /// A map of GitHub usernames to AAD user principal names is cached in memory to avoid making multiple calls to the
+ /// OpenSource portal API. The cache is initialized with the full alias list on the first call to
+ /// GetUserPrincipalNameFromGithubAsync.
+ ///
public class GitHubToAADConverter
{
+ private readonly TokenCredential credential;
+ private readonly ILogger logger;
+ private readonly SemaphoreSlim cacheLock = new(1);
+ private Dictionary lookupCache;
+
///
/// GitHubToAadConverter constructor for generating new token, and initialize http client.
///
/// The aad token auth class.
/// Logger
- public GitHubToAADConverter(
- ClientSecretCredential credential,
- ILogger logger)
+ public GitHubToAADConverter(TokenCredential credential, ILogger logger)
{
+ this.credential = credential;
this.logger = logger;
- var opsAuthToken = "";
+
+ }
+
+ public async Task GetUserPrincipalNameFromGithubAsync(string gitHubUserName)
+ {
+ await EnsureCacheExistsAsync();
+
+ if (this.lookupCache.TryGetValue(gitHubUserName, out string aadUserPrincipalName))
+ {
+ return aadUserPrincipalName;
+ }
+
+ return null;
+ }
+
+ public async Task EnsureCacheExistsAsync()
+ {
+ await this.cacheLock.WaitAsync();
try
{
- // This is aad scope of opensource rest API.
- string[] scopes = new string[]
+ if (this.lookupCache == null)
{
- "api://2789159d-8d8b-4d13-b90b-ca29c1707afd/.default"
- };
- opsAuthToken = credential.GetToken(new TokenRequestContext(scopes)).Token;
+ var peopleLinks = await GetPeopleLinksAsync();
+ this.lookupCache = peopleLinks.ToDictionary(
+ x => x.GitHub.Login,
+ x => x.Aad.UserPrincipalName,
+ StringComparer.OrdinalIgnoreCase);
+ }
}
- catch (Exception ex)
+ finally
{
- logger.LogError("Failed to generate aad token. " + ex.Message);
+ this.cacheLock.Release();
}
- client = new HttpClient();
- client.DefaultRequestHeaders.Add("content_type", "application/json");
- client.DefaultRequestHeaders.Add("api-version", "2019-10-01");
- client.DefaultRequestHeaders.Add("Authorization", $"Bearer {opsAuthToken}");
}
- private readonly HttpClient client;
- private readonly ILogger logger;
-
- ///
- /// Get the user principal name from github. User principal name is in format of ms email.
- ///
- /// github user name
- /// Aad user principal name
- public string GetUserPrincipalNameFromGithub(string githubUserName)
+ private async Task GetPeopleLinksAsync()
{
- return GetUserPrincipalNameFromGithubAsync(githubUserName).Result;
- }
+ AccessToken opsAuthToken;
- public async Task GetUserPrincipalNameFromGithubAsync(string githubUserName)
- {
try
{
- var responseJsonString = await client.GetStringAsync($"https://repos.opensource.microsoft.com/api/people/links/github/{githubUserName}");
- dynamic contentJson = JsonConvert.DeserializeObject(responseJsonString);
- return contentJson.aad.userPrincipalName;
- }
- catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
- {
- logger.LogWarning("Github username {Username} not found", githubUserName);
+ // This is aad scope of opensource rest API.
+ string[] scopes = new [] { "api://66b6ea26-954d-4b68-8f48-71e3faec7ad1/.default" };
+ opsAuthToken = await credential.GetTokenAsync(new TokenRequestContext(scopes), CancellationToken.None);
}
catch (Exception ex)
{
- logger.LogError(ex.Message);
+ this.logger.LogError("Failed to generate aad token. {ExceptionMessage}", ex.Message);
+ throw;
}
- return null;
- }
-
- public async Task GetPeopleLinksAsync()
- {
try
{
- logger.LogInformation("Calling GET https://repos.opensource.microsoft.com/api/people/links");
- var responseJsonString = await client.GetStringAsync($"https://repos.opensource.microsoft.com/api/people/links");
- var allLinks = JsonConvert.DeserializeObject(responseJsonString);
+ using HttpClient client = new ();
+ client.DefaultRequestHeaders.Add("content_type", "application/json");
+ client.DefaultRequestHeaders.Add("api-version", "2019-10-01");
+ client.DefaultRequestHeaders.Add("Authorization", $"Bearer {opsAuthToken.Token}");
- return allLinks;
+ this.logger.LogInformation("Calling GET https://repos.opensource.microsoft.com/api/people/links");
+ string responseJsonString = await client.GetStringAsync($"https://repos.opensource.microsoft.com/api/people/links");
+ return JsonConvert.DeserializeObject(responseJsonString);
}
catch (Exception ex)
{
- logger.LogError(ex.Message);
+ this.logger.LogError(ex, "Error getting people links from opensource.microsoft.com: {ExceptionMessage}", ex.Message);
+ throw;
}
-
- return null;
}
}
}
diff --git a/tools/identity-resolution/Services/AzureDevOpsService.cs b/tools/identity-resolution/Services/AzureDevOpsService.cs
index 2ee8b37a289..8981963cb8b 100644
--- a/tools/identity-resolution/Services/AzureDevOpsService.cs
+++ b/tools/identity-resolution/Services/AzureDevOpsService.cs
@@ -9,11 +9,12 @@
using Microsoft.VisualStudio.Services.Notifications.WebApi;
using System;
using Microsoft.Extensions.Logging;
-using Microsoft.VisualStudio.Services.Common;
+using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Identity.Client;
using System.Threading;
using System.Linq;
using MicrosoftIdentityAlias = Microsoft.VisualStudio.Services.Identity;
+using Azure.Core;
namespace Azure.Sdk.Tools.NotificationConfiguration.Services
{
@@ -27,9 +28,9 @@ public class AzureDevOpsService
private Dictionary clientCache = new Dictionary();
private SemaphoreSlim clientCacheSemaphore = new SemaphoreSlim(1);
- public static AzureDevOpsService CreateAzureDevOpsService(string token, string url, ILogger logger)
+ public static AzureDevOpsService CreateAzureDevOpsService(TokenCredential tokenCredential, string url, ILogger logger)
{
- var devOpsCreds = new VssBasicCredential("nobody", token);
+ var devOpsCreds = new VssAzureIdentityCredential(tokenCredential);
var devOpsConnection = new VssConnection(new Uri(url), devOpsCreds);
var result = new AzureDevOpsService(devOpsConnection, logger);
diff --git a/tools/identity-resolution/Services/GitHubService.cs b/tools/identity-resolution/Services/GitHubService.cs
index c59e527771b..fdd2a280670 100644
--- a/tools/identity-resolution/Services/GitHubService.cs
+++ b/tools/identity-resolution/Services/GitHubService.cs
@@ -3,8 +3,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
using Azure.Sdk.Tools.CodeownersUtils.Parsing;
using System.IO;
diff --git a/tools/identity-resolution/identity-resolution.csproj b/tools/identity-resolution/identity-resolution.csproj
index 24e592eb1aa..2c61094a460 100644
--- a/tools/identity-resolution/identity-resolution.csproj
+++ b/tools/identity-resolution/identity-resolution.csproj
@@ -7,8 +7,9 @@
-
-
+
+
+
diff --git a/tools/notification-configuration/ci.yml b/tools/notification-configuration/ci.yml
index ae36769a7dc..c32e6bc0a19 100644
--- a/tools/notification-configuration/ci.yml
+++ b/tools/notification-configuration/ci.yml
@@ -29,3 +29,4 @@ extends:
template: /eng/pipelines/templates/stages/archetype-sdk-tool-dotnet.yml
parameters:
ToolDirectory: tools/notification-configuration
+ TestMatrix: {}
diff --git a/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs b/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs
index e7a92d50636..8e9f80a1929 100644
--- a/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs
+++ b/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs
@@ -9,9 +9,6 @@ public class ProgramTests
[Test]
public void ThrowsVssUnauthorizedException()
{
- Environment.SetEnvironmentVariable("aadAppIdVar", "aadAppIdVarValue");
- Environment.SetEnvironmentVariable("aadAppSecretVar", "aadAppSecretVarValue");
- Environment.SetEnvironmentVariable("aadTenantVar", "aadTenantVarValue");
Assert.ThrowsAsync(
async () =>
// Act
@@ -19,10 +16,6 @@ await Program.Main(
organization: "fooOrg",
project: "barProj",
pathPrefix: "qux",
- tokenVariableName: "token",
- aadAppIdVar: "aadAppIdVar",
- aadAppSecretVar: "aadAppSecretVar",
- aadTenantVar: "aadTenantVar",
selectionStrategy: PipelineSelectionStrategy.Scheduled,
dryRun: true)
);
diff --git a/tools/notification-configuration/notification-creator/NotificationConfigurator.cs b/tools/notification-configuration/notification-creator/NotificationConfigurator.cs
index 4e0bef8308f..097a3205850 100644
--- a/tools/notification-configuration/notification-creator/NotificationConfigurator.cs
+++ b/tools/notification-configuration/notification-creator/NotificationConfigurator.cs
@@ -196,7 +196,7 @@ private async Task SyncTeamWithCodeownersFile(
if (!contactsCache.ContainsKey(contact))
{
// TODO: Better to have retry if no success on this call.
- var userPrincipal = gitHubToAADConverter.GetUserPrincipalNameFromGithub(contact);
+ var userPrincipal = await gitHubToAADConverter.GetUserPrincipalNameFromGithubAsync(contact);
if (!string.IsNullOrEmpty(userPrincipal))
{
contactsCache[contact] = await service.GetDescriptorForPrincipal(userPrincipal);
diff --git a/tools/notification-configuration/notification-creator/Program.cs b/tools/notification-configuration/notification-creator/Program.cs
index dbc343717f6..e53750f77a5 100644
--- a/tools/notification-configuration/notification-creator/Program.cs
+++ b/tools/notification-configuration/notification-creator/Program.cs
@@ -1,11 +1,12 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
-using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Azure.Sdk.Tools.NotificationConfiguration.Services;
using Azure.Sdk.Tools.NotificationConfiguration.Helpers;
using Azure.Identity;
+using Azure.Core;
+using Microsoft.VisualStudio.Services.Client;
namespace Azure.Sdk.Tools.NotificationConfiguration;
@@ -23,10 +24,6 @@ public static class Program
/// Azure DevOps Organization
/// Name of the DevOps project
/// Path prefix to include pipelines (e.g. "\net")
- /// Environment variable token name (e.g. "SYSTEM_ACCESSTOKEN")
- /// AAD App ID environment variable name (OpensourceAPI access)
- /// AAD App Secret environment variable name (OpensourceAPI access)
- /// AAD Tenant environment variable name (OpensourceAPI access)
/// Pipeline selection strategy
/// Prints changes but does not alter any objects
///
@@ -34,10 +31,6 @@ public static async Task Main(
string organization,
string project,
string pathPrefix,
- string tokenVariableName,
- string aadAppIdVar,
- string aadAppSecretVar,
- string aadTenantVar,
PipelineSelectionStrategy selectionStrategy = PipelineSelectionStrategy.Scheduled,
bool dryRun = false)
{
@@ -45,54 +38,55 @@ public static async Task Main(
{
builder.AddSimpleConsole(config => { config.IncludeScopes = true; });
});
+
var logger = loggerFactory.CreateLogger(nameof(Program));
+
logger.LogInformation(
"Executing Azure.Sdk.Tools.NotificationConfiguration.Program.Main with following arguments: "
+ "organization: '{organization}' "
+ "project: '{project}' "
+ "pathPrefix: '{pathPrefix}' "
- + "tokenVariableName: '{tokenVariableName}' "
- + "aadAppIdVar: '{aadAppIdVar}' "
- + "aadAppSecretVar: '{aadAppSecretVar}' "
- + "aadTenantVar: '{aadTenantVar}' "
+ "selectionStrategy: '{selectionStrategy}' "
+ "dryRun: '{dryRun}' "
, organization
, project
, pathPrefix
- , tokenVariableName
- , aadAppIdVar
- , aadAppSecretVar
- , aadTenantVar
, selectionStrategy
, dryRun);
+ var azureCredential = new ChainedTokenCredential(
+ new AzureCliCredential(),
+ new AzurePowerShellCredential()
+ );
+
var notificationConfigurator = new NotificationConfigurator(
- AzureDevOpsService(organization, tokenVariableName, loggerFactory),
+ AzureDevOpsService(organization, azureCredential, loggerFactory),
GitHubService(loggerFactory),
loggerFactory.CreateLogger());
await notificationConfigurator.ConfigureNotifications(
project,
pathPrefix,
- GitHubToAADConverter(aadTenantVar, aadAppIdVar, aadAppSecretVar, loggerFactory),
+ GitHubToAADConverter(azureCredential, loggerFactory),
persistChanges: !dryRun,
strategy: selectionStrategy);
}
private static AzureDevOpsService AzureDevOpsService(
string organization,
- string tokenVariableName,
+ TokenCredential azureCredential,
ILoggerFactory loggerFactory)
{
- var devOpsToken = Environment.GetEnvironmentVariable(tokenVariableName);
- var devOpsCreds = new VssBasicCredential("nobody", devOpsToken);
+ var devOpsCreds = new VssAzureIdentityCredential(azureCredential);
+
var devOpsConnection = new VssConnection(
new Uri($"https://dev.azure.com/{organization}/"),
devOpsCreds);
+
var devOpsService = new AzureDevOpsService(
devOpsConnection,
loggerFactory.CreateLogger());
+
return devOpsService;
}
@@ -100,20 +94,14 @@ private static GitHubService GitHubService(ILoggerFactory loggerFactory)
=> new GitHubService(loggerFactory.CreateLogger());
private static GitHubToAADConverter GitHubToAADConverter(
- string aadTenantVar,
- string aadAppIdVar,
- string aadAppSecretVar,
+ TokenCredential azureCredential,
ILoggerFactory loggerFactory)
{
- var credential = new ClientSecretCredential(
- Environment.GetEnvironmentVariable(aadTenantVar),
- Environment.GetEnvironmentVariable(aadAppIdVar),
- Environment.GetEnvironmentVariable(aadAppSecretVar));
-
var githubToAadResolver = new GitHubToAADConverter(
- credential,
+ azureCredential,
loggerFactory.CreateLogger()
);
+
return githubToAadResolver;
}
}
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/ISecretClientProvider.cs b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/ISecretClientProvider.cs
deleted file mode 100644
index bf56d88701c..00000000000
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/ISecretClientProvider.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-using Azure.Security.KeyVault.Secrets;
-
-namespace Azure.Sdk.Tools.PipelineOwnersExtractor.Configuration
-{
- public interface ISecretClientProvider
- {
- SecretClient GetSecretClient(Uri vaultUri);
- }
-}
\ No newline at end of file
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PipelineOwnerSettings.cs b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PipelineOwnerSettings.cs
index 2cd0d05cd5a..883186446ee 100644
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PipelineOwnerSettings.cs
+++ b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PipelineOwnerSettings.cs
@@ -8,12 +8,6 @@ public class PipelineOwnerSettings
public string OpenSourceAadAppId { get; set; }
- public string OpenSourceAadSecret { get; set; }
-
- public string OpenSourceAadTenantId { get; set; }
-
- public string AzureDevOpsPat { get; set; }
-
public string Output { get; set; }
}
}
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PostConfigureKeyVaultSettings.cs b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PostConfigureKeyVaultSettings.cs
deleted file mode 100644
index 63437d3fb03..00000000000
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/PostConfigureKeyVaultSettings.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Linq;
-using System.Text.RegularExpressions;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace Azure.Sdk.Tools.PipelineOwnersExtractor.Configuration
-{
- public class PostConfigureKeyVaultSettings : IPostConfigureOptions where T : class
- {
- private static readonly Regex secretRegex = new Regex(@"(?https://[a-zA-Z0-9-]+\.vault\.azure\.net)/secrets/(?.*)", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
- private readonly ILogger logger;
- private readonly ISecretClientProvider secretClientProvider;
-
- public PostConfigureKeyVaultSettings(ILogger> logger, ISecretClientProvider secretClientProvider)
- {
- this.logger = logger;
- this.secretClientProvider = secretClientProvider;
- }
-
- public void PostConfigure(string name, T options)
- {
- var stringProperties = typeof(T)
- .GetProperties()
- .Where(x => x.PropertyType == typeof(string));
-
- foreach (var property in stringProperties)
- {
- var value = (string)property.GetValue(options);
-
- if (value != null)
- {
- var match = secretRegex.Match(value);
-
- if (match.Success)
- {
- var vaultUrl = match.Groups["vault"].Value;
- var secretName = match.Groups["secret"].Value;
-
- try
- {
- var secretClient = this.secretClientProvider.GetSecretClient(new Uri(vaultUrl));
- this.logger.LogInformation("Replacing setting property {PropertyName} with value from secret {SecretUrl}", property.Name, value);
-
- var response = secretClient.GetSecret(secretName);
- var secret = response.Value;
-
- property.SetValue(options, secret.Value);
- }
- catch (Exception exception)
- {
- this.logger.LogError(exception, "Unable to read secret {SecretName} from vault {VaultUrl}", secretName, vaultUrl);
- }
- }
- }
- }
- }
- }
-}
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/SecretClientProvider.cs b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/SecretClientProvider.cs
deleted file mode 100644
index 7cb566f8275..00000000000
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Configuration/SecretClientProvider.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-
-using Azure.Core;
-using Azure.Security.KeyVault.Secrets;
-
-namespace Azure.Sdk.Tools.PipelineOwnersExtractor.Configuration
-{
- public class SecretClientProvider : ISecretClientProvider
- {
- private readonly TokenCredential tokenCredential;
-
- public SecretClientProvider(TokenCredential tokenCredential)
- {
- this.tokenCredential = tokenCredential;
- }
-
- public SecretClient GetSecretClient(Uri vaultUri)
- {
- return new SecretClient(vaultUri, this.tokenCredential);
- }
- }
-}
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Processor.cs b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Processor.cs
index b096740c922..7ff53acb4cc 100644
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Processor.cs
+++ b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Processor.cs
@@ -14,7 +14,6 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.TeamFoundation.Build.WebApi;
-using Models.OpenSourcePortal;
using Newtonsoft.Json;
namespace Azure.Sdk.Tools.PipelineOwnersExtractor
@@ -76,17 +75,9 @@ await File.WriteAllTextAsync(
IEnumerable pipelines,
Dictionary> codeownersEntriesByRepository)
{
- UserLink[] linkedGithubUsers = await githubToAadResolver.GetPeopleLinksAsync();
-
- Dictionary microsoftAliasMap = linkedGithubUsers.ToDictionary(
- x => x.GitHub.Login,
- x => x.Aad.UserPrincipalName,
- StringComparer.OrdinalIgnoreCase);
-
- List<(BuildDefinition Pipeline, List Owners)> microsoftPipelineOwners =
- new List<(BuildDefinition Pipeline, List Owners)>();
+ List<(BuildDefinition Pipeline, List Owners)> microsoftPipelineOwners = new ();
- HashSet unrecognizedGitHubAliases = new HashSet();
+ HashSet unrecognizedGitHubAliases = new ();
foreach (BuildDefinition pipeline in pipelines)
{
@@ -135,7 +126,8 @@ await File.WriteAllTextAsync(
foreach (string githubOwner in githubOwners)
{
- if (microsoftAliasMap.TryGetValue(githubOwner, out string microsoftOwner))
+ string microsoftOwner = await this.githubToAadResolver.GetUserPrincipalNameFromGithubAsync(githubOwner);
+ if (!string.IsNullOrEmpty(microsoftOwner))
{
microsoftOwners.Add(microsoftOwner);
}
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Program.cs b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Program.cs
index 6ccafe9a312..42eb8ac41a9 100644
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Program.cs
+++ b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/Program.cs
@@ -12,7 +12,7 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using Microsoft.VisualStudio.Services.Common;
+using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.WebApi;
namespace Azure.Sdk.Tools.PipelineOwnersExtractor
@@ -28,13 +28,8 @@ public static async Task Main(string[] args)
.UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.ConfigureServices((context, services) =>
{
- services.AddSingleton(
- DefaultAzureCredentialWithoutManagedIdentity);
- services.AddSingleton();
+ services.AddSingleton(BuildAzureCredential);
services.Configure(context.Configuration);
- services
- .AddSingleton,
- PostConfigureKeyVaultSettings>();
services.AddSingleton();
services.AddSingleton(CreateGithubAadConverter);
services.AddSingleton(CreateAzureDevOpsService);
@@ -48,45 +43,21 @@ public static async Task Main(string[] args)
}
///
- /// Instead of using DefaultAzureCredential [1] we use ChainedTokenCredential [2] which works
- /// as DefaultAzureCredential, but most importantly, it excludes ManagedIdentityCredential.
- /// We do so because there is an undesired managed identity available when we run this
- /// code in CI/CD pipelines, which takes priority over the desired AzureCliCredential coming
- /// from the calling AzureCLI@2 task.
- ///
- /// Besides, the returned ChainedTokenCredential also excludes following credentials:
- ///
- /// - SharedTokenCredential, as it appears to fail on linux with following error:
- /// SharedTokenCacheCredential authentication failed: Persistence check failed. Data was written but it could not be read. Possible cause: on Linux, LibSecret is installed but D-Bus isn't running because it cannot be started over SSH.
- ///
- /// - VisualStudioCodeCredential, as it doesn't work, as explained here:
- /// https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#defaultazurecredential
- ///
- /// The remaining credentials are in the same order as in DefaultAzureCredential.
- ///
- /// For debugging aids helping determine which credential is used and how,
- /// please see the following tags in azure-sdk-tools repo:
- /// - kojamroz_debug_aid_default_azure_credentials
- /// Code from @hallipr showing how to get credential data using Microsoft Graph and JwtSecurityToken
- /// - kojamroz_debug_aid_diag_log_on_creds
- /// Code from kojamroz showing how to use Azure.Identity diagnostic output to get information on which
- /// credential ends up being in use (additional flags must be set to see the full info [3])
- ///
+ /// Build a TokenCredential supporting and
+ ///
+ ///
/// Full context provided here, on internal Azure SDK Engineering System Teams channel:
/// https://teams.microsoft.com/l/message/19:59dbfadafb5e41c4890e2cd3d74cc7ba@thread.skype/1675713800408?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47&groupId=3e17dcb0-4257-4a30-b843-77f47f1d4121&parentMessageId=1675713800408&teamName=Azure%20SDK&channelName=Engineering%20System%20%F0%9F%9B%A0%EF%B8%8F&createdTime=1675713800408
///
- /// [1] https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#defaultazurecredential
- /// [2] https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#define-a-custom-authentication-flow-with-chainedtokencredential
- /// [3] https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/README.md#logging
- ///
- private static Func DefaultAzureCredentialWithoutManagedIdentity
- => _
- => new ChainedTokenCredential(
- new EnvironmentCredential(),
- new VisualStudioCredential(),
- new AzureCliCredential(),
- new AzurePowerShellCredential(),
- new InteractiveBrowserCredential());
+ /// [1] https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#define-a-custom-authentication-flow-with-chainedtokencredential
+ /// [2] https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/README.md#logging
+ ///
+ private static TokenCredential BuildAzureCredential(IServiceProvider provider) {
+ return new ChainedTokenCredential(
+ new AzureCliCredential(),
+ new AzurePowerShellCredential()
+ );
+ }
private static AzureDevOpsService CreateAzureDevOpsService(IServiceProvider provider)
{
@@ -94,8 +65,10 @@ private static AzureDevOpsService CreateAzureDevOpsService(IServiceProvider prov
var settings = provider.GetRequiredService>().Value;
var uri = new Uri($"https://dev.azure.com/{settings.Account}");
- var credentials = new VssBasicCredential("pat", settings.AzureDevOpsPat);
- var connection = new VssConnection(uri, credentials);
+
+ var azureCredential = provider.GetRequiredService();
+ var devopsCredential = new VssAzureIdentityCredential(azureCredential);
+ var connection = new VssConnection(uri, devopsCredential);
return new AzureDevOpsService(connection, logger);
}
@@ -103,14 +76,9 @@ private static AzureDevOpsService CreateAzureDevOpsService(IServiceProvider prov
private static GitHubToAADConverter CreateGithubAadConverter(IServiceProvider provider)
{
var logger = provider.GetRequiredService>();
- var settings = provider.GetRequiredService>().Value;
-
- var credential = new ClientSecretCredential(
- settings.OpenSourceAadTenantId,
- settings.OpenSourceAadAppId,
- settings.OpenSourceAadSecret);
+ var azureCredential = provider.GetRequiredService();
- return new GitHubToAADConverter(credential, logger);
+ return new GitHubToAADConverter(azureCredential, logger);
}
}
}
diff --git a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/appsettings.json b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/appsettings.json
index c5bd54b8af4..8982ece9d56 100644
--- a/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/appsettings.json
+++ b/tools/pipeline-owners-extractor/Azure.Sdk.Tools.PipelineOwnersExtractor/appsettings.json
@@ -9,9 +9,5 @@
},
"Account": "azure-sdk",
"Projects": "internal,public",
- "OpenSourceAadAppId": "https://AzureSDKEngKeyVault.vault.azure.net/secrets/opensource-aad-app-id",
- "OpenSourceAadSecret": "https://AzureSDKEngKeyVault.vault.azure.net/secrets/opensource-aad-secret",
- "OpenSourceAadTenantId": "https://AzureSDKEngKeyVault.vault.azure.net/secrets/opensource-aad-tenant-id",
- "AzureDevOpsPat": "https://AzureSDKEngKeyVault.vault.azure.net/secrets/azure-sdk-notification-tools-pat",
"Output": "pipelineOwners.json"
}