Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for connecting to storage using managed identity in DurableTask.AzureStorage and Dotnet Framework 4.8 #1093

Open
cool-mist opened this issue May 16, 2024 · 9 comments
Assignees

Comments

@cool-mist
Copy link

cool-mist commented May 16, 2024

Problem

How to use managed identity while connecting to storage account when using DurableTask.AzureStorage?

Hi, we are a microsoft internal team using durable task framework in our service that runs on dotnet framework 4.8. We are trying to use managed identity to connect to storage. The following code already works to connect to storage using managed identity for dotnet 8+, but fails with the error Unhandled Exception: System.InvalidOperationException: Token credential is not supported for this service. when using dotnet 4.8.

Dependencies

    <PackageReference Include="azure.identity" Version="1.11.0" />
    <PackageReference Include="Microsoft.Azure.DurableTask.AzureStorage" Version="1.17.1" />
    <PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />

Repro

The following code correctly uses managed identity for <TargetFramework>net8.0</TargetFramework> but fails with an error when using <TargetFramework>net48</TargetFramework>.

Unhandled Exception: System.InvalidOperationException: Token credential is not supported for this service.
   at Microsoft.WindowsAzure.Storage.Table.CloudTableClient.get_AuthenticationHandler()
   at Microsoft.WindowsAzure.Storage.Table.TableOperation.InsertImpl(TableOperation operation, CloudTableClient client, CloudTable table, TableRequestOptions requestOptions)
   at Microsoft.WindowsAzure.Storage.Table.TableOperation.BeginExecute(CloudTableClient client, CloudTable table, TableRequestOptions requestOptions, OperationContext operationContext, AsyncCallback callback, Ob

Code

using Azure.Core;
using Azure.Identity;
using DurableTask.AzureStorage;
using DurableTask.Core;
using Microsoft.WindowsAzure.Storage.Auth;

internal class Program
{
    private static async Task Main(string[] args)
    {
        var credential = new AzureCliCredential();
        var initialToken = credential.GetToken(new TokenRequestContext(new[] { "https://storage.azure.com/.default" }));
        var service = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
        {
            StorageAccountDetails = new StorageAccountDetails
            {
                AccountName = "sndeltest",
                EndpointSuffix = "core.windows.net",
                StorageCredentials = new StorageCredentials(
                    new Microsoft.WindowsAzure.Storage.Auth.TokenCredential(
                      initialToken.Token,
                      RenewTokenFuncAsync,
                      null,
                      TimeSpan.FromMinutes(5)))
            },
        });
        var client = new TaskHubClient(service);
        var worker = new TaskHubWorker(service);
        worker.AddTaskOrchestrations(typeof(SampleOrchestration));
        worker.AddTaskActivities(typeof(SampleActivity));
        Console.WriteLine("Starting worker...");
        await worker.StartAsync();

        Console.WriteLine("Creating orchestration...");
        var orch = await client.CreateOrchestrationInstanceAsync(typeof(SampleOrchestration), "World");

        Console.WriteLine("Waiting orchestration...");
        var result = await client.WaitForOrchestrationAsync(orch, TimeSpan.FromMinutes(1));

        Console.WriteLine($"Orchestration result : {result.Output}");

        Console.WriteLine("Stopping worker...");
        await worker.StopAsync();
    }

    public 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));
    }
}

public class SampleOrchestration : TaskOrchestration<string, string>
{
    public override async Task<string> RunTask(OrchestrationContext context, string input)
    {
        return await context.ScheduleTask<string>(typeof(SampleActivity), input);
    }
}

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

@cool-mist
Copy link
Author

I think this might have to do with the usage of WindowsAzure.Storage in the DTF library

@ggghhhhhh112
Copy link

have any undate? it's an security issue, need to be solved.

@nytian nytian self-assigned this Jun 12, 2024
@nytian
Copy link
Collaborator

nytian commented Jun 19, 2024

@cool-mist When using .net48, the DTFx.AS will use dependencies for framework net462 since it's companiable. And thus WindowsAzure.Storage v7.2.1 will be picked. However, this version doesn't support token credentials. So you have to use .NET framework 5.x and onwards to use the dependency WindowsAzure.Storage 9.3.1 which can support token credential class.

@nytian
Copy link
Collaborator

nytian commented Jun 19, 2024

If .NET framework 4.8 has to be used, I would recommend using DTFx.AS v2. This package uses the latest Azure Storage SDK and thus can support managed identity with dotnet 4.x. But please notice that this package is still in preview. GA is targeting at the end of this month right now.

Samples to use token credential with DTFx.AS v2:


        var credential = new DefaultAzureCredential(); // use configuration to create credential, it can also be other token credential.
        var service = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
        {
            StorageAccountClientProvider = new StorageAccountClientProvider(yourStorageAccountName, credential),
        });

@ismailkareem
Copy link

Hey @nytian, as the month's end approaches, do you have an updated ETA for the release?

@nytian
Copy link
Collaborator

nytian commented Jun 25, 2024

@KareemIsmail-M Thanks for being interested at Durable v3! You can refer to this Github issue page to check the progress of v3 GA. The only blocker right now is to decide our .NET versions in our repo, since there will be a change about this on Functions scope. We should decide this by tomorrow and then I can provide a more accurate release date.

Also, this is not a big work item so the release shouldn't be impacted a lot.

@saguiitay
Copy link
Contributor

@nytian, please be aware that we (myself and @KareemIsmail-M) are actually interested with the DTFx (and the AS package) specifically, and not with Durable Functions. We're from an internal MS team that have a dependency on DTfx, and needs the MI capability.

@nytian
Copy link
Collaborator

nytian commented Jul 9, 2024

@saguiitay DurableTask Framework can support managed identity. Same as DF, if you are using DTFx.AS v1.x, .NET 4.x and lower versions don't support managed identity. For DTFx.AS v2, there is no requirement on .NET versions to use managed identity

@cool-mist
Copy link
Author

@nytian - what would be an approximate date by when you would promote the v2rc.3 version of dtf.AS version to a stable version ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants