Azure DevOps Multistage Pipeline Approval Gates

Introduction

As you know we mostly used YAML for build pipelines and Classic UI approach for release pipelines until last month. It was a demand from developer community to add capability to support YAML in release pipelines. I am glad to update you all, Microsoft has recently announced(previously it was in preview mode) the multi-stage pipelines for CI/CD in order to club build, test and deploy related templates/tasks at one place i.e. in single YAML file along with environment and deployment strategy.

In order to use multi-stage pipelines you need to do certain steps i.e. activating multi-stage pipeline feature, and defining your environment(DEV, TEST, ACC and PROD). The environment would help you to incorporate all current features from Classic UI release pipelines like approval gates, invoke azure function, validating build task etc. You can refer hoe to article Converting Classic Azure DevOps Pipelines to YAML

YAML build pipeline enables developer to save pipeline as code, however multi-stage YAML pipelines provides ability to scale your pipeline to support both continuous integration (CI) and continuous deployment (CD). In this post I will explain how multi-stage pipelines works and naming conventions to create environment. Let’s start.

Get Started

Activate multi-stage pipeline support feature

1. Navigate to Azure DevOps
2. Go to User settings and click on Preview features option

3. Select the suitable option for evaluation(for me[Login user name] or Organization) and choose Multi-stage pipelines option ON

 

Create environment

1. Navigate to Pipelines => Environments

2. Click on New environment button and required information. Here we would use naming conventions to create environment as this section is common to all teams in project so expect as many environments 🙂
Lets create two environment i.e. Testing(t) and Production(p)

Naming convention: <project name>-<team name>-<environment>-<resource short name> with all characters in small 🙂
Example: github-githubteam-p-env,  github-githubteam-t-env

Set approval gates for environment

Select an environment which you want to set approval gate before deployment. In my example I will set approval gates for Production

Click on three dots at right corner and click Approval and gates option from menu

Click on + button to add checks and select Approvals. Add the approver and click on Create button

Approver list appear in your list. If you want to add more approver the simply click on row and it will render user interface in editable mode.

Setup multi-stage pipeline

Let’s assume I want to deploy my Azure DevOps Extension to marketplace. In order to deploy my extension to marketplace, I have created two publisher namely {publisher}-test and {publisher}

Now, I have my source code with brand new azure-pipelies.yml file with me(Please refer my previous post to setup your first yaml pipeline using Azure DevOps and Git Repos)

Replace/manage all code of azure-pipelies.yml with below source code where I implemented multi-stage deployment


trigger:
- master

# Link variable group
variables:
- name: Version
  value: 2.0.4
- group: Csharpdocs.AzureDevOps.Extension

# Build number format
name: $(Date:yyyyMMdd)$(Rev:.r)

stages:
  - stage: Build
    displayName: 'Build - CI'
    jobs:
    - job: BuildAndPackage
      displayName: Build & Package Extension
      pool:
        vmImage: 'windows-2019'
      workspace:
        clean: all
      
      steps:
        - template: build-artifact.yml
  
  - stage: TEST
    displayName: 'Testing - CD'
    jobs:
    - deployment: PublishExtension
      displayName: Publish extension to marketplace
      pool:
        vmImage: 'windows-2019'
      environment: github-githubteam-p-env
      strategy:
        runOnce:
          deploy:
            steps:
              - template: publish-extension.yml
      
  - stage: PROD
    displayName: 'Production - CD'
    jobs:
    - deployment: PublishExtension
      displayName: Publish extension to marketplace
      pool:
        vmImage: 'windows-2019'
      environment: github-githubteam-p-env
      strategy:
        runOnce:
          deploy:
            steps:
              - template: publish-extension.yml

In above code snippets, I have three stages namely Build(Build – CI), TEST(Testing – CD) and PROD(Production – CD). TEST and PROD has deployment stage where environment is mentioned which we created at start i.e. github-githubteam-p-env, github-githubteam-t-env. Save the code and it will trigger automated build.

The Build and TEST stages are completed and pipeline is waiting for approval

Click on Review button, it would open Approval window with Resource waiting for approval, Approve/Reject button and Instruction body created at time of setting Approver for environment. Let’s Approve it for now 🙂

It will immediately start production deployment. Pipeline successfully deployed production environment

The improved look and feel at pipelines page displays multi-stage pipeline status as

References

Create azure resource manager service connection for azure devops

Introduction:

In my previous post, I explained about creation of service principal using azure cli. In this post I’d explains creation of azure resource manager service connection for azure devops. Basically, Service connection allows to communicate with external systems like GitHub, SonarCloud, Bitbucket, Azure etc.

In order to deploy azure cloud based solution, you need azure resource manager service connection to connect to Microsoft Azure. This service connection would be used in Azure DevOps Release Pipelines to deploy application artifact(called as pipeline artifact) to target Azure service(Web App, Functions App, SQL Database etc.) or to Provision new azure resource(Virtual machine, Web App, Storage Account, AKS etc.)

Pre-requisite:

1. Azure Account with Service Principal
3. Azure DevOps Account

Get Started:

Before starting to create azure resource manager service connection for azure devops, Open the service principal details copied at time of creation.

1. Navigate to https://dev.azure.com/{Your organization name}
2. Select the Project where you want to create service connection
3. Select Project Settings
4. In Pipelines section find Service connections* and click on it


5. Click on New service connection button


6. Select Azure Resource Manager from available options and click on Next button


7. Select option Service principal(manual) and click on Next button


8. Now select correct options as shown in below snapshot and copy values from notepad and paste in text boxes


9. Click on verify button to make sure connection succeeds
10.If succeed, then enter the name of service connection with description(optional). For now allow grant a access permission to all pipelines option and click Verify and save option.


11. The new service connection would appear in your projects service connection list.

In this post, I explained how to create azure resource manager service connection. Here we allowed all pipelines to use this service connection. It is not recommended to allow all pipelines to use service connection due to security issues(as other teams can use it and ultimately you would be charged for it 🙂 )

Additional Tip to apply Service Connection Security:

If your team size is very small or you are working with smaller organization(without much processes) then I would advise you to follow below steps(without going too much in Azure DevOps in-built security groups, roles and permissions) to permit this service connection only to specific developers from other team(s).

Please note, by default, your project team has access to use service connection created by teams administrator.

1. Select the service connection where you want to permit only to limited developers


2. Click on three dots at right corner of service connection selected


3. Click on +Add button to allow other team member(s) or team(s) to use service connection. Make sure added member(s) has only “User” role to use service connection and not to administrator it.

In above snapshot, by default project team has access to use service connection(please don’t allow team role to be administrator except project administrator). I have added another teams developer to user this service connection and project administrator has administrator access to service connection. It also has endpoint administrator(Azure DevOps In-Built role as administrator access)

At Last,
In my next articles about Azure DevOps security I will explain Azure DevOps role based access security(RBAC). The series of post would explain security related to Pipelines, Repositories, Service connections etc.

Create azure service principal using azure cli commands

Introduction:

Before starting to this post please go through Azure Service Principal and Azure Resource Manager which I had written before. In yesterday’s post, I explained how to create azure service principal or app registration using azure portal user interface step by step. This post is written to explain same steps using Azure CLI commands to provision resource group and service principal creation. I would only cover first six steps from my previous post.

Pre-requisite:

1. Azure Account with Subscription
2. Your role should be Administrator

Advice:

Before starting hands on, I’d recommend you to follow azure naming conventions. I’m currently in process to write separate post on azure resources naming conventions, so be patient. At this moment, I’d advise you to go through Azure naming conventions defined by Microsoft.

In this post I’m following below naming conventions for azure resource creation.

Azure Resource Naming Convention Example
Azure Resource Manager rg-<application name>-<environment>-<short location> rg-csharpdocs-d-eus
Service Principal sp-<application name>-<environment>-<short location> sp-csharpdocs-d-eus

Get Started:

Before starting to this exercise, I’d recommend you to create notepad with below details

1. Navigate to https://portal.azure.com
2. Sign in with your credentials
3. Navigate to Azure shell in browsers new tab using https://shell.azure.com/
4. Create Azure resource group


# Create azure resource group 
az group create --name 'rg-csharpdocs-d-eus' \
                --location 'eastus' \
                --subscription 'Free Trial' \
                --tags "Environment=Deleopment" "Purpose=Demonstration" "Type=Learning" \
                --output table \


5. Verify newly created resource group


4. Now, Register application(Create Service Principal) with Azure Active Directory. The below Azure CLI command will create service principal and secret both.

It is always good to restrict service principal permission to only allow access to the minimal set of resources. Set the –role to reader instead of contributor if you only need read access. Also you can use the –scope argument to limit the scope to only allow management of a single resource group.

In following CLI Command, I am restricting service principal to contribute(read/write) permission to one azure resource group only and the expiration of service principal would be 1 year from date of creation. If you remove –years parameters then it would never expire


# Create service principal with roles, subscription, resource group
az ad sp create-for-rbac --name 'sp-csharpdocs-d-eus' \
                         --role contributor \
                         --scopes /subscriptions/{subscriptionid}/resourceGroups/rg-csharpdocs-d-eus \
                         --years 1 --output table \


5. Verify newly created service principal. Note details in notepad.


6. Note secret of newly created. This would be one time visible. Note details in notepad and keep for future reference. If you forget secret then you have to regenerate it.


7. Done!! You have successfully created service principal for your application.

Conclusion:

The Azure CLI or Azure Powershell enables you to automate azure infrastructure provisioning using Infrastructure as a Code in Azure DevOps. Azure CLI commands enables to create azure resource without any hard work, Isn’t it ??

In my next articles, I will explain how to use Infrastructure as a Code in Azure DevOps to automate azure resource provisioning. In order to provision resource from Azure DevOps you need to create Azure resource manager service connection in Azure DevOps. Hence we will use all information noted in notepad to create it.

Create azure service principal using azure portal

Introduction

In my last post, I have explained Azure Service Principal and Azure Resource Manager importance. Today, in this post I will try to walk you through to create azure service principal or app registration using azure portal user interface step by step. I would recommend you to create service principal via Azure CLI/Powershell commands but my intention here to explain this via beautiful user interface provided by Microsoft Azure so that new developer would not surprise what is happening behind the commands.

Pre-requisite:

1. Azure Account with Subscription
2. Your role should be Administrator

Advice:

Before starting hands on, I’d recommend you to follow azure naming conventions. I’m currently in process to
write separate post on azure resources naming conventions, so be patient. At this moment, I’d advise you to go through Azure naming conventions defined by Microsoft.

In this post I’m following below naming conventions for azure resource creation.

Azure Resource Naming Convention Example
Azure Resource Manager rg-<application name>-<environment>-<short location> rg-csharpdocs-d-eas
Service Principal sp-<application name>-<environment>-<short location> sp-csharpdocs-d-eas
Service Principal Secret rbac-sp-<application name>-<environment>-<short location> rbac-sp-csharpdocs-d-eas

Get Started

Before starting to this exercise, I’d recommend you to create notepad with below details

1. Navigate to https://portal.azure.com
2. Sign in with your credentials
3. Create Azure resource group
  • Go to Resource groups
  • Enter the your azure subscription, resource group name and region.
  • It is optional but always recommended to tag your azure resources with environment and product specific
  • Review and Create
  • Youre azure resource is ready to use
4. Register application(Create Service Principal) with Azure Active Directory
  • Go to Azure Active Directory. This would be your organizations active directory page
  • You would land to Default Directory | Overview of Azure Active Directory
  • Please note Tenant ID of your organization. Also verify you have administrator role.
  • Search for App Registrations and click to New registration link
  • Enter the appropriate values shown in below screenshot and click Register button
  • Application successfully registered in Azure Active Directory
  • Please note Application (client) ID, Directory (tenant) ID to notepad table created
5. Create Secret for application(Service principal)
  • Navigate to left menu bar’s Certificates & secrets link
  • Click on New client secret and add secret with expire time
  • Service principal secret generated. Please note the secret value. It would never appear again.Be default Service principal has Contributor role and only administrator can apply policy to service principal

6. Done!! You have successfully created service principal for your application.

7. Additional permissions – If you want to allow this service principal to invoke Azure Service Management, Azure Storage, Azure DevOps Rest API programmatically then follow below steps
  • Navigate to API permissions from left menu and in Configured permissions section Add a permission one by one
  • Select Add permissions and select Azure Service Management

Now, We are ready to start to create azure resources like Virtual Machines, Deploying application to App service, containers and so on. In my next post I will show you, how to create Azure resource manager service connection in Azure DevOps so that you can Deploy your application(s) or provision azure infrastructure using Azure Pipelines.

Now one question could come to your mind? Can I enable service principal to work with multiple Azure Subscriptions? then the answer is YES. (However I’d not recommend to do this to make it more complex. I like KISS[Keep It Simple Silly])In order to do that, you can perform below steps

  • Navigate to your Azure Subscriptions
  • Open the subscription which you want to enable by clicking on it
  • Click on Access Control(IAM) and click on Add button and select Add role assignment
  • Select below Role, Assign access to and search for service principle and click save button
  • Done. That’s it !!

How to trigger build pipeline from Azure Automation Runbook

Introduction

I am writing this post based on my last weeks new learning. I learnt to trigger Azure DevOps build pipeline form Azure Automation runbook.

In my previous post, I have explained step by step approach to create azure automation account and runbook. I have also explained how to reference Azure Repos and GitHub repository to automation account in order to synchronize and publish runbook as soon as developer commit code to source repository.

Pre-requisite

 Before starting to write runbook make sure you have,

  1. Azure Account and Subscription
  2. Azure Automation Account
  3. Azure DevOps Account with handy Personal Access Token(PAT) 

Let’s Get Started

1. Create Azure DevOps Repo and Pipeline

1.1.Create empty git repository say something like azure-devops-runbook-pipelines

1.2 Setup build Pipeline as a Code

Navigate to Azure DevOps project => Repos => Pipeline => New Pipeline => Azure Repos Git(Yaml) => Select repository created in step 1.1 => Starter pipeline => replace below code to azure-pipelines.yml file

trigger:
- master

variables:
  MyPipelineVaiable: "MyPipelineVaiable"

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: PowerShell@2
  displayName: 'Print Pipeline Variables'
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "--------------------------------------"
      Write-Host "Azure DevOps Pipeline Variables"
      Write-Host "--------------------------------------"
      Write-Host "Organization Name: $(OrganizationName)"
      Write-Host "Project Name: $(ProjectName)"
      Write-Host "Parameter1: $(Parameter1)"
      Write-Host "Parameter2: $(Parameter2)"
      Write-Host "MyPipelineVaiable: $(MyPipelineVaiable)"

1.3 Create below Pipeline Variables

1.4 Save and run

It would display following output

2. Create Powershell script to trigger from Azure Automation Runbook

2.1 Create automation account variable VSTSToken and paste your azure devops PAT token and keep it secret by enabling encrypting it

2.2 Create variables required for automation runbook

[CmdLetBinding()]
Param(
    # Parameters to be received from ServiceNow 
    [Parameter(Mandatory = $true)][String] $OrganizationName = "MyOrganization",
    [Parameter(Mandatory = $true)][String] $ProjectName = "MyProject",
    [Parameter(Mandatory = $true)][String] $Parameter1 = "Parameter1",
    [Parameter(Mandatory = $true)][String] $Parameter2 = "Parameter2"
)

2.2. Get encoded access token to invoke Azure DevOps REST API’s 

$authorisationType = "Basic"
$vstsToken =  Get-AutomationVariable -Name "VSTSToken"
$encodedToken = [Convert]::ToBase64String(([Text.Encoding])::ASCII.GetBytes((":{1}" -f "", $vstsToken)))

2.3 Get the build definition of pipeline created in Step 1(Create Azure DevOps Repo and Pipeline). This can be done by invoking Azure DevOps REST API for Definitions – List

$buildDefinitionApiUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/definitions?name={pipeline_name}&api-version=5.1"
[PSObject]$buildDefinitions = Invoke-RestMethod -Uri $buildDefinitionApiUrl -Method Get -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken); ContentType = ("application/json"); }  

2.4 Update the retrieved build definition to set build agent, repository and pipeline parameters to pass. This is little tough to find. in order to find request details I ran my existing pipeline and observed the ajax request and based on Azure DevOps Request Body to Update Build Definition, I prepared below object  

$updateBuildJsonContent = [PSCustomObject]@{
        "comment" = "your build definition comment goes here"
        "description" = "your build definition comment description goes here"
        "id" = "$($buildDefinitions.value[0].id)"
        "name" = "$($buildDefinitions.value[0].name)"
        "revision" = "$($buildDefinitions.value[0].revision)"
        "queue" = [PSCustomObject]@{
            "pool" = [PSCustomObject]@{
                "name" = "Hosted VS2019"
                "isHosted" = "true"
            }
        }
        "process" = [PSCustomObject]@{
            "yamlFilename" = "/azure-pipelines.yml"
            "type" = 2
        }
        "repository" = [PSCustomObject]@{
            "name" = "your_git_repository_name"
            "url" = "https://dev.azure.com/{organization}/{project}/_git/{your_git_repository_name}"
            "defaultBranch" =  "refs/heads/master"
            "type" = "TfsGit"
        }
        "variables" = [PSCustomObject]@{               
            "Parameter1"   = [PSCustomObject]@{
                                        "allowOverride" = "false"
                                        "isSecret" = "false"
                                        "value" = "$($Parameter1)"
                                    }
            "Parameter2"             = [PSCustomObject]@{
                                        "allowOverride" = "false"
                                        "isSecret" = "false"
                                        "value" = "$($Parameter2)"
                                    }            
        }

2.5 Convert the request to JSON and Invoke Azure DevOps REST API to Update Build Definition

$updateBuildJsonContent = ($updateBuildJsonContent | ConvertTo-Json -Compress -Depth 100)
    $updateBuildDefinitionUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/definitions/$($buildDefinitions.value[0].id)?api-version=5.1"
    Invoke-RestMethod -Uri $updateBuildDefinitionUrl -Method Put -Body $updateBuildJsonContent -ContentType 'application/json' -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken) } | Out-Null

2.6 Create json request body to queue build

$jsonContent = [PSCustomObject]@{
        "definition" = [PSCustomObject]@{
                                            "id" = "$($buildDefinitions.value[0].id)"
                                        }
        "repository" = [PSCustomObject]@{
                                            "name" = "your_build_pipeline_name"
                                            "url" = "https://dev.azure.com/{organization}/{project}/_git/{your_git_repository_name}"
                                            "defaultBranch" =  "refs/heads/master"
                                        }
        "reason"     = [PSCustomObject]@{
                                            "userCreated" = "User defined some comment to queue build from azure automation runbook"
                                        }
    }

 2.7 Queue Azure DevOps build using Azure DevOps REST API for Queue Build

$jsonContent = ($jsonContent | ConvertTo-Json -Compress -Depth 100)
    $queueBuildUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.1"
    $queuedBuild = Invoke-RestMethod -Uri $queueBuildUrl -Method Post -Body $jsonContent -ContentType 'application/json' -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken) } 

2.8 Check the queue status until pipeline complete execution. Status of running build pipeline can be found at Azure DevOps REST API 

$buildId = $queuedBuild.id
    $queuedBuildDetailsApiUrl = "https://dev.azure.com/{organization}/{project}/_apis/build/builds/$($buildId)?api-version=5.1"
    $automationBuildStatus = Invoke-RestMethod -Uri $queuedBuildDetailsApiUrl -Method Get -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken); ContentType = ("application/json"); }  
    While ($true) {
        Start-Sleep -Seconds 60
        $automationBuildStatus = Invoke-RestMethod -Uri $queuedBuildDetailsApiUrl -Method Get -Headers @{ Authorization = ("{0} {1}" -f $authorisationType, $encodedToken); ContentType = ("application/json"); }  
        Write-Output("MESSAGE: ...")
        If ($automationBuildStatus.status -ne 'inProgress') {
            Break
        }
    }

Check build is triggered

2.9 Save the powershell script in runbook and test it. This will display following output on console

2.10 Verify pipeline output 

Yes the output is what I expected. The variables passed from azure automation are reflected here