diff --git a/DurableTask.sln b/DurableTask.sln
index a82b8ec8e..bfb8afce6 100644
--- a/DurableTask.sln
+++ b/DurableTask.sln
@@ -64,12 +64,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DurableTask.ApplicationInsights", "src\DurableTask.ApplicationInsights\DurableTask.ApplicationInsights.csproj", "{331D783C-C3AF-43DD-9270-6CF22459B2C1}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DistributedTraceSample", "DistributedTraceSample", "{240FA679-D5A7-41CA-BA22-70B45A966088}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DistributedTrace", "DistributedTrace", "{240FA679-D5A7-41CA-BA22-70B45A966088}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationInsightsSample", "samples\DistributedTraceSample\ApplicationInsights\ApplicationInsightsSample.csproj", "{C831792B-00EE-4030-988F-F4492DA9BCE7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetrySample", "samples\DistributedTraceSample\OpenTelemetry\OpenTelemetrySample.csproj", "{D818ED4C-29B9-431F-8D09-EE8C82510E25}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ManagedIdentity", "ManagedIdentity", "{8B797A00-0F43-46F9-8F1A-C945FD4F304F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedIdentity.AzStorageV1", "samples\ManagedIdentitySample\DTFx.AzureStorage v1.x\ManagedIdentity.AzStorageV1.csproj", "{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedIdentity.AzStorageV2", "samples\ManagedIdentitySample\DTFx.AzureStorage v2.x\ManagedIdentity.AzStorageV2.csproj", "{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -246,6 +252,22 @@ Global
{D818ED4C-29B9-431F-8D09-EE8C82510E25}.Release|Any CPU.Build.0 = Release|Any CPU
{D818ED4C-29B9-431F-8D09-EE8C82510E25}.Release|x64.ActiveCfg = Release|Any CPU
{D818ED4C-29B9-431F-8D09-EE8C82510E25}.Release|x64.Build.0 = Release|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|x64.Build.0 = Debug|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|x64.ActiveCfg = Release|Any CPU
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|x64.Build.0 = Release|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|x64.Build.0 = Debug|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|x64.ActiveCfg = Release|Any CPU
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -274,9 +296,12 @@ Global
{240FA679-D5A7-41CA-BA22-70B45A966088} = {AF4E71A6-B16E-4488-B22D-2761101A601A}
{C831792B-00EE-4030-988F-F4492DA9BCE7} = {240FA679-D5A7-41CA-BA22-70B45A966088}
{D818ED4C-29B9-431F-8D09-EE8C82510E25} = {240FA679-D5A7-41CA-BA22-70B45A966088}
+ {8B797A00-0F43-46F9-8F1A-C945FD4F304F} = {AF4E71A6-B16E-4488-B22D-2761101A601A}
+ {CFFC5AD7-5B82-48CF-879D-295D327B5CA6} = {8B797A00-0F43-46F9-8F1A-C945FD4F304F}
+ {87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24} = {8B797A00-0F43-46F9-8F1A-C945FD4F304F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- EnterpriseLibraryConfigurationToolBinariesPath = packages\TransientFaultHandling.Core.5.1.1209.1\lib\NET4
SolutionGuid = {2D63A120-9394-48D9-8CA9-1184364FB854}
+ EnterpriseLibraryConfigurationToolBinariesPath = packages\TransientFaultHandling.Core.5.1.1209.1\lib\NET4
EndGlobalSection
EndGlobal
diff --git a/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ConsoleApp.csproj b/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ConsoleApp.csproj
deleted file mode 100644
index 509de1e63..000000000
--- a/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ConsoleApp.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- Exe
- net6.0
- enable
- enable
-
-
-
-
-
-
-
-
diff --git a/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ManagedIdentity.AzStorageV1.csproj b/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ManagedIdentity.AzStorageV1.csproj
new file mode 100644
index 000000000..f9c44f4a9
--- /dev/null
+++ b/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ManagedIdentity.AzStorageV1.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Latest
+ enable
+ Exe
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/Program.cs b/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/Program.cs
index 57ebd6066..78bf4e524 100644
--- a/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/Program.cs
+++ b/samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/Program.cs
@@ -1,72 +1,96 @@
-using Azure.Core;
+// ----------------------------------------------------------------------------------
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Azure.Core;
using Azure.Identity;
using DurableTask.AzureStorage;
using DurableTask.Core;
+using Microsoft.Extensions.Azure;
+using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Auth;
-internal class Program
+// Create a DefaultAzureCredential used to access the Azure Storage Account.
+// The identity will require the following roles on the resource:
+// - Azure Blob Data Contributor
+// - Azure Queue Data Contributor
+// - Azure Table Data Contributor
+DefaultAzureCredential credential = new();
+
+// Create a diagnostic logger factory for reading telemetry
+ILoggerFactory loggerFactory = LoggerFactory.Create(b => b
+ .AddConsole()
+ .AddFilter("Azure.Core", LogLevel.Warning)
+ .AddFilter("Azure.Identity", LogLevel.Warning));
+
+// The Azure SDKs used by the Azure.Identity library write their telemetry via Event Sources
+using AzureEventSourceLogForwarder logForwarder = new(loggerFactory);
+logForwarder.Start();
+
+NewTokenAndFrequency initialTokenInfo = await GetTokenInfoAsync(credential);
+AzureStorageOrchestrationService service = new(new AzureStorageOrchestrationServiceSettings
{
- private static async Task Main(string[] args)
+ StorageAccountDetails = new StorageAccountDetails
{
- // Create credential based on the configuration
- var credential = new DefaultAzureCredential();
- string[] scopes = new string[] { "https://storage.azure.com/.default" }; // Scope for Azure Storage
-
- static Task RenewTokenFuncAsync(object state, CancellationToken cancellationToken)
- {
- var credential = new DefaultAzureCredential();
- var initialToken = credential.GetToken(new TokenRequestContext(new[] { "https://storage.azure.com/.default" }));
- var expiresAfter = initialToken.ExpiresOn - DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10);
- return Task.FromResult(new NewTokenAndFrequency(initialToken.Token, expiresAfter));
- }
-
- // Get the token
- var accessToken = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes));
-
- var service = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
- {
- StorageAccountDetails = new StorageAccountDetails
- {
- AccountName = "YourStorageAccount",
- EndpointSuffix = "core.windows.net",
- StorageCredentials = new StorageCredentials(new Microsoft.WindowsAzure.Storage.Auth.TokenCredential(
- accessToken.Token,
- RenewTokenFuncAsync,
- null,
- TimeSpan.FromMinutes(5)))
- }
- });
-
- var client = new TaskHubClient(service);
- var worker = new TaskHubWorker(service);
-
- worker.AddTaskOrchestrations(typeof(SampleOrchestration));
- worker.AddTaskActivities(typeof(SampleActivity));
-
- await worker.StartAsync();
-
- var instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
-
- var result = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
-
- Console.WriteLine($"Orchestration result : {result.Output}");
-
- await worker.StopAsync();
- }
+ AccountName = "YourStorageAccount",
+ EndpointSuffix = "core.windows.net",
+ StorageCredentials = new StorageCredentials(new Microsoft.WindowsAzure.Storage.Auth.TokenCredential(
+ initialTokenInfo.Token,
+ GetTokenInfoAsync,
+ credential,
+ initialTokenInfo.Frequency.GetValueOrDefault()))
+ },
+ LoggerFactory = loggerFactory,
+});
+
+TaskHubClient client = new(service, loggerFactory: loggerFactory);
+TaskHubWorker worker = new(service, loggerFactory);
+
+worker.AddTaskOrchestrations(typeof(SampleOrchestration));
+worker.AddTaskActivities(typeof(SampleActivity));
+
+await worker.StartAsync();
+
+OrchestrationInstance instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
+OrchestrationState state = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
+
+ILogger logger = loggerFactory.CreateLogger(nameof(Program));
+logger.LogInformation("Orchestration output: {Output}", state.Output);
+
+await worker.StopAsync();
+
+static async Task GetTokenInfoAsync(object state, CancellationToken cancellationToken = default)
+{
+ const string AzureStorageScope = "https://storage.azure.com/.default";
+
+ if (state is not DefaultAzureCredential credential)
+ throw new InvalidOperationException();
+
+ AccessToken accessToken = await credential.GetTokenAsync(new TokenRequestContext([AzureStorageScope]), cancellationToken);
+ TimeSpan refreshFrequency = accessToken.ExpiresOn - DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10); // 10 minutes before expiration
+ return new NewTokenAndFrequency(accessToken.Token, refreshFrequency);
}
-public class SampleOrchestration : TaskOrchestration
+internal sealed class SampleOrchestration : TaskOrchestration
{
- public override async Task RunTask(OrchestrationContext context, string input)
- {
- return await context.ScheduleTask(typeof(SampleActivity), input);
- }
+ public override Task RunTask(OrchestrationContext context, string input) =>
+ context.ScheduleTask(typeof(SampleActivity), input);
}
-public class SampleActivity : TaskActivity
+internal sealed class SampleActivity : TaskActivity
{
- protected override string Execute(TaskContext context, string input)
- {
- return "Hello, " + input + "!";
- }
+ protected override string Execute(TaskContext context, string input) =>
+ "Hello, " + input + "!";
}
diff --git a/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ConsoleApp.csproj b/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ConsoleApp.csproj
deleted file mode 100644
index 4238bec7b..000000000
--- a/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ConsoleApp.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- Exe
- net8.0
- enable
- enable
-
-
-
-
-
-
-
-
diff --git a/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ManagedIdentity.AzStorageV2.csproj b/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ManagedIdentity.AzStorageV2.csproj
new file mode 100644
index 000000000..956fc14c2
--- /dev/null
+++ b/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ManagedIdentity.AzStorageV2.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Latest
+ enable
+ Exe
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/Program.cs b/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/Program.cs
index 8450a0bc1..7b072452a 100644
--- a/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/Program.cs
+++ b/samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/Program.cs
@@ -1,53 +1,71 @@
-using DurableTask.AzureStorage;
-using DurableTask.Core;
+// ----------------------------------------------------------------------------------
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Threading.Tasks;
using Azure.Identity;
+using DurableTask.AzureStorage;
+using DurableTask.Core;
+using Microsoft.Extensions.Azure;
+using Microsoft.Extensions.Logging;
+
+// Create a DefaultAzureCredential used to access the Azure Storage Account.
+// The identity will require the following roles on the resource:
+// - Azure Blob Data Contributor
+// - Azure Queue Data Contributor
+// - Azure Table Data Contributor
+DefaultAzureCredential credential = new();
+
+// Create a diagnostic logger factory for reading telemetry
+ILoggerFactory loggerFactory = LoggerFactory.Create(b => b
+ .AddConsole()
+ .AddFilter("Azure.Core", LogLevel.Warning)
+ .AddFilter("Azure.Identity", LogLevel.Warning));
-internal class Program
+// The Azure SDKs used by the Azure.Identity and Azure Storage client libraries write their telemetry via Event Sources
+using AzureEventSourceLogForwarder logForwarder = new(loggerFactory);
+logForwarder.Start();
+
+AzureStorageOrchestrationService service = new(new AzureStorageOrchestrationServiceSettings
{
- private static async Task Main(string[] args)
- {
- var credential = new DefaultAzureCredential();
-
- // Pass the credential created to the StorageAccountClientProvider to start an AzureStorageOrchestrationService
- var service = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
- {
- StorageAccountClientProvider = new StorageAccountClientProvider("AccountName", credential),
- });
+ LoggerFactory = loggerFactory,
+ StorageAccountClientProvider = new StorageAccountClientProvider("YourStorageAccount", credential),
+});
- var client = new TaskHubClient(service);
- var worker = new TaskHubWorker(service);
+TaskHubClient client = new(service, loggerFactory: loggerFactory);
+TaskHubWorker worker = new(service, loggerFactory);
- worker.AddTaskOrchestrations(typeof(SampleOrchestration));
- worker.AddTaskActivities(typeof(SampleActivity));
+worker.AddTaskOrchestrations(typeof(SampleOrchestration));
+worker.AddTaskActivities(typeof(SampleActivity));
- await worker.StartAsync();
+await worker.StartAsync();
- var instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
+OrchestrationInstance instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
+OrchestrationState state = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
- var result = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
+ILogger logger = loggerFactory.CreateLogger(nameof(Program));
+logger.LogInformation("Orchestration output: {Output}", state.Output);
- Console.WriteLine($"Orchestration result : {result.Output}");
-
- await worker.StopAsync();
- }
-}
+await worker.StopAsync();
-public class SampleOrchestration : TaskOrchestration
+internal sealed class SampleOrchestration : TaskOrchestration
{
- public override async Task RunTask(OrchestrationContext context, string input)
- {
- await context.ScheduleTask(typeof(SampleActivity), input);
-
- return "Orchestrator Finished!";
- }
+ public override Task RunTask(OrchestrationContext context, string input) =>
+ context.ScheduleTask(typeof(SampleActivity), input);
}
-public class SampleActivity : TaskActivity
+internal sealed class SampleActivity : TaskActivity
{
- protected override string Execute(TaskContext context, string input)
- {
- Console.WriteLine("saying hello to " + input);
- return "Hello " + input + "!";
- }
+ protected override string Execute(TaskContext context, string input) =>
+ "Hello, " + input + "!";
}
-