-
Notifications
You must be signed in to change notification settings - Fork 67
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
Support managed identity with normalized event hub #90
Conversation
@@ -353,6 +364,48 @@ | |||
] | |||
} | |||
}, | |||
{ | |||
"type": "Microsoft.EventHub/namespaces/eventhubs/providers/roleAssignments", | |||
"apiVersion": "2018-07-01", |
There was a problem hiding this comment.
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/lib/Microsoft.Health.Events/EventHubProcessor/EventProcessorClientFactory.cs
Show resolved
Hide resolved
{ | ||
EnsureArg.IsNotNull(options); | ||
|
||
if (options.ServiceManagedIdentityAuth) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
public class EventProcessorClientFactory | ||
{ | ||
private BlobContainerClient _blobContainerClient; |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
Apologies, I forgot to submit the pending review. |
src/console/Normalize/Processor.cs
Outdated
var eventHubProducerOptions = new EventHubProducerClientOptions(); | ||
_env.GetSection("OutputEventHub").Bind(eventHubProducerOptions); | ||
|
||
var eventHubProducerFactory = new EventHubProducerFactory(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/console/Program.cs
Outdated
@@ -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()); |
There was a problem hiding this comment.
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().
There was a problem hiding this comment.
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; } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
No description provided.