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 Settings → Service Connections → New service connection.
Then we search for the type "Incoming WebHook" and select this type and click Next.
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"
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 Settings → Service Hooks, there we click onto the "+" button and choose "Web Hooks".
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)
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:
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: We just provided "Automation Test" as title and "automation" as Tag (important).
Now the pipeline has automatically been triggered...
After the pipeline has been successfully executed, it should look something like this in the jobs overview:
If we now look into the user story, we should find three newly linked child items there.
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.