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

Added support for system-assigned identity to EventGrid output bindings #36484

Merged
merged 5 commits into from Jun 6, 2023
Merged

Added support for system-assigned identity to EventGrid output bindings #36484

merged 5 commits into from Jun 6, 2023

Conversation

andrewjw1995
Copy link
Contributor

@andrewjw1995 andrewjw1995 commented May 24, 2023

This change allows users to declare EventGrid bindings that use DefaultAzureCredential for authentication, so they can authenticate with the EventGrid topic using a system-assigned identity and RBAC rules instead of managing the access keys:

[FunctionName("Example")]
public async Task RunAsync(
    [TimerTrigger("0 * * * * *", RunOnStartup = true)] TimerInfo timerInfo,
    [EventGrid(TopicEndpointUri = "EventGridEndpoint", UseDefaultAzureCredential = true)] IAsyncCollector<CloudEvent> eventCollector
)

The original binding properties continue to function the same as before:

[FunctionName("Example")]
public async Task RunAsync(
    [TimerTrigger("0 * * * * *", RunOnStartup = true)] TimerInfo timerInfo,
    [EventGrid(TopicEndpointUri = "EventGridEndpoint", TopicKeySetting = "EventGridKey")] IAsyncCollector<CloudEvent> eventCollector
)

Looking for feedback on:

  • Do we need a dedicated 'UseDefaultAzureCredential' property, or should that be the default behaviour if the 'TopicKeySetting' is null? I added the property to ensure backwards-compatible behaviour.
  • The 'TopicKeySetting' property is decorated with the [AppSetting] attribute, and I've disabled the null-check when 'UseDefaultAzureCredential' is set to true. Will the functions runtime ignore a null value, or will this cause a runtime exception?

@github-actions github-actions bot added Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Event Grid Functions labels May 24, 2023
@github-actions
Copy link

Thank you for your contribution @andrewjw1995! We will review the pull request and get back to you soon.

@andrewjw1995
Copy link
Contributor Author

@microsoft-github-policy-service agree

@JoshLove-msft
Copy link
Member

JoshLove-msft commented May 25, 2023

Hi @andrewjw1995,
Thank you for the contribution! For Identity integration, we have established a pattern in several of the other extensions. Essentially, we defined a config pattern that can be used to configure an identity-based connection. It would be great if we can use that same pattern for the EventGrid output binding. It is a bit different for this binding from the other extensions we've worked on because EventGrid doesn't support connection strings, but I think we can still do it. I'm thinking we add a new Connection property that adheres to the semantics used by the other extensions. A user would specify the name of their Connection setting like so:

[FunctionName("Example")]
public async Task RunAsync(
    [TimerTrigger("0 * * * * *", RunOnStartup = true)] TimerInfo timerInfo,
    [EventGrid(Connection = "MyConnection")] IAsyncCollector<CloudEvent> eventCollector
)

In their app settings or local.settings.json, the minimal way to set up Identity, which would use DefaultAzureCredential would be the following:
MyConnection__TopicEndpointUri: "{Eg Endpoint}"

Users can optionally add other settings to further configure (these are the common settings among the various extensions):

MyConnection__credential: "managedIdentity",
MyConnection__clientId: "{clientId}"  

The actual credential can be created by passing in the configuration section to the AzureComponentFactory.CreateCredential method.

Here is an example of how this is done for the Service Bus extension.

Thinking out loud, if a user uses the existing TopicEndpointUri property then they are signaling that they are not using Identity and must also specific the TopicKeySetting. I would also be okay with allowing the TopicEndpointUri to be set in either the connection setting or in the explicit property, and we would use whether or not the TopicKeySetting is set to indicate whether we should use Identity or not. This seems the most user-friendly given that we already have the TopicEndpointUri property, so I suggest we do this instead.

The logic would work as follows:

  1. If Connection is set, TopicKeySetting must not be set. Throw if the user has set both.
  2. If Connection is set, the TopicEndpointUri can be included within the Connection OR the TopicEndpointUri property. Throw if both are set to different values.
  3. When using a connection pass the connection section into the AzureComponentFactory.CreateCredential method.

Please let us know if you are interested in pursuing these changes. It sounds like a lot but I think it probably won't be much more code than you have right now.

@andrewjw1995
Copy link
Contributor Author

andrewjw1995 commented May 25, 2023 via email

@andrewjw1995
Copy link
Contributor Author

@JoshLove-msft the attribute validator and the collector factory don't currently have access to the configuration. I've moved them into a dedicated 'EventGridAsyncCollectorFactory' class which has the configuration injected into it, and I moved the classes into a 'config' namespace so it's more similar to the ServiceBus example you pointed me to.

I'm calling builder.Services.AddAzureClientsCore(); to get the AzureComponentFactory registered. I can't see the source for this so I'm not sure if this is the most appropriate/specific method, but it's what the ServiceBus extension does.

Are you ok with:

  • the namespace change (it only affects internal classes)
  • A protected constructor on an internal class used solely for mocking - I could make an interface instead

I would also like to add some new unit tests to make sure the attribute validation works as expected, so I'll work on that some time over the next few days

@JoshLove-msft
Copy link
Member

JoshLove-msft commented May 30, 2023

@JoshLove-msft the attribute validator and the collector factory don't currently have access to the configuration. I've moved them into a dedicated 'EventGridAsyncCollectorFactory' class which has the configuration injected into it, and I moved the classes into a 'config' namespace so it's more similar to the ServiceBus example you pointed me to.

I'm calling builder.Services.AddAzureClientsCore(); to get the AzureComponentFactory registered. I can't see the source for this so I'm not sure if this is the most appropriate/specific method, but it's what the ServiceBus extension does.

Are you ok with:

  • the namespace change (it only affects internal classes)
  • A protected constructor on an internal class used solely for mocking - I could make an interface instead

I would also like to add some new unit tests to make sure the attribute validation works as expected, so I'll work on that some time over the next few days

This all sounds good to me. One scenario that I hadn't covered is what we should do if a user sets the TopicEndpointUri property but nothing else. I think we can treat this as a scenario where DefaultAzureCredential can be used by just passing in an empty configuration section to the CreateCredential call, though I suppose we would need to create a dummy section class that implements IConfiguration. What do you think?

@andrewjw1995
Copy link
Contributor Author

I've added the unit tests, and updated the behaviour to be more like you describe. No need for a dummy IConfiguration class, I'm just creating an empty ConfigurationBuilder and building it.

I notice in the existing examples, the uri is always a child of the 'Connection' section, not the direct value of the section. E.g:

{
  "IsEncrypted": false,
  "Values": {
    "<CONNECTION_NAME_PREFIX>__blobServiceUri": "<blobServiceUri>",
    "<CONNECTION_NAME_PREFIX>__fullyQualifiedNamespace": "<serviceBusFqdn>"
  }
}

The code I've written is currently looking for the event grid uri in <CONNECTION_NAME_PREFIX> but I think it would be more consistent to look for something like <CONNECTION_NAME_PREFIX>__eventGridTopicUri. What do you think?

@JoshLove-msft
Copy link
Member

The code I've written is currently looking for the event grid uri in <CONNECTION_NAME_PREFIX> but I think it would be more consistent to look for something like <CONNECTION_NAME_PREFIX>__eventGridTopicUri. What do you think?

Agreed, the Uri should be nested. Since there is no connection string in Event Grid, users would never set the app setting to a string, but instead it should always be a JSON object.

@andrewjw1995
Copy link
Contributor Author

Done. It now looks for <CONNECTION_NAME_PREFIX>__topicEndpointUri

@JoshLove-msft
Copy link
Member

You will need to run the Export-Api.ps1 script and commit the updated API listing file:

eng\scripts\Export-API.ps1 eventgrid

Copy link
Member

@JoshLove-msft JoshLove-msft left a comment

Choose a reason for hiding this comment

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

This looks awesome! Once the API listing is updated I will share with some folks on my team to get sign off on the API surface and then we should be able to merge. Thanks @andrewjw1995!

@JoshLove-msft
Copy link
Member

/azp run net - eventgrid - tests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@JoshLove-msft JoshLove-msft merged commit a7063e4 into Azure:main Jun 6, 2023
23 of 26 checks passed
@JoshLove-msft
Copy link
Member

Fixes #23169

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Event Grid Functions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants