Debugging Guide for AZD Deployments
February 5, 2026 ยท View on GitHub
Chapter Navigation:
- ๐ Course Home: AZD For Beginners
- ๐ Current Chapter: Chapter 7 - Troubleshooting & Debugging
- โฌ ๏ธ Previous: Common Issues
- โก๏ธ Next: AI-Specific Troubleshooting
- ๐ Next Chapter: Chapter 8: Production & Enterprise Patterns
Introduction
This comprehensive guide provides advanced debugging strategies, tools, and techniques for diagnosing and resolving complex issues with Azure Developer CLI deployments. Learn systematic troubleshooting methodologies, log analysis techniques, performance profiling, and advanced diagnostic tools to efficiently resolve deployment and runtime issues.
Learning Goals
By completing this guide, you will:
- Master systematic debugging methodologies for Azure Developer CLI issues
- Understand advanced logging configuration and log analysis techniques
- Implement performance profiling and monitoring strategies
- Use Azure diagnostic tools and services for complex problem resolution
- Apply network debugging and security troubleshooting techniques
- Configure comprehensive monitoring and alerting for proactive issue detection
Learning Outcomes
Upon completion, you will be able to:
- Apply the TRIAGE methodology to systematically debug complex deployment issues
- Configure and analyze comprehensive logging and tracing information
- Use Azure Monitor, Application Insights, and diagnostic tools effectively
- Debug network connectivity, authentication, and permission issues independently
- Implement performance monitoring and optimization strategies
- Create custom debugging scripts and automation for recurring issues
Debugging Methodology
The TRIAGE Approach
- Time: When did the issue start?
- Reproduce: Can you consistently reproduce it?
- Isolate: What component is failing?
- Analyze: What do the logs tell us?
- Gather: Collect all relevant information
- Escalate: When to seek additional help
Enabling Debug Mode
Environment Variables
# Enable comprehensive debugging
export AZD_DEBUG=true
export AZD_LOG_LEVEL=debug
export AZURE_CORE_DIAGNOSTICS_DEBUG=true
# Azure CLI debugging
export AZURE_CLI_DIAGNOSTICS=true
# Disable telemetry for cleaner output
export AZD_DISABLE_TELEMETRY=true
Debug Configuration
# Set debug configuration globally
azd config set debug.enabled true
azd config set debug.logLevel debug
azd config set debug.verboseOutput true
# Enable trace logging
azd config set trace.enabled true
azd config set trace.outputPath ./debug-traces
๐ Log Analysis Techniques
Understanding Log Levels
TRACE - Most detailed, includes internal function calls
DEBUG - Detailed diagnostic information
INFO - General operational messages
WARN - Warning conditions that should be noted
ERROR - Error conditions that need attention
FATAL - Critical errors that cause application termination
Structured Log Analysis
# View logs with Azure Monitor (via azd monitor)
azd monitor --logs
# View application logs in real-time
azd monitor --live
# For detailed log analysis, use Azure CLI with your App Service or Container App:
# App Service logs
az webapp log tail --name <app-name> --resource-group <rg-name>
# Container App logs
az containerapp logs show --name <app-name> --resource-group <rg-name> --follow
# Export Application Insights logs for analysis
az monitor app-insights query \
--app <app-insights-name> \
--analytics-query "traces | where timestamp > ago(1h) | where severityLevel >= 3"
Log Correlation
#!/bin/bash
# correlate-logs.sh - Correlate logs across services using Azure Monitor
TRACE_ID=\$1
APP_INSIGHTS_NAME=\$2
if [ -z "$TRACE_ID" ] || [ -z "$APP_INSIGHTS_NAME" ]; then
echo "Usage: \$0 <trace-id> <app-insights-name>"
exit 1
fi
echo "Correlating logs for trace ID: $TRACE_ID"
# Search Application Insights for correlated logs
az monitor app-insights query \
--app "$APP_INSIGHTS_NAME" \
--analytics-query "union traces, exceptions, requests, dependencies | where operation_Id == '$TRACE_ID' | order by timestamp asc"
# Search Azure activity logs
az monitor activity-log list --correlation-id "$TRACE_ID"
๐ ๏ธ Advanced Debugging Tools
Azure Resource Graph Queries
# Query resources by tags
az graph query -q "Resources | where tags['azd-env-name'] == 'production' | project name, type, location"
# Find failed deployments
az graph query -q "ResourceContainers | where type == 'microsoft.resources/resourcegroups' | extend deploymentStatus = properties.provisioningState | where deploymentStatus != 'Succeeded'"
# Check resource health
az graph query -q "HealthResources | where properties.targetResourceId contains 'myapp' | project properties.targetResourceId, properties.currentHealthStatus"
Network Debugging
# Test connectivity between services
test_connectivity() {
local source=\$1
local dest=\$2
local port=\$3
echo "Testing connectivity from $source to $dest:$port"
az network watcher test-connectivity \
--source-resource "$source" \
--dest-address "$dest" \
--dest-port "$port" \
--output table
}
# Usage
test_connectivity "/subscriptions/.../myapp-web" "myapp-api.azurewebsites.net" 443
Container Debugging
# Debug container app issues
debug_container() {
local app_name=\$1
local resource_group=\$2
echo "=== Container App Status ==="
az containerapp show --name "$app_name" --resource-group "$resource_group" \
--query "properties.{provisioningState:provisioningState,runningState:runningState}"
echo "=== Container App Revisions ==="
az containerapp revision list --name "$app_name" --resource-group "$resource_group" \
--query "[].{name:name,active:properties.active,createdTime:properties.createdTime}"
echo "=== Container Logs ==="
az containerapp logs show --name "$app_name" --resource-group "$resource_group" --follow
}
Database Connection Debugging
# Debug database connectivity
debug_database() {
local db_server=\$1
local db_name=\$2
echo "=== Database Server Status ==="
az postgres flexible-server show --name "$db_server" --resource-group "$resource_group" \
--query "{state:state,version:version,location:location}"
echo "=== Firewall Rules ==="
az postgres flexible-server firewall-rule list --name "$db_server" --resource-group "$resource_group"
echo "=== Connection Test ==="
timeout 10 bash -c "</dev/tcp/$db_server.postgres.database.azure.com/5432" && echo "Port 5432 is open" || echo "Port 5432 is closed"
}
๐ฌ Performance Debugging
Application Performance Monitoring
# Enable Application Insights debugging
export APPLICATIONINSIGHTS_CONFIGURATION_CONTENT='{
"role": {
"name": "myapp-debug"
},
"sampling": {
"percentage": 100
},
"instrumentation": {
"logging": {
"level": "DEBUG"
}
}
}'
# Custom performance monitoring
monitor_performance() {
local endpoint=\$1
local duration=${2:-60}
echo "Monitoring $endpoint for $duration seconds..."
for i in $(seq 1 $duration); do
response_time=$(curl -o /dev/null -s -w "%{time_total}" "$endpoint")
status_code=$(curl -o /dev/null -s -w "%{http_code}" "$endpoint")
echo "$(date '+%Y-%m-%d %H:%M:%S') - Status: $status_code, Response Time: ${response_time}s"
sleep 1
done
}
Resource Utilization Analysis
# Monitor resource usage
monitor_resources() {
local resource_group=\$1
echo "=== CPU Usage ==="
az monitor metrics list \
--resource-group "$resource_group" \
--resource-type "Microsoft.Web/sites" \
--metric "CpuPercentage" \
--interval PT1M \
--aggregation Average
echo "=== Memory Usage ==="
az monitor metrics list \
--resource-group "$resource_group" \
--resource-type "Microsoft.Web/sites" \
--metric "MemoryPercentage" \
--interval PT1M \
--aggregation Average
}
๐งช Testing and Validation
Integration Test Debugging
#!/bin/bash
# debug-integration-tests.sh
set -e
echo "Running integration tests with debugging..."
# Set debug environment
export NODE_ENV=test
export DEBUG=*
export LOG_LEVEL=debug
# Get service endpoints
WEB_URL=$(azd show --output json | jq -r '.services.web.endpoint')
API_URL=$(azd show --output json | jq -r '.services.api.endpoint')
echo "Testing endpoints:"
echo "Web: $WEB_URL"
echo "API: $API_URL"
# Test health endpoints
test_health() {
local service=\$1
local url=\$2
echo "Testing $service health..."
response=$(curl -s -o /dev/null -w "%{http_code},%{time_total}" "$url/health")
status_code=$(echo $response | cut -d',' -f1)
response_time=$(echo $response | cut -d',' -f2)
if [ "$status_code" = "200" ]; then
echo "โ
$service is healthy (${response_time}s)"
else
echo "โ $service health check failed ($status_code)"
return 1
fi
}
# Run tests
test_health "Web" "$WEB_URL"
test_health "API" "$API_URL"
# Run custom integration tests
npm run test:integration
Load Testing for Debugging
# Simple load test to identify performance bottlenecks
load_test() {
local url=\$1
local concurrent=${2:-10}
local requests=${3:-100}
echo "Load testing $url with $concurrent concurrent connections, $requests total requests"
# Using Apache Bench (install: apt-get install apache2-utils)
ab -n "$requests" -c "$concurrent" -v 2 "$url" > load-test-results.txt
# Extract key metrics
echo "=== Load Test Results ==="
grep -E "(Time taken|Requests per second|Time per request)" load-test-results.txt
# Check for failures
grep -E "(Failed requests|Non-2xx responses)" load-test-results.txt
}
๐ง Infrastructure Debugging
Bicep Template Debugging
# Validate Bicep templates with detailed output
validate_bicep() {
local template_file=\$1
echo "Validating Bicep template: $template_file"
# Syntax validation
az bicep build --file "$template_file" --stdout > /dev/null
# Lint validation
az bicep lint --file "$template_file"
# What-if deployment
az deployment group what-if \
--resource-group "myapp-dev-rg" \
--template-file "$template_file" \
--parameters @main.parameters.json
}
# Debug template deployment
debug_deployment() {
local deployment_name=\$1
local resource_group=\$2
echo "=== Deployment Status ==="
az deployment group show \
--name "$deployment_name" \
--resource-group "$resource_group" \
--query "properties.{provisioningState:provisioningState,timestamp:timestamp}"
echo "=== Deployment Operations ==="
az deployment operation group list \
--name "$deployment_name" \
--resource-group "$resource_group" \
--query "[].{operationId:operationId,provisioningState:properties.provisioningState,resourceType:properties.targetResource.resourceType,error:properties.statusMessage.error}"
}
Resource State Analysis
# Analyze resource states for inconsistencies
analyze_resources() {
local resource_group=\$1
echo "=== Resource Analysis for $resource_group ==="
# List all resources with their states
az resource list --resource-group "$resource_group" \
--query "[].{name:name,type:type,provisioningState:properties.provisioningState,location:location}" \
--output table
# Check for failed resources
failed_resources=$(az resource list --resource-group "$resource_group" \
--query "[?properties.provisioningState != 'Succeeded'].{name:name,state:properties.provisioningState}" \
--output tsv)
if [ -n "$failed_resources" ]; then
echo "โ Failed resources found:"
echo "$failed_resources"
else
echo "โ
All resources provisioned successfully"
fi
}
๐ Security Debugging
Authentication Flow Debugging
# Debug Azure authentication
debug_auth() {
echo "=== Current Authentication Status ==="
az account show --query "{user:user.name,tenant:tenantId,subscription:name}"
echo "=== Token Information ==="
token=$(az account get-access-token --query accessToken -o tsv)
# Decode JWT token (requires jq and base64)
echo "$token" | cut -d'.' -f2 | base64 -d | jq '.'
echo "=== Role Assignments ==="
user_id=$(az account show --query user.name -o tsv)
az role assignment list --assignee "$user_id" --query "[].{role:roleDefinitionName,scope:scope}"
}
# Debug Key Vault access
debug_keyvault() {
local vault_name=\$1
echo "=== Key Vault Access Policies ==="
az keyvault show --name "$vault_name" --query "properties.accessPolicies[].{objectId:objectId,permissions:permissions}"
echo "=== RBAC Assignments ==="
vault_id=$(az keyvault show --name "$vault_name" --query id -o tsv)
az role assignment list --scope "$vault_id"
echo "=== Test Secret Access ==="
az keyvault secret list --vault-name "$vault_name" --query "[].name" || echo "โ Cannot access secrets"
}
Network Security Debugging
# Debug network security groups
debug_network_security() {
local resource_group=\$1
echo "=== Network Security Groups ==="
az network nsg list --resource-group "$resource_group" --query "[].{name:name,location:location}"
# Check security rules
for nsg in $(az network nsg list --resource-group "$resource_group" --query "[].name" -o tsv); do
echo "=== Rules for $nsg ==="
az network nsg rule list --nsg-name "$nsg" --resource-group "$resource_group" \
--query "[].{name:name,priority:priority,direction:direction,access:access,protocol:protocol,sourcePortRange:sourcePortRange,destinationPortRange:destinationPortRange}"
done
}
๐ฑ Application-Specific Debugging
Node.js Application Debugging
// debug-middleware.js - Express debugging middleware
const debug = require('debug')('app:debug');
module.exports = (req, res, next) => {
const start = Date.now();
// Log request details
debug(`${req.method} ${req.url}`, {
headers: req.headers,
query: req.query,
body: req.body,
userAgent: req.get('User-Agent'),
ip: req.ip
});
// Override res.json to log responses
const originalJson = res.json;
res.json = function(data) {
const duration = Date.now() - start;
debug(`Response ${res.statusCode} in ${duration}ms`, data);
return originalJson.call(this, data);
};
next();
};
Database Query Debugging
// database-debug.js - Database debugging utilities
const { Pool } = require('pg');
const debug = require('debug')('app:db');
class DebuggingPool extends Pool {
async query(text, params) {
const start = Date.now();
debug('Executing query:', { text, params });
try {
const result = await super.query(text, params);
const duration = Date.now() - start;
debug(`Query completed in ${duration}ms`, {
rowCount: result.rowCount,
command: result.command
});
return result;
} catch (error) {
const duration = Date.now() - start;
debug(`Query failed after ${duration}ms:`, error.message);
throw error;
}
}
}
module.exports = DebuggingPool;
๐จ Emergency Debugging Procedures
Production Issue Response
#!/bin/bash
# emergency-debug.sh - Emergency production debugging
set -e
RESOURCE_GROUP=\$1
ENVIRONMENT=\$2
if [ -z "$RESOURCE_GROUP" ] || [ -z "$ENVIRONMENT" ]; then
echo "Usage: \$0 <resource-group> <environment>"
exit 1
fi
echo "๐จ EMERGENCY DEBUGGING STARTED: $(date)"
echo "Resource Group: $RESOURCE_GROUP"
echo "Environment: $ENVIRONMENT"
# Switch to correct environment
azd env select "$ENVIRONMENT"
# Collect critical information
echo "=== 1. System Status ==="
azd show --output json > emergency-status.json
cat emergency-status.json | jq '.services[].endpoint'
echo "=== 2. Application Health ==="
for endpoint in $(cat emergency-status.json | jq -r '.services[].endpoint'); do
echo "Testing $endpoint/health"
curl -f "$endpoint/health" || echo "โ Health check failed for $endpoint"
done
echo "=== 3. Recent Errors ==="
# Use Azure Monitor for error logs
azd monitor --logs
echo "Check Application Insights for detailed error analysis"
echo "=== 4. Resource Status ==="
az resource list --resource-group "$RESOURCE_GROUP" \
--query "[?properties.provisioningState != 'Succeeded']" > failed-resources.json
if [ -s failed-resources.json ]; then
echo "โ Failed resources found!"
cat failed-resources.json
else
echo "โ
All resources are healthy"
fi
echo "=== 5. Recent Deployments ==="
az deployment group list --resource-group "$RESOURCE_GROUP" \
--query "[?properties.timestamp >= '$(date -d '1 hour ago' -Iseconds)']" \
> recent-deployments.json
echo "Emergency debugging completed: $(date)"
echo "Files generated:"
echo " - emergency-status.json"
echo " - emergency-errors.log"
echo " - failed-resources.json"
echo " - recent-deployments.json"
Rollback Procedures
# Quick rollback script
quick_rollback() {
local environment=\$1
local previous_commit=\$2
echo "๐ INITIATING ROLLBACK for $environment"
# Switch environment
azd env select "$environment"
# Rollback using Git (AZD doesn't have built-in rollback)
git checkout "$previous_commit"
azd deploy
# Verify rollback
echo "Verifying rollback..."
azd show
# Test critical endpoints
WEB_URL=$(azd show --output json | jq -r '.services.web.endpoint')
curl -f "$WEB_URL/health" || echo "โ Rollback verification failed"
echo "โ
Rollback completed"
}
๐ Debugging Dashboards
Custom Monitoring Dashboard
# Create Application Insights queries for debugging
create_debug_queries() {
local app_insights_name=\$1
# Query for errors
az monitor app-insights query \
--app "$app_insights_name" \
--analytics-query "exceptions | where timestamp > ago(1h) | summarize count() by problemId, outerMessage"
# Query for performance issues
az monitor app-insights query \
--app "$app_insights_name" \
--analytics-query "requests | where timestamp > ago(1h) and duration > 5000 | project timestamp, name, duration, resultCode"
# Query for dependency failures
az monitor app-insights query \
--app "$app_insights_name" \
--analytics-query "dependencies | where timestamp > ago(1h) and success == false | project timestamp, name, target, resultCode"
}
Log Aggregation
# Aggregate logs from multiple Azure sources
aggregate_logs() {
local output_file="aggregated-logs-$(date +%Y%m%d_%H%M%S).json"
local app_insights_name=\$1
echo "Aggregating logs to $output_file"
{
echo '{"source": "azure-activity", "logs": '
az monitor activity-log list --start-time "$(date -d '1 hour ago' -Iseconds)" --output json
echo '}'
if [ -n "$app_insights_name" ]; then
echo ',{"source": "app-insights", "logs": '
az monitor app-insights query --app "$app_insights_name" \
--analytics-query "union traces, exceptions | where timestamp > ago(1h)" --output json
echo '}'
fi
} > "$output_file"
echo "Logs aggregated in $output_file"
}
๐ Advanced Resources
Custom Debug Scripts
Create a scripts/debug/ directory with:
health-check.sh- Comprehensive health checkingperformance-test.sh- Automated performance testinglog-analyzer.py- Advanced log parsing and analysisresource-validator.sh- Infrastructure validation
Monitoring Integration
# azure.yaml - Add debugging hooks
hooks:
postdeploy:
shell: sh
run: |
echo "Running post-deployment debugging..."
./scripts/debug/health-check.sh
./scripts/debug/performance-test.sh
if [ "$?" -ne 0 ]; then
echo "โ Post-deployment checks failed"
exit 1
fi
Best Practices
- Always enable debug logging in non-production environments
- Create reproducible test cases for issues
- Document debugging procedures for your team
- Automate health checks and monitoring
- Keep debug tools updated with your application changes
- Practice debugging procedures during non-incident times
Next Steps
- Capacity Planning - Plan resource requirements
- SKU Selection - Choose appropriate service tiers
- Preflight Checks - Pre-deployment validation
- Cheat Sheet - Quick reference commands
Remember: Good debugging is about being systematic, thorough, and patient. These tools and techniques will help you diagnose issues faster and more effectively.
Navigation
-
Previous Lesson: Common Issues
-
Next Lesson: Capacity Planning