Skip to content

Commit

Permalink
Update Managed Identity Samples with Logging (#1124)
Browse files Browse the repository at this point in the history
  • Loading branch information
wsugarman authored Jul 1, 2024
1 parent 8ddb4a6 commit 934a5b6
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 128 deletions.
29 changes: 27 additions & 2 deletions DurableTask.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Microsoft.Azure.DurableTask.AzureStorage" Version="1.17.3" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.4" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
</ItemGroup>

</Project>
140 changes: 82 additions & 58 deletions samples/ManagedIdentitySample/DTFx.AzureStorage v1.x/Program.cs
Original file line number Diff line number Diff line change
@@ -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<NewTokenAndFrequency> 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<NewTokenAndFrequency> 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<string, string>
internal sealed class SampleOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
return await context.ScheduleTask<string>(typeof(SampleActivity), input);
}
public override Task<string> RunTask(OrchestrationContext context, string input) =>
context.ScheduleTask<string>(typeof(SampleActivity), input);
}

public class SampleActivity : TaskActivity<string, string>
internal sealed class SampleActivity : TaskActivity<string, string>
{
protected override string Execute(TaskContext context, string input)
{
return "Hello, " + input + "!";
}
protected override string Execute(TaskContext context, string input) =>
"Hello, " + input + "!";
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Microsoft.Azure.DurableTask.AzureStorage" Version="2.0.0-rc.3" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.4" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>

</Project>
94 changes: 56 additions & 38 deletions samples/ManagedIdentitySample/DTFx.AzureStorage v2.x/Program.cs
Original file line number Diff line number Diff line change
@@ -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<string, string>
internal sealed class SampleOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
await context.ScheduleTask<string>(typeof(SampleActivity), input);

return "Orchestrator Finished!";
}
public override Task<string> RunTask(OrchestrationContext context, string input) =>
context.ScheduleTask<string>(typeof(SampleActivity), input);
}

public class SampleActivity : TaskActivity<string, string>
internal sealed class SampleActivity : TaskActivity<string, string>
{
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 + "!";
}

0 comments on commit 934a5b6

Please sign in to comment.