Automated Workitem Creation With Service Hooks in Azure DevOps

Automated Workitem Creation With Service Hooks in Azure DevOps

I have been asked many times as an admin if there is a way to automatically create tasks after a user story is created. The good news: Yes, it is possible!

But what all do we need for this?

  • Service Connection
  • Service Hook
  • Powershell to create tasks
  • Pipeline, which executes the Powershell

Service Connection

First, we create the Service Connection.

For this we go to Project SettingsService ConnectionsNew service connection.

Then we search for the type "Incoming WebHook" and select this type and click Next.

WebHook_1.png

Now we set a name for the property WebHook Name - in our case "WitAutomation", furthermore we set the property Service connection name - again we take "WitAutomation"

WebHook_2.png

Important: Be sure to use meaningful names, because the set values are needed in the further process.

Service Hook

In the next step, the service hook is created, which reacts when a user story is created and contains the tag "automation".

To create a new Service Hook you need to be in Project Administrators group or a collection administrator needs to setup individual permissions for you.

To create a new Service Hook we need to navigate as follows: Project SettingsService Hooks, there we click onto the "+" button and choose "Web Hooks".

WebHook_0.png

Now we need to add some Infos

  • Trigger on this type of event: Work item created
  • Filters
    • Area Path: [Any]
    • Work item type: User Story
  • Tag: automation (only for our case, you can leave it empty if you want to trigger on every created User Story)

ServiceHook_1.png

After, we click on "Next". There we need to enter one really important info for "URL", because only then everything will be working. The URL looks like that:

https://dev.azure.com/YOUR_ORGA/_apis/public/distributedtask/webhooks/SERVICE_CONNECTION_NAME?api-version=6.0-preview

So in our case we entered WitAutomation for "SERVICE_CONNECTION_NAME". You remember? - Thats the name of our Service Connection. In my case now it looks like that:

ServiceHook_2.png

We can now click on Finish

Powershell to create Tasks

Next step is to write a Powershell script, which will create our workitems, this could look like this (you need to store in in a Git-repo in same project.):

param(
    [Parameter(Mandatory=$false)]
    [string]$collectionUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI,
    [Parameter(Mandatory=$false)]
    [string]$teamProjectId = $env:SYSTEM_TEAMPROJECTID,
    # Parent Workitem Id
    [Parameter(Mandatory=$false)]
    [string]$parent,
    # Title of new Workitem
    [Parameter(Mandatory=$false)]
    [string]$title
)

Write-Host "New User Story With Workitem ID : $parent Was Created"
Write-Host "Will Now Create New Child Workitem..."

$token = $env:SYSTEM_ACCESSTOKEN

if (!$token) {
    Write-Error "SYSTEM_ACCESSTOKEN Was Not Provided - Please Check Your Pipeline! => More Info: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#systemaccesstoken"
    exit -1
}

#Body For WIT-Creation:
$Form = @(
    @{
    op = 'add'
    path = '/fields/System.Title'
    value = "$title"
    },
    @{
        op = 'add'
        path = '/relations/-'
        value = @{
                "rel" = "System.LinkTypes.Hierarchy-Reverse"
                "url" = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI$teamProjectId/_apis/wit/workitems/$parent"
                }
            }
        )

$url = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI$teamProjectId/_apis/wit/workitems/`$Task?api-version=6.0"

Write-Host "URL: $url"
$workitem = Invoke-RestMethod -Method Post -Uri $url -Body ($Form | ConvertTo-Json -AsArray) -Headers @{
    Authorization = "Bearer $token"
    "Content-Type" = "application/json-patch+json"
}
Write-Host "New Workitem With Id $($workitem.id) Was Added And Linked As Child To Workitem With Id $parent"

exit 0

As you can see, we only provide title for new workitems. Of course with more payload it is possible to add some more info.

Pipeline, which executes the Powershell

Last step is to create new Pipeline (YAML), our pipeline looks like that:

trigger: none

resources:
  webhooks:
  # URL: https://<OrgName>/_apis/public/distributedtask/webhooks/<HookName>?api-version=6.0-preview
  - webhook: WitAutomation
    connection: 'WitAutomation'
    filters:
    - path: "eventType"
      value: "workitem.created"
    - path: "publisherId"
      value: "tfs"

pool:
  vmImage: ubuntu-latest

parameters:
  - name: mytitles
    type: object
    default:
      - "'First Task'"
      - "'Second Task'"
      - "'Third Task'"

steps:
- checkout: self

# JSON payload data is available in the form of ${{ parameters.<WebhookAlias>.<JSONPath>}}
- powershell: |
    Write-Host "eventType   : ${{ parameters.WitAutomation.eventType }}"
    Write-Host "publisherId : ${{ parameters.WitAutomation.publisherId }}"
    Write-Host "resource.id : ${{ parameters.WitAutomation.resource.id }}"
  displayName: Infos

- ${{ each v in parameters.mytitles }}:
  - task: PowerShell@2
    displayName: 'Create WIT with title ${{ v }}'
    inputs:
      filePath: 'CreateChildWorkItem.ps1'
      arguments: '-parent ${{parameters.WitAutomation.resource.id}} -title ${{ v }}'
      pwsh: true
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Some description:

First what we need is a new resource, the webhook. If you don´t add it, the pipeline won´t be triggered Of course also we need to add a pool, in our case "ubuntu-latest"

Next what I did is to provide some workitem titles (First Task, Second Task,...)

Now about the steps:

First is to checkout the repo (because we will use the Powershell which we stored there in last step). Second step is to do some outputs, just to have some infos about the User Story, which triggered the pipline (not necessary). Third step is the usage of the Powershell. But what´s that each-loop there? With help of the each-loop Azure DevOps is automatically creating steps for each entry in "mytitles", so in our case three new steps.

Important parts:

  • Arguments of Powershell step: there you need to set params (parent and title)
  • You need to set SYSTEM_ACCESSTOKEN (without it will not be possible to create the new workitems, because the agentservice will not be able to do necessary api-calls!)
    • env:
          SYSTEM_ACCESSTOKEN: $(System.AccessToken)
      

That´s it, now we can create a new User Story to test everything: NewStory_0.png We just provided "Automation Test" as title and "automation" as Tag (important).

Now the pipeline has automatically been triggered...

PipelineTriggered.png

After the pipeline has been successfully executed, it should look something like this in the jobs overview: PipelineRun.png

If we now look into the user story, we should find three newly linked child items there.

Childs.png

And that's it, I personally would have liked a slightly simpler approach, I found it a bit confusing at the beginning with service hook, webhook and the corresponding link.