DEV Community

Cover image for Automate pull request & associate work-items using Az Devops
Arindam Mitra
Arindam Mitra

Posted on • Edited on

Automate pull request & associate work-items using Az Devops

Greetings my fellow Technology Advocates and Specialists.

In this Session, I will demonstrate how to Automate Pull Request (PR) and Associate Work-Items Using Azure DevOps.

I had the Privilege to talk on this topic in TWO Azure Communities:-

NAME OF THE AZURE COMMUNITY TYPE OF SPEAKER SESSION
Microsoft Azure Bern User Group In-Person
Microsoft Azure Pakistan Community Virtual
IN-PERSON SESSION:-
I presented this Demo as a part of AZURE DEVOPS: TAKEAWAYS BEST PRACTISES AND LIVE DEMOS In-Person Speaker Session in MICROSOFT AZURE BERN USER GROUP Forum/Platform.
Event Meetup Announcement:-
Image description
Moment Captured with Founders of MICROSOFT AZURE BERN USER GROUP "STEFAN JOHNER", "STEFAN ROTH", "PAUL AFFENTRANGER" and Co-organizer "DAMIEN BOWDEN":-
Image description
VIRTUAL SESSION:-
Event Meetup Announcement:-
Image description
LIVE DEMO was Recorded as part of my Presentation in MICROSOFT AZURE PAKISTAN COMMUNITY Forum/Platform
Duration of My Demo = 48 Mins 23 Secs
AUTOMATION OBJECTIVE:-
Create Random Generated Work-Items in Azure DevOps Boards.
Create Pull Request (PR).
Associate Work-Item with Pull Request (PR).
Complete Pull Request (PR) with Squash Commit.
Delete the Working Branch (For Example: "Dev" or "Feature/AM".
REQUIREMENTS:-
  1. Azure Subscription.
  2. Azure DevOps Organisation and Project.
  3. Azure DevOps Personal Access Token (PAT).
  4. Service Principal with Required RBAC ( Contributor) applied on Subscription or Resource Group(s).
  5. Azure Resource Manager Service Connection in Azure DevOps.
  6. Microsoft DevLabs Terraform Extension Installed in Azure DevOps.
CODE REPOSITORY:-

GitHub logo arindam0310018 / 20-July-2022-DevOps__Automate-Pull-Request-And-Associate-Work-Item

AUTOMATE PULL REQUEST & ASSOCIATE WORK-ITEMS USING AZ DEVOPS

AUTOMATE PULL REQUEST & ASSOCIATE WORK-ITEMS USING AZ DEVOPS

Greetings my fellow Technology Advocates and Specialists.

In this Session, I will demonstrate how to Automate Pull Request (PR) and Associate Work-Items Using Azure DevOps.

I had the Privilege to talk on this topic in TWO Azure Communities:-

NAME OF THE AZURE COMMUNITY TYPE OF SPEAKER SESSION
Microsoft Azure Bern User Group In-Person
Microsoft Azure Pakistan Community Virtual
IN-PERSON SESSION:-
I presented this Demo as a part of AZURE DEVOPS: TAKEAWAYS BEST PRACTISES AND LIVE DEMOS In-Person Speaker Session in MICROSOFT AZURE BERN USER GROUP Forum/Platform.
Event Meetup Announcement:-
Image description
Moment Captured with Founders of MICROSOFT AZURE BERN USER GROUP "STEFAN JOHNER", "STEFAN ROTH", "PAUL AFFENTRANGER" and Co-organizer "DAMIEN BOWDEN":-
Image description
VIRTUAL SESSION:-
Event Meetup Announcement:-
Image description
LIVE DEMO was Recorded as part of my Presentation in MICROSOFT AZURE PAKISTAN COMMUNITY Forum/Platform
Duration of My Demo = 48 Mins 23 Secs
IMAGE ALT TEXT HERE
AUTOMATION OBJECTIVE:-
Create
โ€ฆ
HOW DOES MY CODE PLACEHOLDER LOOKS LIKE:-
Image description
PIPELINE CODE SNIPPET:-
AZURE DEVOPS YAML PIPELINE (azure-pipelines-automate-pr-workitems-v1.0.yml):-


#####################################################
# TRIGGER CONDITION CAN BE ALTERED LIKE BELOW :-
#####################################################
# trigger:
#   branches:
#     include:
#     - feature/*
#   paths:
#     include:
#     - Automate-PR-and-Associate-WorkItems/*
#####################################################


#######################
# TRIGGER CONDITION :-
#######################
trigger: none

########################################################################
#DECLARE VARIABLES:-
# ONLY VARIABLE VALUES NEEDS TO BE ALTERED TO MAKE THE PIPELINE WORK.
########################################################################
variables:
  PlanFilename: tfplan
  TfvarFilename: usrmid.tfvars
  KV-Name: ampockv
  ServiceConnection: amcloud-cicd-service-connection
  ResourceGroup: tfpipeline-rg
  StorageAccount: tfpipelinesa
  Container: terraform
  TfstateFile: PR/createprworkitem.tfstate
  BuildAgent: ubuntu-latest
  PipelineEnv: NonProd
  Terraform_Version: 1.2.3
  WorkingDir: $(System.DefaultWorkingDirectory)/Automate-PR-and-Associate-WorkItems
  Target: $(build.artifactstagingdirectory)/AMTF
  Artifact: AM
  anyTfChanges: false
  DevOpsOrganisation: https://dev.azure.com/ArindamMitra0251
  DevOpsProjName: AMCLOUD
  DevOpsRepoName: PR
  DevOpsDestinationBranch: main

######################
#DECLARE BUILD AGENT:-
######################
pool:
  vmImage: $(BuildAgent)

###################
#DECLARE STAGES:-
###################

#################
# STAGE: BUILD
#################

stages:

- stage: BUILD
  jobs:
  - job: BUILD
    displayName: BUILD
    steps:
# Install Terraform Installer in the Build Agent:-
    - task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0
      displayName: INSTALL LATEST TERRAFORM VERSION
      inputs:
        terraformVersion: '$(Terraform_Version)'
# Terraform Init:-
    - task: TerraformTaskV2@2
      displayName: TERRAFORM INIT
      inputs:
        command: 'init'
        provider: 'azurerm'
        workingDirectory: '$(WorkingDir)'
        backendServiceArm: '$(ServiceConnection)' 
        backendAzureRmResourceGroupName: '$(ResourceGroup)' 
        backendAzureRmStorageAccountName: '$(StorageAccount)'
        backendAzureRmContainerName: '$(Container)'
        backendAzureRmKey: '$(TfstateFile)'
# Terraform Validate:-
    - task: TerraformTaskV2@2
      displayName: TERRAFORM VALIDATE
      inputs:
        command: 'validate'
        provider: 'azurerm'
        workingDirectory: '$(WorkingDir)'
        environmentServiceNameAzureRM: '$(ServiceConnection)'
# Terraform Plan:-
    - task: TerraformTaskV2@2
      displayName: TERRAFORM PLAN
      inputs:
        command: 'plan'
        provider: 'azurerm'
        workingDirectory: '$(WorkingDir)'
        commandOptions: '--var-file=$(TfvarFilename) --out=$(PlanFilename)'
        environmentServiceNameAzureRM: '$(ServiceConnection)'
# Detect Terraform Changes:-
    - task: PowerShell@2
      name: DetectTFChanges
      displayName: DETECT TERRAFORM CHANGES
      inputs:
        workingDirectory: '$(workingDir)'
        targetType: 'inline'
        script: |
          Write-Host "#######################################################"
          Write-Host "Intial value of variable: $(anyTfChanges)"
          Write-Host "#######################################################"
          $plan = $(terraform show -json tfplan | ConvertFrom-Json)
          $count = $plan.resource_changes.change.actions.length
          $actions = ($plan.resource_changes | where { 'no-op' -notcontains $_.change.actions }).length -ne 0
          Write-Host "##vso[task.setvariable variable=anyTfChanges;isOutput=true]$actions"
          Write-Host "#######################################################"
          Write-Host "Are there Changes in Infrastruture: $actions"
          Write-Host "#######################################################"
          Write-Host "TOTAL NO OF CHANGES: $count"
          Write-Host "#######################################################"
# Copy Files to Artifacts Staging Directory:-
    - task: CopyFiles@2
      displayName: COPY FILES ARTIFACTS STAGING DIRECTORY
      inputs:
        SourceFolder: '$(WorkingDir)'
        Contents: |
          **/*.tf
          **/*.tfvars
          **/*$(PlanFilename)*
        TargetFolder: '$(Target)'
# Publish Artifacts:-
    - task: PublishBuildArtifacts@1
      displayName: PUBLISH ARTIFACTS
      inputs:
        targetPath: '$(Target)'
        artifactName: '$(Artifact)'

#################
# STAGE: DEPLOY
#################

- stage: DEPLOY
  condition: |
     and(succeeded(),
       ne(variables['Build.SourceBranch'], 'refs/heads/main'),
       eq(dependencies.BUILD.outputs['build.DetectTFChanges.anyTfChanges'], 'true')
     )
  jobs:
  - deployment: 
    displayName: Deploy
    environment: '$(PipelineEnv)'
    pool:
      vmImage: '$(BuildAgent)'
    strategy:
      runOnce:
        deploy:
          steps:
# Download Artifacts:-
          - task: DownloadBuildArtifacts@0
            displayName: DOWNLOAD ARTIFACTS
            inputs:
              buildType: 'current'
              downloadType: 'single'
              artifactName: '$(Artifact)'
              downloadPath: '$(System.ArtifactsDirectory)' 
# Install Terraform Installer in the Build Agent:-
          - task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0
            displayName: INSTALL LATEST TERRAFORM VERSION
            inputs:
              terraformVersion: '$(Terraform_Version)'
# Terraform Init:-
          - task: TerraformTaskV2@2
            displayName: TERRAFORM INIT
            inputs:
              provider: 'azurerm'
              command: 'init'
              workingDirectory: '$(System.ArtifactsDirectory)/$(Artifact)/AMTF/'
              backendServiceArm: '$(ServiceConnection)' 
              backendAzureRmResourceGroupName: '$(ResourceGroup)' 
              backendAzureRmStorageAccountName: '$(StorageAccount)'
              backendAzureRmContainerName: '$(Container)'
              backendAzureRmKey: '$(TfstateFile)'
# Terraform Apply:-
          - task: TerraformTaskV2@2
            displayName: TERRAFORM APPLY
            inputs:
              provider: 'azurerm'
              command: 'apply'
              workingDirectory: '$(System.ArtifactsDirectory)/$(Artifact)/AMTF'
              commandOptions: '--var-file=$(TfvarFilename)'
              environmentServiceNameAzureRM: '$(ServiceConnection)'

##################################################################################################
# STAGE: CREATE PR
# CREATE AND COMPLETE PULL REQUEST BY ASSOCIATING WORKITEMS AND DELETING SOURCE BRANCH
#################################################################################################

- stage: PULL_REQUEST_ASSOCIATE_WORKITEMS
  condition: |
     and(succeeded(), 
       ne(variables['Build.SourceBranch'], 'refs/heads/main') 
     )
  dependsOn: DEPLOY
  jobs:
  - job: PULL_REQUEST_WORKITEMS
    displayName: CREATE PR | ASSOCIATE WORKITEMS | COMPLETE
    steps:
# Download Keyvault Secrets:-
    - task: AzureKeyVault@2
      inputs:
        azureSubscription: '$(ServiceConnection)'
        KeyVaultName: '$(KV-Name)'
        SecretsFilter: '*'
        RunAsPreJob: false
# Install Az DevOps CLI Extension in the Build Agent:-
    - task: AzureCLI@1
      displayName: INSTALL DEVOPS CLI EXTENSION
      inputs:
        azureSubscription: '$(ServiceConnection)'
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          az extension add --name azure-devops
          az extension show --name azure-devops --output table
# Validate Az DevOps CLI Extension in the Build Agent:-
    - task: PowerShell@2
      displayName: VALIDATE AZ DEVOPS CLI
      inputs:
        targetType: 'inline'
        script: |
          az devops -h
# Set Default DevOps Organization and Project:-
    - task: PowerShell@2
      displayName: DEVOPS LOGIN + SET DEFAULT DEVOPS ORG & PROJECT
      inputs:
        targetType: 'inline'
        script: |
         echo "$(PAT)" | az devops login  
         az devops configure --defaults organization=$(DevOpsOrganisation) project=$(DevOpsProjName)
# Create Workitem + Create PR + Associate Workitem with PR + Complete the PR + Delete Source Branch:-
    - task: PowerShell@2
      displayName: CREATE & COMPLETE PULL REQUEST + WORKITEMS + DELETE SOURCE BRANCH
      inputs:
        targetType: 'inline'
        script: |
          Write-Host "#######################################################"
          Write-Host "NAME OF THE SOURCE BRANCH: $(Build.SourceBranchName)"
          Write-Host "#######################################################"
          $i="PR-"
          $j=Get-Random -Maximum 1000
          Write-Host "###################################################"
          Write-Host "WORKITEM NUMBER GENERATED IN DEVOPS BOARD: $i$j"
          Write-Host "###################################################"
          $wid = az boards work-item create --title $i$j --type "Issue" --query "id"
          Write-Host "#######################################################" 
          Write-Host "WORKITEM ID is: $wid"
          Write-Host "#######################################################"
          $prid = az repos pr create --repository $(DevOpsRepoName) --source-branch $(Build.SourceBranchName) --target-branch $(DevOpsDestinationBranch) --work-items $wid --transition-work-items true --query "pullRequestId"
          Write-Host "#######################################################"
          Write-Host "PULL REQUEST ID is: $prid"
          Write-Host "#######################################################"
          Write-Host "##### TO BE MERGED FROM $(Build.SourceBranchName) TO Main #####"
          az repos pr update --id $prid --auto-complete true --squash true --status completed --delete-source-branch true
          Write-Host "##### MERGE SUCCESSFULL #####"



Enter fullscreen mode Exit fullscreen mode

Now, let me explain each part of YAML Pipeline for better understanding.

PART #1:-
BELOW FOLLOWS PIPELINE VARIABLES CODE SNIPPET:-


########################################################################
#DECLARE VARIABLES:-
# ONLY VARIABLE VALUES NEEDS TO BE ALTERED TO MAKE THE PIPELINE WORK.
########################################################################
variables:
  PlanFilename: tfplan
  TfvarFilename: usrmid.tfvars
  KV-Name: ampockv
  ServiceConnection: amcloud-cicd-service-connection
  ResourceGroup: tfpipeline-rg
  StorageAccount: tfpipelinesa
  Container: terraform
  TfstateFile: PR/createprworkitem.tfstate
  BuildAgent: ubuntu-latest
  PipelineEnv: NonProd
  Terraform_Version: 1.2.3
  WorkingDir: $(System.DefaultWorkingDirectory)/Automate-PR-and-Associate-WorkItems
  Target: $(build.artifactstagingdirectory)/AMTF
  Artifact: AM
  anyTfChanges: false
  DevOpsOrganisation: https://dev.azure.com/ArindamMitra0251
  DevOpsProjName: AMCLOUD
  DevOpsRepoName: PR
  DevOpsDestinationBranch: main



Enter fullscreen mode Exit fullscreen mode
NOTE:-
Please feel free to change the values of the variables.
The entire YAML pipeline is build using variables. No Values are Hardcoded.
"Working Directory" Path should be based on your Code Placeholder.
PART #2:-
PIPELINE STAGE DETAILS FOLLOW BELOW:-
  1. This is a 3 Stage Pipeline.
  2. The Names of the Stages are - 1) BUILD 2) DEPLOY, and 3) PULL_REQUEST_ASSOCIATE_WORKITEMS
PIPELINE STAGE - BUILD:-


#################
# STAGE: BUILD
#################

stages:

- stage: BUILD
  jobs:
  - job: BUILD
    displayName: BUILD
    steps:
# Install Terraform Installer in the Build Agent:-
    - task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0
      displayName: INSTALL LATEST TERRAFORM VERSION
      inputs:
        terraformVersion: '$(Terraform_Version)'
# Terraform Init:-
    - task: TerraformTaskV2@2
      displayName: TERRAFORM INIT
      inputs:
        command: 'init'
        provider: 'azurerm'
        workingDirectory: '$(WorkingDir)'
        backendServiceArm: '$(ServiceConnection)' 
        backendAzureRmResourceGroupName: '$(ResourceGroup)' 
        backendAzureRmStorageAccountName: '$(StorageAccount)'
        backendAzureRmContainerName: '$(Container)'
        backendAzureRmKey: '$(TfstateFile)'
# Terraform Validate:-
    - task: TerraformTaskV2@2
      displayName: TERRAFORM VALIDATE
      inputs:
        command: 'validate'
        provider: 'azurerm'
        workingDirectory: '$(WorkingDir)'
        environmentServiceNameAzureRM: '$(ServiceConnection)'
# Terraform Plan:-
    - task: TerraformTaskV2@2
      displayName: TERRAFORM PLAN
      inputs:
        command: 'plan'
        provider: 'azurerm'
        workingDirectory: '$(WorkingDir)'
        commandOptions: '--var-file=$(TfvarFilename) --out=$(PlanFilename)'
        environmentServiceNameAzureRM: '$(ServiceConnection)'
# Detect Terraform Changes:-
    - task: PowerShell@2
      name: DetectTFChanges
      displayName: DETECT TERRAFORM CHANGES
      inputs:
        workingDirectory: '$(workingDir)'
        targetType: 'inline'
        script: |
          Write-Host "#######################################################"
          Write-Host "Intial value of variable: $(anyTfChanges)"
          Write-Host "#######################################################"
          $plan = $(terraform show -json tfplan | ConvertFrom-Json)
          $count = $plan.resource_changes.change.actions.length
          $actions = ($plan.resource_changes | where { 'no-op' -notcontains $_.change.actions }).length -ne 0
          Write-Host "##vso[task.setvariable variable=anyTfChanges;isOutput=true]$actions"
          Write-Host "#######################################################"
          Write-Host "Are there Changes in Infrastruture: $actions"
          Write-Host "#######################################################"
          Write-Host "TOTAL NO OF CHANGES: $count"
          Write-Host "#######################################################"
# Copy Files to Artifacts Staging Directory:-
    - task: CopyFiles@2
      displayName: COPY FILES ARTIFACTS STAGING DIRECTORY
      inputs:
        SourceFolder: '$(WorkingDir)'
        Contents: |
          **/*.tf
          **/*.tfvars
          **/*$(PlanFilename)*
        TargetFolder: '$(Target)'
# Publish Artifacts:-
    - task: PublishBuildArtifacts@1
      displayName: PUBLISH ARTIFACTS
      inputs:
        targetPath: '$(Target)'
        artifactName: '$(Artifact)'



Enter fullscreen mode Exit fullscreen mode
BUILD STAGE PERFORMS BELOW:-
## TASKS
1. Terraform Installer installed in Azure DevOps Build Agent.
2. Terraform Init.
3. Terraform Validate.
4. Terraform Plan.
5. Detect Terraform Changes (Powershell Inline Script).
6. Copy the Terraform files (Most Importantly Terraform Plan Output) to Artifacts Staging Directory.
6. Publish Artifacts.
NOTE:-


- task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0



Enter fullscreen mode Exit fullscreen mode
EXPLANATION:-
Instead of using TerraformInstaller@0 YAML Task, I have specified the Full Name. This is because I have two Terraform Extensions in my DevOps Organisation and with each of the Terraform Extension, exists the Terraform Install Task
The Names of the Extensions are listed below:-
1. Terraform by Microsoft DevLabs
2. Azure Pipelines Terraform Tasks by Charles Zipp
If Full Name is not provided, then below Error is Encountered:-
Image description
DETECT TERRAFORM CHANGES:-


# Detect Terraform Changes:-
    - task: PowerShell@2
      name: DetectTFChanges
      displayName: DETECT TERRAFORM CHANGES
      inputs:
        workingDirectory: '$(workingDir)'
        targetType: 'inline'
        script: |
          Write-Host "#######################################################"
          Write-Host "Intial value of variable: $(anyTfChanges)"
          Write-Host "#######################################################"
          $plan = $(terraform show -json tfplan | ConvertFrom-Json)
          $count = $plan.resource_changes.change.actions.length
          $actions = ($plan.resource_changes | where { 'no-op' -notcontains $_.change.actions }).length -ne 0
          Write-Host "##vso[task.setvariable variable=anyTfChanges;isOutput=true]$actions"
          Write-Host "#######################################################"
          Write-Host "Are there Changes in Infrastruture: $actions"
          Write-Host "#######################################################"
          Write-Host "TOTAL NO OF CHANGES: $count"
          Write-Host "#######################################################"



Enter fullscreen mode Exit fullscreen mode
EXPLANATION:-
The Original Creator of this Powershell Script is HOUSSEM DELLAI. I modified his Script to meet my requirements. TRUE or FALSE value is returned along with Total Count of Changes observed in Terraform Plan.
PIPELINE STAGE - DEPLOY:-


#################
# STAGE: DEPLOY
#################

- stage: DEPLOY
  condition: |
     and(succeeded(),
       ne(variables['Build.SourceBranch'], 'refs/heads/main'),
       eq(dependencies.BUILD.outputs['build.DetectTFChanges.anyTfChanges'], 'true')
     )
  jobs:
  - deployment: 
    displayName: Deploy
    environment: '$(PipelineEnv)'
    pool:
      vmImage: '$(BuildAgent)'
    strategy:
      runOnce:
        deploy:
          steps:
# Download Artifacts:-
          - task: DownloadBuildArtifacts@0
            displayName: DOWNLOAD ARTIFACTS
            inputs:
              buildType: 'current'
              downloadType: 'single'
              artifactName: '$(Artifact)'
              downloadPath: '$(System.ArtifactsDirectory)' 
# Install Terraform Installer in the Build Agent:-
          - task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0
            displayName: INSTALL LATEST TERRAFORM VERSION
            inputs:
              terraformVersion: '$(Terraform_Version)'
# Terraform Init:-
          - task: TerraformTaskV2@2
            displayName: TERRAFORM INIT
            inputs:
              provider: 'azurerm'
              command: 'init'
              workingDirectory: '$(System.ArtifactsDirectory)/$(Artifact)/AMTF/'
              backendServiceArm: '$(ServiceConnection)' 
              backendAzureRmResourceGroupName: '$(ResourceGroup)' 
              backendAzureRmStorageAccountName: '$(StorageAccount)'
              backendAzureRmContainerName: '$(Container)'
              backendAzureRmKey: '$(TfstateFile)'
# Terraform Apply:-
          - task: TerraformTaskV2@2
            displayName: TERRAFORM APPLY
            inputs:
              provider: 'azurerm'
              command: 'apply'
              workingDirectory: '$(System.ArtifactsDirectory)/$(Artifact)/AMTF'
              commandOptions: '--var-file=$(TfvarFilename)'
              environmentServiceNameAzureRM: '$(ServiceConnection)'



Enter fullscreen mode Exit fullscreen mode
DEPLOY STAGE PERFORMS BELOW:-
## TASKS
1. DEPLOY Stage will Execute only if the following conditions are met - 1) BUILD Stage gets completed successfully. 2) Source/Working Branch NOT EQUAL to Main Branch. 3) If there are CHANGES detected in Terraform Plan.
2. DEPLOY Stage will Execute only after Approval. The Approval is integrated with Pipeline Environment defined and applied in Deploy Stage.
3. Download the Published Artifacts.
4. Terraform Installer installed in Azure DevOps Build Agent.
5. Terraform Init.
6. Terraform Apply.
PIPELINE STAGE - PULL_REQUEST_ASSOCIATE_WORKITEMS:-


##################################################################################################
# STAGE: CREATE PR
# CREATE AND COMPLETE PULL REQUEST BY ASSOCIATING WORKITEMS AND DELETING SOURCE BRANCH
#################################################################################################

- stage: PULL_REQUEST_ASSOCIATE_WORKITEMS
  condition: |
     and(succeeded(), 
       ne(variables['Build.SourceBranch'], 'refs/heads/main') 
     )
  dependsOn: DEPLOY
  jobs:
  - job: PULL_REQUEST_WORKITEMS
    displayName: CREATE PR | ASSOCIATE WORKITEMS | COMPLETE
    steps:
# Download Keyvault Secrets:-
    - task: AzureKeyVault@2
      inputs:
        azureSubscription: '$(ServiceConnection)'
        KeyVaultName: '$(KV-Name)'
        SecretsFilter: '*'
        RunAsPreJob: false
# Install Az DevOps CLI Extension in the Build Agent:-
    - task: AzureCLI@1
      displayName: INSTALL DEVOPS CLI EXTENSION
      inputs:
        azureSubscription: '$(ServiceConnection)'
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          az extension add --name azure-devops
          az extension show --name azure-devops --output table
# Validate Az DevOps CLI Extension in the Build Agent:-
    - task: PowerShell@2
      displayName: VALIDATE AZ DEVOPS CLI
      inputs:
        targetType: 'inline'
        script: |
          az devops -h
# Set Default DevOps Organization and Project:-
    - task: PowerShell@2
      displayName: DEVOPS LOGIN + SET DEFAULT DEVOPS ORG & PROJECT
      inputs:
        targetType: 'inline'
        script: |
         echo "$(PAT)" | az devops login  
         az devops configure --defaults organization=$(DevOpsOrganisation) project=$(DevOpsProjName)
# Create Workitem + Create PR + Associate Workitem with PR + Complete the PR + Delete Source Branch:-
    - task: PowerShell@2
      displayName: CREATE & COMPLETE PULL REQUEST + WORKITEMS + DELETE SOURCE BRANCH
      inputs:
        targetType: 'inline'
        script: |
          Write-Host "#######################################################"
          Write-Host "NAME OF THE SOURCE BRANCH: $(Build.SourceBranchName)"
          Write-Host "#######################################################"
          $i="PR-"
          $j=Get-Random -Maximum 1000
          Write-Host "###################################################"
          Write-Host "WORKITEM NUMBER GENERATED IN DEVOPS BOARD: $i$j"
          Write-Host "###################################################"
          $wid = az boards work-item create --title $i$j --type "Issue" --query "id"
          Write-Host "#######################################################" 
          Write-Host "WORKITEM ID is: $wid"
          Write-Host "#######################################################"
          $prid = az repos pr create --repository $(DevOpsRepoName) --source-branch $(Build.SourceBranchName) --target-branch $(DevOpsDestinationBranch) --work-items $wid --transition-work-items true --query "pullRequestId"
          Write-Host "#######################################################"
          Write-Host "PULL REQUEST ID is: $prid"
          Write-Host "#######################################################"
          Write-Host "##### TO BE MERGED FROM $(Build.SourceBranchName) TO Main #####"
          az repos pr update --id $prid --auto-complete true --squash true --status completed --delete-source-branch true
          Write-Host "##### MERGE SUCCESSFULL #####"



Enter fullscreen mode Exit fullscreen mode
PULL REQUEST STAGE PERFORMS BELOW:-
## TASKS
1. PULL REQUEST Stage will Execute only if the following conditions are met - 1) DEPLOY Stage gets completed successfully. 2) Source/Working Branch NOT EQUAL to Main Branch.
2. Download Secrets from Keyvault (DevOps Personal Access Token [PAT]).
3. Install Azure DevOps CLI Extension in Build Agent.
4. Validate Azure DevOps CLI Extension in Build Agent.
5. Set Default DevOps Organization and Project.
6. Create Work-Item In DevOps Board.
7. Create Pull Request.
8. Associate Work-Item with Pull Request.
9. Complete Pull Request with Squash Commit.
10. Delete Source Branch.
PART #3:-
OBJECTIVE OF TERRAFORM CODE SNIPPET:-
Create a Resource Group.
Create a User Assigned System Managed Identity.
TERRAFORM (main.tf):-


terraform {
  required_version = ">= 1.2.3"

   backend "azurerm" {
    resource_group_name  = "tfpipeline-rg"
    storage_account_name = "tfpipelinesa"
    container_name       = "terraform"
    key                  = "PR/createprworkitem.tfstate"
  }
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.2"
    }   
  }
}
provider "azurerm" {
  features {}
  skip_provider_registration = true
}



Enter fullscreen mode Exit fullscreen mode
TERRAFORM (usrmid.tf):-


## Azure Resource Group:-
resource "azurerm_resource_group" "rg" {
 name     = var.rg-name
 location = var.rg-location
}

## Azure User Assigned Managed Identities:-
resource "azurerm_user_assigned_identity" "az-usr-mid" {

 name                = var.usr-mid-name
 resource_group_name = azurerm_resource_group.rg.name
 location            = azurerm_resource_group.rg.location

 depends_on          = [azurerm_resource_group.rg]
 }



Enter fullscreen mode Exit fullscreen mode
TERRAFORM (variables.tf):-


variable "rg-name" {
  type        = string
  description = "Name of the Resource Group"
}

variable "rg-location" {
  type        = string
  description = "Resource Group Location"
}

variable "usr-mid-name" {
  type        = string
  description = "Name of the User Assigned Managed Identity"
}



Enter fullscreen mode Exit fullscreen mode
TERRAFORM (usrmid.tfvars):-


rg-name         = "AMTest100"
rg-location     = "West Europe"
usr-mid-name    = "AMUSRMID100"



Enter fullscreen mode Exit fullscreen mode

NOW ITS TIME TO TEST !!!...

TEST CASES:-
TEST CASE #1: PIPELINE EXECUTED FROM WORKING BRANCH (FOR EXAMPLE: DEV) WITH CHANGES:-
DESIRED OUTPUT: BUILD, DEPLOY AND PR STAGE EXECUTED SUCCESSFULLY.
BRANCHES:-
Image description
PIPELINE RUN:-
Image description
PIPELINE STAGE BUILD EXECUTED SUCCESSFULLY:-
Image description
PIPELINE STAGE DEPLOY WAITING APPROVAL:-
Image description
PIPELINE STAGE DEPLOY EXECUTED SUCCESSFULLY:-
Image description
PIPELINE STAGE PULL REQUEST EXECUTED SUCCESSFULLY:-
Image description
OVERALL PIPELINE RUN STATUS:-
Image description
WORK-ITEMS WITH RANDOM NAME CREATED SUCCESSFULLY:-
Image description
PR ASSOCIATING WORK-ITEM COMPLETED SUCCESSFULLY WITH SQUASH COMMIT:-
Image description
Image description
SOURCE/WORKING BRANCH DELETED SUCCESSFULLY:-
Image description
AZURE RESOURCES DEPLOYED SUCCESSFULLY:-
Image description
TEST CASE #2:- PIPELINE EXECUTED FROM WORKING BRANCH (FOR EXAMPLE: DEV) WITH NO CHANGES:-
DESIRED OUTPUT:- BUILD STAGE EXECUTED SUCCESSFULLY. DEPLOY AND PR STAGES ARE SKIPPED.
BRANCHES:-
Image description
PIPELINE RUN:-
Image description
PIPELINE STAGE BUILD EXECUTED SUCCESSFULLY:-
Image description
PIPELINE STAGE DEPLOY AND PR GETS SKIPPED:-
Image description
WORKING BRANCH REMAINS (FOR EXAMPLE - DEV):-
Image description
TEST CASE #3: PIPELINE EXECUTED FROM MAIN BRANCH:-
DESIRED OUTPUT:- BUILD STAGE EXECUTED SUCCESSFULLY. DEPLOY AND PR STAGES ARE SKIPPED.
BRANCHES:-
Image description
PIPELINE RUN:-
Image description
PIPELINE STAGE BUILD EXECUTED SUCCESSFULLY:-
Image description
PIPELINE STAGE DEPLOY AND PR GETS SKIPPED:-
Image description

Hope You Enjoyed the Session!!!

Stay Safe | Keep Learning | Spread Knowledge

Top comments (2)

Collapse
 
rakesh_suryawanshi profile image
Rakesh Suryawanshi

I found a small bug in script you have declared a variable $(PlanFilename) which you are using it in terraform plan step but not using the same in DetectTFChanges step, in DetectTFChanges the name of the plan is hardcoded :)

Collapse
 
arindam0310018 profile image
Arindam Mitra • Edited

Hi Rakesh, thank you for your inputs. I might have missed out. Itโ€™s line 110 in my Github. I see it now.
I did a quick sanity check and no other place, I could find anything hard-coded. But if you spot, please do let me know.

Again thank you very much for having a look at my automation with eagleโ€™s eye

Highly appreciated!!! I thank you ๐Ÿ˜Š