Azure-docs: Is there a way to bind event hub with MSI(Managed Service Identity) instead of Connection string?

Created on 10 Jul 2019  Â·  12Comments  Â·  Source: MicrosoftDocs/azure-docs

So that we could grant permission for azure function to listen event hub without connection string.


Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Pri1 azure-functionsvc cxp product-question triaged

Most helpful comment

Hi, I had to make a few more adjustments, but got it working eventually. I'm on vacation at the moment, but I'll send in a PR for this in the next few weeks. Thanks.

All 12 comments

@qinezh While functions supports having a managed identity, the event hub binding doesn't support it yet.

There is a feature request which you could up vote for it to gain traction. Also, there are a couple of similar issues raised on the azure-webjobs-sdk repo for service bus.

A workaround would be to use Managed Identity along with the KeyVault references feature to get the connection strings. Note that this feature is still in preview.

You could also fetch the secrets from code as discussed in this issue - Azure/azure-webjobs-sdk#2212

Thanks for the information! @PramodValavala-MSFT

@qinezh Glad we could help!

Since there is no doc update required at the moment, we will now proceed to close this thread. If there are further questions regarding this matter, please tag me in your reply. We will gladly continue the discussion and we will reopen the issue.

Not sure if this is new or not, but I was able to achieve this with the following, after setting up functions for dependency injection:

builder.Services.Configure<EventHubOptions>(options =>
{
    var endpointAddress = new Uri($"sb://{eventHubNamespace}.servicebus.windows.net/");
    var eventHubClient = EventHubClient.CreateWithManagedServiceIdentity(endpointAddress, eventHubName);
    options.AddEventHubClient(eventHubClient);
});

Then just omit the connection string on the EventHub attribute on the function. It will use the client registered with the given name.

@mj1856 While this would ensure you getting the client object using MSI, I believe the scale controller won't be able to access your Event Hub to scale your function app.

If you have tried this on Azure, could you confirm if you do see your Event Hub triggered function being triggered?

This would indeed be an option if you aren't using the trigger directly but would still like to interact with Event Hub directly.

@PramodValavala-MSFT - At the moment, I've only been using this to send events into to EventHub with an EventHub binding on an IAsyncCollector parameter. I'll run some more tests and get back to you.

I haven't tried scaling up yet, but I did find that I also needed to register the EventProcessorHost if I want triggers to fire for incoming events.

public static void AddEventHubClient(this IServiceCollection services, string eventHubNamespace, string eventHubName, ITokenProvider tokenProvider)
{
    services.AddOptions<EventHubOptions>().Configure(options =>
    {
        var endpointAddress = new Uri($"sb://{eventHubNamespace}.servicebus.windows.net/");
        var eventHubClient = EventHubClient.Create(endpointAddress, eventHubName, tokenProvider);

        options.AddEventHubClient(eventHubClient);
    });
}

public static void AddEventHubProcessorHost(this IServiceCollection services, string eventHubNamespace, string eventHubName, ITokenProvider tokenProvider)
{
    services.AddOptions<EventHubOptions>().Configure<IConfiguration>((options, configuration) =>
    {
        var host = new EventProcessorHost(
            hostName: Guid.NewGuid().ToString(),
            endpointAddress: new Uri($"sb://{eventHubNamespace}.servicebus.windows.net/"),
            eventHubPath: eventHubName,
            consumerGroupName: PartitionReceiver.DefaultConsumerGroupName,
            tokenProvider: tokenProvider,
            cloudStorageAccount: CloudStorageAccount.Parse(configuration.GetWebJobsConnectionString(ConnectionStringNames.Storage)),
            leaseContainerName: EventHubOptions.LeaseContainerName,
            storageBlobPrefix: EventHubOptions.GetBlobPrefix(eventHubName, eventHubNamespace));

        options.AddEventProcessorHost(eventHubName, host);
    });
}

Usage in Functions DI Configure method:

var tokenProvider = new ManagedServiceIdentityTokenProvider();
builder.Services.AddEventHubProcessorHost("my-eventhub-namespace", "my-inbound-eventhub", tokenProvider);
builder.Services.AddEventHubClient("my-eventhub-namespace", "my-outbound-eventhub", tokenProvider);

That works locally anyway. I'll publish to Azure soon and try there. If it works, I'll send a PR to the Azure/azure-functions-eventhubs-extension project to have them included.

Also, note that I made the tokenProvider a parameter, so that you can pass other token providers than just MSI. For example, in local development I like to use the new Azure.Identity SDK, so I can pass a user credential:

public class AzureIdentityTokenProvider : TokenProvider
{
    private readonly string _tenantId;

    public AzureIdentityTokenProvider (string tenantId = null)
    {
        _tenantId = tenantId;
    }

    public override async Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout)
    {
        var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
        {
            InteractiveBrowserTenantId = _tenantId,
            SharedTokenCacheTenantId = _tenantId
        });

        var requestContext = new TokenRequestContext(new[] { "https://eventhubs.azure.net/.default" });
        var token = await credential.GetTokenAsync(requestContext);

        return new JsonSecurityToken(token.Token, appliesTo);
    }
}

@mj1856 Hi Matt, did you ever end up preparing a PR for your impl, it would be very nice to get rid of the connection strings in my code base.

@mj1856 Hi Matt, can you please update if your code works as expected? Thanks!

Hi, I had to make a few more adjustments, but got it working eventually. I'm on vacation at the moment, but I'll send in a PR for this in the next few weeks. Thanks.

@mj1856 Thanks for sharing this!

I still believe this won't support scaling out on Azure because the scale controller won't be able to use MSI to check for messages before it decides to scale out. @brettsam Could you confirm?

_But_ this indeed is a way to get it to work on the dedicated plans or in a container I suppose.

Well, unfortunately I can confirm that the scaling fails as you had anticipated. My app works fine with one instance, but the container start times-out when a second instance tries to start (using the premium plan).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

spottedmahn picture spottedmahn  Â·  3Comments

bityob picture bityob  Â·  3Comments

jamesgallagher-ie picture jamesgallagher-ie  Â·  3Comments

behnam89 picture behnam89  Â·  3Comments

jharbieh picture jharbieh  Â·  3Comments