datadog/azure-functions
>
npx skills add https://github.com/DataDog/dd-trace-dotnet --skill azure-functions
This skill helps tracer engineers test changes to the Datadog.AzureFunctions package: build a local dev version, deploy it to a test Azure Function App, trigger the function, and analyze traces/logs to verify instrumentation behavior.
This skill requires the following tools (assume they are installed and only troubleshoot if errors occur):
pwsh (PowerShell 7+) preferred, or powershell.exe (PowerShell 5.1+ on Windows)pwsh over powershell.exe when availableExpand-Archiveaz (must be authenticated)funcOnly if a tool fails, provide installation links:
When invoked with an argument, perform the corresponding workflow:
/azure-functions build-nuget - Build the Datadog.AzureFunctions NuGet package/azure-functions deploy [app-name] - Deploy to Azure Function App/azure-functions configure [app-name] - Configure environment variables for Datadog instrumentation/azure-functions test [app-name] - Trigger and verify function execution/azure-functions logs [app-name] - Download and analyze logsIf no argument is provided, guide the user through the full workflow interactively.
Current repository: This skill assumes you are working from the root of the dd-trace-dotnet repository.
Prerequisites: Users provide their own Azure Function App name (-AppName), resource group (-ResourceGroup), and sample app path (-SampleAppPath). The sample app must:
Datadog.AzureFunctions NuGet packagenuget.config file (in the app directory or a parent directory) that defines a local NuGet feed pointing to a directory on disk, for example: <?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="local" value="nuget/local-source" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
The -CopyTo parameter of Build-AzureFunctionsNuget.ps1 must point to the same directory as the local feed (e.g. if the feed value is nuget/local-source relative to the nuget.config location, then -CopyTo should be the absolute path to that directory).
CRITICAL: Before building, verify that tracer/src/Datadog.AzureFunctions/Datadog.AzureFunctions.csproj uses PackageReference (not ProjectReference) for Datadog.Trace and Datadog.Trace.Annotations. This ensures the locally-built package references the latest releases from nuget.org instead of building them from source.
Check the .csproj — it should contain:
<PackageReference Include="Datadog.Trace" Version="*"/>
<PackageReference Include="Datadog.Trace.Annotations" Version="*" />
If instead it contains ProjectReference lines like these, replace them with the PackageReference lines above:
<!-- These are the production references — replace for local testing: -->
<ProjectReference Include="$(MSBuildThisFileDirectory)..\Datadog.Trace.Manual\Datadog.Trace.Manual.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\Datadog.Trace.Annotations\Datadog.Trace.Annotations.csproj" />
IMPORTANT: The PackageReference change is for local testing only. Do NOT commit it. If it's already using PackageReference, no change is needed.
Build the Datadog.AzureFunctions NuGet package with your changes:
./tracer/tools/Build-AzureFunctionsNuget.ps1 -CopyTo <output-dir>
What this does:
-BuildId specified and files not already downloaded) Downloads bundle from Azure DevOps build once3.38.0-dev20260209143022)Datadog.Trace (net6.0)Datadog.AzureFunctions.nupkg with the generated version (referencing latest nuget.org releases)-CopyToVersioning: Each build gets a unique version, so NuGet caching is never an issue.
The sample app should use a floating version like 3.38.0-dev.* in its package reference
(or Directory.Packages.props) to always resolve the latest local dev build.
Options:
-CopyTo <output-dir> - Copy the built package to the specified directory (typically your local NuGet feed)-Version '3.38.0-dev.custom' - Use a specific version instead of auto-generating-BuildId 12345 - One-time download of bundle files from Azure DevOps build (only needed once per dd-trace-dotnet release, then reused for subsequent local builds)Examples:
# Typical local build (after bundle files already downloaded)
./tracer/tools/Build-AzureFunctionsNuget.ps1 -CopyTo <output-dir>
# First build after new dd-trace-dotnet release (download bundle files once)
./tracer/tools/Build-AzureFunctionsNuget.ps1 -BuildId 12345 -CopyTo <output-dir>
IMPORTANT: Before deploying, verify prerequisites:
Verify nuget.config exists:
$nugetConfig = ./.claude/skills/azure-functions/Find-NuGetConfig.ps1 -StartPath "<path-to-sample-app>"
if (-not $nugetConfig) {
Write-Error "nuget.config not found in sample app directory or parent directories"
exit 1
}
Write-Host "Found nuget.config at: $nugetConfig"
Verify environment variables are configured (skip if already done on a previous deploy):
$envCheck = ./.claude/skills/azure-functions/Test-EnvVars.ps1 -AppName "<app-name>" -ResourceGroup "<resource-group>"
if (-not $envCheck.AllRequiredPresent) {
Write-Warning "Required environment variables are missing. Run '/azure-functions configure' first, or proceed if you plan to configure after deploying."
}
Use the Deploy-AzureFunction.ps1 script to automate deployment, wait, and trigger:
./tracer/tools/Deploy-AzureFunction.ps1 `
-AppName "<app-name>" `
-ResourceGroup "<resource-group>" `
-SampleAppPath "<path-to-sample-app>"
What this does:
dotnet restore in the sample app directoryfunc azure functionapp publishOptions:
-SkipBuild - Skip dotnet restore-SkipWait - Skip 2-minute wait (not recommended)-WaitSeconds 60 - Custom wait duration-SkipTrigger - Skip HTTP trigger-TriggerUrl "https://..." - Custom trigger URLNote: -AppName and -ResourceGroup are required parameters.
Pipeline usage (save output for log analysis):
$deploy = ./tracer/tools/Deploy-AzureFunction.ps1 `
-AppName "<app-name>" `
-ResourceGroup "<resource-group>" `
-SampleAppPath "<path-to-sample-app>"
Configure Datadog instrumentation environment variables for an Azure Function App using Set-EnvVars.ps1:
./.claude/skills/azure-functions/Set-EnvVars.ps1 `
-AppName "<app-name>" `
-ResourceGroup "<resource-group>" `
-ApiKey "<api-key>" `
-Tier recommended `
-Env "dev-lucas"
What this does:
-SkipRestart)Tiers:
Options:
-Tier required|recommended|debug - Configuration tier (default: required)-Env "dev-lucas" - Set DD_ENV-Service "my-service" - Set DD_SERVICE-Version "1.0.0" - Set DD_VERSION-SamplingRules '<json>' - Set DD_TRACE_SAMPLING_RULES-ExtraSettings @{"KEY"="value"} - Set additional variables-SkipRestart - Don't restart the app after applying-WhatIf - Preview changes without applyingPreview before applying:
./.claude/skills/azure-functions/Set-EnvVars.ps1 `
-AppName "<app-name>" `
-ResourceGroup "<resource-group>" `
-ApiKey "<api-key>" `
-Tier debug `
-WhatIf
Complete reference: See environment-variables.md for all available variables.
Trigger an already-deployed function and capture the execution timestamp. Useful for re-testing after a deploy, or testing an app that was deployed earlier.
Discover available triggers:
# List HTTP-triggered functions and their URLs
func azure functionapp list-functions <app-name> --show-keys
Or via Azure CLI:
az functionapp function list --name <app-name> --resource-group <resource-group> --query "[].{name:name, href:invokeUrlTemplate}" -o table
Trigger and capture timestamp:
$timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss")
$response = Invoke-WebRequest -Uri "<trigger-url>" -UseBasicParsing
Write-Host "HTTP Status: $($response.StatusCode)"
Write-Host "Execution timestamp (UTC): $timestamp"
Save the timestamp — you'll need it for log filtering in the next step.
Note: The Deploy script (step 2) already triggers and captures a timestamp. Use this step when you want to re-test without redeploying.
Use the Get-AzureFunctionLogs.ps1 script to download, extract, and analyze logs:
./tracer/tools/Get-AzureFunctionLogs.ps1 `
-AppName "<app-name>" `
-ResourceGroup "<resource-group>" `
-OutputPath $env:TEMP `
-ExecutionTimestamp "<YYYY-MM-DD HH:MM:SS>" `
-All
IMPORTANT: Always specify -OutputPath $env:TEMP to save logs to the system temp folder instead of cluttering the repository directory.
What this does:
Analysis options:
-ShowVersion - Display Datadog tracer version from worker logs-ShowSpans - Count spans at execution timestamp (split by host/worker)-CheckParenting - Validate trace parenting (detect root span duplication)-All - Enable all analysis (recommended)Pipeline usage (with Deploy script):
$deploy = ./tracer/tools/Deploy-AzureFunction.ps1 `
-AppName "<app-name>" `
-ResourceGroup "<resource-group>" `
-SampleAppPath "<path-to-sample-app>"
./tracer/tools/Get-AzureFunctionLogs.ps1 `
-AppName $deploy.AppName `
-ResourceGroup "<resource-group>" `
-OutputPath $env:TEMP `
-ExecutionTimestamp $deploy.ExecutionTimestamp `
-All
Log file patterns:
dotnet-tracer-managed-Microsoft.Azure.WebJobs.Script.WebHost-{pid}.logdotnet-tracer-managed-dotnet-{pid}.logCRITICAL: The script automatically filters logs by execution timestamp. Raw log files are append-only and contain entries from multiple deployments/restarts.
Parenting analysis:
s_id (span ID), p_id (parent ID), t_id (trace ID)t_id as host, p_id matching host span IDst_id or p_id: null (orphaned root)After deployment and testing:
p_id → s_id links)aas.function.process:host or worker)General guidance: For environment variable configuration issues, see environment-variables.md for complete reference on required, recommended, and debugging variables.
First, check if the app is running:
$envCheck = ./.claude/skills/azure-functions/Test-EnvVars.ps1 -AppName "<app-name>" -ResourceGroup "<resource-group>"
if ($envCheck.State -ne "Running") {
Write-Host "App is '$($envCheck.State)' — starting it..."
az functionapp start --name <app-name> --resource-group <resource-group>
}
If the app is running but not responding:
# Restart function app
az functionapp restart --name <app-name> --resource-group <resource-group>
func azure functionapp publish)If the publish command fails, diagnose with:
# Check recent deployment status
az functionapp deployment list \
--name <app-name> \
--resource-group <resource-group> \
--query "[0].{status:status, message:message, startTime:startTime}" -o table
# Stream live logs to see startup errors
func azure functionapp logstream <app-name>
Common causes:
az login and retryaz functionapp start --name <app-name> --resource-group <resource-group>dotnet restore output in the sample app directory# Check all worker initializations
grep "Assembly metadata" LogFiles/datadog/dotnet-tracer-managed-dotnet-*.log
# If old version, rebuild from the dd-trace-dotnet repo root (each build gets a unique version, no cache issues)
./tracer/tools/Build-AzureFunctionsNuget.ps1 -CopyTo <output-dir> -Verbose
# Then restore and redeploy the sample app
Verify all required environment variables (including DD_API_KEY, profiler paths, etc.):
./.claude/skills/azure-functions/Test-EnvVars.ps1 -AppName "<app-name>" -ResourceGroup "<resource-group>" -IncludeRecommended
If all env vars pass, check worker initialization in logs:
grep "Datadog Tracer initialized" LogFiles/datadog/dotnet-tracer-managed-dotnet-*.log
Complete reference: See environment-variables.md for all available variables.
p_id: null instead of parent IDs matching host spansaz functionapp config appsettings set --name <app> --resource-group <resource-group> --settings DD_TRACE_DEBUG=1When traces or logs have reached Datadog (e.g., verifying spans look correct, correlating logs with a trace ID), use these scripts. Both require DD_API_KEY and DD_APPLICATION_KEY environment variables.
Retrieve all spans for a trace ID:
# Table view (default)
./tracer/tools/Get-DatadogTrace.ps1 -TraceId "<trace-id>"
# Hierarchy view — shows span parent-child tree with process tags
./tracer/tools/Get-DatadogTrace.ps1 -TraceId "<trace-id>" -OutputFormat hierarchy
# Search further back in time (default: 2h)
./tracer/tools/Get-DatadogTrace.ps1 -TraceId "<trace-id>" -TimeRange "1d"
Query logs from Datadog:
./tracer/tools/Get-DatadogLogs.ps1 -Query "service:<app-name>"
./tracer/tools/Get-DatadogLogs.ps1 -Query "service:<app-name> error" -TimeRange "2h" -Limit 100
See scripts-reference.md for full parameter reference.
-All analysis isn't sufficient)docs/development/AzureFunctions.mddocs/development/for-ai/AzureFunctions-Architecture.mdTake datadog/azure-functions from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.