NOTE: You must create an Event Grid via the Azure portal, and give it a managed identity (which is new and not shown in current examples) I created this test code using Managed Identity versus Service Principal (SP always seems to confuse me and leave me wondering - I'm sure I'm not alone!)
NOTE NOTE: This code creates and then DELETES the subscription - as I wanted to know how to remove it via code as well. Put a break point in, or comment out the code if you want to see it in the portal.
The following code uses these variables:
tenantId : be careful with this one, and understand that you may have
more than one tenant ID. For example as B2C/B2B tenant Ids are both
different than the Azure Portal AD which is created when you created
the MS Account (not email).
subscriptionId: which reflect the
subscription which will pay for the charges incurred
ResourceGroupName: Anything that you want, string field
TopicName: a name like resource, but for Event Grid (some reading
here between event grid topic, and event grid domain name - hint,
Event Grid Domain must have a least one TopicName - this code only
creates a Topic not a Domain
EndPointURL: a public URL in which the Event Grid Topic will use
to "Validate" the creation of the topic. It must be active and
return what is expected - I've included this code in the second
code block below (Using Service Bus Relay and [TunnelRelay][1]
managedIdentityClientId: not yet in the sample documentation but is in the portal. Create a managed identity in the AD for the Event Grid, and put value here - super easy.
eventGridSubscriptionName: a name for the subscription to the Event
Grid Topic
DefaultLocation: Azure Location, i used "West US 3"
Code to Create the Event Grid Topic:
public static async Task Main(string[] args)
{
await PerformTopicAndEventSubscriptionOperations();
}
static async Task PerformTopicAndEventSubscriptionOperations()
{
using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();
var cc = new DefaultAzureCredential(new DefaultAzureCredentialOptions()
{
ManagedIdentityClientId = managedIdentityClientId,
ExcludeVisualStudioCodeCredential = false,
ExcludeManagedIdentityCredential = false,
ExcludeAzureCliCredential = true,
ExcludeAzurePowerShellCredential = true,
ExcludeEnvironmentCredential = true,
ExcludeInteractiveBrowserCredential = true,
ExcludeSharedTokenCacheCredential = true,
//debugging diagnosis
Diagnostics =
{
LoggedHeaderNames = { "x-ms-request-id" },
LoggedQueryParameters = { "api-version" },
IsLoggingContentEnabled = true
}
});
var trc = new TokenRequestContext(new string[] { "https://management.azure.com" }, tenantId: tenantId);
Console.WriteLine($"Attempting to obtain Token for Mananged Identity using Tenant Id:{tenantId}");
var accessToken = await cc.GetTokenAsync(trc);
var token = accessToken.Token;
TokenCredentials credential = new TokenCredentials(token);
//try to manage the resource with those credentials and associated claim
ResourceManagementClient resourcesClient = new ResourceManagementClient(credential)
{
SubscriptionId = subscriptionId
};
//try to create a new topic
EventGridManagementClient eventGridManagementClient = new EventGridManagementClient(credential)
{
SubscriptionId = subscriptionId,
LongRunningOperationRetryTimeout = 2
};
try
{
// Register the EventGrid Resource Provider with the Subscription
//await RegisterEventGridResourceProviderAsync(resourcesClient);
// Create a new resource group
await CreateResourceGroupAsync(ResourceGroupName, resourcesClient);
// Create a new Event Grid topic in a resource group
await CreateEventGridTopicAsync(ResourceGroupName, TopicName, eventGridManagementClient);
//// Get the keys for the topic
//TopicSharedAccessKeys topicKeys = await eventGridManagementClient.Topics.ListSharedAccessKeysAsync(ResourceGroupName, TopicName);
//Console.WriteLine($"The key1 value of topic {TopicName} is: {topicKeys.Key1}");
// Create an event subscription
await CreateEventGridEventSubscriptionAsync(ResourceGroupName, TopicName, eventGridSubscriptionName, eventGridManagementClient, EndpointUrl);
//// Delete the event subscription
//await DeleteEventGridEventSubscriptionAsync(ResourceGroupName, TopicName, seatActorId, eventGridManagementClient);
//// Delete an EventGrid topic with the given topic name and a resource group
//await DeleteEventGridTopicAsync(ResourceGroupName, TopicName, eventGridManagementClient);
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.ReadLine();
}
}
public static async Task RegisterEventGridResourceProviderAsync(ResourceManagementClient resourcesClient)
{
Console.WriteLine("Registering EventGrid Resource Provider with subscription...");
await resourcesClient.Providers.RegisterAsync("Microsoft.EventGrid");
Console.WriteLine("EventGrid Resource Provider registered.");
}
static async Task CreateResourceGroupAsync(string rgname, ResourceManagementClient resourcesClient)
{
Console.WriteLine("Creating a resource group...");
var resourceGroup = await resourcesClient.ResourceGroups.CreateOrUpdateAsync(
rgname,
new ResourceGroup
{
Location = DefaultLocation
});
Console.WriteLine("Resource group created with name " + resourceGroup.Name);
}
static async Task CreateEventGridTopicAsync(string rgname, string topicName, EventGridManagementClient EventGridMgmtClient)
{
Console.WriteLine("Creating an EventGrid topic...");
Dictionary<string, string> defaultTags = new Dictionary<string, string>
{
{"key1","value1"},
{"key2","value2"}
};
var userIdentfierProperty = new UserIdentityProperties(clientId: $"/subscriptions/{MainPhoenixSubscriptionId}/resourceGroups/EventGridTest/providers/Microsoft.ManagedIdentity/userAssignedIdentities/EventGridManagedIdentity");
Dictionary<string, UserIdentityProperties> userIdentityProperties = new();
userIdentityProperties.Add("EventGridManagedIdentity", userIdentfierProperty);
//add the managed user Id...
IdentityInfo managedUserIdentityInfo = new(type: "UserAssigned", tenantId: tenantId, userAssignedIdentities: userIdentityProperties);
managedUserIdentityInfo.TenantId = tenantId;
Topic topic = new Topic()
{
Tags = defaultTags,
Location = DefaultLocation,
InputSchema = InputSchema.EventGridSchema,
InputSchemaMapping = null//, Identity = managedUserIdentityInfo
};
Topic createdTopic = await EventGridMgmtClient.Topics.CreateOrUpdateAsync(rgname, topicName, topic);
Console.WriteLine("EventGrid topic created with name " + createdTopic.Name);
}
static async Task CreateEventGridEventSubscriptionAsync(string rgname, string topicName, string eventSubscriptionName, EventGridManagementClient eventGridMgmtClient, string endpointUrl)
{
Topic topic = await eventGridMgmtClient.Topics.GetAsync(rgname, topicName);
string eventSubscriptionScope = topic.Id;
Console.WriteLine($"Creating an event subscription to topic {topicName}...");
EventSubscription eventSubscription = new EventSubscription()
{
Destination = new WebHookEventSubscriptionDestination()
{
EndpointUrl = endpointUrl
},
// The below are all optional settings
EventDeliverySchema = EventDeliverySchema.EventGridSchema,
Filter = new EventSubscriptionFilter()
{
// By default, "All" event types are included
IsSubjectCaseSensitive = false,
SubjectBeginsWith = "",
SubjectEndsWith = ""
}
};
EventSubscription createdEventSubscription = await eventGridMgmtClient.EventSubscriptions.CreateOrUpdateAsync(eventSubscriptionScope, eventSubscriptionName, eventSubscription);
Console.WriteLine("EventGrid event subscription created with name " + createdEventSubscription.Name);
}
static async Task DeleteEventGridTopicAsync(string rgname, string topicName, EventGridManagementClient EventGridMgmtClient)
{
Console.WriteLine($"Deleting EventGrid topic {topicName} in resource group {rgname}");
await EventGridMgmtClient.Topics.DeleteAsync(rgname, topicName);
Console.WriteLine("EventGrid topic " + topicName + " deleted");
}
static async Task DeleteEventGridEventSubscriptionAsync(string rgname, string topicName, string eventSubscriptionName, EventGridManagementClient eventGridMgmtClient)
{
Console.WriteLine($"Deleting event subscription {eventSubscriptionName} created for topic {topicName} in resource group {rgname}...");
Topic topic = await eventGridMgmtClient.Topics.GetAsync(rgname, topicName);
string eventSubscriptionScope = topic.Id;
await eventGridMgmtClient.EventSubscriptions.DeleteAsync(eventSubscriptionScope, eventSubscriptionName);
Console.WriteLine("Event subcription " + eventSubscriptionName + " deleted");
}
}
Event Grid Validation Code - must be at a public endpoint, or use VS port
namespace EventGridValidationEvent
{
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using Azure.Messaging.EventGrid;
using Azure.Messaging.EventGrid.SystemEvents;
namespace EventGridValidationfunc
{
public static class EventGridValidation
{
[FunctionName("EventGridValidation")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string response = string.Empty;
BinaryData events = await BinaryData.FromStreamAsync(req.Body);
log.LogInformation($"Received events: {events}");
EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events);
foreach (EventGridEvent eventGridEvent in eventGridEvents)
{
// Handle system events
if (eventGridEvent.TryGetSystemEventData(out object eventData))
{
// Handle the subscription validation event
if (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
{
log.LogInformation($"Got SubscriptionValidation event data, validation code: {subscriptionValidationEventData.ValidationCode}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) and then return back the below response
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = subscriptionValidationEventData.ValidationCode
};
return new OkObjectResult(responseData);
}
}
}
return new OkObjectResult(response);
}
}
}
}
Forwarding (new in VS2022 preview), or use [TunneyRelay][1]
[1]: https://techcommunity.microsoft.com/t5/microsoft-teams-blog/introducing-tunnel-relay/ba-p/149990