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 managed identity with normalized event hub #90

Merged
merged 3 commits into from
Feb 18, 2021

Conversation

wi-y
Copy link
Contributor

@wi-y wi-y commented Feb 10, 2021

No description provided.

@@ -353,6 +364,48 @@
]
}
},
{
"type": "Microsoft.EventHub/namespaces/eventhubs/providers/roleAssignments",
"apiVersion": "2018-07-01",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this apiVersion is duplicated consider making it a variable.

src/console/Program.cs Outdated Show resolved Hide resolved
{
EnsureArg.IsNotNull(options);

if (options.ServiceManagedIdentityAuth)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ServiceManagedIdentityAuth [](start = 24, length = 26)

Will this be extended to support cert based auth as well for customer facing MI?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For cert based auth I created a second method in EventProcessorClientFactory which takes an IAzureCredentialProvider which is a way to specify a custom way of retrieving a token. I was thinking of passing in a provider that does the cert based auth.

EventProcessorClient CreateProcessorClient(IAzureCredentialProvider provider, BlobContainerClient blobContainerClient, EventProcessorClientFactoryOptions options, EventProcessorClientOptions eventProcessorClientOptions)

Copy link
Member

@dustinburson dustinburson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

src/console/Program.cs Outdated Show resolved Hide resolved
{
public class EventProcessorClientFactory
{
private BlobContainerClient _blobContainerClient;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this does not sound a member of factory but a component to compose the EventProcessorClient, this can be a method argument.


if (options.ServiceManagedIdentityAuth)
{
var tokenCredential = new DefaultAzureCredential();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIt: when the developer customer has the creds in the environment, this may not get the expected creds. https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet

Do we need to explain this in the documents? Any reason we can't use the ManagedIdentityCredential? https://docs.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential?view=azure-dotnet

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that we would want to make it easy to deploy and use service managed identity (where ManagedIdentityCredential is most appropriate) but also allow develops to test/develop locally with their own credentials (where VisualStudio/VisualStudioCode/environment variables credentials might be most appropriate) and DefaultAzureCredential seemed to handle that nicely. I felt like that benefit of testing locally using DefaultAzureCredential was worth it over only using ManagedIdentityCredential and not really having an easy way of testing locally.

var tokenCredential = new DefaultAzureCredential();
return new EventProcessorClient(_blobContainerClient, options.EventHubConsumerGroup, options.EventHubNamespaceFQDN, options.EventHubName, tokenCredential, eventProcessorClientOptions);
}
else if (!string.IsNullOrEmpty(options.ConnectionString))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need a todo to update the document explaining that the managed identity option take over the ConnectionString.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we have a backlog item - right now none of this console stuff is documented because we were planning on doing a couple more iterations before putting out some documentation just we don't have to keep changing it.


namespace Microsoft.Health.Events.EventProducers
{
public class EventHubProducerClientOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: suggest to rename this EventHubProducerClientConfig, but this may just be a personal preference, feel free to skip.

@ms-teli
Copy link
Contributor

ms-teli commented Feb 11, 2021

Apologies, I forgot to submit the pending review.

var eventHubProducerOptions = new EventHubProducerClientOptions();
_env.GetSection("OutputEventHub").Bind(eventHubProducerOptions);

var eventHubProducerFactory = new EventHubProducerFactory();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just add the EventHubProducerFactory to the Service Collection and then "inject" this into the constructor. I think adding most of these factories to the Service Collection and letting the DI framework take care of dependency resolution would reduce a lot of this code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - updated to inject most of these. I moved most of the logic to Startup.cs. It did reduce the amount of code.

@@ -54,7 +48,11 @@ public static async Task Main()
var storageCheckpointClient = GetStorageCheckpointClient(blobContainerClientFactory, checkpointContainerOptions, storageOptions, logger, eventHubName);
var eventConsumerService = new EventConsumerService(eventConsumers, logger);

var incomingEventReader = GetEventProcessorClient(storageCheckpointClient.GetBlobContainerClient(), eventHubOptions);
var eventProcessorOptions = GetEventProcessorFactoryOptions(config);
var eventProcessorClientFactory = new EventProcessorClientFactory(storageCheckpointClient.GetBlobContainerClient());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in all cases where you are using new to create these items could be simplified by adding these options objects and factory objects to the Service Collection. Then result would probably be getting the service provider, getting the EventProcessor from the provider and calling RunAsync().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated - moved to the service collection.


public string EventHubName { get; set; }

public string ConnectionString { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than overloading these 2 options objects with both authentication methods, could we create separate implementations, create a common interface (IEventProcessorClientFactory), and just change which one gets added to the Service Collection at startup? We could just default to ConnectionString in the project, and allow consumers to just change it if needed.

e.g. EventProcessorClientConnectionStringFactoryOptions is injected into EventProcessorClientConnectionStringFactory.

There would be an implementation for Managed Identity - EventProcessorClientManagedIdentityFactoryOptions is injected into EventProcessorClientManagedIdentityFactory.

Both implement IEventProcessorClientFactory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this out and like the idea of separating in principal, but the problem seems to be what methods IEventProcessorClientFactory should have. We want to be able to pass in options into the factories but if the options can either be EventProcessorClientConnectionStringFactoryOptions or EventProcessorClientManagedIdentityFactoryOptions then I think that means that IEventProcessorClientFactory would have to have methods that accept those options, or some sort of generic options (but then we have to check/validate the generic options that are being passed in are connection options or MI options which seems pretty similar to what the code is currently doing).

@wi-y wi-y requested a review from namalu February 18, 2021 07:36
@wi-y wi-y merged commit 58ec243 into master Feb 18, 2021
@dustinburson dustinburson deleted the personal/wiyochum/support-mi-with-normalized-event-hub branch November 8, 2022 18:36
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

Successfully merging this pull request may close these issues.

5 participants