Skip to content

Create standard tasks

Because it is useful to create standard (DoD) tasks for a User Story so that you do not forget anything, this script checks all User Stories in the current Iteration and creates the necessary tasks for these stories.

Supplies

  1. PAT token from your DevOps project
  2. Project and organization name.
  3. A way to automate your powershell script, this can be done through a scheduled task, scheduled pipeline or, for example, a webhook.

Script

The script uses the DevOps API to create tasks, the tasks are defined in a json file, make sure this file is in the same location as the script.

workitems.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
{
"Title": "Documentation",
"Description": "Describe the changes or new functionality on https://docs.loevencloud.nl"
},
{
"Title": "Step-by-step plan",
"Description": "define a step-by-step plan."
},
{
"Title": "The Test",
"Description": "Describe tests and implement them where possible."
},
{
"Title": "Research",
"Description": "Go out and do some research."
},
{
"Title": "Automatiseer",
"Description": "Can it be done automatically?."
},
{
"Title": "Integrate",
"Description": "integrates it into the current setup (https://docs.loevencloud.nl"
}
]
automateTasks.ps1
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
<#
.SYNOPSIS
Automation script for common devops tasks
#>
[CmdletBinding()]
param (
[Parameter()]
[string]$org = "DEVOPSORG",
[Parameter()]
[string]$project = "DEVOPSPROJECT",
[Parameter()]
[string]$pat = "DEVOPSPAT"
)
#Global Variables
$B64Pat = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("`:$Pat"))
$header = @{
Authorization = "Basic $B64pat"
}
$iterationBaseUri = 'https://dev.azure.com/ORG/PROJECT/_apis/work/teamsettings/iterations?$timeframe=current
$iterationUri = $iterationBaseUri.replace("ORG/PROJECT","$org/$project")
$workItemBasePostUri = 'https://dev.azure.com/ORG/PROJECT/_apis/wit/workitems/$Task?api-version=6.1-preview.3'
$workItemPostUri = $workItemBasePostUri.replace("ORG/PROJECT", "$org/$project")
$currentIteration = Invoke-RestMethod -Method GET -Headers $header -Uri $iterationUri -ContentType "application/json"
$workitemsUri = "https://dev.azure.com/$org/$project/_apis/work/teamsettings/iterations/$($currentIteration.value.id)/workitems?api-version=7.0"
$allWorkitems = Invoke-RestMethod -Method GET -Headers $header -Uri $workitemsUri -ContentType "application/json"
$defaultWorkItems = Get-Content -Raw -Path /scripts/workitems.json | ConvertFrom-Json
$userStories = $allWorkitems.workItemRelations | Where-Object { $_.source -eq $null }
$excludedStories = @("OPS", "OOTS")

#Functions
function Add-devOpsWorkItem {
[CmdletBinding()]
param (
[Parameter()]
[string]$Title,
[Parameter()]
[string]$userStoryId,
[Parameter()]
[string]$description,
[Parameter()]
[string]$iterationPath
)
$body = @(
@{
op = "add"
path  = "/fields/System.IterationPath"
value = "$iterationPath"
},
@{
op = "add"
path  = "/fields/System.Title"
value = "$title"
},
@{
op = "add"
path  = "/fields/System.Description"
value = "$description"
},
@{
op = "add"
path  = "/relations/-"
value = @{
rel = "System.LinkTypes.Hierarchy-Reverse"
url = "https://dev.azure.com/$org/$project/_apis/wit/workitems/$userStoryId"
}
}
) | ConvertTo-Json -Depth 10
$response = Invoke-RestMethod -Uri $workItemPostUri -Headers $header -Method POST -Body $body -ContentType "application/json-patch json"

if ($response.id) {
Write-Host "`tTask $title created and linked"
}
else {
Write-Host "Error creating task and linking to user story: $($response | ConvertTo-Json -Depth 10)"
}
}

# Main script
foreach ($userStory in $userStories) {
$storyProperties = Invoke-RestMethod -Method get -Headers $header -Uri $userstory.target.url -ContentType "application/json"
$iteration = $($storyProperties.fields.'System.IterationPath')
Write-Output "$iteration - $($storyProperties.fields.'System.Title')"
if ($excludedStories -contains $($storyProperties.fields.'System.Title')) {
Write-Output "$($storyProperties.fields.'System.Title') is excluded for default work process."
}
else {
$childItems = $allWorkitems.workItemRelations | Where-Object { $_.source.id -eq $userstory.target.id }
$activeTasks = @()
foreach ($child in $childItems) {
$taskMeta = @{} | Select-Object Title
$childProperties = Invoke-RestMethod -Method get -Headers $header -Uri $child.target.url -ContentType "application/json"
$taskMeta.Title = "`t$($childProperties.fields.'System.Title')"
$activeTasks  = $taskMeta
}
foreach ($defaultTask in $defaultWorkItems) {
if (!($activeTasks.Title -match $defaultTask.Title)) {
Add-devOpsWorkItem -Title $defaultTask.Title -userStoryId $userstory.target.id -iterationPath $iteration -description $defaultTask.Description
}
}
}
}