diff --git a/samples/README.md b/samples/README.md index 34a70a4..a1cd401 100644 --- a/samples/README.md +++ b/samples/README.md @@ -46,10 +46,10 @@ A quick-reference matrix showing which patterns are available in each language a ### Durable Functions -| Pattern | .NET | Python | Java | JavaScript | -|---------|------|--------|------|------------| -| Hello Cities (Quickstart) | [✅](./durable-functions/dotnet/HelloCities) | | [✅](./durable-functions/java/HelloCities) | [✅](./durable-functions/javascript/HelloCities) | -| Fan-out/Fan-in | | [✅](./durable-functions/python/fan-out-fan-in) | [✅](./durable-functions/java/HelloCities) | [✅](./durable-functions/javascript/HelloCities) | +| Pattern | .NET | Python | Java | JavaScript | PowerShell | +|---------|------|--------|------|------------|------------| +| Hello Cities (Quickstart) | [✅](./durable-functions/dotnet/HelloCities) | | [✅](./durable-functions/java/HelloCities) | [✅](./durable-functions/javascript/HelloCities) | [✅](./durable-functions/powershell/HelloCities) | +| Fan-out/Fan-in | | [✅](./durable-functions/python/fan-out-fan-in) | [✅](./durable-functions/java/HelloCities) | [✅](./durable-functions/javascript/HelloCities) | [✅](./durable-functions/powershell/HelloCities) | | Order Processor | [✅](./durable-functions/dotnet/OrderProcessor) | | | | | Saga Pattern | [✅](./durable-functions/dotnet/Saga) | | | | | Distributed Tracing | [✅](./durable-functions/dotnet/DistributedTracing) | | | | @@ -155,6 +155,12 @@ A quick-reference matrix showing which patterns are available in each language a |--------|---------|-------------| | [Hello Cities](./durable-functions/javascript/HelloCities) | Function Chaining, Fan-out/Fan-in | JavaScript quickstart with sequential and parallel orchestration patterns | +### PowerShell + +| Sample | Pattern | Description | +|--------|---------|-------------| +| [Hello Cities](./durable-functions/powershell/HelloCities) | Function Chaining, Fan-out/Fan-in | PowerShell quickstart with sequential and parallel orchestration patterns | + --- ## Durable Extension for Microsoft Agent Framework diff --git a/samples/durable-functions/powershell/HelloCities/.gitignore b/samples/durable-functions/powershell/HelloCities/.gitignore new file mode 100644 index 0000000..819b029 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/.gitignore @@ -0,0 +1 @@ +Modules/ diff --git a/samples/durable-functions/powershell/HelloCities/ChainingOrchestration/function.json b/samples/durable-functions/powershell/HelloCities/ChainingOrchestration/function.json new file mode 100644 index 0000000..0c950e3 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/ChainingOrchestration/function.json @@ -0,0 +1,9 @@ +{ + "bindings": [ + { + "name": "Context", + "type": "orchestrationTrigger", + "direction": "in" + } + ] +} diff --git a/samples/durable-functions/powershell/HelloCities/ChainingOrchestration/run.ps1 b/samples/durable-functions/powershell/HelloCities/ChainingOrchestration/run.ps1 new file mode 100644 index 0000000..dfb9ad1 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/ChainingOrchestration/run.ps1 @@ -0,0 +1,9 @@ +param($Context) + +$output = @() + +$output += Invoke-DurableActivity -FunctionName 'SayHello' -Input 'Tokyo' +$output += Invoke-DurableActivity -FunctionName 'SayHello' -Input 'Seattle' +$output += Invoke-DurableActivity -FunctionName 'SayHello' -Input 'London' + +$output diff --git a/samples/durable-functions/powershell/HelloCities/FanOutFanInOrchestration/function.json b/samples/durable-functions/powershell/HelloCities/FanOutFanInOrchestration/function.json new file mode 100644 index 0000000..0c950e3 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/FanOutFanInOrchestration/function.json @@ -0,0 +1,9 @@ +{ + "bindings": [ + { + "name": "Context", + "type": "orchestrationTrigger", + "direction": "in" + } + ] +} diff --git a/samples/durable-functions/powershell/HelloCities/FanOutFanInOrchestration/run.ps1 b/samples/durable-functions/powershell/HelloCities/FanOutFanInOrchestration/run.ps1 new file mode 100644 index 0000000..ea47141 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/FanOutFanInOrchestration/run.ps1 @@ -0,0 +1,14 @@ +param($Context) + +$cities = @('Tokyo', 'Seattle', 'London', 'Paris', 'Berlin') + +# Fan-out: schedule all activities in parallel +$parallelTasks = @() +foreach ($city in $cities) { + $parallelTasks += Invoke-DurableActivity -FunctionName 'SayHello' -Input $city -NoWait +} + +# Fan-in: wait for all to complete +$output = Wait-ActivityFunction -Task $parallelTasks + +$output diff --git a/samples/durable-functions/powershell/HelloCities/README.md b/samples/durable-functions/powershell/HelloCities/README.md new file mode 100644 index 0000000..ae2c3dc --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/README.md @@ -0,0 +1,114 @@ +# Hello Cities — Durable Functions PowerShell Quickstart + +PowerShell | Durable Functions + +## Description + +This quickstart demonstrates Durable Functions with PowerShell using the Durable Task Scheduler backend. It includes two patterns: + +1. **Function Chaining** — An orchestration that calls three "say hello" activities sequentially +2. **Fan-out/Fan-in** — An orchestration that greets multiple cities in parallel and aggregates results + +## Prerequisites + +1. [PowerShell 7.4+](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) +2. [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local) +3. [Docker](https://www.docker.com/products/docker-desktop/) (for the emulator) + +## Quick Run + +1. Start the emulator: + ```bash + docker run -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest + ``` + +2. Navigate to the sample directory: + ```bash + cd samples/durable-functions/powershell/HelloCities + ``` + +3. Create a `local.settings.json` file: + ```json + { + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "powershell", + "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None" + } + } + ``` + +4. Run the function app: + ```bash + func start + ``` + +5. Trigger the function chaining orchestration: + ```bash + curl -X POST http://localhost:7071/api/StartChaining + ``` + +6. Trigger the fan-out/fan-in orchestration: + ```bash + curl -X POST http://localhost:7071/api/StartFanOutFanIn + ``` + +7. View in the dashboard: http://localhost:8082 + +## Expected Output + +The HTTP triggers return a **202 Accepted** response with status query URLs. Use the `statusQueryGetUri` from the response to poll for completion: + +```bash +# Replace with the statusQueryGetUri value from the 202 response +curl "" +``` + +Once completed, the chaining orchestration output greets Tokyo, Seattle, and London sequentially: +```json +["Hello Tokyo!", "Hello Seattle!", "Hello London!"] +``` + +The fan-out/fan-in orchestration output greets all five cities in parallel and returns combined results: +```json +["Hello Tokyo!", "Hello Seattle!", "Hello London!", "Hello Paris!", "Hello Berlin!"] +``` + +## Using a Deployed Scheduler (Azure) + +To use a Durable Task Scheduler in Azure instead of the emulator: + +1. Update the connection string in `local.settings.json`: + ```json + { + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "powershell", + "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=;TaskHub=;Authentication=ManagedIdentity" + } + } + ``` + +2. Run the sample using the same commands as above. + +See the [Durable Task Scheduler documentation](https://learn.microsoft.com/azure/azure-functions/durable/durable-task-scheduler/develop-with-durable-task-scheduler) for setup instructions. + +## Code Walkthrough + +- **SayHello/** — Activity function that returns a greeting for a given city name. +- **ChainingOrchestration/** — Orchestrator that calls `SayHello` three times in sequence. +- **FanOutFanInOrchestration/** — Orchestrator that calls `SayHello` for five cities in parallel. +- **StartChaining/** — HTTP trigger that starts the chaining orchestration. +- **StartFanOutFanIn/** — HTTP trigger that starts the fan-out/fan-in orchestration. + +## Viewing in the Dashboard + +- **Emulator:** Navigate to http://localhost:8082 → select the "default" task hub +- **Azure:** Navigate to your Scheduler resource in the Azure Portal → Task Hub → Dashboard URL + +## Learn More + +- [Durable Functions PowerShell Developer Guide](https://learn.microsoft.com/azure/azure-functions/durable/quickstart-powershell-vscode) +- [Durable Task Scheduler Documentation](https://aka.ms/dts-documentation) diff --git a/samples/durable-functions/powershell/HelloCities/SayHello/function.json b/samples/durable-functions/powershell/HelloCities/SayHello/function.json new file mode 100644 index 0000000..50ce1e6 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/SayHello/function.json @@ -0,0 +1,9 @@ +{ + "bindings": [ + { + "name": "city", + "type": "activityTrigger", + "direction": "in" + } + ] +} diff --git a/samples/durable-functions/powershell/HelloCities/SayHello/run.ps1 b/samples/durable-functions/powershell/HelloCities/SayHello/run.ps1 new file mode 100644 index 0000000..20721ec --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/SayHello/run.ps1 @@ -0,0 +1,4 @@ +param($city) + +Write-Host "Saying hello to $city." +"Hello $city!" diff --git a/samples/durable-functions/powershell/HelloCities/StartChaining/function.json b/samples/durable-functions/powershell/HelloCities/StartChaining/function.json new file mode 100644 index 0000000..308d192 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/StartChaining/function.json @@ -0,0 +1,24 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "name": "Request", + "type": "httpTrigger", + "direction": "in", + "methods": [ + "post" + ], + "route": "StartChaining" + }, + { + "name": "Response", + "type": "http", + "direction": "out" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" + } + ] +} diff --git a/samples/durable-functions/powershell/HelloCities/StartChaining/run.ps1 b/samples/durable-functions/powershell/HelloCities/StartChaining/run.ps1 new file mode 100644 index 0000000..fc20ec1 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/StartChaining/run.ps1 @@ -0,0 +1,7 @@ +param($Request, $TriggerMetadata) + +$instanceId = Start-DurableOrchestration -FunctionName 'ChainingOrchestration' +Write-Host "Started chaining orchestration with ID = '$instanceId'." + +$response = New-DurableOrchestrationCheckStatusResponse -Request $Request -InstanceId $instanceId +Push-OutputBinding -Name Response -Value $response diff --git a/samples/durable-functions/powershell/HelloCities/StartFanOutFanIn/function.json b/samples/durable-functions/powershell/HelloCities/StartFanOutFanIn/function.json new file mode 100644 index 0000000..65b91fd --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/StartFanOutFanIn/function.json @@ -0,0 +1,24 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "name": "Request", + "type": "httpTrigger", + "direction": "in", + "methods": [ + "post" + ], + "route": "StartFanOutFanIn" + }, + { + "name": "Response", + "type": "http", + "direction": "out" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" + } + ] +} diff --git a/samples/durable-functions/powershell/HelloCities/StartFanOutFanIn/run.ps1 b/samples/durable-functions/powershell/HelloCities/StartFanOutFanIn/run.ps1 new file mode 100644 index 0000000..58103c1 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/StartFanOutFanIn/run.ps1 @@ -0,0 +1,7 @@ +param($Request, $TriggerMetadata) + +$instanceId = Start-DurableOrchestration -FunctionName 'FanOutFanInOrchestration' +Write-Host "Started fan-out/fan-in orchestration with ID = '$instanceId'." + +$response = New-DurableOrchestrationCheckStatusResponse -Request $Request -InstanceId $instanceId +Push-OutputBinding -Name Response -Value $response diff --git a/samples/durable-functions/powershell/HelloCities/host.json b/samples/durable-functions/powershell/HelloCities/host.json new file mode 100644 index 0000000..497a2bc --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/host.json @@ -0,0 +1,24 @@ +{ + "version": "2.0", + "logging": { + "logLevel": { + "DurableTask.Core": "Warning" + } + }, + "extensions": { + "durableTask": { + "hubName": "default", + "storageProvider": { + "type": "azureManaged", + "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[4.*, 5.0.0)" + }, + "managedDependency": { + "enabled": true + } +} diff --git a/samples/durable-functions/powershell/HelloCities/profile.ps1 b/samples/durable-functions/powershell/HelloCities/profile.ps1 new file mode 100644 index 0000000..8eddf75 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/profile.ps1 @@ -0,0 +1,18 @@ +# Azure Functions profile.ps1 +# +# This profile.ps1 will get executed every "cold start" of your Function App. +# "cold start" occurs when: +# +# * A Function App starts up for the very first time +# * A Function App starts up after being de-allocated due to inactivity +# +# You can define helper functions, run commands, or specify environment variables. +# NOTE: variables defined outside a function are stored in the script scope and +# are not automatically available inside your function scripts unless you +# explicitly pass them in or define them in a shared module. + +# Register HttpStatusCode type accelerator (required by Durable Functions module) +$accelerator = [PowerShell].Assembly.GetType('System.Management.Automation.TypeAccelerators') +if (-not $accelerator::Get.ContainsKey('HttpStatusCode')) { + $accelerator::Add('HttpStatusCode', [System.Net.HttpStatusCode]) +} diff --git a/samples/durable-functions/powershell/HelloCities/requirements.psd1 b/samples/durable-functions/powershell/HelloCities/requirements.psd1 new file mode 100644 index 0000000..64afb38 --- /dev/null +++ b/samples/durable-functions/powershell/HelloCities/requirements.psd1 @@ -0,0 +1,4 @@ +@{ + # Managed dependencies - add modules here if needed + # 'Az.Accounts' = '4.*' +} diff --git a/samples/durable-functions/powershell/README.md b/samples/durable-functions/powershell/README.md new file mode 100644 index 0000000..3999c94 --- /dev/null +++ b/samples/durable-functions/powershell/README.md @@ -0,0 +1,15 @@ +# PowerShell Samples for Durable Functions + +This directory contains sample applications demonstrating Durable Functions patterns using PowerShell with the Durable Task Scheduler backend. + +## Prerequisites + +- [PowerShell 7.4+](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) +- [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local) +- [Docker](https://www.docker.com/products/docker-desktop/) + +## Available Samples + +| Sample | Pattern | +|--------|---------| +| [HelloCities](./HelloCities/) | Function Chaining, Fan-out/Fan-in |