最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

azure - IoT HubEvent Hub EventHubConnection using AzureCredentialsRBAC error (not SAS Key) - Stack Overflow

programmeradmin0浏览0评论

I am able to read events from Iot Hub (Event Hub) when using SAS Key but when I try using Azure Credentials, it gives me the error


What's working:

When trying to use EventHubConnection with the SAS Key, it works as expected, and I am able to read the events using EventHubCOnsumerClient something like this (but with using):

is taken from Event Hub-compatible endpoint from IoT Hub settings, and it looks like this:

"Endpoint=sb://iothub-xxxxxx.servicebus.windows/;SharedAccessKeyName=iothubowner;SharedAccessKey=;EntityPath=";


var connection = new EventHubConnection("<MyConnectionString>");
var consumer = new EventHubConsumerClient("<MyConsumerGroup>", connection);

...
await foreach (PartitionEvent partitionEvent in consumer.ReadEventsAsync(cancellationToken).ConfigureAwait(false))
{
    Debug.WriteLine("Received message from device");
}


What's not working:

Now, instead of SAS ConnectionString I would like to use AzureCredentials, and specifically AzureCliCredentials. In my scenario, just in case I specify the TenantId in the options as well:

var credential = new AzureCliCredential(new AzureCliCredentialOptions()
{
     TenantId = "<MyTenantId>"
});

var connection = new EventHubConnection("<MyEventHubNamespace">, "<MyEventHubName">, credential);

var consumer = new EventHubConsumerClient("<MyConsumerGroup>", connection);


// call the consumer.ReadEventsAsync as earlier

when using that and trying to get the messages, I get the error:

Azure.Messaging.EventHubs.EventHubsException(ServiceCommunicationProblem): InvalidIssuer: Token issuer is invalid.

I put a breakpoint and look inside of the consumer > Connection > InnerClient > _accessToken, and I see that the payload has the correct information, matching my tenant:

(partial)
"aud" : ";,
"iss" : "/<MyTenantId>/",
"tid" : "<MyTenantId>"

I also tried to get the token from the cli like this:

`az account get-access-token --resource ";

and I get very similar values but this time with my reference to service bus, not eventhubs which I was getting originally from the connection:

(partial)
"aud" : ";,
"iss" : "/<MyTenantId>/",
"tid" : "<MyTenantId>"

then I use this token for the connection explicitly and still get the same Token issuer is invalid error.


Thoughts:

It looks like I have setup the sure I have the configuration right about the names of the hub, namespace and tenant, since it does work with SAS Connection string in the first place.

The user I am accessing it with does have the following roles:

  • Azure Event Hubs Data Owner: f526a384-b230-433a-b45c-95f59c4a2dec
  • Azure Event Hubs Data Receiver: a638d3c7-ab3a-418d-83e6-5f17a39d4fde
  • Azure Event Hubs Data Sender: 2b629674-e913-4c01-ae53-ef4638d8f975

but since the error message is about issuer, I do not think this has to do with the roles.

I ran out of ideas on how I could possibly fix it or where to look for.

Any ideas?

I am able to read events from Iot Hub (Event Hub) when using SAS Key but when I try using Azure Credentials, it gives me the error


What's working:

When trying to use EventHubConnection with the SAS Key, it works as expected, and I am able to read the events using EventHubCOnsumerClient something like this (but with using):

is taken from Event Hub-compatible endpoint from IoT Hub settings, and it looks like this:

"Endpoint=sb://iothub-xxxxxx.servicebus.windows/;SharedAccessKeyName=iothubowner;SharedAccessKey=;EntityPath=";


var connection = new EventHubConnection("<MyConnectionString>");
var consumer = new EventHubConsumerClient("<MyConsumerGroup>", connection);

...
await foreach (PartitionEvent partitionEvent in consumer.ReadEventsAsync(cancellationToken).ConfigureAwait(false))
{
    Debug.WriteLine("Received message from device");
}


What's not working:

Now, instead of SAS ConnectionString I would like to use AzureCredentials, and specifically AzureCliCredentials. In my scenario, just in case I specify the TenantId in the options as well:

var credential = new AzureCliCredential(new AzureCliCredentialOptions()
{
     TenantId = "<MyTenantId>"
});

var connection = new EventHubConnection("<MyEventHubNamespace">, "<MyEventHubName">, credential);

var consumer = new EventHubConsumerClient("<MyConsumerGroup>", connection);


// call the consumer.ReadEventsAsync as earlier

when using that and trying to get the messages, I get the error:

Azure.Messaging.EventHubs.EventHubsException(ServiceCommunicationProblem): InvalidIssuer: Token issuer is invalid.

I put a breakpoint and look inside of the consumer > Connection > InnerClient > _accessToken, and I see that the payload has the correct information, matching my tenant:

(partial)
"aud" : "https://eventhubs.azure",
"iss" : "https://sts.windows/<MyTenantId>/",
"tid" : "<MyTenantId>"

I also tried to get the token from the cli like this:

`az account get-access-token --resource "https://iothub-xxxxxx.servicebus.windows"

and I get very similar values but this time with my reference to service bus, not eventhubs which I was getting originally from the connection:

(partial)
"aud" : "https://iothub-xxxxxx.servicebus.windows",
"iss" : "https://sts.windows/<MyTenantId>/",
"tid" : "<MyTenantId>"

then I use this token for the connection explicitly and still get the same Token issuer is invalid error.


Thoughts:

It looks like I have setup the sure I have the configuration right about the names of the hub, namespace and tenant, since it does work with SAS Connection string in the first place.

The user I am accessing it with does have the following roles:

  • Azure Event Hubs Data Owner: f526a384-b230-433a-b45c-95f59c4a2dec
  • Azure Event Hubs Data Receiver: a638d3c7-ab3a-418d-83e6-5f17a39d4fde
  • Azure Event Hubs Data Sender: 2b629674-e913-4c01-ae53-ef4638d8f975

but since the error message is about issuer, I do not think this has to do with the roles.

I ran out of ideas on how I could possibly fix it or where to look for.

Any ideas?

Share Improve this question edited Mar 4 at 16:29 Sampath 3,9192 gold badges6 silver badges24 bronze badges Recognized by Microsoft Azure Collective asked Mar 4 at 15:56 tridytridy 1,2841 gold badge14 silver badges23 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

In short - you cannot do this.

The built-in Event Hubs namespace that is associated with an IoT Hub instance is provisioned and owned by IoT Hub on your behalf. Because it does not exist in your tenant, identities associated with your tenant are untrusted. This is why the error that you're seeing indicates that the issuer is invalid. As a result, you cannot grant your local development identity access.

You may want to take a look at IoT Hub routing, which allows you to route IoT Hub messages and events to other downstream resources - such as an Event Hubs instance in your own tenant.

Following the first answer, I need to clarify that the solution I am looking for is when a temporary monitoring is needed, in the way VSCode with IoT Hub extension works, when it can temporarily listen to the event messages from the Event Hub behind the Iot Hub, for the development or debugging purposes. This is not a solution for the actual event listening and the mentioned IoT Hub routing should be used to consume the event.

I am trying to reproduce what IoT Hub plugin for VS Code is doing.

Here is an example of getting the "Event Hub-compatible endpoint" SAS key from IoT Hub that later can be used to listen to the devices events. For this to work one needs the proper RBAC rules to access it, and then:

  • Subscription Id
  • Resource Group name
  • IoT Hub name

In my case, getting the SAS key looks like this in C#:

using System;
using System.Threading.Tasks;

using Azure;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.IotHub;
using Azure.ResourceManager.IotHub.Models;
using Azure.ResourceManager.Resources;

namespace MyTools.Utilities.IotDevices;

public class IotHubConnectionStringHelper
{
    private readonly string _subscriptionId;
    private readonly string _resourceGroupName;
    private readonly string _iotHubName;
    
    public async Task<string> GetConnectionStringForPolicy(string policyName)
    {
        Response<IotHubDescriptionResource> iotHub = await GetIotHubAsync().ConfigureAwait(false);
        
        string endpoint = GetIotHubEndpoint(iotHub);
        IotHubDescriptionResource iotHubDescription = iotHub.Value;
        SharedAccessSignatureAuthorizationRule policy = await GetPolicyAsync(iotHubDescription, policyName);
        string result = $"Endpoint={endpoint};SharedAccessKeyName={policy.KeyName};SharedAccessKey={policy.PrimaryKey};EntityPath={_iotHubName}";
        return result;
    }

    private async Task<SharedAccessSignatureAuthorizationRule> GetPolicyAsync(IotHubDescriptionResource iotHub, string policyName)
    {
        AsyncPageable<SharedAccessSignatureAuthorizationRule>? policiesEnum = iotHub.GetKeysAsync();

        await foreach (SharedAccessSignatureAuthorizationRule policy in policiesEnum)
        {
            if (policy.KeyName == policyName)
            {
                return policy;
            }
        }
        
        throw new Exception("Policy not found.");
    }
    
    private static string GetIotHubEndpoint(Response<IotHubDescriptionResource> iotHub)
    {
        string endpoint = string.Empty;

        if (iotHub.Value.Data.Properties.EventHubEndpoints.TryGetValue("events", out EventHubCompatibleEndpointProperties? eventHubProps))
        {
            if (eventHubProps == null || eventHubProps.Endpoint == null)
            {
                throw new Exception("No event hub endpoint found.");
            }
            
            endpoint = eventHubProps.Endpoint;
        }

        return endpoint;
    }

    private async Task<Response<IotHubDescriptionResource>> GetIotHubAsync()
    {
        ArmClient armClient = new ArmClient(new AzureCliCredential());
        SubscriptionResource subscription = await armClient.GetSubscriptions().GetAsync(_subscriptionId);
        ResourceGroupResource resourceGroup = await subscription.GetResourceGroups().GetAsync(_resourceGroupName);
        IotHubDescriptionCollection iotHubCollection = resourceGroup.GetIotHubDescriptions();
        Response<IotHubDescriptionResource> iotHub = await iotHubCollection.GetAsync(_iotHubName);
        return iotHub;
    }
}

And to emphasize it one more time, this is not for the production use, but a method for a temporary listening to the messages when debugging, developing or similar.

发布评论

评论列表(0)

  1. 暂无评论