From d6f6ccefda7251002e0413b9fd6508fffce6c02b Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Wed, 2 Jul 2025 12:04:33 -0600 Subject: [PATCH 1/4] DT change PoC --- ...osoft.Azure.Functions.PowerShellWorker.psm1 | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 b/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 index 7c1c5396..8a48603e 100644 --- a/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 +++ b/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 @@ -132,9 +132,25 @@ function Start-DurableOrchestration { } $Body = $InputObject | ConvertTo-Json -Compress + + try { + $activity = Start-FunctionsOpenTelemetrySpan + $traceID = $activity.activity.TraceId + # Do whatever you need to do with the trace information using the activity here + } catch { + # Do something better - correctly handle errors when the OTel SDK is not available + # Output errors from this command in a reasonable way + # Detect if calling Stop-FunctionsOpenTelemetrySpan is necessary and change that logic too + } $null = Invoke-RestMethod -Uri $Uri -Method 'POST' -ContentType 'application/json' -Body $Body - + + try { + Stop-FunctionsOpenTelemetrySpan -Activity $activity + } catch { + # Do something better here + } + return $instanceId } From 46daa71177efaa10add1ebc3282f77beed20880d Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Thu, 3 Jul 2025 17:54:47 -0700 Subject: [PATCH 2/4] updated how traceparent is created --- ...soft.Azure.Functions.PowerShellWorker.psm1 | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 b/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 index 8a48603e..8cd09af7 100644 --- a/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 +++ b/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 @@ -133,9 +133,28 @@ function Start-DurableOrchestration { $Body = $InputObject | ConvertTo-Json -Compress + $invokeParams = @{ + Uri = $Uri + Method = 'POST' + ContentType = 'application/json' + Body = $Body + } + try { - $activity = Start-FunctionsOpenTelemetrySpan - $traceID = $activity.activity.TraceId + $activity = Start-FunctionsOpenTelemetrySpan -ActivityName "Starting orchestration" + + $traceId = $activity.activity.TraceId + $spanId = $activity.activity.SpanId + + # Construct the traceparent header + $traceParent = "00-$traceId-$spanId-01" + + $traceState = $activity.activity.TraceState + + $invokeParams.Headers = @{ + 'traceparent' = $traceParent + 'tracestate' = $traceState + } # Do whatever you need to do with the trace information using the activity here } catch { # Do something better - correctly handle errors when the OTel SDK is not available @@ -143,7 +162,7 @@ function Start-DurableOrchestration { # Detect if calling Stop-FunctionsOpenTelemetrySpan is necessary and change that logic too } - $null = Invoke-RestMethod -Uri $Uri -Method 'POST' -ContentType 'application/json' -Body $Body + $null = Invoke-RestMethod @invokeParams try { Stop-FunctionsOpenTelemetrySpan -Activity $activity From b44cdc1f660c4624142449af24fea2e0377d1b65 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Fri, 12 Sep 2025 14:18:39 -0700 Subject: [PATCH 3/4] Added Invocation ID to SetFunctionInvocationContextCommand and added a call to Get-CurrentActivityForInvocation --- .../SetFunctionInvocationContextCommand.cs | 12 +++ src/DurableSDK/IPowerShellServices.cs | 2 + src/DurableSDK/PowerShellServices.cs | 7 ++ src/DurableWorker/DurableController.cs | 3 +- ...soft.Azure.Functions.PowerShellWorker.psm1 | 74 +++++++++++-------- src/PowerShell/PowerShellManager.cs | 2 +- 6 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/DurableSDK/Commands/SetFunctionInvocationContextCommand.cs b/src/DurableSDK/Commands/SetFunctionInvocationContextCommand.cs index 943e8362..c7a05d76 100644 --- a/src/DurableSDK/Commands/SetFunctionInvocationContextCommand.cs +++ b/src/DurableSDK/Commands/SetFunctionInvocationContextCommand.cs @@ -18,6 +18,7 @@ public class SetFunctionInvocationContextCommand : PSCmdlet { internal const string ContextKey = "OrchestrationContext"; private const string DurableClientKey = "DurableClient"; + private const string InvocationIdKey = "InvocationId"; [Parameter(Mandatory = true, ParameterSetName = ContextKey)] public OrchestrationContext OrchestrationContext { get; set; } @@ -28,6 +29,12 @@ public class SetFunctionInvocationContextCommand : PSCmdlet [Parameter(Mandatory = true, ParameterSetName = DurableClientKey)] public object DurableClient { get; set; } + /// + /// The invocation id. + /// + [Parameter(Mandatory = true, ParameterSetName = InvocationIdKey)] + public string InvocationId { get; set; } + [Parameter(Mandatory = true, ParameterSetName = "Clear")] public SwitchParameter Clear { get; set; } @@ -44,11 +51,16 @@ protected override void EndProcessing() privateData[DurableClientKey] = DurableClient; break; + case InvocationIdKey: + privateData[InvocationIdKey] = InvocationId; + break; + default: if (Clear.IsPresent) { privateData.Remove(ContextKey); privateData.Remove(DurableClientKey); + privateData.Remove(InvocationIdKey); } break; } diff --git a/src/DurableSDK/IPowerShellServices.cs b/src/DurableSDK/IPowerShellServices.cs index 3b7b49f3..bde168d8 100644 --- a/src/DurableSDK/IPowerShellServices.cs +++ b/src/DurableSDK/IPowerShellServices.cs @@ -21,6 +21,8 @@ internal interface IPowerShellServices void SetDurableClient(object durableClient); + void SetInvocationId(string invocationId); + OrchestrationBindingInfo SetOrchestrationContext(ParameterBinding context, out IExternalOrchestrationInvoker externalInvoker); void ClearOrchestrationContext(); diff --git a/src/DurableSDK/PowerShellServices.cs b/src/DurableSDK/PowerShellServices.cs index 6f9a0c03..c554ad65 100644 --- a/src/DurableSDK/PowerShellServices.cs +++ b/src/DurableSDK/PowerShellServices.cs @@ -72,6 +72,13 @@ public void SetDurableClient(object durableClient) _hasInitializedDurableFunctions = true; } + public void SetInvocationId(string invocationId) + { + _pwsh.AddCommand(SetFunctionInvocationContextCommand) + .AddParameter("InvocationId", invocationId) + .InvokeAndClearCommands(); + } + public OrchestrationBindingInfo SetOrchestrationContext( ParameterBinding context, out IExternalOrchestrationInvoker externalInvoker) diff --git a/src/DurableWorker/DurableController.cs b/src/DurableWorker/DurableController.cs index efaaaf9c..cc9a85ce 100644 --- a/src/DurableWorker/DurableController.cs +++ b/src/DurableWorker/DurableController.cs @@ -85,7 +85,7 @@ private void tryEnablingExternalSDK() } } - public void InitializeBindings(IList inputData, out bool hasExternalSDK) + public void InitializeBindings(IList inputData, out bool hasExternalSDK, string invocationId = "") { this.tryEnablingExternalSDK(); @@ -121,6 +121,7 @@ public void InitializeBindings(IList inputData, out bool hasEx _orchestrationInvoker.SetExternalInvoker(externalInvoker); } hasExternalSDK = _powerShellServices.HasExternalDurableSDK(); + _powerShellServices.SetInvocationId(invocationId); } public void AfterFunctionInvocation() diff --git a/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 b/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 index 8cd09af7..816ff957 100644 --- a/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 +++ b/src/Modules/Microsoft.Azure.Functions.PowerShellWorker/Microsoft.Azure.Functions.PowerShellWorker.psm1 @@ -19,6 +19,16 @@ function GetDurableClientFromModulePrivateData { } } +function GetInvocationIdFromModulePrivateData { + $PrivateData = $PSCmdlet.MyInvocation.MyCommand.Module.PrivateData + if ($null -eq $PrivateData -or $null -eq $PrivateData['InvocationId']) { + return $null + } + else { + return $PrivateData['InvocationId'] + } +} + function Get-DurableStatus { [CmdletBinding()] param( @@ -121,6 +131,9 @@ function Start-DurableOrchestration { $InstanceId = (New-Guid).Guid } + $invocationId = GetInvocationIdFromModulePrivateData + $headers = Get-TraceHeaders -InvocationId $invocationId + $Uri = if ($DurableClient.rpcBaseUrl) { # Fast local RPC path @@ -133,44 +146,47 @@ function Start-DurableOrchestration { $Body = $InputObject | ConvertTo-Json -Compress - $invokeParams = @{ - Uri = $Uri - Method = 'POST' - ContentType = 'application/json' - Body = $Body - } + $null = Invoke-RestMethod -Uri $Uri -Method 'POST' -ContentType 'application/json' -Body $Body -Headers $headers + + return $instanceId +} - try { - $activity = Start-FunctionsOpenTelemetrySpan -ActivityName "Starting orchestration" +function Get-TraceHeaders { + param( + [string] $InvocationId + ) + + if ($null -eq $InvocationId -or $InvocationId -eq "") { + return @{} # Return an empty headers object + } - $traceId = $activity.activity.TraceId - $spanId = $activity.activity.SpanId + # Check if Get-CurrentActivityForInvocation is available + if (-not (Get-Command -Name Get-CurrentActivityForInvocation -ErrorAction SilentlyContinue)) { + Write-Warning "Get-CurrentActivityForInvocation is not available. Skipping call." + return @{} # Return an empty headers object + } - # Construct the traceparent header - $traceParent = "00-$traceId-$spanId-01" + $activityResponse = Get-CurrentActivityForInvocation -InvocationId $invocationId + $activity = $activityResponse.activity - $traceState = $activity.activity.TraceState + $traceId = $activity.TraceId + $spanId = $activity.SpanId + $traceFlags = $activity.TraceFlags + $traceState = $activity.TraceStateString - $invokeParams.Headers = @{ - 'traceparent' = $traceParent - 'tracestate' = $traceState - } - # Do whatever you need to do with the trace information using the activity here - } catch { - # Do something better - correctly handle errors when the OTel SDK is not available - # Output errors from this command in a reasonable way - # Detect if calling Stop-FunctionsOpenTelemetrySpan is necessary and change that logic too + $flag = "00" + if ($null -ne $traceFlags -and $traceFlags -eq "Recorded") { + $flag = "01" } - - $null = Invoke-RestMethod @invokeParams - try { - Stop-FunctionsOpenTelemetrySpan -Activity $activity - } catch { - # Do something better here + $traceparent = "00-$traceId-$spanId-$flag" + + $headers = @{ + "traceparent" = $traceparent + "tracestate" = $traceState } - return $instanceId + return $headers } function Stop-DurableOrchestration { diff --git a/src/PowerShell/PowerShellManager.cs b/src/PowerShell/PowerShellManager.cs index a8c506ac..a0f11ff1 100644 --- a/src/PowerShell/PowerShellManager.cs +++ b/src/PowerShell/PowerShellManager.cs @@ -215,7 +215,7 @@ public Hashtable InvokeFunction( try { - durableFunctionsUtils.InitializeBindings(inputData, out bool hasExternalDFsdk); + durableFunctionsUtils.InitializeBindings(inputData: inputData, invocationId: otelContext.InvocationId, hasExternalSDK: out bool hasExternalDFsdk); Logger.Log(isUserOnlyLog: false, LogLevel.Trace, String.Format(PowerShellWorkerStrings.UtilizingExternalDurableSDK, hasExternalDFsdk)); ImportEntryPointModule(functionInfo); From 41e94dcb2e864fba3a4520a393ece51e1c5d47f5 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Fri, 12 Sep 2025 15:01:37 -0700 Subject: [PATCH 4/4] removing unnecessarycode to set invocation id --- src/DurableSDK/IPowerShellServices.cs | 2 -- src/DurableSDK/PowerShellServices.cs | 7 ------- src/DurableWorker/DurableController.cs | 3 +-- src/PowerShell/PowerShellManager.cs | 2 +- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/DurableSDK/IPowerShellServices.cs b/src/DurableSDK/IPowerShellServices.cs index bde168d8..3b7b49f3 100644 --- a/src/DurableSDK/IPowerShellServices.cs +++ b/src/DurableSDK/IPowerShellServices.cs @@ -21,8 +21,6 @@ internal interface IPowerShellServices void SetDurableClient(object durableClient); - void SetInvocationId(string invocationId); - OrchestrationBindingInfo SetOrchestrationContext(ParameterBinding context, out IExternalOrchestrationInvoker externalInvoker); void ClearOrchestrationContext(); diff --git a/src/DurableSDK/PowerShellServices.cs b/src/DurableSDK/PowerShellServices.cs index c554ad65..6f9a0c03 100644 --- a/src/DurableSDK/PowerShellServices.cs +++ b/src/DurableSDK/PowerShellServices.cs @@ -72,13 +72,6 @@ public void SetDurableClient(object durableClient) _hasInitializedDurableFunctions = true; } - public void SetInvocationId(string invocationId) - { - _pwsh.AddCommand(SetFunctionInvocationContextCommand) - .AddParameter("InvocationId", invocationId) - .InvokeAndClearCommands(); - } - public OrchestrationBindingInfo SetOrchestrationContext( ParameterBinding context, out IExternalOrchestrationInvoker externalInvoker) diff --git a/src/DurableWorker/DurableController.cs b/src/DurableWorker/DurableController.cs index cc9a85ce..efaaaf9c 100644 --- a/src/DurableWorker/DurableController.cs +++ b/src/DurableWorker/DurableController.cs @@ -85,7 +85,7 @@ private void tryEnablingExternalSDK() } } - public void InitializeBindings(IList inputData, out bool hasExternalSDK, string invocationId = "") + public void InitializeBindings(IList inputData, out bool hasExternalSDK) { this.tryEnablingExternalSDK(); @@ -121,7 +121,6 @@ public void InitializeBindings(IList inputData, out bool hasEx _orchestrationInvoker.SetExternalInvoker(externalInvoker); } hasExternalSDK = _powerShellServices.HasExternalDurableSDK(); - _powerShellServices.SetInvocationId(invocationId); } public void AfterFunctionInvocation() diff --git a/src/PowerShell/PowerShellManager.cs b/src/PowerShell/PowerShellManager.cs index a0f11ff1..a8c506ac 100644 --- a/src/PowerShell/PowerShellManager.cs +++ b/src/PowerShell/PowerShellManager.cs @@ -215,7 +215,7 @@ public Hashtable InvokeFunction( try { - durableFunctionsUtils.InitializeBindings(inputData: inputData, invocationId: otelContext.InvocationId, hasExternalSDK: out bool hasExternalDFsdk); + durableFunctionsUtils.InitializeBindings(inputData, out bool hasExternalDFsdk); Logger.Log(isUserOnlyLog: false, LogLevel.Trace, String.Format(PowerShellWorkerStrings.UtilizingExternalDurableSDK, hasExternalDFsdk)); ImportEntryPointModule(functionInfo);