We have a scenario where the ADO web hooks are just falling short in capabilities. I'm using the web hooks to trigger an Azure Logic App but its getting a bit over complicated with the different actions I'd need to compensate for a lot of the information that the Web hook does not have available (custom fields, dynamic project and organization names, etc)
A web hook trigger to run a build pipeline that has a custom script is unfortunately slow since the time it takes for that task to trigger would already be about 10 seconds on a good day, we'd need something to process some work item data and then trigger some API calls in 2-3 seconds from the time of the event (in this case the event is a work item getting created)
I've not included a lot of details as they're not necessary but the end-to-end process looks like this:
I'd appreciate any suggestions that including only azure and azure devops products. Company policy prevent me from setting some third party service or application to fill in this gap.
You may consider using Azure DevOps WebHooks to trigger your workflow.
In addition, you may use WebHook resource to trigger pipeline that uses InvokeRESTAPI@1
task in an agent-less job to call API request to external endpoint. Since the pipeline uses an agent-less job, it will skip the waiting time for the pipeline to pick up an agent from the pool. Here is a simple sample for your reference.
Incoming WebHook
service connection the Azure DevOps project settings and take notes of the WebHook Name ADOWebHook
and Service Connection Name ADOWebHookSvcCnn
;
ADOWebHook
and Service Connection Name ADOWebHookSvcCnn
defined in the Incoming WebHook
service connection during step 1
;resources:
webhooks:
- webhook: ADOWebHook
connection: ADOWebHookSvcCnn
jobs:
- job: AgentlessJob
pool: server
condition: ${{ eq( parameters.ADOWebHook.resource.fields['System.Reason'], 'New defect reported') }}
steps:
- task: InvokeRESTAPI@1 # Send custom notification to Microsoft Teams (as the external service)
inputs:
connectionType: 'connectedServiceName'
serviceConnection: 'ADOTeamsIncomingWebHookConnector'
method: 'POST'
body: |
{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "0076D7",
"summary": "${{ parameters.ADOWebHook.resource.fields['System.CreatedBy'].displayName }} created a new ${{ parameters.ADOWebHook.resource.fields['System.WorkItemType'] }}",
"sections": [{
"activityTitle": "${{ parameters.ADOWebHook.resource.fields['System.CreatedBy'].displayName }} created a new ${{ parameters.ADOWebHook.resource.fields['System.WorkItemType'] }}",
"activitySubtitle": "On Project ${{ parameters.ADOWebHook.resource.fields['System.TeamProject'] }}",
"activityImage": "https://adaptivecards.io/content/cats/3.png",
"facts": [{
"name": "Assigned to",
"value": "${{ parameters.ADOWebHook.resource.fields['System.CreatedBy'].displayName }}"
}, {
"name": "Created date",
"value": "${{ parameters.ADOWebHook.resource.fields['System.CreatedDate'] }}"
}, {
"name": "Status",
"value": "${{ parameters.ADOWebHook.resource.fields['System.State'] }}"
}],
"markdown": true
}],
"potentialAction": [{
"@type": "ActionCard",
"name": "Add a comment",
"inputs": [{
"@type": "TextInput",
"id": "comment",
"isMultiline": false,
"title": "Add a comment here for this task"
}],
"actions": [{
"@type": "HttpPOST",
"name": "Add comment",
"target": "https://learn.microsoft.com/outlook/actionable-messages"
}]
}, {
"@type": "ActionCard",
"name": "Set due date",
"inputs": [{
"@type": "DateInput",
"id": "dueDate",
"title": "Enter a due date for this task"
}],
"actions": [{
"@type": "HttpPOST",
"name": "Save",
"target": "https://learn.microsoft.com/outlook/actionable-messages"
}]
}, {
"@type": "OpenUri",
"name": "Learn More",
"targets": [{
"os": "default",
"uri": "https://learn.microsoft.com/outlook/actionable-messages"
}]
}, {
"@type": "ActionCard",
"name": "Change status",
"inputs": [{
"@type": "MultichoiceInput",
"id": "list",
"title": "Select a status",
"isMultiSelect": "false",
"choices": [{
"display": "In Progress",
"value": "1"
}, {
"display": "Active",
"value": "2"
}, {
"display": "Closed",
"value": "3"
}]
}],
"actions": [{
"@type": "HttpPOST",
"name": "Save",
"target": "https://learn.microsoft.com/outlook/actionable-messages"
}]
}]
}
waitForCompletion: 'false'
# - job: AgentJob
# dependsOn: []
# steps:
# - script: echo "The reason is ${{ parameters.ADOWebHook.resource.fields['System.Reason'] }}"
<ADO Organization Name>
with your Azure DevOps organization name and <WebHook Name>
with ADOWebHook
from our example;https://dev.azure.com/<ADO Organization Name>/_apis/public/distributedtask/webhooks/<WebHook Name>?api-version=6.0-preview
Create WebHook event of Work item created
; add the WebHook
URL from step 3
into this event to trigger this pipeline;
My sample pipeline will be triggered once a new work item is created; it evaluates the value of field System.Reason
of the newly created work item and then send API request to the external endpoint (Microsoft Teams connector message); you can customize this request sent to external endpoint with the parameter information from the event payload using the format ${{ parameters.<WebhookAlias>.<JSONPath>}}
;
Here is the Test
payload of the triggering event to create a new work item;
Method: POST
URI: https://<dev.azure.com/ADO Organization Name>/_apis/public/distributedtask/webhooks/ADOWebHook?api-version=6.0-preview
HTTP Version: 1.1
Headers:
{
Authorization: Basic ********
Content-Type: application/json; charset=utf-8
}
Content:
{
"subscriptionId": "bb3faa0d-6063-42ef-8820-5d5037161241",
"notificationId": 12,
"id": "d2d46fb1-dba5-403c-9373-427583f19e8c",
"eventType": "workitem.created",
"publisherId": "tfs",
"message": {
"text": "Bug #5 (Some great new idea!) created by Jamal Hartnett.\r\n(http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)",
"html": "<a href=\"http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5\">Bug #5</a> (Some great new idea!) created by Jamal Hartnett.",
"markdown": "[Bug #5](http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) created by Jamal Hartnett."
},
"detailedMessage": {
"text": "Bug #5 (Some great new idea!) created by Jamal Hartnett.\r\n(http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- Area: FabrikamCloud\r\n- Iteration: FabrikamCloud\\Release 1\\Sprint 1\r\n- State: New\r\n- Assigned to: \r\n- Comment: \r\n- Severity: 3 - Medium\r\n",
"html": "<a href=\"http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5\">Bug #5</a> (Some great new idea!) created by Jamal Hartnett.<ul>\r\n<li>Area: FabrikamCloud</li>\r\n<li>Iteration: FabrikamCloud\\Release 1\\Sprint 1</li>\r\n<li>State: New</li>\r\n<li>Assigned to: </li>\r\n<li>Comment: </li>\r\n<li>Severity: 3 - Medium</li></ul>",
"markdown": "[Bug #5](http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) created by Jamal Hartnett.\r\n\r\n* Area: FabrikamCloud\r\n* Iteration: FabrikamCloud\\Release 1\\Sprint 1\r\n* State: New\r\n* Assigned to: \r\n* Comment: \r\n* Severity: 3 - Medium\r\n"
},
"resource": {
"id": 5,
"rev": 1,
"fields": {
"System.AreaPath": "FabrikamCloud",
"System.TeamProject": "FabrikamCloud",
"System.IterationPath": "FabrikamCloud\\Release 1\\Sprint 1",
"System.WorkItemType": "Bug",
"System.State": "New",
"System.Reason": "New defect reported",
"System.CreatedDate": "2014-07-15T17:42:44.663Z",
"System.CreatedBy": {
"displayName": "Jamal Hartnett",
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"_links": {
"avatar": {
"href": "https://dev.azure.com/mseng/_apis/GraphProfile/MemberAvatars/aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
}
},
"id": "e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"uniqueName": "Jamal Hartnett",
"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"descriptor": "ukn.VXkweExUVXRNakV0TWpFME5qYzNNekE0TlMwNU1ETXpOak15T0RVdE56RTVNelEwTnpBM0xURXpPRGswTlRN"
},
"System.ChangedDate": "2014-07-15T17:42:44.663Z",
"System.ChangedBy": {
"displayName": "Jamal Hartnett",
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"_links": {
"avatar": {
"href": "https://dev.azure.com/mseng/_apis/GraphProfile/MemberAvatars/aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
}
},
"id": "e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"uniqueName": "Jamal Hartnett",
"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"descriptor": "ukn.VXkweExUVXRNakV0TWpFME5qYzNNekE0TlMwNU1ETXpOak15T0RVdE56RTVNelEwTnpBM0xURXpPRGswTlRN"
},
"System.Title": "Some great new idea!",
"Microsoft.VSTS.Common.Severity": "3 - Medium",
"WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New"
},
"_links": {
"self": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5"
},
"workItemUpdates": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5/updates"
},
"workItemRevisions": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5/revisions"
},
"workItemType": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug"
},
"fields": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/fields"
}
},
"url": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5"
},
"resourceVersion": "1.0",
"resourceContainers": {
"collection": {
"id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
},
"account": {
"id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
},
"project": {
"id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
}
},
"createdDate": "2024-01-30T03:13:52.0705853Z"
}
Here the output in the external service (a notification card in Microsoft Teams);