1

I'm looking for an example of using EventGridManagementClient and using the extension method of CreateOrUpdateTopic in TopicsOperationsExtensions (referred here)

Essentially, its the same question asked here, but that was 2 years ago prior to the update of the SDK.

And is also similar to this question here but without showing a use of the class.

I wish the MS documentation here would refer to the SDK as well as the CLI, as its exactly what I would like to do.

I got to this point:

      EventGridManagementClient eventGridManagementClient = new EventGridManagementClient(credential)
  {
    SubscriptionId = subscriptionId,
    LongRunningOperationRetryTimeout = 2
  };

  var topic = new Topic("West US3", name: "topicName");
  topic.DisableLocalAuth = true;
  topic.PublicNetworkAccess = "Disabled";

  eventGridManagementClient.Topics.CreateOrUpdate("resourceGroup", "topicName", topic);

but have not got it to the point of executing it yet due to the creation of credentials .

Would love to have a working example in C# code, but can't find it so far....

codeputer
  • 1,987
  • 3
  • 19
  • 45

1 Answers1

0

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

codeputer
  • 1,987
  • 3
  • 19
  • 45
  • I'll get around to put this all into a public repo rather than code snippets here - I have to use a config file to hide the variables! – codeputer Aug 03 '22 at 16:38