Relationship Commands
Overview
The azdw relationship commands provide powerful capabilities for working with work item relationships in Azure DevOps. These commands enable you to:
- Resolve: Discover and map all relationships for work items across connections
- Analyze: Get insights into relationship patterns, cross-organizational connections, and metrics
- Validate: Ensure relationships comply with organizational policies and best practices
- Find-Closure: Compute the complete transitive closure of work item hierarchies with automatic merging of related items
All relationship commands support:
- ✅ Cross-organizational relationship resolution
- ✅ Hyperlink-based work item references
- ✅ File-based relationship mapping (ADRs, Wiki pages, Git files)
- ✅ Multiple output formats (table, JSON)
- ✅ Configurable traversal depth
- ✅ Relationship type filtering
- ✅ Connection-scoped operations
Commands
relationship resolve
Purpose: Discover and resolve all relationships for specified work items, including cross-organizational connections.
Basic Syntax
azdw relationship resolve --ids <IDs> --connection <NAME> [OPTIONS]
Key Options
| Option | Short | Description | Example |
|---|---|---|---|
--ids | -w | Work item IDs (comma-separated) | 34130,34131 |
--connection | -c | Source connection name | Portfolio |
--max-depth | -d | Maximum relationship depth (default: 10) | --max-depth 2 |
--no-cross-conn | Disable cross-connection traversal (enabled by default) | --no-cross-conn | |
--limit-conns | Limit to specific connections | --limit-conns TeamA,TeamB | |
--relationship-types | -t | Filter by relationship types | Hierarchy,Dependency |
--no-resolve-hyperlinks | Disable hyperlink resolution (enabled by default) | --no-resolve-hyperlinks | |
--output | -o | Save to file | --output results.json |
--json | JSON output format | --json | |
--verbose | Detailed logging | --verbose | |
--silent | Minimal output | --silent |
Available Relationship Types
Hierarchy- Parent/Child relationshipsDependency- Predecessor/Successor relationshipsRelated- Related linksRemote- Remote Work Link Types (Remote-Related, Produces-For, Consumes-From) for cross-organizational relationships within the same Entra IDAll- All relationship types (default)
Relationship Type Naming
All commands emit logical relationship type names, never the raw Azure DevOps link reference names. The raw name of each resolved link remains available in the originalRelationshipType field of the JSON output.
| Azure DevOps link reference name | Logical name emitted by azdw |
|---|---|
System.LinkTypes.Hierarchy-Forward | Child |
System.LinkTypes.Hierarchy-Reverse | Parent |
System.LinkTypes.Related | Related |
System.LinkTypes.Dependency-Forward | Successor |
System.LinkTypes.Dependency-Reverse | Predecessor |
System.LinkTypes.Duplicate-Forward | Duplicate |
System.LinkTypes.Duplicate-Reverse | DuplicateOf |
System.LinkTypes.Remote.Related | Remote-Related |
System.LinkTypes.Remote.Dependency-Forward | Produces-For |
System.LinkTypes.Remote.Dependency-Reverse | Consumes-From |
--relationship-types accepts either spelling, so existing scripts that pass System.LinkTypes.* names keep working.
Examples
Basic Resolution
# Resolve relationships for a single work item
azdw relationship resolve --ids 34130 --connection Portfolio
Limited Depth
# Resolve only immediate relationships (depth 1)
azdw relationship resolve --ids 34130 --connection Portfolio --max-depth 1
# Resolve up to 2 levels deep
azdw relationship resolve --ids 34130 --connection Portfolio --max-depth 2
Cross-Organizational Resolution
# Cross-connection traversal is enabled by default
azdw relationship resolve --ids 34130 --connection Portfolio
# Limit to specific connections
azdw relationship resolve --ids 34130 --connection Portfolio \
--limit-conns TeamA,TeamB
Filtered by Relationship Type
# Only hierarchy relationships
azdw relationship resolve --ids 34130 --connection Portfolio \
--relationship-types Hierarchy
# Multiple relationship types
azdw relationship resolve --ids 34130 --connection Portfolio \
--relationship-types Hierarchy,Dependency
With Hyperlink Resolution
# Hyperlink and cross-connection resolution are enabled by default
azdw relationship resolve --ids 34130 --connection Portfolio
Save to File
# Output to JSON file
azdw relationship resolve --ids 34130 --connection Portfolio \
--output relationships.json
# JSON format with cross-connection support (enabled by default)
azdw relationship resolve --ids 34130 --connection Portfolio \
--max-depth 2 --json --output relationships.json
Output Structure
Table Format (default):
=== Relationship Resolution Summary ===
Total relationships found: 99
Organizations involved: contoso-engineering, contoso-portfolio
Cross-connection relationships: 2
JSON Format (--json or file output):
{
"summary": {
"totalRelationships": 99,
"organizationsInvolved": 2,
"crossOrgRelationships": 2,
"processedAt": "2025-11-13T01:51:44.750547Z"
},
"organizations": [
"contoso-engineering",
"contoso-portfolio"
],
"relationships": [
{
"relationType": "Child",
"source": {
"sourceOrganization": "contoso-engineering",
"sourceProject": "Portfolio Management",
"sourceWorkItemId": 34130,
"sourceWorkItemType": "Initiative"
},
"target": {
"targetOrganization": "contoso-engineering",
"targetProject": "Portfolio Management",
"targetWorkItemId": 142775,
"targetWorkItemType": "Epic",
"targetUrl": "https://dev.azure.com/..."
},
"isCrossConnection": false,
"validationIssues": []
}
]
}
relationship analyze
Purpose: Analyze relationship patterns and provide insights about work item connections.
Basic Syntax
azdw relationship analyze --ids <IDs> --connection <NAME> [OPTIONS]
Key Options
| Option | Short | Description | Example |
|---|---|---|---|
--ids | -w | Work item IDs (comma-separated) | 34130,34131 |
--connection | -c | Source connection name | Portfolio |
--max-depth | -d | Maximum relationship depth | --max-depth 1 |
--include-metrics | Include detailed metrics | --include-metrics | |
--output | -o | Save to file | --output analysis.json |
--json | JSON output format | --json |
Examples
Basic Analysis
# Analyze relationships with limited depth
azdw relationship analyze --ids 34130 --connection Portfolio --max-depth 1
With Metrics
# Include detailed metrics
azdw relationship analyze --ids 34130 --connection Portfolio \
--include-metrics --max-depth 1
Save to File
# Save analysis results to JSON
azdw relationship analyze --ids 34130 --connection Portfolio \
--include-metrics --max-depth 2 --output analysis.json
JSON Output
# Direct JSON output
azdw relationship analyze --ids 34130 --connection Portfolio \
--include-metrics --max-depth 1 --json
Output Structure
Table Format (default):
=== Relationship Analysis Summary ===
Total relationships: 99
Organizations: 2
Cross-connection: 2
Relationship types:
Child: 66
Related: 30
Hyperlink-WorkItem: 2
Parent: 1
JSON Format (with --include-metrics):
{
"analysis": {
"overview": {
"totalRelationships": 99,
"crossOrganizationalCount": 2,
"uniqueOrganizations": 2,
"uniqueProjects": 3
},
"relationshipTypes": {
"Child": {
"count": 66,
"crossOrgCount": 0
},
"Related": {
"count": 30,
"crossOrgCount": 0
},
"Hyperlink-WorkItem": {
"count": 2,
"crossOrgCount": 2
}
},
"organizationBreakdown": {
"contoso-engineering": {
"sourceCount": 99,
"targetCount": 97,
"totalCount": 196
},
"contoso-portfolio": {
"sourceCount": 0,
"targetCount": 2,
"totalCount": 2
}
},
},
"metrics": {
"averageRelationshipsPerWorkItem": 99,
"maxRelationshipsPerWorkItem": 99,
"crossOrgPercentage": 2.02,
"hierarchicalRelationships": 67,
"dependencyRelationships": 0,
"otherRelationships": 32
}
}
relationship validate
Purpose: Validate relationships against organizational policies and best practices.
Basic Syntax
azdw relationship validate --ids <IDs> --connection <NAME> --policy-file <PATH> [OPTIONS]
Key Options
| Option | Short | Description | Example |
|---|---|---|---|
--ids | -w | Work item IDs (comma-separated) | 34130,34131 |
--connection | -c | Source connection name | Portfolio |
--policy-file | -p | Path to policy file | --policy-file policy.json |
--max-depth | -d | Maximum relationship depth | --max-depth 1 |
--strict | Fail on any violation | --strict | |
--output | -o | Save to file | --output validation.json |
--json | JSON output format | --json |
Examples
Basic Validation
# Validate with default policy
azdw relationship validate --ids 34130 --connection Portfolio \
--policy-file config/relationship-policy.jsonc --max-depth 1
Strict Mode
# Fail on any policy violation
azdw relationship validate --ids 34130 --connection Portfolio \
--policy-file config/relationship-policy.jsonc --strict --max-depth 1
Save Results
# Save validation results to file
azdw relationship validate --ids 34130 --connection Portfolio \
--policy-file config/relationship-policy.jsonc --max-depth 1 \
--output validation-results.json
Output Structure
Table Format (default):
=== Validation Summary ===
Policy enforcement mode: Warn
Total relationships validated: 99
Policy violations found: 0
✅ Validation PASSED
JSON Format:
{
"summary": {
"totalRelationships": 99,
"validationErrors": 0,
"policyMode": "Warn",
"processedAt": "2025-11-13T01:54:00.037916Z"
},
"policyViolations": {},
"relationshipViolations": []
}
relationship find-closure
Purpose: Find the complete hierarchy closure for one or more work items, computing all ancestors (up to a specified top-level type) and all descendants.
Basic Syntax
azdw relationship find-closure --id <IDs> --connection <NAME> [OPTIONS]
Key Options
| Option | Short | Description | Example |
|---|---|---|---|
--id | -w | Work item ID(s) - comma-separated | -w 100,200,300 |
--connection | -c | Azure DevOps connection name | --connection MyOrg |
--top-type | -t | Top-level work item type (default: Epic) | -t "Initiative" |
--include-top-siblings | Include siblings of top-level items | --include-top-siblings | |
--include-predecessors | Include predecessor relationships | --include-predecessors | |
--no-resolve-hyperlinks | Disable hyperlink resolution (enabled by default) | --no-resolve-hyperlinks | |
--hyperlinks-as | How to interpret hyperlinks (Parent, Child, Related, Dependency) | --hyperlinks-as Parent | |
--no-cross-conn | Disable cross-connection resolution (enabled by default) | --no-cross-conn | |
--limit-conns | Limit cross-connection to specific connections | --limit-conns TeamA,TeamB | |
--max-size | -m | Maximum closure size (1-10000, default: 10000) | -m 500 |
--states-exclude | Work item states to exclude | --states-exclude Closed,Removed | |
--output | -o | Output file path | -o closure.json |
--format | -f | Output format: json, table, or csv | -f json |
Multiple Work Item IDs
The --id option accepts multiple work item IDs as a comma-separated list:
# Comma-separated
azdw relationship find-closure -w 100,200,300 -c MyOrg
Automatic Closure Merging: When multiple work item IDs share relationships or common ancestors, their closures are automatically merged into a single closure. Unrelated work items produce separate closures.
Examples
Single Work Item Closure
# Find closure for a single work item with Epic as top-level type
azdw relationship find-closure -w 12345 -c MyOrg
# Specify a different top-level type
azdw relationship find-closure -w 12345 -c MyOrg -t "Initiative"
Multiple Work Items with Merging
# Find closures for multiple work items
# Related items are automatically merged
azdw relationship find-closure -w 100,200,300 -c MyOrg -t Feature
# Cross-connection and hyperlink resolution are enabled by default
azdw relationship find-closure -w 100,200,300 -c MyOrg
Cross-Connection with Hyperlinks
# Cross-connection and hyperlink resolution are enabled by default
azdw relationship find-closure -w 12345 -c MyOrg \
-t "Initiative"
Save to File
# Output to JSON file
azdw relationship find-closure -w 100,200,300 -c MyOrg \
-f json -o closures.json
# Output to CSV
azdw relationship find-closure -w 100,200,300 -c MyOrg \
-f csv -o closures.csv
Output Structure
Single Work Item (returns ClosureResult):
Closure Status: Complete
Total Items: 15
Start Work Item: 12345
Top-Level Items: 100
Work Items (Hierarchical Order)
===============================
Depth Connection ID Type State Title Reason
---------------------------------------------------------------------------
-1 LPM 100 Epic Active Parent Epic TopLevel
0 MyOrg 12345 Feature Active My Feature StartItem
1 MyOrg 12346 UserStory Active Child Story Descendant
Multiple Work Items (returns MultiClosureResult):
=== Closure 1 of 2 [merged from IDs: 100, 200] ===
Closure Status: Complete
Total Items: 25
Merge Reason: Closures share common work items
Work Items (Hierarchical Order)
...
=== Closure 2 of 2 [ID: 300] ===
Closure Status: Complete
Total Items: 8
Work Items (Hierarchical Order)
...
JSON Format (multiple work items):
{
"closures": [
{
"sourceWorkItemIds": [100, 200],
"isMerged": true,
"mergeReason": "Closures share common work items",
"closure": {
"status": "Complete",
"startWorkItemId": 100,
"totalCount": 25,
"nodes": [...]
}
},
{
"sourceWorkItemIds": [300],
"isMerged": false,
"mergeReason": null,
"closure": {
"status": "Complete",
"startWorkItemId": 300,
"totalCount": 8,
"nodes": [...]
}
}
],
"hasMergedClosures": true,
"invalidWorkItemIds": [],
"processingTimeMs": 1234
}
Inclusion Reasons
Each work item in the closure is tagged with an inclusion reason:
| Reason | Description |
|---|---|
TopLevel | Matches the specified top-level type (e.g., Epic) |
StartItem | The work item ID specified in the request |
Ancestor | Parent or grandparent of the start item |
Descendant | Child or grandchild of the start/top-level item |
Sibling | Sibling of a top-level item (when --include-top-siblings is used) |
Predecessor | Predecessor relationship (when --include-predecessors is used) |
Hyperlink | Discovered via hyperlink resolution |
RemoteLink | Discovered via an Azure DevOps remote work link (Produces-For, Consumes-From, Remote-Related); requires cross-connection traversal, so it is skipped with --no-cross-conn |
Common Options
Performance Options
Max Depth (--max-depth, -d)
- Controls how deep the relationship tree is traversed
- Default: 10 levels
- Recommended: 1-2 for large work items to avoid timeouts
- Use case: Work items with extensive relationships can have hundreds or thousands of connections at depth 2+
# Quick scan - immediate relationships only
--max-depth 1
# Medium scan - 2 levels deep
--max-depth 2
# Deep scan - up to 5 levels
--max-depth 5
Cross-Connection Options
Cross-Connection Traversal (enabled by default)
- Following relationships across different Azure DevOps organizations is enabled by default
- Use
--no-cross-connto disable this behavior - Use
--limit-connsto restrict to specific connections
# Cross-connection is enabled by default
# Limit to specific connections if needed
--limit-conns Connection1,Connection2
# Disable cross-connection if needed
--no-cross-conn
Hyperlink Resolution (enabled by default)
- Resolves hyperlinks that point to Azure DevOps work items
- Also maps file hyperlinks (Git files, Wiki pages, ADRs) to navigable relationships via
file-relationship-config.jsonc - Use
--no-resolve-hyperlinksto disable this behavior
# Hyperlink resolution is enabled by default
# To disable:
--no-resolve-hyperlinks
File Content Extraction (--resolve-file-content / -rfc)
Since hyperlink resolution is enabled by default, this option fetches the actual content of Git-hosted files and extracts field values using regex patterns defined in file-relationship-config.jsonc. This enables:
- Extracting ADR status (e.g., “Accepted”, “Deprecated”) from markdown files
- Populating fake work item fields from file content
# Extract field values from file content (hyperlink resolution is on by default)
azdw relationship resolve --ids 12345 --resolve-file-content
⚠️ Required PAT Permission: To use
--resolve-file-content, your PAT must include the Code (Read) scope. Without this permission, file content fetching will fail with HTTP 401. See Azure DevOps Permissions for details.
File Relationship Configuration
With hyperlink resolution enabled by default, hyperlinks that don’t match Azure DevOps work item URLs are tested against patterns in config/file-relationship-config.jsonc. Matching URLs are converted to “fake” work items with:
- Synthetic IDs starting from 900000
- Work item types based on configuration (e.g., “Architecture Decision Record”)
- Organization/project derived from URL captures
See Cross-Org-Relationship-Handling.md for configuration details.
Output Options
Output Format
# Default table format
azdw relationship resolve --ids 34130 -c Portfolio
# JSON output to console
azdw relationship resolve --ids 34130 -c Portfolio --json
# Save to file (auto-detects JSON format)
azdw relationship resolve --ids 34130 -c Portfolio -o results.json
Verbosity
# Verbose - detailed logging
--verbose
# Silent - minimal output
--silent
Use Cases and Scenarios
1. Impact Analysis
Scenario: Before making changes to a critical work item, understand all dependent work items.
#!/bin/bash
# impact-analysis.sh
WORK_ITEM="34130"
CONNECTION="Portfolio"
OUTPUT_FILE="impact-analysis-${WORK_ITEM}.json"
echo "Analyzing impact for work item ${WORK_ITEM}..."
# Resolve all relationships with depth 2
# Note: cross-conn and resolve-hyperlinks are enabled by default
azdw relationship resolve \
--ids "${WORK_ITEM}" \
--connection "${CONNECTION}" \
--max-depth 2 \
--output "${OUTPUT_FILE}"
# Analyze the results
azdw relationship analyze \
--ids "${WORK_ITEM}" \
--connection "${CONNECTION}" \
--max-depth 2 \
--include-metrics \
--output "analysis-${WORK_ITEM}.json"
echo "Impact analysis complete. Check ${OUTPUT_FILE} and analysis-${WORK_ITEM}.json"
2. Cross-Organizational Dependency Tracking
Scenario: Track dependencies between teams in different Azure DevOps organizations.
#!/bin/bash
# cross-org-dependencies.sh
EPIC_ID="1234"
PRIMARY_CONN="TeamA"
echo "Tracking cross-organizational dependencies for Epic ${EPIC_ID}..."
# Resolve with cross-connection support (enabled by default)
azdw relationship resolve \
--ids "${EPIC_ID}" \
--connection "${PRIMARY_CONN}" \
--max-depth 3 \
--relationship-types Hierarchy,Dependency \
--json \
--output "cross-org-deps-${EPIC_ID}.json"
# Extract cross-org relationships using jq
jq '.relationships[] | select(.isCrossConnection == true) | {
source: .source.sourceWorkItemId,
target: .target.targetWorkItemId,
sourceOrg: .source.sourceOrganization,
targetOrg: .target.targetOrganization,
type: .relationType
}' "cross-org-deps-${EPIC_ID}.json"
3. Relationship Health Check
Scenario: Regularly validate that relationships comply with organizational policies.
#!/bin/bash
# health-check.sh
WORK_ITEMS="34130,34131,34132"
CONNECTION="Portfolio"
POLICY_FILE="config/relationship-policy.jsonc"
echo "Running relationship health check..."
# Validate relationships
azdw relationship validate \
--ids "${WORK_ITEMS}" \
--connection "${CONNECTION}" \
--policy-file "${POLICY_FILE}" \
--max-depth 1 \
--strict \
--output "validation-report.json"
if [ $? -eq 0 ]; then
echo "✅ Health check PASSED"
else
echo "❌ Health check FAILED - see validation-report.json"
exit 1
fi
4. Portfolio Rollup Analysis
Scenario: Analyze all work items under a portfolio epic to understand scope and dependencies.
#!/bin/bash
# portfolio-rollup.sh
PORTFOLIO_EPIC="5000"
CONNECTION="Portfolio"
echo "Analyzing portfolio epic ${PORTFOLIO_EPIC}..."
# Get all hierarchical relationships
azdw relationship resolve \
--ids "${PORTFOLIO_EPIC}" \
--connection "${CONNECTION}" \
--relationship-types Hierarchy \
--max-depth 5 \
--output "portfolio-${PORTFOLIO_EPIC}-hierarchy.json"
# Analyze the portfolio
azdw relationship analyze \
--ids "${PORTFOLIO_EPIC}" \
--connection "${CONNECTION}" \
--max-depth 5 \
--include-metrics \
--json | jq '{
total: .analysis.overview.totalRelationships,
organizations: .analysis.overview.uniqueOrganizations,
projects: .analysis.overview.uniqueProjects,
types: .analysis.workItemTypePatterns
}'
5. Orphaned Work Item Detection
Scenario: Find work items with no parent relationships.
#!/bin/bash
# find-orphans.sh
CONNECTION="MyProject"
WORK_ITEMS="1000,1001,1002,1003,1004"
echo "Checking for orphaned work items..."
azdw relationship resolve \
--ids "${WORK_ITEMS}" \
--connection "${CONNECTION}" \
--relationship-types Hierarchy \
--max-depth 1 \
--json \
--output "hierarchy-check.json"
# Find work items with no Hierarchy-Reverse (no parent)
jq -r '.relationships |
group_by(.source.sourceWorkItemId) |
map({
id: .[0].source.sourceWorkItemId,
hasParent: map(select(.relationType == "Parent")) | length > 0
}) |
map(select(.hasParent == false)) |
.[].id' hierarchy-check.json
Advanced Examples
Multi-Connection Relationship Map
Create a comprehensive relationship map across multiple connections:
#!/bin/bash
# multi-connection-map.sh
WORK_ITEM="34130"
PRIMARY_CONN="Portfolio"
ALLOWED_CONNS="Portfolio,TeamA,TeamB"
echo "Creating multi-connection relationship map..."
# Resolve across all connections (cross-conn and resolve-hyperlinks enabled by default)
azdw relationship resolve \
--ids "${WORK_ITEM}" \
--connection "${PRIMARY_CONN}" \
--limit-conns "${ALLOWED_CONNS}" \
--max-depth 2 \
--output "full-map.json"
# Extract connection statistics
echo "Connection Statistics:"
jq -r '.relationships |
group_by(.source.sourceOrganization, .target.targetOrganization) |
map({
from: .[0].source.sourceOrganization,
to: .[0].target.targetOrganization,
count: length
})' full-map.json
Relationship Type Distribution
Analyze the distribution of relationship types:
#!/bin/bash
# relationship-distribution.sh
WORK_ITEM="34130"
CONNECTION="Portfolio"
echo "Analyzing relationship type distribution..."
azdw relationship analyze \
--ids "${WORK_ITEM}" \
--connection "${CONNECTION}" \
--include-metrics \
--max-depth 2 \
--json | jq -r '
.analysis.relationshipTypes |
to_entries |
map("\(.key): \(.value.count) (cross-org: \(.value.crossOrgCount))") |
.[]'
Dependency Chain Visualization
Extract and visualize dependency chains:
#!/bin/bash
# dependency-chain.sh
WORK_ITEM="34130"
CONNECTION="Portfolio"
echo "Extracting dependency chain..."
azdw relationship resolve \
--ids "${WORK_ITEM}" \
--connection "${CONNECTION}" \
--relationship-types Dependency \
--max-depth 5 \
--json \
--output "dependencies.json"
# Create a simple dependency graph (DOT format)
echo "digraph Dependencies {" > dependencies.dot
jq -r '.relationships[] |
"\"\(.source.sourceWorkItemId)\" -> \"\(.target.targetWorkItemId)\" [label=\"\(.relationType)\"];"' \
dependencies.json >> dependencies.dot
echo "}" >> dependencies.dot
# Convert to PNG (requires Graphviz)
if command -v dot &> /dev/null; then
dot -Tpng dependencies.dot -o dependencies.png
echo "Dependency graph saved to dependencies.png"
fi
Output Processing
Using jq for JSON Processing
Extract Specific Relationships
# Get all cross-connection relationships
azdw relationship resolve --ids 34130 -c Portfolio --json | \
jq '.relationships[] | select(.isCrossConnection == true)'
# Get all hierarchy relationships
azdw relationship resolve --ids 34130 -c Portfolio --json | \
jq '.relationships[] | select(.relationType | contains("Hierarchy"))'
# Get relationships to specific work item type
azdw relationship resolve --ids 34130 -c Portfolio --json | \
jq '.relationships[] | select(.target.targetWorkItemType == "Feature")'
Generate Reports
# Count relationships by type
azdw relationship analyze --ids 34130 -c Portfolio --include-metrics --json | \
jq '.analysis.relationshipTypes | to_entries | map({type: .key, count: .value.count})'
# List all unique work item types
azdw relationship resolve --ids 34130 -c Portfolio --json | \
jq '[.relationships[].target.targetWorkItemType] | unique'
# Calculate cross-org percentage
azdw relationship analyze --ids 34130 -c Portfolio --include-metrics --json | \
jq '.metrics.crossOrgPercentage'
Using PowerShell for Processing
# Process-Relationships.ps1
# Load relationship data
$data = azdw relationship resolve --ids 34130 -c Portfolio --json | ConvertFrom-Json
# Group by organization
$orgGroups = $data.relationships | Group-Object { $_.target.targetOrganization }
# Display summary
$orgGroups | ForEach-Object {
[PSCustomObject]@{
Organization = $_.Name
Count = $_.Count
Percentage = [math]::Round(($_.Count / $data.summary.totalRelationships) * 100, 2)
}
} | Format-Table -AutoSize
# Find work items with most relationships
$data.relationships |
Group-Object { $_.target.targetWorkItemId } |
Sort-Object Count -Descending |
Select-Object -First 10 |
ForEach-Object {
[PSCustomObject]@{
WorkItemId = $_.Name
RelationshipCount = $_.Count
}
} | Format-Table -AutoSize
Using Python for Analysis
#!/usr/bin/env python3
# analyze-relationships.py
import json
import subprocess
from collections import defaultdict
def get_relationships(work_item, connection):
"""Fetch relationships using azdw CLI."""
cmd = [
'azdw', 'relationship', 'resolve',
'--ids', work_item,
'--connection', connection,
'--json'
]
result = subprocess.run(cmd, capture_output=True, text=True)
return json.loads(result.stdout)
def analyze_relationship_depth(relationships):
"""Analyze the depth of relationship chains."""
depth_map = defaultdict(list)
for rel in relationships:
source_id = rel['source']['sourceWorkItemId']
target_id = rel['target']['targetWorkItemId']
depth_map[source_id].append(target_id)
return depth_map
def main():
work_item = '34130'
connection = 'Portfolio'
print(f"Analyzing relationships for work item {work_item}...")
data = get_relationships(work_item, connection)
# Summary statistics
summary = data['summary']
print(f"\nSummary:")
print(f" Total relationships: {summary['totalRelationships']}")
print(f" Organizations: {summary['organizationsInvolved']}")
print(f" Cross-org: {summary['crossOrgRelationships']}")
# Relationship type breakdown
type_counts = defaultdict(int)
for rel in data['relationships']:
type_counts[rel['relationType']] += 1
print(f"\nRelationship Types:")
for rel_type, count in sorted(type_counts.items(), key=lambda x: x[1], reverse=True):
print(f" {rel_type}: {count}")
# Cross-org analysis
cross_org = [r for r in data['relationships'] if r.get('isCrossConnection', False)]
if cross_org:
print(f"\nCross-Organizational Relationships: {len(cross_org)}")
org_pairs = defaultdict(int)
for rel in cross_org:
pair = f"{rel['source']['sourceOrganization']} → {rel['target']['targetOrganization']}"
org_pairs[pair] += 1
for pair, count in org_pairs.items():
print(f" {pair}: {count}")
if __name__ == '__main__':
main()
Best Practices
1. Performance Optimization
Use Appropriate Max Depth
- Start with
--max-depth 1for initial exploration - Increase depth only when needed
- Large work items can have 100+ relationships at depth 1, 900+ at depth 2
# Good: Quick exploration
azdw relationship analyze -w 34130 -c Portfolio --max-depth 1
# Careful: Can be slow for highly connected work items
azdw relationship analyze -w 34130 -c Portfolio --max-depth 3
Filter Relationship Types
# Only get what you need
azdw relationship resolve -w 34130 -c Portfolio \
--relationship-types Hierarchy --max-depth 2
2. Cross-Connection Best Practices
Limit Connections When Possible
# Specify which connections to traverse (cross-conn is enabled by default)
azdw relationship resolve -w 34130 -c Portfolio \
--limit-conns Portfolio,TeamA
Disable cross-connection/hyperlinks if needed
# Only when you need to restrict to a single connection
azdw relationship resolve -w 34130 -c Portfolio \
--no-cross-conn --no-resolve-hyperlinks
3. Output Management
Always Save Important Results
# Save to timestamped files
DATE=$(date +%Y%m%d-%H%M%S)
azdw relationship resolve -w 34130 -c Portfolio \
--output "relationships-${DATE}.json"
Use JSON for Processing
# Pipe to jq for filtering
azdw relationship resolve -w 34130 -c Portfolio --json | \
jq '.relationships[] | select(.isCrossConnection == true)'
4. Validation Strategy
Regular Health Checks
# Weekly validation in CI/CD
azdw relationship validate -w "${CRITICAL_WORK_ITEMS}" \
-c "${CONNECTION}" \
--policy-file policy.json \
--strict \
--max-depth 1
Progressive Validation
# Start with warnings, move to strict
# Phase 1: Gather violations
azdw relationship validate -w 34130 -c Portfolio \
--policy-file policy.json -o violations.json
# Phase 2: After fixing, enforce strictly
azdw relationship validate -w 34130 -c Portfolio \
--policy-file policy.json --strict
5. Batch Processing
Process Multiple Work Items
#!/bin/bash
# batch-analysis.sh
WORK_ITEMS=("34130" "34131" "34132" "34133")
CONNECTION="Portfolio"
for WI in "${WORK_ITEMS[@]}"; do
echo "Processing work item ${WI}..."
azdw relationship analyze \
--ids "${WI}" \
--connection "${CONNECTION}" \
--max-depth 1 \
--include-metrics \
--output "analysis-${WI}.json"
echo "✓ Completed ${WI}"
done
echo "All work items processed!"
Troubleshooting
Common Issues
1. Command Takes Too Long
Problem: Relationship resolution times out or takes 5+ minutes.
Solution: Reduce max-depth or filter relationship types.
# Instead of this (too deep)
azdw relationship resolve -w 34130 -c Portfolio --max-depth 5
# Use this
azdw relationship resolve -w 34130 -c Portfolio --max-depth 1
# Or filter types
azdw relationship resolve -w 34130 -c Portfolio \
--relationship-types Hierarchy --max-depth 2
2. Authentication Failures
Problem: Cross-connection resolution fails with auth errors.
Solution: Ensure all connections are authenticated.
# Test each connection first
azdw connection test --name Portfolio
azdw connection test --name LPM
azdw connection test --name TeamB
# Then run cross-connection resolution (enabled by default)
azdw relationship resolve -w 34130 -c Portfolio
3. Missing Cross-Org Relationships
Problem: Expected cross-organizational relationships not found.
Solution: Ensure connections are configured and authenticated.
# Cross-connection and hyperlink resolution are enabled by default
# Make sure connections are properly configured:
azdw connection list
azdw connection test --all
4. Too Much Output
Problem: JSON output is too large to process.
Solution: Use filters and limited depth.
# Limit depth
azdw relationship resolve -w 34130 -c Portfolio \
--max-depth 1 --output limited.json
# Filter during extraction
azdw relationship resolve -w 34130 -c Portfolio --json | \
jq '.relationships[0:50]' > first-50.json
5. Policy Validation Errors
Problem: Validation fails with unclear errors.
Solution: Use verbose mode to see details.
# Add verbose flag
azdw relationship validate -w 34130 -c Portfolio \
--policy-file policy.json \
--verbose
# Check policy file syntax
cat policy.json | jq .
Performance Tips
- Start Small: Always test with
--max-depth 1first - Use Filters: Apply relationship type filters when possible
- Limit Connections: Specify
--limit-connsfor cross-org resolution - Save Results: Cache relationship data to avoid re-fetching
- Batch Smartly: Process related work items together
Debugging
Enable Verbose Logging
azdw relationship resolve -w 34130 -c Portfolio --verbose
Check Connection Status
azdw connection list
azdw connection test --all
Validate Policy File
# Check JSON syntax
jq . < config/relationship-policy.jsonc
# Test with single work item
azdw relationship validate -w 34130 -c Portfolio \
--policy-file config/relationship-policy.jsonc \
--max-depth 1
Additional Resources
- Cross-Org Relationship Handling - Detailed cross-organizational relationship documentation
- CLI Use Cases - General CLI usage scenarios
- Output Rendering Automation - Advanced output processing
- Filtering Query Results - Query and filter techniques
Summary
The azdw relationship commands provide powerful capabilities for understanding and managing work item relationships across Azure DevOps organizations. Key takeaways:
✅ Three main commands: resolve, analyze, validate
✅ Performance: Use --max-depth to control traversal depth
✅ Cross-org: Enabled by default; use --no-cross-conn and --no-resolve-hyperlinks to disable
✅ Filtering: Use relationship type filters to reduce scope
✅ Output: JSON format enables powerful post-processing
✅ Automation: Combine with scripts for health checks and reporting
Start with basic commands and progressively add options as needed. Always consider performance when working with highly connected work items.