Cross-Connectional Relationship Handling
Executive Summary
The azdw tool currently implements comprehensive cross-connection work item relationship handling with sophisticated support for resolving relationships across different Azure DevOps organizations and Entra ID tenants. The tool uses a four-track approach for cross-connection relationships:
- Native Azure DevOps Relationships: Standard work item links (Parent, Child, Related, Dependency, etc.) stored directly in Azure DevOps
- Remote Work Link Types: Azure DevOps native cross-organizational link types (Remote-Related, Produces-For, Consumes-From) designed for linking work items across organizations managed by the same Microsoft Entra ID
- Hyperlink-based Relationships: Work item URLs stored as “Hyperlink” entries that are automatically detected and resolved as relationships to remote work items
- File-based Relationships: Hyperlinks to non-work-item resources (Git files, Wiki pages, external documents) converted to navigable “fake” work item relationships via configurable regex patterns
This multi-track approach enables teams to reference work items across organizational boundaries using native Azure DevOps functionality when available (Remote Work Link Types), or fallback to hyperlink-based resolution when organizations are in different tenants or for legacy scenarios. The file-based relationship mapping extends this to architecture artifacts stored outside Azure DevOps work items.
The tool provides a unified visualization command (visualize graph) that supports multiple output formats (GraphViz DOT, D3/Gephi JSON) and layout styles (general graph, hierarchical tree, network clustering) for flexible cross-connection visualizations.
Current Implementation Overview
Architecture
The cross-connection relationship resolution is implemented in the following components:
WorkItemRelationshipService
├── ResolveRelationshipsAsync() // Main orchestration
│ ├── ProcessWorkItemRelationshipsAsync() // Processes each work item
│ │ ├── ProcessSingleRelationshipAsync() // Handles native relationships
│ │ └── ProcessHyperlinksAsync() // Handles hyperlink-based relationships
│ └── ExtractWorkItemInfoFromUrl() // Parses Azure DevOps URLs
│
RelationshipFilter
├── IncludeCrossConnection. (bool) // Enable/disable cross-org traversal
├── ResolveHyperlinks (bool) // Enable/disable hyperlink resolution
├── ResolveFieldReferences (bool) // Enable/disable field reference extraction
├── LimitToConnections (List<string>) // Restrict to specific connections
├── MaxDepth (int?) // Control traversal depth
└── RelationshipTypes (List<string>) // Filter by relationship type
WorkItemRelationship
├── IsCrossConnection (bool) // Identifies cross-connection links
├── SourceConnection (string) // Source connection name
├── TargetConnection (string) // Target connection name
├── RelationType (string) // Type (including "Hyperlink-WorkItem")
└── IsExternal (bool) // Alternative cross-connection flag
Key Features
1. Multi-Connection Management
The tool supports multiple Azure DevOps organizations, each with independent authentication:
- Connection per Organization: Each Azure DevOps organization is configured as a separate connection
- Multi-Tenant Support: Organizations can be in different Entra ID tenants
- Flexible Authentication: Supports PAT tokens, OAuth2 Device Code Flow, and Interactive Browser authentication
- Per-Organization Credentials: Each connection has its own credential storage
2. Relationship Resolution Engine
Key Capabilities:
- Depth-Limited Traversal: Configurable maximum depth to control recursive relationship resolution
- Cycle Detection: Tracks visited items to prevent infinite loops in circular relationships
- Cross-Org Filtering: Can include or exclude cross-organization relationships
- Partial Success: Continues resolution even if some organizations are unreachable
- Multi-Connection Traversal: Automatically switches between connections when following relationships
Remote Work Link Types Support
Full support for Azure DevOps Remote Work Link Types, which are designed specifically for cross-organizational relationships.
What are Remote Work Link Types?
Remote Work Link Types are native Azure DevOps link types that enable work items to be linked across different organizations managed by the same Microsoft Entra ID (formerly Azure AD). These are different from both regular work item links (which only work within the same organization) and hyperlinks.
Available Remote Link Types:
| Link Type | Reference Name | Display Name | Topology | Use Case |
|---|---|---|---|---|
| Remote Related | System.LinkTypes.Remote.Related | Remote-Related | Network | Nondirectional link to relate work items across organizations |
| Produces For | System.LinkTypes.Remote.Dependency-Forward | Produces-For | Dependency | Directional link indicating one organization produces work for another |
| Consumes From | System.LinkTypes.Remote.Dependency-Reverse | Consumes-From | Dependency | Reverse of Produces For |
Key Characteristics:
- Cross-Organizational: Designed specifically for linking work items in different Azure DevOps organizations
- Same Entra ID Required: Both organizations must be managed by the same Microsoft Entra ID tenant
- Native Support: Unlike hyperlinks, these are first-class work item links recognized by Azure DevOps
- Counted Separately: The “Remote Link Count” field tracks these relationships
Visual Distinction:
The tool provides distinct visualization for Remote Work Link Types:
- Remote-Related: Dark orange color with bold lines (distinct from regular Related links)
- Produces-For / Consumes-From: Dark green color with bold-dotted lines (distinct from regular Dependencies)
- Clear Labels: Edges are labeled with friendly names (“Remote Related”, “Produces For”, “Consumes From”)
How azdw Handles Remote Work Link Types:
The tool automatically:
- Recognizes Remote Work Link Types when parsing work item relationships from Azure DevOps
- Maps them to friendly names (Remote-Related, Produces-For, Consumes-From)
- Identifies them as cross-organizational relationships (IsExternal = true)
- Visualizes them with distinct colors and styles in GraphViz outputs
- Tracks them separately in relationship statistics
Example Use Case:
Organization A (Shared Services Team)
┌────────────────────────────────┐
│ Work Item 100 │
│ Type: API Service │────────────────┐
│ │ │
└────────────────────────────────┘ │
│ Produces-For
│
▼
┌────────────────────────────────┐
│ Work Item 200 │
│ Type: Product Feature │
│ │
└────────────────────────────────┘
Organization B (Product Team)
In this scenario:
- Organization A provides shared services (API, infrastructure)
- Organization B builds products that consume these services
- The “Produces-For” remote link explicitly captures this dependency relationship
- Both organizations can see and track this cross-org dependency
- azdw visualizes this with a dark green, bold-dotted line labeled “Produces For”
Requirements:
- Both organizations must be in the same Microsoft Entra ID tenant
- User must have appropriate permissions in both organizations
- azdw must have connections configured for both organizations
Comparison with Other Cross-Org Approaches:
| Approach | Native Support | Works Cross-Tenant | Counted in Fields | Requires Same Entra ID |
|---|---|---|---|---|
| Remote Work Link Types | ✅ Yes | ❌ No | ✅ Yes (Remote Link Count) | ✅ Required |
| Hyperlink Resolution | ⚠️ Partial | ✅ Yes | ✅ Yes (Hyperlink Count) | ❌ Not required |
| Regular Work Links | ✅ Yes | ❌ No | ✅ Yes (Related Link Count) | ✅ Same org only |
When to Use Remote Work Link Types:
- Organizations are in the same Entra ID tenant
- You want native Azure DevOps relationship tracking
- You need bidirectional visibility of cross-org relationships
- You want Azure DevOps to enforce relationship constraints
When to Use Hyperlink Resolution:
- Organizations are in different Entra ID tenants
- You need maximum flexibility
- You’re working with legacy systems or external partners
- Organizations don’t have native Remote Work Link Type support enabled
3. Hyperlink Resolution Feature
The most significant feature for cross-organization scenarios is the automatic hyperlink resolution.
What This Means:
When users add a hyperlink to a work item in Organization A that points to a work item in Organization B (or a TFS server), the tool:
- Detects the hyperlink during relationship resolution
- Parses the Azure DevOps or TFS URL to extract organization/collection, project, and work item ID
- Converts the hyperlink to a relationship of type
Hyperlink-WorkItem - Resolves the target work item using the appropriate connection
- Traverses recursively if depth limits allow
Supported URL Formats:
Supports:
- Legacy format:
https://{org}.visualstudio.com/{project}/_workitems/edit/{id} - Modern format:
https://dev.azure.com/{org}/{project}/_workitems/edit/{id} - TFS format:
https://{host}/tfs/{collection}/{project}/_workitems/edit/{id}(HTTP also supported)
Creating Hyperlinks (write path):
In addition to resolving existing hyperlinks, the tool can create hyperlinks on work items. This is the recommended way to establish a cross-organization link from an Azure DevOps work item to a target in a different tenant or provider (for example a GitHub Enterprise issue), where native Remote Work Link Types are unavailable. The target URL is arbitrary, so any HTTP(S) resource can be linked. Once created, the hyperlink is picked up by the resolution feature above on the next traversal.
The capability is available across all surfaces:
- CLI:
azdw workitem update <id> --connection <conn> --add-hyperlink <url> [--hyperlink-comment <text>]and--remove-hyperlink <url>(both accept multiple values). - Work item spec files: a relation entry with
"type": "hyperlink"and"targetUrl": "<url>". - MCP tools:
AddWorkItemHyperlink/RemoveWorkItemHyperlink(approval-gated, high risk). - REST API:
POST /api/v1/workitems/{sourceId}/hyperlinksandDELETE /api/v1/workitems/{sourceId}/hyperlinks. - GraphQL:
addHyperlink/removeHyperlinkmutations.
3.1. Field-Based Work Item Reference Resolution
In addition to hyperlink resolution, the tool supports automatic extraction of work item references from custom field values. This feature is particularly useful when work items contain cross-references stored in custom fields rather than hyperlinks.
Use Case Example:
A work item may have a custom field like MyCompany.Defect.ExternalID with a value such as ABC.705271. This value encodes:
- A prefix (
ABC) that identifies the source system or organization - A work item ID (
705271) that identifies the specific work item ID
How It Works:
- Configuration: Define field mappings in
field-reference-mappings.jsoncspecifying:- Which field types to scan (e.g.,
MyCompany.Defect.ExternalID) - A regex pattern to extract prefix and ID (default:
^(?<prefix>[A-Za-z]*)\W*(?<id>\d+)$) - Optional prefix-to-connection mappings for ambiguous prefixes
- Which field types to scan (e.g.,
- Extraction: During relationship resolution with
--resolve-field-refs:- Configured fields are scanned for values matching the pattern
- Prefix and ID are extracted using named regex groups
- Resolution: The connection is resolved using this priority:
- Exact match of prefix against connection names
- Case-insensitive match against connection names
- Lookup in explicit prefix-to-connection mappings
- Relationship Creation: If a matching work item is found, a relationship of type
Hyperlink-WorkItemis created (same as hyperlink-resolved relationships)
Configuration File (config/field-reference-mappings.jsonc):
{
"$schema": "./field-reference-mappings-schema.json",
"fieldMappings": [
{
"fieldType": "MyCompany.Defect.ExternalID",
"pattern": "^(?<prefix>[A-Za-z]*)\\W*(?<id>\\d+)$",
"prefixMappings": {
"ABC": "myConn1"
}
},
{
"fieldType": "Custom.ExternalReference",
"prefixMappings": {
"LEGACY": "legacy-tfs",
"PARTNER": "partner-org"
}
}
]
}
CLI Usage:
# Enable field reference resolution
# Cross-connection and hyperlink resolution are enabled by default
azdw relationship resolve \
--ids 12345 \
--connection MyOrg \
--resolve-field-refs \
--output relationships.json
# Visualization with field references
azdw visualize graph \
--ids 400 \
--connection MyOrg \
--format graphviz \
--resolve-field-refs \
--output graph.dot
Resolution Priority:
When resolving a prefix to a connection, the following order is used:
- Exact match: If the prefix exactly matches a connection name (case-sensitive)
- Case-insensitive match: If the prefix matches a connection name ignoring case
- Prefix mapping: If the prefix is defined in the
prefixMappingsfor that field type
Example Resolution Flow:
Field: MyCompany.Defect.ExternalID = "ABC.705271"
1. Extract: prefix="ABC", id=705271
2. Resolve connection:
- Check: Is "ABC" an exact connection name? → No
- Check: Is "abc" a connection name (case-insensitive)? → No
- Check: Is "ABC" in prefixMappings? → Yes, maps to "myConn1"
3. Use connection "myConn1" to search for work item 705271
4. If found, create Hyperlink-WorkItem relationship
Error Handling:
- If a configured field is not found on a work item, it is silently skipped
- If the pattern doesn’t match the field value, a warning is logged
- If no connection can be resolved for a prefix, a warning is logged
- If the target work item is not found, a warning is logged
- Errors on individual fields don’t stop processing of other fields
Key Differences from Hyperlink Resolution:
| Aspect | Hyperlink Resolution | Field Reference Resolution |
|---|---|---|
| Source | URL in hyperlink field | Value in any configured field |
| Enabled by | Enabled by default (use --no-resolve-hyperlinks to disable) | --resolve-field-refs |
| Configuration | Built-in URL parsing | Requires field-reference-mappings.jsonc |
| Connection Matching | By organization name in URL | By prefix mapping or connection name |
| Default State | Enabled | Disabled (must be explicitly enabled) |
3.2. File-Based Relationship Mapping
In addition to resolving hyperlinks that point to Azure DevOps work items, the tool supports mapping hyperlinks to non-work-item resources as navigable relationships. This is particularly valuable for architecture artifacts stored outside work items:
- Architecture Decision Records (ADRs) stored as Markdown files in Git repositories
- Wiki Pages containing design documentation
- API Specifications (OpenAPI, AsyncAPI) in Git repos
- Architecture Diagrams (PlantUML, C4, Mermaid) stored in repos
- External Documentation in SharePoint, Confluence, or other systems
How It Works:
- Configuration: Define file relationship mappings in
config/file-relationship-config.jsoncspecifying:- URL patterns (regex) to match specific file types or locations
- Work item type to assign to matched files (e.g., “Architecture Decision Record”)
- Field mappings to extract organization, project, and metadata from URL captures
- Processing: During relationship resolution (hyperlink resolution is enabled by default):
- Hyperlinks that don’t match Azure DevOps work item URLs are tested against file patterns
- Matching URLs are converted to “fake” work items with synthetic IDs (starting from 900000)
- Organization and project are derived from URL captures or explicit field mappings
- Output: File relationships appear in JSON output with full metadata:
targetWorkItemType: The configured type (e.g., “Architecture Decision Record”)targetUrl: The original file URLtargetOrganization/targetProject: Derived from URL or field mappingstargetWorkItemId: Synthetic ID for the file reference
Configuration File (config/file-relationship-config.jsonc):
{
"$schema": "./file-relationship-config-schema.json",
"mappings": [
{
"name": "Azure DevOps Git ADR",
"urlPattern": "https://dev\\.azure\\.com/(?<Organization>[^/]+)/(?<Project>[^/]+)?/?_git/(?<Repo>[^/?]+)\\?path=(?<Path>[^&]*\\.md)",
"workItemType": "Architecture Decision Record",
"fieldMappings": {
"Organization": "${Organization}",
"Project": "${Project}",
"Title": "${Path}"
}
},
{
"name": "Azure DevOps Wiki",
"urlPattern": "https://dev\\.azure\\.com/(?<Organization>[^/]+)/(?<Project>[^/]+)/_wiki/wikis/(?<Wiki>[^/]+)/(?<PageId>\\d+)/(?<PageName>.+)",
"workItemType": "Wiki Page",
"fieldMappings": {
"Organization": "${Organization}",
"Project": "${Project}",
"Title": "${PageName}"
}
}
]
}
CLI Usage:
# Resolve relationships including file-based mappings
# Cross-connection and hyperlink resolution are enabled by default
azdw relationship resolve \
--ids 12345 \
--connection MyOrg \
--json
# With file content extraction (e.g., to get ADR status from file content)
azdw relationship resolve \
--ids 12345 \
--connection MyOrg \
--resolve-file-content \
--json
# Filter output to show only file-based relationships
azdw relationship resolve \
--ids 12345 \
--connection MyOrg \
--json | jq '[.relationships[] | select(.target.targetWorkItemId >= 900000)]'
Example Output:
{
"source": {
"sourceWorkItemId": 12345,
"sourceTitle": "Implement Authentication Service"
},
"target": {
"targetOrganization": "MyOrg",
"targetProject": "Architecture",
"targetWorkItemId": 900001,
"targetWorkItemType": "Architecture Decision Record",
"targetTitle": "/docs/adr/ADR-0042-authentication-approach.md",
"targetUrl": "https://dev.azure.com/MyOrg/Architecture/_git/adr-repo?path=/docs/adr/ADR-0042-authentication-approach.md"
},
"relationType": "Hyperlink-WorkItem",
"isCrossConnection": false
}
Key Differences from Work Item Hyperlink Resolution:
| Aspect | Work Item Hyperlinks | File-Based Relationships |
|---|---|---|
| Target | Azure DevOps work item | Git file, Wiki page, external doc |
| ID Generation | Real work item ID | Synthetic ID (900000+) |
| Configuration | Built-in URL parsing | Requires file-relationship-config.jsonc |
| Use Case | Cross-org work item links | ADRs, design docs, architecture artifacts |
| Traversal | Can follow recursively | Terminal nodes (no further traversal) |
⚠️ PAT Permission for Content Extraction: When using
--resolve-file-contentto extract field values from Git files (e.g., ADR status), your PAT requires the Code (Read) scope in addition to the standard Work Items scopes. See Azure DevOps Permissions for details.
Architecture Governance Use Case:
Organizations following ADR practices (see adr.github.io) often store Architecture Decision Records as Markdown files in Git. File-based relationship mapping allows:
- Work items (Features, Epics) to link to ADRs via hyperlinks
azdw relationship resolvediscovers these ADR links automatically (hyperlink resolution is enabled by default)- Output includes ADRs as navigable relationships with derived metadata
- Architecture governance dashboards can report on ADR coverage
4. Cross-Organizational Filtering
The RelationshipFilter provides fine-grained control.
5. Relationship Metadata
Each resolved relationship contains comprehensive metadata:
CLI Usage
Basic Relationship Resolution
# Resolve relationships for specific work items
# Cross-connection resolution and hyperlink resolution are enabled by default
azdw relationship resolve \
--ids 12345 12346 \
--connection MyOrg \
--max-depth 3 \
--output relationships.json
Cross-Organizational Queries
# Include cross-connection relationships with specific types
# Cross-connection resolution is enabled by default
azdw relationship resolve \
--ids 100 \
--connection OrgA \
--relationship-types "Hierarchy" "Related" "Hyperlink-WorkItem" \
--max-depth 5 \
--output cross-org-graph.json
# Limit to specific organizations
azdw relationship resolve \
--ids 200 \
--connection OrgA \
--limit-conns OrgA OrgB OrgC \
--output limited-connections.json
Relationship Analysis
# Analyze relationship patterns
# Cross-connection resolution is enabled by default
azdw relationship analyze \
--ids 300 \
--connection MyOrg \
--output analysis.json
Visualization
The unified visualize graph command supports multiple formats and layouts for cross-connection visualizations.
Note: Cross-connection relationship resolution is enabled by default. Use
--no-cross-connto disable it if needed.
# Generate GraphViz visualization with cross-org grouping
azdw visualize graph \
--ids 400 \
--connection MyOrg \
--format graphviz \
--by-conn \
--show-legend \
--output graph.dot
# Generate cross-org network diagram (JSON format for D3.js)
azdw visualize graph \
--ids 500 \
--connection MyOrg \
--format graph \
--by-conn \
--output network.json
# Network layout with GraphViz (for cross-connection clusters)
azdw visualize graph \
--ids 500 \
--connection MyOrg \
--format graphviz \
--layout network \
--by-conn \
--output network.dot
# Hierarchical tree layout with GraphViz
azdw visualize graph \
--ids 600 \
--connection MyOrg \
--format graphviz \
--layout tree \
--direction tb \
--output tree.dot
CLI Options Reference
Relationship Command Options
From RelationshipCommand.cs:
| Option | Aliases | Description | Default |
|---|---|---|---|
--no-cross-conn | Disable cross-connection relationship resolution (enabled by default) | N/A (enabled) | |
--no-resolve-hyperlinks | Disable hyperlink resolution to work items (enabled by default) | N/A (enabled) | |
--resolve-field-refs | Resolve work item references from custom fields | false | |
--limit-conns | Limit resolution to specific connections | ||
--max-depth | -d | Maximum relationship depth to traverse | |
--relationship-types | -r | Filter by relationship types | |
--ids | -w | Work item IDs to start from | |
--connection | -c | Source organization connection | |
--output | -o | Output file path (JSON) |
Note: Cross-connection relationship resolution and hyperlink resolution are enabled by default. Use the negation options (--no-cross-conn, --no-resolve-hyperlinks) to disable these features when needed.
Visualization Command Options
From VisualizationCommand.cs:
| Option | Aliases | Description | Default |
|---|---|---|---|
--ids | -w | Work item IDs to visualize | |
--connection | -c, --conn | Azure DevOps connection name | |
--format | -f | Output format: graphviz (DOT) or graph (JSON) | graphviz |
--layout | Layout style: graph, tree, network (DOT only) | graph | |
--direction | -r | Layout direction: tb, lr, bt, rl (DOT only) | tb |
--max-depth | -d | Maximum relationship depth to traverse | |
--relationship-types | Specific relationship types to include | ||
--hierarchy-only | --hierarchy | Include only hierarchical parent-child relationships | false |
--dependencies-only | --dependencies | Include only dependency relationships | false |
--related-only | --related | Include only Related links | false |
--cross-conn-only | Include only cross-connection relationships | false | |
--all-relationships | --all | Include all relationship types | true |
--no-cross-conn | Disable cross-connection relationship resolution (enabled by default) | N/A (enabled) | |
--resolve-field-refs | Resolve work item references from custom fields | false | |
--by-conn | Group results by connection | false | |
--by-project | Group results by project | false | |
--exclude-disabled-types | Exclude disabled work item types | false | |
--show-legend | Include legend in visualization (DOT only) | false | |
--title | Title for the visualization | ||
--color-map | Path to JSON color map file | ||
--node-fields | Additional work item fields to display in nodes | ||
--include-pii | Include personal information like AssignedTo | false | |
--output | -o | Output file path (defaults to stdout) |
Programmatic Usage
.NET Library API
using Azdw.Lib.Services;
using Azdw.Lib.Models;
// Setup service (typically via DI)
var relationshipService = serviceProvider.GetService<WorkItemRelationshipService>();
// Define filter for cross-org resolution
var filter = new RelationshipFilter
{
IncludeCrossOrganizational = true,
ResolveHyperlinks = true,
MaxDepth = 5,
RelationshipTypes = new List<string>
{
"System.LinkTypes.Hierarchy-Forward",
"System.LinkTypes.Related",
"Hyperlink-WorkItem"
},
LimitToOrganizations = new List<string> { "OrgA", "OrgB", "OrgC" }
};
// Resolve relationships
var result = await relationshipService.ResolveRelationshipsAsync(
workItemIds: new List<int> { 12345, 12346 },
connectionName: "OrgA",
filter: filter);
// Process results
Console.WriteLine($"Found {result.TotalRelationshipsFound} relationships");
Console.WriteLine($"Connections involved: {string.Join(", ", result.ConnectionsInvolved)}");
foreach (var relationship in result.Relationships)
{
if (relationship.IsCrossConnection)
{
Console.WriteLine($"Cross-org: {relationship.SourceConnection}:{relationship.SourceWorkItemId} " +
$"-> {relationship.TargetConnection}:{relationship.TargetWorkItemId}");
}
}
REST API
POST /api/relationships/resolve
Content-Type: application/json
{
"workItemIds": [12345, 12346],
"connectionName": "OrgA",
"filter": {
"includeCrossConnection": true,
"resolveHyperlinks": true,
"maxDepth": 5,
"limitToConnections": ["OrgA", "OrgB", "OrgC"]
}
}
GraphQL API
query GetRelationships {
workItem(connectionName: "OrgA", id: 12345) {
id
title
relations {
relationType
targetWorkItemId
targetConnectionUrl
isCrossConnection
}
}
}
How Cross-Organizational Relationships Work
Scenario 1: Native Azure DevOps Relationships
When: Both work items are in the same organization or have federation enabled
Organization A (Project X) Organization A (Project Y)
┌────────────────────┐ ┌────────────────────┐
│ Work Item 100 │────────────>│ Work Item 200 │
│ Type: Epic │ Related │ Type: Feature │
└────────────────────┘ └────────────────────┘
Resolution:
- Tool queries work item 100 from Org A
- Finds native “Related” link to work item 200
- Uses same connection (Org A) to fetch work item 200
- Creates
WorkItemRelationshipwithIsCrossConnection = false(same org, different projects)
Scenario 2: Cross-Org via Hyperlinks
When: Work items are in different organizations (most common for cross-tenant scenarios)
Organization A Organization B
┌────────────────────┐ ┌────────────────────┐
│ Work Item 100 │ │ Work Item 300 │
│ Type: Epic │ │ Type: Epic │
│ │ │ │
│ Hyperlinks: │ │ │
│ • https://dev. │─────────────> │
│ azure.com/orgb/ │ │ │
│ proj/_workitems/ │ │ │
│ edit/300 │ │ │
└────────────────────┘ └────────────────────┘
Resolution:
- Tool queries work item 100 from Org A
- Finds “Hyperlink” entry with Azure DevOps URL
- Parses URL to extract:
org=orgb,project=proj,id=300 - Looks up connection for “orgb” in configured connections
- Uses Org B connection to fetch work item 300
- Creates
WorkItemRelationshipwith:RelationType = "Hyperlink-WorkItem"IsCrossConnection = trueSourceOrganization = "orga"TargetOrganization = "orgb"ResolvedAutomatically = true
Scenario 3: Multi-Level Cross-Org Hierarchy
Complex hierarchies spanning multiple organizations and TFS servers:
Org A (Tenant 1) TFS Server (On-Prem) Org C (Tenant 1)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Epic 100 │──link──> │ Epic 200 │──link──> │ Feature 300 │
│ │ │ │ │ │
│ Hyperlinks: │ │ Hyperlinks: │ │ │
│ • WI 200 │ │ • WI 300 │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
│
│ Child
v
┌─────────────┐
│ Task 400 │
│ │
└─────────────┘
Resolution with MaxDepth = 3:
- Start: Work item 100 (Org A)
- Depth 0→1: Resolve hyperlink to 200 (TFS Server) - uses TFS connection
- Depth 1→2: Resolve hyperlink to 300 (Org C) - switches to Org C connection
- Depth 2→3: Resolve native child to 400 (Org C) - same connection
- Result: 3 relationships spanning 2 cloud organizations and 1 TFS server
Tracked Relationships:
100 (OrgA) --[Hyperlink-WorkItem]--> 200 (TFS:DefaultCollection)✓ Cross-org200 (TFS:DefaultCollection) --[Hyperlink-WorkItem]--> 300 (OrgC)✓ Cross-org300 (OrgC) --[Child]--> 400 (OrgC)✗ Same org
TFS (Team Foundation Server) Support
The tool fully supports TFS work item URLs in hyperlink-based relationships, enabling hybrid scenarios where work items span both cloud Azure DevOps organizations and on-premises TFS servers.
TFS URL Format
TFS work item URLs follow the pattern:
http(s)://{host}/tfs/{collection}/{project}/_workitems/edit/{id}
Examples:
https://tfs.example.com/tfs/DefaultCollection/MyProject/_workitems/edit/123http://tfs.local/tfs/Engineering/Legacy/_workitems/edit/456https://devops.internal.corp/tfs/ProductTeam/Current/_workitems/edit/789
TFS Connection Configuration
Configure TFS servers just like Azure DevOps organizations:
# Add TFS connection with a friendly name
azdw connection add \
--name MyLegacyTFS \
--url https://tfs.example.com/tfs/DefaultCollection \
--auth-type pat
# Add credentials
azdw credential add-pat \
--connection MyLegacyTFS \
--token <tfs-pat-token>
Note: Also for TFS URLs the tool automatically extracts the organization/collection name from the connection’s BaseUrl to match against hyperlink URLs.
How Connection Matching Works
When resolving cross-connection relationships from hyperlinks, the tool:
- Extracts the organization/collection name from the hyperlink URL:
- Azure DevOps cloud:
https://dev.azure.com/{org}/...→ extracts “org” - Azure DevOps legacy:
https://{org}.visualstudio.com/...→ extracts “org” - TFS on-premises:
https://host/tfs/{collection}/...→ extracts “collection”
- Azure DevOps cloud:
- Extracts the organization/collection name from each connection’s BaseUrl:
- For connection “MyOrgA” with BaseUrl
https://dev.azure.com/orga→ extracts “orga” - For connection “LegacyTFS” with BaseUrl
https://tfs.corp/tfs/Engineering→ extracts “Engineering”
- For connection “MyOrgA” with BaseUrl
- Matches by comparing the extracted names (case-insensitive):
- Hyperlink
https://dev.azure.com/orga/...matches connection with BaseUrlhttps://dev.azure.com/orga - Hyperlink
https://tfs.corp/tfs/Engineering/...matches connection with BaseUrlhttps://tfs.corp/tfs/Engineering
- Hyperlink
This approach allows you to use descriptive connection names like “Production-TFS”, “Legacy-Engineering”, or “Partner-Org” while the tool correctly resolves relationships based on the actual organization/collection names in the URLs.
Hybrid Cloud + TFS Scenarios
Scenario: Cloud-to-TFS Relationship
Azure DevOps (Cloud) TFS Server (On-Prem)
┌──────────────────────┐ ┌──────────────────────┐
│ Work Item 100 │ │ Work Item 200 │
│ Type: Epic │ │ Type: Feature │
│ Org: CloudOrg │ │ Collection: Legacy │
│ │ │ Project: OldSystem │
│ Hyperlinks: │─────────────> │
│ • https://tfs.corp/ │ │ │
│ tfs/Legacy/ │ │ │
│ OldSystem/ │ │ │
│ _workitems/ │ │ │
│ edit/200 │ │ │
└──────────────────────┘ └──────────────────────┘
Setup:
# Configure cloud connection (friendly name can be anything)
azdw connection add \
--name "Production-Cloud" \
--url https://dev.azure.com/cloudorg
# Configure TFS connection (friendly name can be descriptive)
azdw connection add \
--name "Legacy-Engineering-TFS" \
--url https://tfs.corp/tfs/Legacy
CLI Resolution:
# Query relationships across cloud and TFS
# Cross-connection and hyperlink resolution are enabled by default
azdw relationship resolve \
--ids 100 \
--connection "Production-Cloud" \
--max-depth 3
Result:
- Automatically detects TFS URL in hyperlink:
https://tfs.corp/tfs/Legacy/OldSystem/_workitems/edit/200 - Extracts collection “Legacy” from URL
- Matches against connection with BaseUrl containing
/tfs/Legacy/ - Finds “Legacy-Engineering-TFS” connection (because its BaseUrl is
https://tfs.corp/tfs/Legacy) - Resolves work item 200 from TFS server
- Creates cross-connection relationship
TFS-Specific Considerations
-
Collection as Organization: For TFS URLs, the collection name is extracted from the URL path (e.g.,
https://tfs.corp/tfs/Engineering→ “Engineering”). The tool automatically matches this against configured connections by comparing BaseUrl values, regardless of the connection’s friendly name. -
HTTP Support: Both
https://andhttp://protocols are supported for TFS URLs. -
Custom Domains: TFS can be hosted on any domain (e.g.,
tfs.internal.corp,devops.company.com). The URL pattern is flexible. -
Authentication: TFS connections support the same authentication methods as Azure DevOps (PAT tokens, NTLM, etc.).
-
API Compatibility: The tool uses Azure DevOps REST API, which is compatible with TFS 2015 Update 2 and later.
Example: Multi-Org + TFS Query
# Configure connections with descriptive friendly names
azdw connection add --name "Production-Azure" --url https://dev.azure.com/orga
azdw connection add --name "Partner-Org" --url https://dev.azure.com/orgb
azdw connection add --name "Legacy-TFS-Main" --url https://tfs.corp/tfs/DefaultCollection
# Resolve relationships across all (use friendly names in CLI)
# Cross-connection and hyperlink resolution are enabled by default
azdw relationship resolve \
--ids 1234 \
--connection "Production-Azure" \
--max-depth 5 \
--output hybrid-graph.json
What happens:
- Tool queries work item 1234 from “Production-Azure” connection
- Finds hyperlink:
https://dev.azure.com/orgb/Project/_workitems/edit/5678- Extracts “orgb” from URL
- Matches against connection with BaseUrl containing “orgb”
- Finds “Partner-Org” connection → resolves work item 5678
- Finds hyperlink:
https://tfs.corp/tfs/DefaultCollection/Legacy/_workitems/edit/9012- Extracts “DefaultCollection” from URL
- Matches against connection with BaseUrl containing “DefaultCollection”
- Finds “Legacy-TFS-Main” connection → resolves work item 9012
This resolves relationships across two cloud organizations and one TFS server, following hyperlink references across all three environments, regardless of connection names.
Scenario 4: Multi-Level Cross-Org Hierarchy (Legacy Naming)
Visualization Support
GraphViz Rendering
The tool generates visual diagrams with special handling for cross-org relationships:
From GraphVizRenderer.cs (Lines 468-504):
// Color coding
"Hyperlink-WorkItem" => "orange" // Special color for hyperlink-based relationships
// Line style
"Hyperlink-WorkItem" => "dashed" // Dashed lines for hyperlinks
// Label formatting
"Hyperlink-WorkItem" => "Link" // Display name
Organization Grouping:
When --by-conn is enabled, work items are visually grouped by organization using GraphViz subgraphs:
digraph "Cross-Org Hierarchy" {
rankdir=TB;
subgraph cluster_OrgA {
label="Organization A";
style=filled;
color=lightgrey;
node100 [label="100: Epic\nOrg A"];
}
subgraph cluster_OrgB {
label="Organization B";
style=filled;
color=lightblue;
node200 [label="200: Epic\nOrg B"];
}
node100 -> node200 [label="Link", style=dashed, color=orange];
}
Interactive HTML Dashboards
Cross-connection relationships are highlighted in interactive visualizations with:
- Color-coded nodes by organization
- Hover tooltips showing source/target org
- Filtering by organization
- Cross-org relationship statistics
Configuration
Connection Setup
Each organization requires a separate connection configuration. Important: Connection names are completely independent from organization/collection names and can be anything you choose for clarity and convenience.
Example 1: Descriptive Connection Names
# Production Azure DevOps (use descriptive name)
azdw connection add \
--name "Production-Main" \
--url https://dev.azure.com/contoso-prod \
--auth-type pat
# Partner Organization (use descriptive name)
azdw connection add \
--name "Partner-Fabrikam" \
--url https://dev.azure.com/fabrikam \
--auth-type code \
--tenant <tenant-id>
# Legacy TFS (use descriptive name)
azdw connection add \
--name "Legacy-Engineering" \
--url https://tfs.corp/tfs/DefaultCollection \
--auth-type pat
How Matching Works:
- Hyperlink:
https://dev.azure.com/contoso-prod/...→ Matches “Production-Main” (BaseUrl contains “contoso-prod”) - Hyperlink:
https://dev.azure.com/fabrikam/...→ Matches “Partner-Fabrikam” (BaseUrl contains “fabrikam”) - Hyperlink:
https://tfs.corp/tfs/DefaultCollection/...→ Matches “Legacy-Engineering” (BaseUrl contains “DefaultCollection”)
Example 2: Simple Connection Names
# You can also use simple names if you prefer
azdw connection add \
--name OrgA \
--url https://dev.azure.com/orga \
--auth-type pat
azdw credential add-pat \
--connection OrgA \
--token <pat-token>
# Organization B (Tenant 2, OAuth)
azdw connection add \
--name OrgB \
--url https://dev.azure.com/orgb \
--auth-type code \
--tenant <tenant-id>
azdw credential add-code \
--connection OrgB \
--tenant <tenant-id>
Matching Still Works:
- Connection name “OrgA” with BaseUrl
https://dev.azure.com/orga - Hyperlink
https://dev.azure.com/orga/...→ Matches because BaseUrl contains “orga” - The connection name doesn’t have to match the organization name!
Multi-Tenant Authentication
For cross-tenant scenarios:
- Device Code Flow: Recommended for automated scenarios
azdw credential add-code \ --connection OrgB \ --tenant <tenant-id-or-name> # Displays code to enter in browser - Interactive Browser: Recommended for interactive use
azdw credential add-interactive \ --connection OrgB \ --tenant <tenant-id-or-name> # Opens browser automatically - PAT Tokens: Simplest but requires manual management
azdw credential add-pat \ --connection OrgB \ --token <pat-token>
Permissions Required
For cross-connection relationship resolution:
| Operation | Required Permission | Scope |
|---|---|---|
| Read work items | vso.work (read) | Each organization |
| Read relationships | vso.work (read) | Each organization |
| Cross-org resolution | Same permissions on all orgs | All involved orgs |
See Azure DevOps Permissions for detailed permission requirements.
Limitations and Considerations
Current Limitations
- Manual Hyperlink Creation:
- Users must manually add hyperlinks to reference cross-org work items
- No automated bidirectional sync of hyperlink-based relationships
- One-Way Hyperlinks:
- Hyperlinks are directional (source → target)
- Reverse navigation requires hyperlinks in both directions
- URL Parsing:
- Recognizes standard Azure DevOps URL formats (cloud and on-premises)
- Supports TFS work item URLs (
https://host/tfs/collection/project/_workitems/edit/id) - Custom domains or URL shorteners are not supported
- Connection Required:
- Target organization/collection must be configured as a connection
- Connection matching is automatic based on BaseUrl (connection name can be anything)
- Cannot resolve relationships to unconfigured organizations
- No Hyperlink Creation via API:
- Tool currently focuses on reading/resolving relationships
- Creating hyperlinks would require write operations
Performance Considerations
- API Call Multiplier:
- Each cross-org relationship requires API call to target org
- Depth 3 across 3 orgs = potentially 3× API calls
- Rate Limiting:
- Each organization has independent rate limits
- Built-in rate limiting respects per-org limits
- Authentication Overhead:
- Each organization requires separate authentication
- Token refresh may occur during resolution
- Caching Benefits:
- Optional LiteDB/Cosmos DB caching reduces repeated API calls
- Cache is per-connection, improving cross-org queries
Best Practices
- Use Depth Limits:
var filter = new RelationshipFilter { MaxDepth = 3 // Prevent excessive traversal }; - Limit Organizations:
filter.LimitToOrganizations = new List<string> { "OrgA", "OrgB" }; - Filter Relationship Types:
filter.RelationshipTypes = new List<string> { "System.LinkTypes.Hierarchy-Forward", "Hyperlink-WorkItem" }; - Enable Caching:
- Configure LiteDB for local development
- Use Azure Cosmos DB for production scenarios
- Standardize Hyperlink Usage:
- Establish team conventions for when to use hyperlinks
- Consider adding “CrossOrgLink” tag for discoverability
Future Enhancement Opportunities
While the current implementation is comprehensive, potential enhancements could include:
- Automatic Hyperlink Creation:
- API to create hyperlinks programmatically
- Bulk hyperlink creation for migrated work items
- Bidirectional Sync:
- Automatically create reverse hyperlinks
- Keep cross-org relationships in sync
- Custom Relationship Types:
- User-defined relationship types for hyperlinks
- Semantic labels (e.g., “Depends-On-Cross-Org”)
- Relationship Validation:
- Detect broken hyperlinks (deleted work items)
- Validate cross-org relationship constraints
- Improved URL Parsing:
- Support for URL shorteners
- Custom domain detection
- Relationship Analytics:
- Cross-org dependency metrics
- Organization coupling analysis
- Bottleneck detection in cross-org workflows
- Relationship Suggestions:
- ML-based suggestions for related work items
- Pattern-based relationship recommendations
Conclusion
The azdw tool currently provides comprehensive support for cross-organization work item relationships through:
✅ Multi-connection management with per-connection authentication
✅ Automatic hyperlink detection and resolution as relationships
✅ Field-based reference extraction for custom field cross-references
✅ Deep traversal across organizational boundaries
✅ Flexible filtering to control cross-org resolution
✅ Unified visualization with multiple formats (GraphViz, JSON) and layouts (graph, tree, network)
✅ Connection grouping for clear cross-org visualizations
✅ Comprehensive testing including cross-org scenarios
✅ CLI, REST API, GraphQL, and MCP interfaces
The multi-track approach (native relationships + hyperlink resolution + field reference extraction) enables teams to establish and navigate relationships across organizational boundaries, even in complex multi-tenant, multi-connection environments. The hyperlink-based relationship resolution is particularly powerful as it works without requiring any changes to Azure DevOps configuration – users simply add hyperlinks to work items, and the tool automatically discovers and resolves them as relationships. The field-based reference extraction extends this capability to custom fields that store work item references in various formats.
The unified visualize graph command provides flexible visualization options with support for different output formats (GraphViz DOT for diagrams, JSON for D3.js) and layout styles (general graph, hierarchical tree, network clustering for cross-connections), making it easy to understand complex cross-organizational relationships.
Related Documentation
- API Documentation - REST, GraphQL, and MCP APIs
- Work Item Type Mapping - Cross-template type handling
- Azure DevOps Permissions - Required permissions
- Output Rendering & Automation - Cross-org reporting
- Filtering Query Results - Query filtering across orgs
- CLI Help Overview - Complete CLI reference