utilities/tools/AzureDevOps/Register-AzureDevOpsPipeline.ps1 (184 lines of code) (raw):

<# .SYNOPSIS Register or update all specified DevOps pipelines in a target DevOps Project .DESCRIPTION Register or update all specified DevOps pipelines in a target DevOps Project If this scripts is run within an Azure pipeline the environment variable AZURE_DEVOPS_EXT_PAT needs to be set with $(System.AccessToken) within your pipeline. Since tty is not supported within a pipelune run, az devops login is using the token which is set via AZURE_DEVOPS_EXT_PAT. .REQUIREMENTS - Azure CLI 2.13.0 - Azure CLI extension devops 0.18.0 - Repository for which the pipeline needs to be configured. - The '<ProjectName>' Build Service needs 'Edit build pipeline' permissions Reference: https://learn.microsoft.com/en-us/azure/devops/pipelines/policies/permissions?view=azure-devops#pipeline-permissions The script can be run as often as you want without breaking anything. Pipelines that already exist will be skipped. .PARAMETER OrganizationName Required. The name of the Azure DevOps organization. .PARAMETER ProjectName Required. The name of the Azure DevOps project. .PARAMETER SourceRepository Optional. The name of the source repository. Defaults to 'Azure/ResourceModules' .PARAMETER SourceRepositoryType Optional. The type of source repository. Either 'GitHub' or 'tfsgit' (for Azure DevOps). Defaults to 'GitHub'. .PARAMETER GitHubServiceConnectionName Optional. The pre-created service connection to the GitHub source repository if the pipeline files are in GitHub. It is recommended to create the service connection using oAuth. .PARAMETER AzureDevOpsPAT Required. The access token with appropriate permissions to create Azure Pipelines. Usually the System.AccessToken from an Azure Pipeline instance run has sufficient permissions as well. Reference: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#how-do-i-determine-the-job-authorization-scope-of-my-yaml-pipeline Needs at least the permissions: - Agent Pool: Read - Build: Read & execute - Service Connections: Read & query .PARAMETER BranchName Optional. Branch name for which the pipelines will be configured. Default to 'main'. .PARAMETER PipelineTargetPath Optional. Folder path in which the pipelines are created. Defaults to 'Modules' .PARAMETER RelativePipelinePath Optional. The relative local path to the folder with the pipeline YAML files. Make sure your workspace is opened in the repository root. Defaults to '.azuredevops/modulePipelines'. .PARAMETER CreateBuildValidation Optional. Create an additional pull request build validation rule for the pipelines. .EXAMPLE $inputObject = @{ OrganizationName = 'Contoso' ProjectName = 'CICD' SourceRepository = 'Azure/ResourceModules' AzureDevOpsPAT = '<Placeholder>' } Register-AzureDevOpsPipeline @inputObject Registers all pipelines in the default path in the DevOps project [Contoso/CICD] by leveraging the given AzureDevOpsPAT and using a pre-created service connection to GitHub .EXAMPLE $inputObject = @{ OrganizationName = 'Contoso' ProjectName = 'CICD' SourceRepositoryType = 'tfsgit' SourceRepository = 'CICD-DefaultRepo' AzureDevOpsPAT = '<Placeholder>' } Register-AzureDevOpsPipeline @inputObject Register all pipelines in a DevOps repository 'CICD-DefaultRepo' of project [Contoso/CICD] with default values in a the target project .NOTES You'll need the 'azure-devops' extension to run this function: `az extension add --upgrade -n azure-devops` The steps you'd want to follow are - (if pipelines are in GitHub) Create a service connection to the target GitHub repository using e.g. oAuth - Create a PAT token for the Azure DevOps environment in which you want to register the pipelines in - Run this script with the corresponding input parameters - Create any required element required to execute the pipelines. For example: - Library group(s) used in the pipeline(s) - Service connection(s) used in the pipeline(s) - Agent pool(s) used in the pipeline(s) if not using the default available agents #> function Register-AzureDevOpsPipeline { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $OrganizationName, [Parameter(Mandatory = $true)] [string] $ProjectName, [Parameter(Mandatory = $true)] [string] $AzureDevOpsPAT, [Parameter(Mandatory = $false)] [string] $SourceRepository = 'Azure/ResourceModules', [Parameter(Mandatory = $false)] [ValidateSet('gitHub', 'tfsgit')] [string] $SourceRepositoryType = 'gitHub', [Parameter(Mandatory = $false)] [string] $GitHubServiceConnectionName = $SourceRepository, [Parameter(Mandatory = $false)] [string] $BranchName = 'main', [Parameter(Mandatory = $false)] [string] $PipelineTargetPath = 'Modules', [Parameter(Mandatory = $false)] [string] $RelativePipelinePath = '.azuredevops/modulePipelines', [Parameter(Mandatory = $false)] [bool] $CreateBuildValidation = $false ) Write-Verbose '##############' Write-Verbose '# Local Data #' Write-Verbose '##############' Write-Verbose 'Identify relevant Azure Pipelines to be updated' $localPipelinePaths = (Get-ChildItem -Path $RelativePipelinePath -Recurse -Filter '*.yml').FullName Write-Verbose ('Found [{0}] local Pipeline(s) in folder path [{1}]' -f $localPipelinePaths.Count, $RelativePipelinePath) $pipelinesArray = @() foreach ($localPipelinePath in $localPipelinePaths) { $line = (Get-Content -Path $localPipelinePath)[0] $pipelineName = ($line -split 'name:')[1].Replace("'", '').Trim() $pipelinesArray += @{ ProjectName = $ProjectName SourceRepository = $SourceRepository BranchName = $BranchName FolderPath = $PipelineTargetPath ymlPath = Join-Path $relativePipelinePath (Split-Path $localPipelinePath -Leaf) pipelineName = $pipelineName } } Write-Verbose '###############' Write-Verbose '# Remote Data #' Write-Verbose '###############' Write-Verbose "Trying to login to Azure DevOps project $OrganizationName/$ProjectName with a PAT" $orgUrl = "https://dev.azure.com/$OrganizationName/" $AzureDevOpsPAT | az devops login Write-Verbose "Set default Azure DevOps configuration to $OrganizationName and $ProjectName" az devops configure --defaults organization=$orgUrl project=$ProjectName --use-git-aliases $true Write-Verbose "Get and list all Azure Pipelines in $PipelineTargetPath" $azurePipelines = az pipelines list --organization $orgUrl --project $ProjectName --folder-path $PipelineTargetPath | ConvertFrom-Json | Sort-Object name Write-Verbose ('Found [{0}] Azure Pipeline(s) in project [{1}]' -f $azurePipelines.Count, $ProjectName) Write-Verbose '############' Write-Verbose '# Evaluate #' Write-Verbose '############' [array] $pipelinesToBeSkipped = $pipelinesArray | Where-Object { $_.pipelineName -in $azurePipelines.name } Write-Verbose ('[{0}] Pipeline(s) will be skipped' -f $pipelinesToBeSkipped.Count) [array] $pipelinesToBeUpdated = $pipelinesArray | Where-Object { $_.pipelineName -notin $azurePipelines.name } Write-Verbose ('[{0}] Pipeline(s) have been identified to be updated' -f $pipelinesToBeUpdated.Count) Write-Verbose '##############' Write-Verbose '# Processing #' Write-Verbose '##############' if ($SourceRepositoryType -eq 'GitHub') { $azureDevOpsToGitHubConnection = az devops service-endpoint list -o 'Json' | ConvertFrom-Json | Where-Object { $_.Name -eq $GitHubServiceConnectionName } } Write-Verbose '----------------------------------' foreach ($pipeline in $pipelinesToBeUpdated) { Write-Verbose ('Create Azure pipeline [{0}]' -f $pipeline.pipelineName) $inputObject = @( '--repository', $pipeline.SourceRepository, '--repository-type', $SourceRepositoryType, '--branch', $pipeline.BranchName, '--folder-path', $pipeline.FolderPath, '--name', $pipeline.pipelineName, '--yml-path', $pipeline.ymlPath.Replace('\', '/'), '--skip-run' ) if ($SourceRepositoryType -eq 'GitHub') { $inputObject += @('--service-connection', $azureDevOpsToGitHubConnection.id) } if ($PSCmdlet.ShouldProcess(('Azure DevOps pipeline [{0}]' -f $pipeline.pipelineName), 'Create')) { $pipelineresult = az pipelines create @inputObject $pipelineobject = $pipelineresult | ConvertFrom-Json } if ($createBuildValidation) { $AzureDevOpsPAThFilter = $pipeline.ymlpath -replace 'pipeline.yml', '*' Write-Verbose ('Configuring build validation rule for pipeline [{0}]' -f $pipeline.pipelineName) $inputObject = @( '--blocking', $true, '--branch', $BranchName, '--build-definition-id', $pipelineobject.id, '--display-name', 'Check {0}' -f $pipeline.pipelineName, '--manual-queue-only', $true, '--queue-on-source-update-only', $true, '--valid-duration', 1440, '--path-filter', $AzureDevOpsPAThFilter, '--repository-id', $pipelineobject.repository.id, '--enabled', $true ) if ($PSCmdlet.ShouldProcess(('Mandatory repository build policy [Check {0}] for pipeline [{0}]' -f $pipeline.pipelineName), 'Create')) { az repos policy build create @inputObject } } } }