HowTo - Automate Azure DevOps Project Creation

TL;DR

My aim with this post is to demonstrate and share a PowerShell script I developed for my team to minimize the time required to set up CI pipelines and other objects in an Azure DevOps project.
The script is available here : AzureDevopsProjectCreator/AzureDevopsProjectCreator.ps1 at main · jeev20/AzureDevopsProjectCreator · GitHub


Background

Back in 2020, UiPath launched an Azure DevOps integration, which is now available in its 5th iteration in the Azure DevOps marketplace UiPath Azure DevOps integration. We have been using this integration since the early days and would like to thank @ThomasStocker, @alexandru.iordan and their teams for the great work done on developing and maintaining this integration.

With time we observed that most of our developers treaded working with CI/CD merely because it took a lot of time setting things up for each project in Azure DevOps. That when this script came to life.

Prerequisites

In order to use the Azure CLI and the Azure DevOps extension, we need to install the following. The first link downloads the latest Azure CLI and the second downloads the dotnet runtime.

  1. https://aka.ms/installazurecliwindows
  2. Download .NET 5.0 Desktop Runtime (v5.0.17) - Windows x64 Installer

A visual overview of the script

graph LR
    subgraph Initialize
        direction TB
        B[Set Arguments]
        C[Set Environment Variables]
        B --> C

        C --> E[Configure Azure CLI \n Auto download modules]
    end

    subgraph CreateProject_Function
        direction TB
        
 
        G[Set Default Configuration \n Login to Azure DevOps]
        H[Create Project]
        I[Create Variable Group]
        J[Create Repositories]
        K[Create Build Pipelines]
        L[Set Pipeline Variables]
        M[Delete Orphan Repository]
        G --> H --> I --> J --> K --> L --> M
    end

    subgraph Invoke
        direction TB
        T[CreateProject_Function]
    end

    Initialize --> CreateProject_Function
    CreateProject_Function --> Invoke

Initialize

Certain basic arguments are needed to get us started.
$ProjectName is the visual name of the project,
$Description is a short string describing the project,
$PAT is the Personal Access Token obtained from your Azure Devops (User Settings)
$Organization is the URL of the Azure DevOps organization.

Note that $PAT needs to have relevant rights to create projects and other resources within a project.

The az config set extension.use_dynamic_install=yes_without_prompt tells the Azure CLI to install necessary extensions (in this case Azure DevOps is an extension in Azure CLI) if it is not found in the host machine.

param(
    [Parameter(Mandatory=$true)]
    [string]$ProjectName,

    [Parameter(Mandatory=$true)]
    [string]$Description,

    [Parameter(Mandatory=$true)]
    [string]$PAT,

    [Parameter(Mandatory=$true)]
    [string]$Organization
)

$env:AZURE_DEVOPS_EXT_PAT = $PAT
$ErrorActionPreference = 'SilentlyContinue'

# Configuration
az config set extension.use_dynamic_install=yes_without_prompt  

Defining the function

function CreateProject{
    # Set Default Configuration
    az devops configure -d organization="https://dev.azure.com/organizationName"
    az devops configure -d project=$ProjectName

    # Login to Azure DevOps
    Write-Output $env:AZURE_DEVOPS_EXT_PAT | az devops login 

    # Create Project
    az devops project create --name $ProjectName  --description $Description --process Basic --source-control  git --output table

    # Creating group library variables 
    az pipelines variable-group create --name BackupLocations  --variables RPA-Robot-Dev="PATHTOFOLDER" RPA-Robot-Prod="PATHTOFOLDER"

    # Creating repos within the project
    az repos create --name "Dispatcher" 
    az repos create --name "Performer" 
    az repos create --name "Utils" 

    # Creating Build Pipelines within the project - path can be specified accordingly
    az pipelines create --name "CI-Dispatcher" --description 'CI Pipeline for Dispatcher' --repository "Dispatcher" --branch main  --repository-type tfsgit --yml-path "AzureDevOpsPipelines/CI_Dispatcher.yml" --skip-first-run
    az pipelines create --name "CI-Performer" --description 'CI Pipeline for Performer' --repository "Performer" --branch main  --repository-type tfsgit --yml-path "AzureDevOpsPipelines/CI_Performer.yml" --skip-first-run

    # Creating Build Pipelines variables 
    az pipelines variable create --name ProjectBuildNumber --pipeline-name "CI-Dispatcher" --value "$[counter('', 1)]"
    az pipelines variable create --name ProjectBuildNumber --pipeline-name "CI-Performer" --value "$[counter('', 1)]"

    # Delete Orphan Repository
    $reposlist = az repos list | ConvertFrom-Json

    # Find the orphan repo
    foreach ($repo in $reposlist)
    {
    if ($repo.name -eq $ProjectName)
    {
        Write-Host "Deleting unwanted orphan repository"
        az repos delete --id $repo.id  --yes   # Delete the default repo and confirm
    }
    }
}

CreateProject

The part which makes this automation amazing is the way we can point a build definition (.yml file) when creating the pipeline.

--branch main --repository-type tfsgit --yml-path "AzureDevOpsPipelines/CI_Performer.yml" --skip-first-run

This means that the developer has full control over how the build task has to be executed by placing a CI_Performer.yml / CI_Dispatcher.yml in a folder AzureDevOpsPipelines when committing the UiPath project. For example, if the project being worked on is a library, the CI definition can mention that the output from the build task should be a UiPath library.

The --skip-first-run ensures that the pipeline is not triggered right after we created it and will run for the first time when the developer commits code to the repo. This also avoids running a CI pipeline when the repo has zero commits nor does it have a CI build definition (yml file). In short, if this is not set, the CI will run and fail.

Invoking the script

You could chose many ways to run the above script, the two easy ways are

  1. Using the CLI
.\AzureDevopsProjectCreator.ps1 -ProjectName "TestProject" -Description "This is a test project" -PAT "YOURPATSTRING" -organization "https://dev.azure.com/ORGANIZATION"
  1. Using UiPath’s Invoke Powershell or Start Process activities.

Screenshots showing the results

How do we automate CD?

Unfortunately, unless a CI has previously run, we cannot create a CD pipeline in Azure DevOps CLI. This means that the repo needs to have a commit for the CI to trigger and for the artifact to be ready. Therefore, we still use the GUI in Azure DevOps to setup the CD pipelines to deploy automations to the UiPath Orchestrators.

References

Questions

For questions on your specific CI/CD case, kindly open a new topic and get individual support.

1 Like