Beispiel #1
0
        /// <summary>
        /// Retrieves github<->ms mapping information given the full name of an employee.
        /// </summary>
        /// <param name="aadAppIdVar">AAD App ID environment variable name (Kusto access)</param>
        /// <param name="aadAppSecretVar">AAD App Secret environment variable name (Kusto access)</param>
        /// <param name="aadTenantVar">AAD Tenant environment variable name (Kusto access)</param>
        /// <param name="kustoUrlVar">Kusto URL environment variable name</param>
        /// <param name="kustoDatabaseVar">Kusto DB environment variable name</param>
        /// <param name="kustoTableVar">Kusto Table environment variable name</param>
        /// <param name="identity">The full name of the employee</param>
        /// <param name="targetvar">The name of DevOps output variable</param>
        /// <returns></returns>
        public static async Task Main(
            string aadAppIdVar,
            string aadAppSecretVar,
            string aadTenantVar,
            string kustoUrlVar,
            string kustoDatabaseVar,
            string kustoTableVar,
            string identity,
            string targetvar
            )
        {
#pragma warning disable CS0618 // Type or member is obsolete
            using (var loggerFactory = LoggerFactory.Create(builder => {
                builder.AddConsole(config => { config.IncludeScopes = true; });
            }))
#pragma warning restore CS0618 // Type or member is obsolete
            {
                try
                {
                    var githubNameResolver = new GitHubNameResolver(
                        Environment.GetEnvironmentVariable(aadAppIdVar),
                        Environment.GetEnvironmentVariable(aadAppSecretVar),
                        Environment.GetEnvironmentVariable(aadTenantVar),
                        Environment.GetEnvironmentVariable(kustoUrlVar),
                        Environment.GetEnvironmentVariable(kustoDatabaseVar),
                        Environment.GetEnvironmentVariable(kustoTableVar),
                        loggerFactory.CreateLogger <GitHubNameResolver>()
                        );

                    var result = await githubNameResolver.GetMappingInformationFromAADName(identity);

                    if (!String.IsNullOrEmpty(targetvar))
                    {
                        Console.WriteLine(String.Format("##vso[task.setvariable variable={0};]{1}", targetvar, result.GithubUserName));
                    }
                    Console.WriteLine(JsonConvert.SerializeObject(result));
                }
                catch (Exception e)
                {
                    if (!String.IsNullOrEmpty(targetvar))
                    {
                        Console.WriteLine(String.Format("##vso[task.setvariable variable={0};]{1}", targetvar, ""));
                    }

                    throw e;
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Retrieves github-to-ms mapping information given the full name of an employee.
        /// </summary>
        /// <param name="aadAppIdVar">AAD App ID environment variable name (Kusto access)</param>
        /// <param name="aadAppSecretVar">AAD App Secret environment variable name (Kusto access)</param>
        /// <param name="aadTenantVar">AAD Tenant environment variable name (Kusto access)</param>
        /// <param name="kustoUrlVar">Kusto URL environment variable name</param>
        /// <param name="kustoDatabaseVar">Kusto DB environment variable name</param>
        /// <param name="kustoTableVar">Kusto Table environment variable name</param>
        /// <param name="identityName">The full name of the employee</param>
        /// <param name="identityEmail">The email of the employee like [email protected]</param>
        /// <param name="targetvar">The name of DevOps output variable</param>
        /// <returns></returns>
        public static async Task Main(
            string aadAppIdVar,
            string aadAppSecretVar,
            string aadTenantVar,
            string kustoUrlVar,
            string kustoDatabaseVar,
            string kustoTableVar,
            string identityName,
            string identityEmail,
            string targetvar
            )
        {
#pragma warning disable CS0618 // Type or member is obsolete
            using (var loggerFactory = LoggerFactory.Create(builder => {
                builder.AddConsole(config => { config.IncludeScopes = true; });
            }))
#pragma warning restore CS0618 // Type or member is obsolete
            {
                try
                {
                    var githubNameResolver = new GitHubNameResolver(
                        Environment.GetEnvironmentVariable(aadAppIdVar),
                        Environment.GetEnvironmentVariable(aadAppSecretVar),
                        Environment.GetEnvironmentVariable(aadTenantVar),
                        Environment.GetEnvironmentVariable(kustoUrlVar),
                        Environment.GetEnvironmentVariable(kustoDatabaseVar),
                        Environment.GetEnvironmentVariable(kustoTableVar),
                        loggerFactory.CreateLogger <GitHubNameResolver>()
                        );

                    if (identityName == null && identityEmail == null)
                    {
                        Colorizer.WriteLine("You must specify either [Yellow!identityName] or [Yellow!identityEmail].");
                        return;
                    }

                    var result = default(IdentityDetail);

                    if (identityName != null)
                    {
                        result = await githubNameResolver.GetMappingInformationFromAADName(identityName);
                    }

                    if (result == default(IdentityDetail) && identityEmail != null)
                    {
                        result = await githubNameResolver.GetMappingInformationFromAADUpn(identityEmail);
                    }

                    if (!String.IsNullOrEmpty(targetvar))
                    {
                        Console.WriteLine(String.Format("##vso[task.setvariable variable={0};]{1}", targetvar, result.GithubUserName));
                    }
                    if (result != default(IdentityDetail))
                    {
                        Colorizer.WriteLine("[Green!User resolved successfully.]");
                        Console.WriteLine(JsonConvert.SerializeObject(result));
                    }
                }
                catch (Exception)
                {
                    if (!String.IsNullOrEmpty(targetvar))
                    {
                        Console.WriteLine(String.Format("##vso[task.setvariable variable={0};]{1}", targetvar, ""));
                    }
                    Colorizer.WriteLine("Ensure that a valid [Yellow!identityName] or [Yellow!identityEmail] has been specified.");
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Synchronizes CODEOWNERS contacts to appropriate DevOps groups
        /// </summary>
        /// <param name="organization">Azure DevOps organization name</param>
        /// <param name="project">Azure DevOps project name</param>
        /// <param name="devOpsTokenVar">Personal Access Token environment variable name</param>
        /// <param name="aadAppIdVar">AAD App ID environment variable name (Kusto access)</param>
        /// <param name="aadAppSecretVar">AAD App Secret environment variable name (Kusto access)</param>
        /// <param name="aadTenantVar">AAD Tenant environment variable name (Kusto access)</param>
        /// <param name="kustoUrlVar">Kusto URL environment variable name</param>
        /// <param name="kustoDatabaseVar">Kusto DB environment variable name</param>
        /// <param name="kustoTableVar">Kusto Table environment variable name</param>
        /// <param name="pathPrefix">Azure DevOps path prefix (e.g. "\net")</param>
        /// <param name="dryRun">Do not persist changes</param>
        /// <returns></returns>
        public static async Task Main(
            string organization,
            string project,
            string devOpsTokenVar,
            string aadAppIdVar,
            string aadAppSecretVar,
            string aadTenantVar,
            string kustoUrlVar,
            string kustoDatabaseVar,
            string kustoTableVar,
            string pathPrefix = "",
            bool dryRun       = false
            )
        {
#pragma warning disable CS0618 // Type or member is obsolete
            using (var loggerFactory = LoggerFactory.Create(builder => {
                builder.AddConsole(config => { config.IncludeScopes = true; });
            }))
#pragma warning restore CS0618 // Type or member is obsolete
            {
                var devOpsService = AzureDevOpsService.CreateAzureDevOpsService(
                    Environment.GetEnvironmentVariable(devOpsTokenVar),
                    $"https://dev.azure.com/{organization}/",
                    loggerFactory.CreateLogger <AzureDevOpsService>()
                    );

                var gitHubServiceLogger = loggerFactory.CreateLogger <GitHubService>();
                var gitHubService       = new GitHubService(gitHubServiceLogger);

                var githubNameResolver = new GitHubNameResolver(
                    Environment.GetEnvironmentVariable(aadAppIdVar),
                    Environment.GetEnvironmentVariable(aadAppSecretVar),
                    Environment.GetEnvironmentVariable(aadTenantVar),
                    Environment.GetEnvironmentVariable(kustoUrlVar),
                    Environment.GetEnvironmentVariable(kustoDatabaseVar),
                    Environment.GetEnvironmentVariable(kustoTableVar),
                    loggerFactory.CreateLogger <GitHubNameResolver>()
                    );

                var logger    = loggerFactory.CreateLogger <Program>();
                var pipelines = (await devOpsService.GetPipelinesAsync(project)).ToDictionary(p => p.Id);

                var pipelineGroupTasks = (await devOpsService.GetAllTeamsAsync(project))
                                         .Where(team =>
                                                YamlHelper.Deserialize <TeamMetadata>(team.Description, swallowExceptions: true)?.Purpose == TeamPurpose.SynchronizedNotificationTeam
                                                ).Select(async team =>
                {
                    BuildDefinition pipeline;
                    var pipelineId = YamlHelper.Deserialize <TeamMetadata>(team.Description).PipelineId;

                    if (!pipelines.ContainsKey(pipelineId))
                    {
                        pipeline = await devOpsService.GetPipelineAsync(project, pipelineId);
                    }
                    else
                    {
                        pipeline = pipelines[pipelineId];
                    }

                    if (pipeline == default)
                    {
                        logger.LogWarning($"Could not find pipeline with id {pipelineId} referenced by team {team.Name}.");
                    }

                    return(new
                    {
                        Pipeline = pipeline,
                        Team = team
                    });
                });

                var pipelineGroups = await Task.WhenAll(pipelineGroupTasks);

                var filteredGroups = pipelineGroups.Where(group => group.Pipeline != default && group.Pipeline.Path.StartsWith(pathPrefix));

                foreach (var group in filteredGroups)
                {
                    using (logger.BeginScope("Team Name = {0}", group.Team.Name))
                    {
                        if (group.Pipeline.Process.Type != PipelineYamlProcessType)
                        {
                            continue;
                        }

                        // Get contents of CODEOWNERS
                        logger.LogInformation("Fetching CODEOWNERS file");
                        var managementUrl    = new Uri(group.Pipeline.Repository.Properties["manageUrl"]);
                        var codeOwnerEntries = await gitHubService.GetCodeownersFile(managementUrl);

                        if (codeOwnerEntries == default)
                        {
                            logger.LogInformation("CODEOWNERS file not found, skipping sync");
                            continue;
                        }

                        var process = group.Pipeline.Process as YamlProcess;

                        logger.LogInformation("Searching CODEOWNERS for matching path for {0}", process.YamlFilename);
                        var codeOwnerEntry = CodeOwnersFile.FindOwnersForClosestMatch(codeOwnerEntries, process.YamlFilename);
                        codeOwnerEntry.FilterOutNonUserAliases();

                        logger.LogInformation("Matching Contacts Path = {0}, NumContacts = {1}", process.YamlFilename, codeOwnerEntry.Owners.Count);

                        // Get set of team members in the CODEOWNERS file
                        var contactResolutionTasks = codeOwnerEntry.Owners
                                                     .Select(contact => githubNameResolver.GetInternalUserPrincipal(contact));
                        var codeownerPrincipals = await Task.WhenAll(contactResolutionTasks);

                        var codeownersDescriptorsTasks = codeownerPrincipals
                                                         .Where(userPrincipal => !string.IsNullOrEmpty(userPrincipal))
                                                         .Select(userPrincipal => devOpsService.GetDescriptorForPrincipal(userPrincipal));
                        var codeownersDescriptors = await Task.WhenAll(codeownersDescriptorsTasks);

                        var codeownersSet = new HashSet <string>(codeownersDescriptors);

                        // Get set of existing team members
                        var teamMembers = await devOpsService.GetMembersAsync(group.Team);

                        var teamContactTasks = teamMembers
                                               .Select(async member => await devOpsService.GetUserFromId(new Guid(member.Identity.Id)));
                        var teamContacts = await Task.WhenAll(teamContactTasks);

                        var teamDescriptors = teamContacts.Select(contact => contact.SubjectDescriptor.ToString());
                        var teamSet         = new HashSet <string>(teamDescriptors);

                        // Synchronize contacts
                        var contactsToRemove = teamSet.Except(codeownersSet);
                        var contactsToAdd    = codeownersSet.Except(teamSet);

                        var teamDescriptor = await devOpsService.GetDescriptorAsync(group.Team.Id);

                        foreach (var descriptor in contactsToRemove)
                        {
                            logger.LogInformation("Delete Contact TeamDescriptor = {0}, ContactDescriptor = {1}", teamDescriptor, descriptor);
                            if (!dryRun)
                            {
                                await devOpsService.RemoveMember(teamDescriptor, descriptor);
                            }
                        }

                        foreach (var descriptor in contactsToAdd)
                        {
                            logger.LogInformation("Add Contact TeamDescriptor = {0}, ContactDescriptor = {1}", teamDescriptor, descriptor);
                            if (!dryRun)
                            {
                                await devOpsService.AddToTeamAsync(teamDescriptor, descriptor);
                            }
                        }
                    }
                }
            }
        }