Skip to content

Commit 934a5b6

Browse files
authored
Update Managed Identity Samples with Logging (#1124)
1 parent 8ddb4a6 commit 934a5b6

File tree

7 files changed

+203
-128
lines changed

7 files changed

+203
-128
lines changed

DurableTask.sln

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
6464
EndProject
6565
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DurableTask.ApplicationInsights", "src\DurableTask.ApplicationInsights\DurableTask.ApplicationInsights.csproj", "{331D783C-C3AF-43DD-9270-6CF22459B2C1}"
6666
EndProject
67-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DistributedTraceSample", "DistributedTraceSample", "{240FA679-D5A7-41CA-BA22-70B45A966088}"
67+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DistributedTrace", "DistributedTrace", "{240FA679-D5A7-41CA-BA22-70B45A966088}"
6868
EndProject
6969
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationInsightsSample", "samples\DistributedTraceSample\ApplicationInsights\ApplicationInsightsSample.csproj", "{C831792B-00EE-4030-988F-F4492DA9BCE7}"
7070
EndProject
7171
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetrySample", "samples\DistributedTraceSample\OpenTelemetry\OpenTelemetrySample.csproj", "{D818ED4C-29B9-431F-8D09-EE8C82510E25}"
7272
EndProject
73+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ManagedIdentity", "ManagedIdentity", "{8B797A00-0F43-46F9-8F1A-C945FD4F304F}"
74+
EndProject
75+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedIdentity.AzStorageV1", "samples\ManagedIdentitySample\DTFx.AzureStorage v1.x\ManagedIdentity.AzStorageV1.csproj", "{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}"
76+
EndProject
77+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedIdentity.AzStorageV2", "samples\ManagedIdentitySample\DTFx.AzureStorage v2.x\ManagedIdentity.AzStorageV2.csproj", "{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}"
78+
EndProject
7379
Global
7480
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7581
Debug|Any CPU = Debug|Any CPU
@@ -246,6 +252,22 @@ Global
246252
{D818ED4C-29B9-431F-8D09-EE8C82510E25}.Release|Any CPU.Build.0 = Release|Any CPU
247253
{D818ED4C-29B9-431F-8D09-EE8C82510E25}.Release|x64.ActiveCfg = Release|Any CPU
248254
{D818ED4C-29B9-431F-8D09-EE8C82510E25}.Release|x64.Build.0 = Release|Any CPU
255+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
256+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
257+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|x64.ActiveCfg = Debug|Any CPU
258+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Debug|x64.Build.0 = Debug|Any CPU
259+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
260+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|Any CPU.Build.0 = Release|Any CPU
261+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|x64.ActiveCfg = Release|Any CPU
262+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6}.Release|x64.Build.0 = Release|Any CPU
263+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
264+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|Any CPU.Build.0 = Debug|Any CPU
265+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|x64.ActiveCfg = Debug|Any CPU
266+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Debug|x64.Build.0 = Debug|Any CPU
267+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|Any CPU.ActiveCfg = Release|Any CPU
268+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|Any CPU.Build.0 = Release|Any CPU
269+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|x64.ActiveCfg = Release|Any CPU
270+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24}.Release|x64.Build.0 = Release|Any CPU
249271
EndGlobalSection
250272
GlobalSection(SolutionProperties) = preSolution
251273
HideSolutionNode = FALSE
@@ -274,9 +296,12 @@ Global
274296
{240FA679-D5A7-41CA-BA22-70B45A966088} = {AF4E71A6-B16E-4488-B22D-2761101A601A}
275297
{C831792B-00EE-4030-988F-F4492DA9BCE7} = {240FA679-D5A7-41CA-BA22-70B45A966088}
276298
{D818ED4C-29B9-431F-8D09-EE8C82510E25} = {240FA679-D5A7-41CA-BA22-70B45A966088}
299+
{8B797A00-0F43-46F9-8F1A-C945FD4F304F} = {AF4E71A6-B16E-4488-B22D-2761101A601A}
300+
{CFFC5AD7-5B82-48CF-879D-295D327B5CA6} = {8B797A00-0F43-46F9-8F1A-C945FD4F304F}
301+
{87CA84DE-A0FE-443C-8B2B-AB89F5DF5C24} = {8B797A00-0F43-46F9-8F1A-C945FD4F304F}
277302
EndGlobalSection
278303
GlobalSection(ExtensibilityGlobals) = postSolution
279-
EnterpriseLibraryConfigurationToolBinariesPath = packages\TransientFaultHandling.Core.5.1.1209.1\lib\NET4
280304
SolutionGuid = {2D63A120-9394-48D9-8CA9-1184364FB854}
305+
EnterpriseLibraryConfigurationToolBinariesPath = packages\TransientFaultHandling.Core.5.1.1209.1\lib\NET4
281306
EndGlobalSection
282307
EndGlobal

samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/ConsoleApp.csproj

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<LangVersion>Latest</LangVersion>
5+
<Nullable>enable</Nullable>
6+
<OutputType>Exe</OutputType>
7+
<TargetFramework>net6.0</TargetFramework>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Azure.Identity" Version="1.12.0" />
12+
<PackageReference Include="Microsoft.Azure.DurableTask.AzureStorage" Version="1.17.3" />
13+
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.4" />
14+
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
15+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
16+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
17+
</ItemGroup>
18+
19+
</Project>
Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,96 @@
1-
using Azure.Core;
1+
// ----------------------------------------------------------------------------------
2+
// Copyright Microsoft Corporation
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
// ----------------------------------------------------------------------------------
13+
14+
using System;
15+
using System.Threading;
16+
using System.Threading.Tasks;
17+
using Azure.Core;
218
using Azure.Identity;
319
using DurableTask.AzureStorage;
420
using DurableTask.Core;
21+
using Microsoft.Extensions.Azure;
22+
using Microsoft.Extensions.Logging;
523
using Microsoft.WindowsAzure.Storage.Auth;
624

7-
internal class Program
25+
// Create a DefaultAzureCredential used to access the Azure Storage Account.
26+
// The identity will require the following roles on the resource:
27+
// - Azure Blob Data Contributor
28+
// - Azure Queue Data Contributor
29+
// - Azure Table Data Contributor
30+
DefaultAzureCredential credential = new();
31+
32+
// Create a diagnostic logger factory for reading telemetry
33+
ILoggerFactory loggerFactory = LoggerFactory.Create(b => b
34+
.AddConsole()
35+
.AddFilter("Azure.Core", LogLevel.Warning)
36+
.AddFilter("Azure.Identity", LogLevel.Warning));
37+
38+
// The Azure SDKs used by the Azure.Identity library write their telemetry via Event Sources
39+
using AzureEventSourceLogForwarder logForwarder = new(loggerFactory);
40+
logForwarder.Start();
41+
42+
NewTokenAndFrequency initialTokenInfo = await GetTokenInfoAsync(credential);
43+
AzureStorageOrchestrationService service = new(new AzureStorageOrchestrationServiceSettings
844
{
9-
private static async Task Main(string[] args)
45+
StorageAccountDetails = new StorageAccountDetails
1046
{
11-
// Create credential based on the configuration
12-
var credential = new DefaultAzureCredential();
13-
string[] scopes = new string[] { "https://storage.azure.com/.default" }; // Scope for Azure Storage
14-
15-
static Task<NewTokenAndFrequency> RenewTokenFuncAsync(object state, CancellationToken cancellationToken)
16-
{
17-
var credential = new DefaultAzureCredential();
18-
var initialToken = credential.GetToken(new TokenRequestContext(new[] { "https://storage.azure.com/.default" }));
19-
var expiresAfter = initialToken.ExpiresOn - DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10);
20-
return Task.FromResult(new NewTokenAndFrequency(initialToken.Token, expiresAfter));
21-
}
22-
23-
// Get the token
24-
var accessToken = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes));
25-
26-
var service = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
27-
{
28-
StorageAccountDetails = new StorageAccountDetails
29-
{
30-
AccountName = "YourStorageAccount",
31-
EndpointSuffix = "core.windows.net",
32-
StorageCredentials = new StorageCredentials(new Microsoft.WindowsAzure.Storage.Auth.TokenCredential(
33-
accessToken.Token,
34-
RenewTokenFuncAsync,
35-
null,
36-
TimeSpan.FromMinutes(5)))
37-
}
38-
});
39-
40-
var client = new TaskHubClient(service);
41-
var worker = new TaskHubWorker(service);
42-
43-
worker.AddTaskOrchestrations(typeof(SampleOrchestration));
44-
worker.AddTaskActivities(typeof(SampleActivity));
45-
46-
await worker.StartAsync();
47-
48-
var instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
49-
50-
var result = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
51-
52-
Console.WriteLine($"Orchestration result : {result.Output}");
53-
54-
await worker.StopAsync();
55-
}
47+
AccountName = "YourStorageAccount",
48+
EndpointSuffix = "core.windows.net",
49+
StorageCredentials = new StorageCredentials(new Microsoft.WindowsAzure.Storage.Auth.TokenCredential(
50+
initialTokenInfo.Token,
51+
GetTokenInfoAsync,
52+
credential,
53+
initialTokenInfo.Frequency.GetValueOrDefault()))
54+
},
55+
LoggerFactory = loggerFactory,
56+
});
57+
58+
TaskHubClient client = new(service, loggerFactory: loggerFactory);
59+
TaskHubWorker worker = new(service, loggerFactory);
60+
61+
worker.AddTaskOrchestrations(typeof(SampleOrchestration));
62+
worker.AddTaskActivities(typeof(SampleActivity));
63+
64+
await worker.StartAsync();
65+
66+
OrchestrationInstance instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
67+
OrchestrationState state = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
68+
69+
ILogger logger = loggerFactory.CreateLogger(nameof(Program));
70+
logger.LogInformation("Orchestration output: {Output}", state.Output);
71+
72+
await worker.StopAsync();
73+
74+
static async Task<NewTokenAndFrequency> GetTokenInfoAsync(object state, CancellationToken cancellationToken = default)
75+
{
76+
const string AzureStorageScope = "https://storage.azure.com/.default";
77+
78+
if (state is not DefaultAzureCredential credential)
79+
throw new InvalidOperationException();
80+
81+
AccessToken accessToken = await credential.GetTokenAsync(new TokenRequestContext([AzureStorageScope]), cancellationToken);
82+
TimeSpan refreshFrequency = accessToken.ExpiresOn - DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10); // 10 minutes before expiration
83+
return new NewTokenAndFrequency(accessToken.Token, refreshFrequency);
5684
}
5785

58-
public class SampleOrchestration : TaskOrchestration<string, string>
86+
internal sealed class SampleOrchestration : TaskOrchestration<string, string>
5987
{
60-
public override async Task<string> RunTask(OrchestrationContext context, string input)
61-
{
62-
return await context.ScheduleTask<string>(typeof(SampleActivity), input);
63-
}
88+
public override Task<string> RunTask(OrchestrationContext context, string input) =>
89+
context.ScheduleTask<string>(typeof(SampleActivity), input);
6490
}
6591

66-
public class SampleActivity : TaskActivity<string, string>
92+
internal sealed class SampleActivity : TaskActivity<string, string>
6793
{
68-
protected override string Execute(TaskContext context, string input)
69-
{
70-
return "Hello, " + input + "!";
71-
}
94+
protected override string Execute(TaskContext context, string input) =>
95+
"Hello, " + input + "!";
7296
}

samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/ConsoleApp.csproj

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<LangVersion>Latest</LangVersion>
5+
<Nullable>enable</Nullable>
6+
<OutputType>Exe</OutputType>
7+
<TargetFramework>net8.0</TargetFramework>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Azure.Identity" Version="1.12.0" />
12+
<PackageReference Include="Microsoft.Azure.DurableTask.AzureStorage" Version="2.0.0-rc.3" />
13+
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.4" />
14+
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
15+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
16+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
17+
</ItemGroup>
18+
19+
</Project>

samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/Program.cs

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,71 @@
1-
using DurableTask.AzureStorage;
2-
using DurableTask.Core;
1+
// ----------------------------------------------------------------------------------
2+
// Copyright Microsoft Corporation
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
// ----------------------------------------------------------------------------------
13+
14+
using System;
15+
using System.Threading.Tasks;
316
using Azure.Identity;
17+
using DurableTask.AzureStorage;
18+
using DurableTask.Core;
19+
using Microsoft.Extensions.Azure;
20+
using Microsoft.Extensions.Logging;
21+
22+
// Create a DefaultAzureCredential used to access the Azure Storage Account.
23+
// The identity will require the following roles on the resource:
24+
// - Azure Blob Data Contributor
25+
// - Azure Queue Data Contributor
26+
// - Azure Table Data Contributor
27+
DefaultAzureCredential credential = new();
28+
29+
// Create a diagnostic logger factory for reading telemetry
30+
ILoggerFactory loggerFactory = LoggerFactory.Create(b => b
31+
.AddConsole()
32+
.AddFilter("Azure.Core", LogLevel.Warning)
33+
.AddFilter("Azure.Identity", LogLevel.Warning));
434

5-
internal class Program
35+
// The Azure SDKs used by the Azure.Identity and Azure Storage client libraries write their telemetry via Event Sources
36+
using AzureEventSourceLogForwarder logForwarder = new(loggerFactory);
37+
logForwarder.Start();
38+
39+
AzureStorageOrchestrationService service = new(new AzureStorageOrchestrationServiceSettings
640
{
7-
private static async Task Main(string[] args)
8-
{
9-
var credential = new DefaultAzureCredential();
10-
11-
// Pass the credential created to the StorageAccountClientProvider to start an AzureStorageOrchestrationService
12-
var service = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
13-
{
14-
StorageAccountClientProvider = new StorageAccountClientProvider("AccountName", credential),
15-
});
41+
LoggerFactory = loggerFactory,
42+
StorageAccountClientProvider = new StorageAccountClientProvider("YourStorageAccount", credential),
43+
});
1644

17-
var client = new TaskHubClient(service);
18-
var worker = new TaskHubWorker(service);
45+
TaskHubClient client = new(service, loggerFactory: loggerFactory);
46+
TaskHubWorker worker = new(service, loggerFactory);
1947

20-
worker.AddTaskOrchestrations(typeof(SampleOrchestration));
21-
worker.AddTaskActivities(typeof(SampleActivity));
48+
worker.AddTaskOrchestrations(typeof(SampleOrchestration));
49+
worker.AddTaskActivities(typeof(SampleActivity));
2250

23-
await worker.StartAsync();
51+
await worker.StartAsync();
2452

25-
var instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
53+
OrchestrationInstance instance = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");
54+
OrchestrationState state = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
2655

27-
var result = await client.WaitForOrchestrationAsync(instance, TimeSpan.FromMinutes(1));
56+
ILogger logger = loggerFactory.CreateLogger(nameof(Program));
57+
logger.LogInformation("Orchestration output: {Output}", state.Output);
2858

29-
Console.WriteLine($"Orchestration result : {result.Output}");
30-
31-
await worker.StopAsync();
32-
}
33-
}
59+
await worker.StopAsync();
3460

35-
public class SampleOrchestration : TaskOrchestration<string, string>
61+
internal sealed class SampleOrchestration : TaskOrchestration<string, string>
3662
{
37-
public override async Task<string> RunTask(OrchestrationContext context, string input)
38-
{
39-
await context.ScheduleTask<string>(typeof(SampleActivity), input);
40-
41-
return "Orchestrator Finished!";
42-
}
63+
public override Task<string> RunTask(OrchestrationContext context, string input) =>
64+
context.ScheduleTask<string>(typeof(SampleActivity), input);
4365
}
4466

45-
public class SampleActivity : TaskActivity<string, string>
67+
internal sealed class SampleActivity : TaskActivity<string, string>
4668
{
47-
protected override string Execute(TaskContext context, string input)
48-
{
49-
Console.WriteLine("saying hello to " + input);
50-
return "Hello " + input + "!";
51-
}
69+
protected override string Execute(TaskContext context, string input) =>
70+
"Hello, " + input + "!";
5271
}
53-

0 commit comments

Comments
 (0)