Is it possible to link items (user stories, etc.) in one Azure Board to another?

59 Views Asked by At

We are setting up a new Azure Devops Board to track all of our work. Some of the projects being tracked are in a different board managed by a different team (under a different Organization).

We would like to be able to link these other Stories, Features, etc. to be linked to our board too so that we only need to write the update in one place.

I know it is possible to create a hyperlink directly to the existing Board, but not all of the users (e.g. leadership team) on the new board will have access.

So is it possible to somehow write an update in one place and have it update on another Board too?

We have created a hyperlink for testing purposes but users that didn't have rights to the existing Board were unable to access it, which is perfectly expected. I haven't found any other way to link the data from one Board to another.

1

There are 1 best solutions below

0
Alvin Zhao - MSFT On

Based on your description to synchronize certain fields of work items between 2 boards from 2 Azure DevOps organizations, I am afraid there is no out-of-box feature as of now. As discussed, to customize an automated workflow for such requirement, the mapping information between the two WITs has to be restored somewhere manually first, so that the workflow is aware which WIT in the target organization should be updated when the WIT in the source organization is modified.

Since the pipeline feature of Azure DevOps itself is an automation tool and we can use a custom field in the WIT to restore the mapping information, I have figured out a custom workflow within Azure DevOps. Let's assume we need to synchronize the two WITs namely UserStory1 of ProjectA from OrgA (as the source WIT) and UserStory2 of ProjectB from OrgB (as the target WIT). Here is the design.

Prerequisites and optimizations taken into consideration

  • To trigger the pipeline when UserStory1 of ProjectA from OrgA is updated, we can subscribe WIT Update Web Hook event in ProjectA and monitor the event from source Project/org in a pipeline of ProjectB by Web Hook resource in YAML pipeline;

  • To update UserStory2 of ProjectB from OrgB with the information extracted from Web Hook payload, we can call this API in InvokeRESTAPI@1 pipeline task; this task is an agentless job supported task, which doesn't request to use a pipeline agent machine; hence, it can save time for agent assignment and won't cause any agent job to be pending in the queue of an agent pool, either; (the simple job only takes 1-2 seconds to complete;)

  • Per the authentication of the API request, we can use $(System.AccessToken) of the pipeline service account, which can not only avoid the risk of Personal Access Tokens leakage, but is also less prone to getting throttled by Rate usage limits;

Steps to generate a sample workflow

  1. Create custom fields in inheritance process for WITs in both source and target organizations to track the mapping WIT IDs; enter image description here

  2. Create an Incoming WebHook service connection in the target organization (OrgB in this sample) and keep notes of the Web Hook name (ADOWebHookWIT) and service connection name (ADOWebHookWITSvcCnn); Image

  3. Generate the Web Hook URL and subscribe the WIT update event in the source organization (ProjectA in OrgA);

    https://dev.azure.com/{Target OrgName (OrgB)}/_apis/public/distributedtask/webhooks/{Web Hook Name (ADOWebHookWIT)}?api-version=6.0-preview
    

    enter image description here

  4. Create Generic service connection like below in ProjectB for the pipeline task InvokeRESTAPI@1; Image

  5. Create a new YAML pipeline in ProjectB with the .yml file contents below to monitor the Web Hook events sent from OrgA; make sure the pipeline service account is granted permission to edit work items in the expected area path (Edit the Security of project root node in my case);

trigger: none

resources:
  webhooks:
  # WIT update Web Hook subscription - sent from OrgA to https://dev.azure.com/OrgB/_apis/public/distributedtask/webhooks/ADOWebHookWIT?api-version=6.0-preview
  - webhook: ADOWebHookWIT
    connection: ADOWebHookWITSvcCnn # Web Hook service connection name

variables:
  # Retrieve target work item Id in OrgB from the triggering Web Hook payload sent from OrgA
  targetWITId: ${{ parameters.ADOWebHookWIT.resource.revision.fields['Custom.MappedWITIDInTargetOrg'] }}
  orgB: ${{ split(variables['System.TeamFoundationCollectionUri'], '/')[5] }} # Split OrgB's name from $(System.TeamFoundationCollectionUri)

jobs:
- job: Synchronization
  displayName: Update workitem with id of ${{ variables.targetWITId }} in ${{variables['System.TeamFoundationCollectionUri']}}
  pool: server # This is an agentless job
  steps:
  - task: InvokeRESTAPI@1
    inputs:
      connectionType: 'connectedServiceName'
      serviceConnection: 'AzureDevOpsServices' # Generic service connection name
      method: 'PATCH'
      # Authenticate against $(System.AccessToken) of the pipeline service account
      headers: |
        {
          'Authorization' : 'Bearer $(System.AccessToken)',
          'Content-Type' : 'application/json-patch+json'
        }
      body: |
        [
          {
              'op' : 'add',
              'path' : '/fields/System.State',
              'value' : '${{ parameters.ADOWebHookWIT.resource.revision.fields['System.State'] }}'
          },
          {
              'op' : 'add',
              'path' : '/fields/System.Description',
              'value' : '${{ parameters.ADOWebHookWIT.resource.revision.fields['System.Description'] }}'
          },
          {
              'op' : 'add',
              'path' : '/fields/Microsoft.VSTS.Scheduling.StoryPoints',
              'value' : '${{ parameters.ADOWebHookWIT.resource.revision.fields['Microsoft.VSTS.Scheduling.StoryPoints'] }}'
          },
          {
              'op' : 'add',
              'path' : '/fields/Microsoft.VSTS.Common.Priority',
              'value' : '${{ parameters.ADOWebHookWIT.resource.revision.fields['Microsoft.VSTS.Common.Priority'] }}'
          },
          {
              'op' : 'add',
              'path' : '/fields/Microsoft.VSTS.Common.Risk',
              'value' : '${{ parameters.ADOWebHookWIT.resource.revision.fields['Microsoft.VSTS.Common.Risk'] }}'
          }
        ]
      urlSuffix: '$(OrgB)/$(System.TeamProject)/_apis/wit/workitems/$(targetWITId)?api-version=7.2-preview.3' # Construct the complete URL with pipeline predefined variables
      waitForCompletion: 'false'
  

enter image description here 6. As soon as I edited the UserStory1 of ProjectA from OrgA, the pipeline of OrgB received the WIT update event sent from OrgA and then pipeline was automatically triggered and updated UserStory2 of ProjectB from OrgB (#615) via API with the information transferred in the payload; we can see the organization name and project name information were not restored in each WIT, as the values were retrieved by the pipeline predefined variables System.TeamFoundationCollectionUri and System.TeamProject. Therefore, this benefits us from creating additional custom fields, but requires that the pipeline and the target work item are from the same project. Image Image

If you would like to synchronize more fields, you can modify the request body accordingly. Hope the sample workflow for your reference may help.