From 7170a1b469095e36711c03b695f774fcbf2b8658 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:30:04 +0000 Subject: [PATCH 1/3] Initial plan From 370b2bbcaddd6eab854678362cafefd48468cfc7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:45:12 +0000 Subject: [PATCH 2/3] Add comprehensive comments to precompiled samples and create README Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com> --- samples/precompiled/BackupSiteContent.cs | 27 ++++++ samples/precompiled/Counter.cs | 39 ++++++-- samples/precompiled/HelloSequence.cs | 23 ++++- samples/precompiled/HttpStart.cs | 20 ++++ samples/precompiled/HttpSyncStart.cs | 25 +++++ samples/precompiled/PhoneVerification.cs | 33 +++++++ samples/precompiled/README.md | 117 +++++++++++++++++++++++ samples/precompiled/RestartVMs.cs | 27 +++++- samples/precompiled/SMSReminder.cs | 45 ++++++--- 9 files changed, 330 insertions(+), 26 deletions(-) create mode 100644 samples/precompiled/README.md diff --git a/samples/precompiled/BackupSiteContent.cs b/samples/precompiled/BackupSiteContent.cs index 71570cb4e..21bde4500 100644 --- a/samples/precompiled/BackupSiteContent.cs +++ b/samples/precompiled/BackupSiteContent.cs @@ -1,6 +1,23 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates the Fan-out/Fan-in pattern. In this pattern, multiple activity + * functions are executed in parallel, and then the orchestrator waits for all of them to + * complete before continuing. This is useful when you need to perform the same operation + * on multiple items concurrently, such as backing up multiple files. + * + * Pattern documentation: + * https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-cloud-backup + * + * To run this sample: + * 1. Configure the AzureWebJobsStorage connection string in local.settings.json + * 2. Start the function app locally using `func host start` or run from Visual Studio + * 3. Make an HTTP POST request to: http://localhost:7071/orchestrators/E2_BackupSiteContent + * Optionally include a JSON body with the root directory path to backup + * + * Required app settings: + * - AzureWebJobsStorage: Azure Storage connection string for storing backup blobs + */ using System.IO; using System.Linq; using System.Threading.Tasks; @@ -13,20 +30,25 @@ namespace VSSample { public static class BackupSiteContent { + // Orchestrator function that demonstrates Fan-out/Fan-in by backing up files in parallel. + // First gets the file list (single activity), then fans out to copy each file concurrently. [FunctionName("E2_BackupSiteContent")] public static async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext backupContext) { + // Get the root directory from input, or default to the assembly directory string rootDirectory = backupContext.GetInput()?.Trim(); if (string.IsNullOrEmpty(rootDirectory)) { rootDirectory = Directory.GetParent(typeof(BackupSiteContent).Assembly.Location).FullName; } + // Step 1: Get the list of files to backup (single activity call) string[] files = await backupContext.CallActivityAsync( "E2_GetFileList", rootDirectory); + // Step 2: Fan-out - start all file copy tasks in parallel (no await yet) var tasks = new Task[files.Length]; for (int i = 0; i < files.Length; i++) { @@ -35,12 +57,15 @@ public static async Task Run( files[i]); } + // Step 3: Fan-in - wait for all parallel tasks to complete await Task.WhenAll(tasks); + // Aggregate results from all tasks long totalBytes = tasks.Sum(t => t.Result); return totalBytes; } + // Activity function that retrieves the list of files from a directory. [FunctionName("E2_GetFileList")] public static string[] GetFileList( [ActivityTrigger] string rootDirectory, @@ -53,6 +78,8 @@ public static string[] GetFileList( return files; } + // Activity function that copies a single file to Azure Blob Storage. + // This function is called in parallel for each file (fan-out). [FunctionName("E2_CopyFileToBlob")] public static async Task CopyFileToBlob( [ActivityTrigger] string filePath, diff --git a/samples/precompiled/Counter.cs b/samples/precompiled/Counter.cs index e69b1af1d..112b7d8f2 100644 --- a/samples/precompiled/Counter.cs +++ b/samples/precompiled/Counter.cs @@ -1,6 +1,28 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates the Aggregator (Entity) pattern using Durable Entities. + * Entities are stateful actors that can be created, updated, and queried. + * Unlike orchestrations, entities have stable identities and their state persists + * indefinitely until explicitly deleted. + * + * Pattern documentation: + * https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-entities + * + * To run this sample, use the built-in entity webhooks (no client function needed): + * 1. Start the function app locally using `func host start` or run from Visual Studio + * 2. Reset the counter: + * curl -X POST -H "Content-Length: 0" "http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Reset" + * 3. Add to the counter: + * curl -d "1" -X POST -H "Content-Type: application/json" http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Add + * curl -d "2" -X POST -H "Content-Type: application/json" http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Add + * 4. Read the counter value: + * curl http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter + * + * The result of the final GET operation should be: {"value":3} + * + * No special app settings are required for this sample. + */ using System.Threading.Tasks; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.DurableTask; @@ -8,27 +30,24 @@ namespace VSSample { - // This sample does not include any "client" functions. Instead, you can just use - // the built-in webhooks to create and interact with this entity type. - // - // Examples: - // curl -X POST -H "Content-Length: 0" "http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Reset" - // curl -d "1" -X POST -H "Content-Type: application/json" http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Add - // curl -d "2" -X POST -H "Content-Type: application/json" http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Add - // curl http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter - // - // The result of the final GET operation should be: {"value":3} + // Entity class that maintains a counter value with Add, Reset, and Get operations. + // The class-based syntax provides a cleaner way to define entity behavior. public class Counter { + // The current counter value - persisted as entity state [JsonProperty("value")] public int CurrentValue { get; set; } + // Operation: Add an amount to the current counter value public void Add(int amount) => this.CurrentValue += amount; + // Operation: Reset the counter to zero public void Reset() => this.CurrentValue = 0; + // Operation: Get the current counter value public int Get() => this.CurrentValue; + // Entity function entry point that dispatches operations to the Counter class [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync(); diff --git a/samples/precompiled/HelloSequence.cs b/samples/precompiled/HelloSequence.cs index 25a8f431d..ddba27611 100644 --- a/samples/precompiled/HelloSequence.cs +++ b/samples/precompiled/HelloSequence.cs @@ -1,6 +1,20 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates the Function Chaining pattern. In this pattern, activity functions + * are called sequentially, where the output of one function can be used as input to the next. + * This is the most basic Durable Functions pattern and is useful when you need to execute + * a sequence of operations in a specific order. + * + * Pattern documentation: + * https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-sequence + * + * To run this sample: + * 1. Start the function app locally using `func host start` or run from Visual Studio + * 2. Make an HTTP POST request to: http://localhost:7071/orchestrators/E1_HelloSequence + * + * No special app settings are required for this sample. + */ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; @@ -10,12 +24,15 @@ namespace VSSample { public static class HelloSequence { + // Orchestrator function that chains multiple activity function calls sequentially. + // Each activity is awaited before calling the next, demonstrating ordered execution. [FunctionName("E1_HelloSequence")] public static async Task> Run( [OrchestrationTrigger] IDurableOrchestrationContext context) { var outputs = new List(); + // Call activities sequentially - each call waits for the previous to complete outputs.Add(await context.CallActivityAsync("E1_SayHello", "Tokyo")); outputs.Add(await context.CallActivityAsync("E1_SayHello", "Seattle")); outputs.Add(await context.CallActivityAsync("E1_SayHello_DirectInput", "London")); @@ -24,6 +41,8 @@ public static async Task> Run( return outputs; } + // Activity function that demonstrates getting input via the activity context. + // This approach allows access to additional context properties if needed. [FunctionName("E1_SayHello")] public static string SayHello([ActivityTrigger] IDurableActivityContext context) { @@ -31,10 +50,12 @@ public static string SayHello([ActivityTrigger] IDurableActivityContext context) return $"Hello {name}!"; } + // Activity function that demonstrates direct input binding. + // This is simpler when you only need the input value. [FunctionName("E1_SayHello_DirectInput")] public static string SayHelloDirectInput([ActivityTrigger] string name) { return $"Hello {name}!"; } } - } +} diff --git a/samples/precompiled/HttpStart.cs b/samples/precompiled/HttpStart.cs index 28c262250..38932c64e 100644 --- a/samples/precompiled/HttpStart.cs +++ b/samples/precompiled/HttpStart.cs @@ -1,6 +1,22 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates how to start orchestrations via HTTP. + * The HttpStart function is a generic HTTP trigger that can start any orchestration + * by name. It returns the standard Durable Functions response containing URLs for + * checking status, sending events, and terminating the orchestration. + * + * To run this sample: + * 1. Start the function app locally using `func host start` or run from Visual Studio + * 2. Start an orchestration by posting to: + * curl -i -X POST http://localhost:7071/orchestrators/{functionName} -H "Content-Length: 0" + * Replace {functionName} with the name of the orchestration to start (e.g., E1_HelloSequence) + * 3. Optionally include JSON input in the request body + * + * The response includes a Location header and URLs for managing the orchestration instance. + * + * No special app settings are required for this sample. + */ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; @@ -12,6 +28,8 @@ namespace VSSample { public static class HttpStart { + // HTTP trigger function that starts an orchestration by name. + // Uses route parameter to determine which orchestration to start. [FunctionName("HttpStart")] public static async Task Run( [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req, @@ -25,6 +43,8 @@ public static async Task Run( log.LogInformation($"Started orchestration with ID = '{instanceId}'."); + // CreateCheckStatusResponse returns the standard Durable Functions response + // containing status query URLs and management endpoints return starter.CreateCheckStatusResponse(req, instanceId); } } diff --git a/samples/precompiled/HttpSyncStart.cs b/samples/precompiled/HttpSyncStart.cs index 136e29ef1..6a9294c33 100644 --- a/samples/precompiled/HttpSyncStart.cs +++ b/samples/precompiled/HttpSyncStart.cs @@ -1,6 +1,26 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates synchronous HTTP orchestration starting. + * Unlike HttpStart which returns immediately with status URLs, this function + * waits for the orchestration to complete before returning the result. + * This is useful when orchestrations complete quickly and you want to get + * the result in a single HTTP request. + * + * To run this sample: + * 1. Start the function app locally using `func host start` or run from Visual Studio + * 2. Start an orchestration and wait for completion: + * curl -i -X POST "http://localhost:7071/orchestrators/{functionName}/wait" -H "Content-Length: 0" + * Replace {functionName} with the name of the orchestration to start (e.g., E1_HelloSequence) + * 3. Optional query parameters: + * - timeout: Maximum seconds to wait (default: 30) + * - retryInterval: Seconds between status checks (default: 1) + * + * If the orchestration completes within the timeout, the result is returned directly. + * If it doesn't complete in time, the standard status check URLs are returned instead. + * + * No special app settings are required for this sample. + */ using System; using System.Net.Http; using System.Threading.Tasks; @@ -16,6 +36,8 @@ public static class HttpSyncStart private const string Timeout = "timeout"; private const string RetryInterval = "retryInterval"; + // HTTP trigger function that starts an orchestration and waits for it to complete. + // Returns the orchestration result if it completes within the timeout. [FunctionName("HttpSyncStart")] public static async Task Run( [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}/wait")] @@ -30,9 +52,11 @@ public static async Task Run( log.LogInformation($"Started orchestration with ID = '{instanceId}'."); + // Parse optional timeout and retry interval from query string TimeSpan timeout = GetTimeSpan(req, Timeout) ?? TimeSpan.FromSeconds(30); TimeSpan retryInterval = GetTimeSpan(req, RetryInterval) ?? TimeSpan.FromSeconds(1); + // Wait for the orchestration to complete, or return status URLs if it takes too long return await starter.WaitForCompletionOrCreateCheckStatusResponseAsync( req, instanceId, @@ -40,6 +64,7 @@ public static async Task Run( retryInterval); } + // Helper method to parse TimeSpan from query string parameters private static TimeSpan? GetTimeSpan(HttpRequestMessage request, string queryParameterName) { string queryParameterStringValue = request.RequestUri.ParseQueryString()[queryParameterName]; diff --git a/samples/precompiled/PhoneVerification.cs b/samples/precompiled/PhoneVerification.cs index 86468a45d..0f03c03fe 100644 --- a/samples/precompiled/PhoneVerification.cs +++ b/samples/precompiled/PhoneVerification.cs @@ -1,6 +1,30 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates the Human Interaction pattern. In this pattern, the orchestrator + * waits for an external event (human input) before continuing. This example implements + * SMS-based phone verification where a code is sent to the user and they must respond + * with the correct code within a timeout period. + * + * Pattern documentation: + * https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-phone-verification + * + * To run this sample: + * 1. Configure the required Twilio app settings (see below) + * 2. Start the function app locally using `func host start` or run from Visual Studio + * 3. Make an HTTP POST request to: http://localhost:7071/orchestrators/E4_SmsPhoneVerification + * Include the phone number as a JSON string in the request body + * 4. After receiving the SMS code, send an event to the orchestration: + * POST to the sendEventPostUri with eventName "SmsChallengeResponse" and the code as body + * + * Required app settings: + * - TwilioAccountSid: Your Twilio account's SID + * - TwilioAuthToken: Your Twilio account's auth token + * - TwilioPhoneNumber: An SMS-capable Twilio phone number + * + * For Twilio trial accounts, you must verify the destination phone number first. + * Twilio: https://www.twilio.com + */ using System; using System.Threading; using System.Threading.Tasks; @@ -14,6 +38,8 @@ namespace VSSample { public static class PhoneVerification { + // Orchestrator function that waits for external human interaction (SMS verification). + // Demonstrates timeout handling and external event correlation. [FunctionName("E4_SmsPhoneVerification")] public static async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext context) @@ -26,6 +52,7 @@ public static async Task Run( "A phone number input is required."); } + // Send an SMS with a verification code to the user int challengeCode = await context.CallActivityAsync( "E4_SendSmsChallenge", phoneNumber); @@ -37,11 +64,14 @@ public static async Task Run( Task timeoutTask = context.CreateTimer(expiration, timeoutCts.Token); bool authorized = false; + // Allow up to 3 retry attempts for incorrect codes for (int retryCount = 0; retryCount <= 3; retryCount++) { + // Wait for the user to send the "SmsChallengeResponse" event with their code Task challengeResponseTask = context.WaitForExternalEvent("SmsChallengeResponse"); + // Race between user response and timeout Task winner = await Task.WhenAny(challengeResponseTask, timeoutTask); if (winner == challengeResponseTask) { @@ -51,6 +81,7 @@ public static async Task Run( authorized = true; break; } + // Wrong code - continue to next retry iteration } else { @@ -69,6 +100,8 @@ public static async Task Run( } } + // Activity function that generates a random code and sends it via SMS using Twilio. + // The code is returned to the orchestrator for verification against user input. [FunctionName("E4_SendSmsChallenge")] public static int SendSmsChallenge( [ActivityTrigger] string phoneNumber, diff --git a/samples/precompiled/README.md b/samples/precompiled/README.md new file mode 100644 index 000000000..76523dea3 --- /dev/null +++ b/samples/precompiled/README.md @@ -0,0 +1,117 @@ +# Precompiled C# Durable Functions Samples + +This folder contains precompiled C# samples demonstrating various Durable Functions patterns. These samples use the Azure Functions In-Process model with the Azure WebJobs SDK. + +## Patterns Demonstrated + +| Sample File | Pattern | Description | +|-------------|---------|-------------| +| [HelloSequence.cs](HelloSequence.cs) | Function Chaining | Sequentially calls activity functions, where each output can be input to the next | +| [BackupSiteContent.cs](BackupSiteContent.cs) | Fan-out/Fan-in | Executes multiple activity functions in parallel and waits for all to complete | +| [Monitor.cs](Monitor.cs) | Monitor | Polls for a condition with configurable intervals and timeout | +| [PhoneVerification.cs](PhoneVerification.cs) | Human Interaction | Waits for external events (human input) with timeout handling | +| [Counter.cs](Counter.cs) | Aggregator (Durable Entity) | Stateful entity that maintains state across operations | +| [SMSReminder.cs](SMSReminder.cs) | Durable Timer | Uses durable timers for delayed/scheduled execution | +| [RestartVMs.cs](RestartVMs.cs) | Managed Identity HTTP | Makes authenticated HTTP calls using managed identity | + +## Getting Started + +### Prerequisites + +- [.NET Core SDK](https://dotnet.microsoft.com/download) 3.1 or later +- [Azure Functions Core Tools](https://docs.microsoft.com/azure/azure-functions/functions-run-local) v3.x or later +- [Azure Storage Emulator](https://docs.microsoft.com/azure/storage/common/storage-use-emulator) or an Azure Storage account + +### Building the Sample + +```bash +dotnet build VSSample.sln +``` + +Or open `VSSample.sln` in Visual Studio and build from there. + +### Running the Sample Locally + +1. Configure `local.settings.json` with your Azure Storage connection string: + + ```json + { + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet" + } + } + ``` + +2. Start the function app: + + ```bash + func host start + ``` + + Or press F5 in Visual Studio. + +### Running the Orchestrations + +Use the following cURL commands to start different orchestrations: + +**Function Chaining (HelloSequence):** +```bash +curl -i -X POST http://localhost:7071/orchestrators/E1_HelloSequence -H "Content-Length: 0" +``` + +**Fan-out/Fan-in (BackupSiteContent):** +```bash +curl -i -X POST http://localhost:7071/orchestrators/E2_BackupSiteContent -H "Content-Length: 0" +``` + +**Aggregator/Entity (Counter):** +```bash +# Reset counter +curl -X POST -H "Content-Length: 0" "http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Reset" + +# Add to counter +curl -d "5" -X POST -H "Content-Type: application/json" http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter?op=Add + +# Get counter value +curl http://localhost:7071/runtime/webhooks/durabletask/entities/Counter/MyCounter +``` + +### Checking Orchestration Status + +The POST response includes a `Location` header and a `statusQueryGetUri` in the JSON body. Use this URL to check the orchestration status: + +```bash +curl -i "http://localhost:7071/runtime/webhooks/durabletask/instances/{instanceId}?taskHub=TestHubName&connection=Storage" +``` + +## Samples Requiring External Services + +Some samples require external service configuration: + +### Monitor, PhoneVerification, SMSReminder (Twilio) + +These samples require a [Twilio](https://www.twilio.com) account. Add the following app settings: + +- `TwilioAccountSid`: Your Twilio account SID +- `TwilioAuthToken`: Your Twilio auth token +- `TwilioPhoneNumber`: Your Twilio SMS-capable phone number + +### RestartVMs (Azure Managed Identity) + +This sample requires: +- Function App with managed identity enabled +- Appropriate Azure RBAC permissions to list and restart VMs + +## Documentation + +For more information on Durable Functions patterns, see the [official documentation](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview): + +- [Function Chaining](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-sequence) +- [Fan-out/Fan-in](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-cloud-backup) +- [Monitor](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-monitor) +- [Human Interaction](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-phone-verification) +- [Durable Entities](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-entities) +- [Durable Timers](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-timers) +- [Durable HTTP](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-http-features) diff --git a/samples/precompiled/RestartVMs.cs b/samples/precompiled/RestartVMs.cs index cffa4de62..bd98758f4 100644 --- a/samples/precompiled/RestartVMs.cs +++ b/samples/precompiled/RestartVMs.cs @@ -1,6 +1,26 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +/* This sample demonstrates how to make authenticated HTTP calls using Managed Identity. + * The orchestrator uses CallHttpAsync with a ManagedIdentityTokenSource to automatically + * acquire and refresh Azure AD tokens for calling Azure Resource Manager APIs. + * + * This example lists all VMs in a subscription and restarts them sequentially. + * + * Documentation: + * https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-http-features + * + * To run this sample: + * 1. Enable managed identity on your Azure Function App + * 2. Grant the managed identity permissions to your subscription (e.g., VM Contributor role) + * 3. Start the function app + * 4. Make an HTTP POST request to: http://localhost:7071/api/RestartVMs_HttpStart + * Include JSON body: {"subscriptionId": "your-subscription-id", "resourceGroup": "your-resource-group"} + * + * Required setup: + * - Function App must have a system-assigned or user-assigned managed identity + * - The identity must have permissions to list and restart VMs in the target subscription + */ using System; using System.Collections.Generic; using System.Net; @@ -15,10 +35,10 @@ namespace VSSample { - // To authorize ARM calls, your subscription has to have permissions to your function app. - // Making the subscription an owner of your function app is one solution to this. public static class RestartVMs { + // Orchestrator function that uses managed identity to call Azure Resource Manager APIs. + // Demonstrates durable HTTP with automatic token acquisition and refresh. [FunctionName("RestartVMs")] public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) @@ -66,6 +86,8 @@ public static async Task RunOrchestrator( } } + // HTTP trigger function to start the VM restart orchestration. + // Validates input and provides an example payload for error cases. [FunctionName("RestartVMs_HttpStart")] public static async Task HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req, @@ -92,6 +114,7 @@ public static async Task HttpStart( return starter.CreateCheckStatusResponse(req, instanceId); } + // Input model for the orchestration containing Azure subscription information class ResourceInfo { [JsonProperty("apiVersion", DefaultValueHandling = DefaultValueHandling.Ignore)] diff --git a/samples/precompiled/SMSReminder.cs b/samples/precompiled/SMSReminder.cs index fbca43203..a9638b594 100644 --- a/samples/precompiled/SMSReminder.cs +++ b/samples/precompiled/SMSReminder.cs @@ -1,3 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +/* This sample demonstrates durable timers for delayed execution (reminder pattern). + * The orchestrator sets a 24-hour timer and then sends an SMS reminder when it fires. + * This is useful for scheduling future actions like reminders, follow-ups, or notifications. + * + * This sample is meant to be used with the To Do List sample at: + * https://github.com/Azure/azure-functions-durable-extension/tree/dev/samples/todolist-aspnetcore + * + * Pattern documentation: + * https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-timers + * + * To run this sample: + * 1. Configure the required Twilio app settings (see below) + * 2. Start the function app locally using `func host start` or run from Visual Studio + * 3. The orchestration is typically started by the To Do List sample when creating tasks + * + * Required app settings: + * - TwilioAccountSid: Your Twilio account's SID + * - TwilioAuthToken: Your Twilio account's auth token + * - TwilioPhoneNumber: An SMS-capable Twilio phone number + * - TwilioPhoneNumberTo: An SMS-capable phone number to send the reminder to + * + * Twilio: https://www.twilio.com + */ using System; using System.Threading; using System.Threading.Tasks; @@ -7,36 +33,29 @@ using Twilio.Rest.Api.V2010.Account; using Twilio.Types; -/* This sample is meant to be used with the To Do List sample at - * https://github.com/Azure/azure-functions-durable-extension/tree/dev/samples/todolist-aspnetcore - * - * To run this sample, you'll need to define the following app settings: - * - * - TwilioAccountSid: your Twilio account's SID - * - TwilioAuthToken: your Twilio account's auth token - * - TwilioPhoneNumber: an SMS-capable Twilio number - * - TwilioPhoneNumberTo: an SMS-capable phone number to send the text to - * - * Twilio: https://www.twilio.com - */ - namespace VSSample { public static class SMSReminder { + // Orchestrator function that waits 24 hours before sending an SMS reminder. + // Demonstrates the durable timer pattern for delayed execution. [FunctionName("SetReminder")] public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { string task = context.GetInput(); + // Schedule the reminder for 24 hours from now + // The timer is durable - the orchestration can be unloaded and resumed when the timer fires DateTime fireAt = context.CurrentUtcDateTime.AddHours(24); await context.CreateTimer(fireAt, CancellationToken.None); + // Send the reminder after the timer fires await context.CallActivityAsync("SendSMS", task); } + // Activity function that sends an SMS reminder using Twilio. [FunctionName("SendSMS")] public static void SendSMS([ActivityTrigger] string taskName, ILogger log, From 40f309d9d04973e9fbef58889321228328a08b9f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:49:53 +0000 Subject: [PATCH 3/3] Fix README prerequisites to use current versions Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com> --- samples/precompiled/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/precompiled/README.md b/samples/precompiled/README.md index 76523dea3..5c1d6723a 100644 --- a/samples/precompiled/README.md +++ b/samples/precompiled/README.md @@ -18,8 +18,8 @@ This folder contains precompiled C# samples demonstrating various Durable Functi ### Prerequisites -- [.NET Core SDK](https://dotnet.microsoft.com/download) 3.1 or later -- [Azure Functions Core Tools](https://docs.microsoft.com/azure/azure-functions/functions-run-local) v3.x or later +- [.NET SDK](https://dotnet.microsoft.com/download/dotnet) 6.0 or later +- [Azure Functions Core Tools](https://docs.microsoft.com/azure/azure-functions/functions-run-local) v4.x or later - [Azure Storage Emulator](https://docs.microsoft.com/azure/storage/common/storage-use-emulator) or an Azure Storage account ### Building the Sample