How to Get Azure Event Hub Connection String in C#? - azure

Given a Event Hub Name, how can I get connection string in C#?
I googled a bit, but nothing useful found so far.
Thanks

Using AAD authentication for an EventHub
var credential = new DefaultAzureCredential();
// or use
// var credential = new Azure.Identity.ClientSecretCredential("tenantId", "clientId", "clientSecret");
EventHubProducerClient producerClient = new EventHubProducerClient(txtNamespace.Text, txtEventHub.Text, credential
var consumerClient = new EventHubConsumerClient(EventHubConsumerClient.DefaultConsumerGroupName, txtNamespace.Text, txtEventHub.Text, credential)
Full example and docs
Acquiring the Connection Strings of configured Access Policies
You can use these two Nuget packages:
Azure.ResourceManager.EventHubs
Azure.Identity
Then you can use the resource group name and the eventhub name to retrieve the connection string. You will need to iterate the subscriptions and resource groups if you don't have this information.
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.EventHubs;
ArmClient client = new ArmClient(new DefaultAzureCredential());
// Or use
// ArmClient client = new ArmClient(new Azure.Identity.ClientSecretCredential("tenantId", "clientId", "clientSecret"));
var subscription = await client.GetDefaultSubscriptionAsync();
var resourceGroup = await subscription.GetResourceGroupAsync("myresourcegroup");
var eventhubNamespace = await resourceGroup.Value.GetEventHubsNamespaceAsync("namespacename");
var rules = eventhubNamespace.Value.GetEventHubsNamespaceAuthorizationRules();
foreach (var rule in rules)
{
var keys = await rule.GetKeysAsync();
Console.WriteLine(keys.Value.PrimaryConnectionString);
Console.WriteLine(keys.Value.SecondaryConnectionString);
}

Not sure if this is what you mean, but if you want to access an Event Hub through C# you need to provide the EH connection string into your code. This can be retrieved by adding a Shared access policy for the Event hub that you are trying to access.
Edit: If you are trying to actually create the connection string yourself you could follow this sample where you create the SAS-token yourself. But you would still need to provide the Primary key that is set on the policy from Azure.

Related

Azure authentification for multiple audience using WithExtraScopesToConsent and AcquireTokenSilent

I am building an app that let user manipulate Azure resource and Azure storage therefore I need to access multiple audiences, however, it's not possible to have one toke with multiple audience in azure. So I am using this tutorial
https://learn.microsoft.com/bs-latn-ba/azure/active-directory/develop/msal-net-user-gets-consent-for-multiple-resources
and my code look like :
IPublicClientApplication client = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs)
.WithDefaultRedirectUri()
// .WithRedirectUri($"msal{clientId}://auth")
.Build();
var accounts = client.GetAccountsAsync().Result;
string[] scopes = { "https://management.azure.com/user_impersonation" };
string[] scopestorage = { "https://storage.azure.com/user_impersonation" };
var result = client.AcquireTokenInteractive(scopes)
.WithAccount(accounts.FirstOrDefault())
.WithExtraScopesToConsent(scopestorage)
.ExecuteAsync().Result;
var result2= client.AcquireTokenSilent(scopestorage, accounts.FirstOrDefault()).ExecuteAsync();
but I am getting an exception while executing the AcquireTokenInteractive method
Microsoft.Identity.Client.MsalUiRequiredException: 'No account or login hint was passed to the AcquireTokenSilent call.'
Also when I look in the locals my variable "accounts" i can see Count=0 and nothing in there.
Any pointer for a solutions would be greatly appreciated.
Regards
Vincent
Your need to make some changes to your code. Here is the working sample for your reference:
string[] scopes = { "https://management.azure.com/user_impersonation" };
string[] scopestorage = { "https://storage.azure.com/user_impersonation" };
IPublicClientApplication client = PublicClientApplicationBuilder
.Create("cbc32712-ac27-4532-802d-303998a6e712")
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
.Build();
var result = client.AcquireTokenInteractive(scopes)
.ExecuteAsync().Result;
var accounts = client.GetAccountsAsync().Result;
var result2 = client.AcquireTokenSilent(scopestorage, accounts.FirstOrDefault()).ExecuteAsync().Result;
Note:
1.As you will get access token for storage resource by using AcquireTokenSilent method, make sure you have granted user/admin consent for your application to access this resource.
2.You can not use WithExtraScopesToConsent method for different resource endpoints.

Azure Service Bus - Message Peek on Subscription

I have a listen access on Topic/Subscription for Azure Service Bus. Is there a way to verify if a subscription has a message and how many messages are there. I don't have manage connection string and I think I cannot run Service Bus Explorer. I don't intent to read the message out of the subscription.
The count of the messages in a Topic Subscription can be retrieved only using Manage Connection string. With the Listen Connection string, you cannot read the properties or count of messages.
But, you can peek the messages in the Subscription using Listen Connection string. Peeking the messages will not lock or remove the messages from the Subscription. You can peek the messages as many times as you need.
Thus, with the help of Listen Connection string, you can find the number of messages in a Subscription by peeking the messages one by one inside a while loop and add a counter inside the loop. The counter value is the number of messages inside the Subscription.
I don't intent to read the message out of the subscription.
The short answer is no. Manage access is required to get the topic or subscription message information.
If you want to get the subscription message count, you could have a try following 2 ways:
1.You have manage access, you could use the following demo code to do that.
var topicName = "topic1";
var subscriptionName = "subscription";
var address = "sb://xxx.servicebus.windows.net/"; //base address of namespace you are connecting to.
MessagingFactorySettings MsgFactorySettings = new MessagingFactorySettings
{
TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("SharedAccessKeyName", "xxxxxx"),
};
MessagingFactory msgFactory = MessagingFactory.Create(address, MsgFactorySettings);
NamespaceManager manager = new NamespaceManager(msgFactory.Address,msgFactory.GetSettings().TokenProvider);
var subscriptioncDescription = manager.GetSubscription(topicName, subscriptionName);
var count = subscriptioncDescription.MessageCountDetails.ActiveMessageCount;
2.Use Azure Microsoft.Azure.Management.ServiceBus.Fluent sdk. You need to registry Azure AD application and assign corresponding role it. For more information please refer to another SO thread.
var subscriptionId = "Azure subscriptionId";
var resourceGroupName = "rgName";
var nameSpace = "tomtestsb";
var topicName = "topic1";
var subscriptionName = "subscription";
var credentials = SdkContext.AzureCredentialsFactory.FromFile(#"D:\Tom\Documents\azureCred.txt");
var client = new ServiceBusManagementClient(credentials);
client.SubscriptionId = subscriptionId;
var topic = client.Topics.GetAsync(resourceGroupName, nameSpace, topicName).Result;
var topicMessagecount = topic.CountDetails.ActiveMessageCount;
var subscription = client.Subscriptions.GetAsync(resourceGroupName, nameSpace, topicName, subscriptionName).Result;
var subscriptionMessagecount = subscription.CountDetails.ActiveMessageCount;

Authenticate to multiple Azure services using Service Principle (.net Core)

I need to get access to Key Vault and Service Bus from code, using a Service Principle for authentication.
I can use the following code to access Service Bus, which works as expected - when I enable to Service Principle in the Access Policies I can pull the list of topics:
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(APPID, APPSECRET, TENANTID, AzureEnvironment.AzureGlobalCloud);
var serviceBusManager = ServiceBusManager.Authenticate(credentials, SUBSCRIPTIONID);
var serviceBusNamespace = serviceBusManager.Namespaces.List().SingleOrDefault(n => n.Name == "SERVICEBUSNAMESPACE");
var topics = serviceBusNamespace.Topics.ListAsync().GetAwaiter().GetResult();
However, I also need to get some information from Key Vault and I was trying to establish a common way to authenticate.
METHOD 1
Similar to the above, I tried this code to access KeyVault:
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(APPID, APPSECRET, TENANTID, AzureEnvironment.AzureGlobalCloud);
var kvManager = new KeyVaultClient(credentials);
var secret = kvManager.GetSecretAsync("https://VAULTNAMESPACE.vault.azure.net", "SECRETNAME").GetAwaiter().GetResult().Value;
I get the the following error:
Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: 'Operation
returned an invalid status code 'Unauthorized''
METHOD 2
This code does work for Key Vault however (showing I have correct permissions):
string GetSecret()
{
var client = new KeyVaultClient(GetAccessToken);
var secret = client.GetSecretAsync("https://VAULTNAMESPACE.vault.azure.net", "SECRETNAME").GetAwaiter().GetResult();
return secret;
}
private static async Task<string> GetAccessToken(string authority, string resource, string scope)
{
var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
var credential = new ClientCredential(appId, appSecret);
var tokenResult = await context.AcquireTokenAsync("https://vault.azure.net", credential);
return tokenResult.AccessToken;
}
But, again, it's a very KeyVault specific way to Authenticate and I was hoping to establish a common mechanism using SdkContext.AzureCredentialsFactory. Any reason why I'd be getting an Unauthorized exception with the code above connecting to Key Vault? (all is set up correctly in Azure).
Thanks for any tips!
When you use SdkContext.AzureCredentialsFactory.FromServicePrincipal to authenticate, it will use https://management.azure.com/ as its Resource Uri.
While Azure Key Vault has its own authorization system and its Resource Uri is https://vault.azure.net, so you may get the Unauthorized error message.
So, you could use Method2 to get access to Azure Key Vault with right Resource Uri.
For more details, you could refer to this article.

Upload Azure Batch Job Application Package programmatically

I have found how to upload/manage Azure Batch job Application Packages through the UI:
https://learn.microsoft.com/en-us/azure/batch/batch-application-packages
And how to upload and manage Resource Packages programmatically:
https://github.com/Azure/azure-batch-samples/tree/master/CSharp/GettingStarted/02_PoolsAndResourceFiles
But I can't quite seem to put 2 and 2 together on how to manage Application Packages programmatically. Is there an API endpoint we can call to upload/manage an Application Package when setting up a batch job?
Since this is not quite straightforward, I'll write down my findings.
These are the steps to programmatically upload Application Packages via an application that is unattended - no user input (e.g. Azure credentials) is needed.
In Azure Portal:
Create the Azure Batch application
Create a new Azure AD application (as Application Type use Web app / API)
Follow these steps to create the secret key and assign the role to the Azure Batch account
Note down the following credentials/ids:
Azure AD application id
Azure AD application secret key
Azure AD tenant id
Subscription id
Batch account name
Batch account resource group name
In your code:
Install NuGet packages Microsoft.Azure.Management.Batch, WindowsAzure.Storage and Microsoft.IdentityModel.Clients.ActiveDirectory
Get the access token and create the BatchManagementClient
Call the ApplicationPackageOperationsExtensions.CreateAsync method, which should return an ApplicationPackage
ApplicationPackage contains the StorageUrl which can now be used to upload the Application Package via the storage API
After you have uploaded the ApplicationPackage you have to activate it via ApplicationPackageOperationsExtensions.ActivateAsync
Put together the whole code looks something like this:
private const string ResourceUri = "https://management.core.windows.net/";
private const string AuthUri = "https://login.microsoftonline.com/" + "{TenantId}";
private const string ApplicationId = "{ApplicationId}";
private const string ApplicationSecretKey = "{ApplicationSecretKey}";
private const string SubscriptionId = "{SubscriptionId}";
private const string ResourceGroupName = "{ResourceGroupName}";
private const string BatchAccountName = "{BatchAccountName}";
private async Task UploadApplicationPackageAsync() {
// get the access token
var authContext = new AuthenticationContext(AuthUri);
var authResult = await authContext.AcquireTokenAsync(ResourceUri, new ClientCredential(ApplicationId, ApplicationSecretKey)).ConfigureAwait(false);
// create the BatchManagementClient and set the subscription id
var bmc = new BatchManagementClient(new TokenCredentials(authResult.AccessToken)) {
SubscriptionId = SubscriptionId
};
// create the application package
var createResult = await bmc.ApplicationPackage.CreateWithHttpMessagesAsync(ResourceGroupName, BatchAccountName, "MyPackage", "1.0").ConfigureAwait(false);
// upload the package to the blob storage
var cloudBlockBlob = new CloudBlockBlob(new Uri(createResult.Body.StorageUrl));
cloudBlockBlob.Properties.ContentType = "application/x-zip-compressed";
await cloudBlockBlob.UploadFromFileAsync("myZip.zip").ConfigureAwait(false);
// create the application package
var activateResult = await bmc.ApplicationPackage.ActivateWithHttpMessagesAsync(ResourceGroupName, BatchAccountName, "MyPackage", "1.0", "zip").ConfigureAwait(false);
}
Azure Batch Application Packages management operations occur on the management plane. The MSDN docs for this namespace are here:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.management.batch
The nuget package for Microsoft.Azure.Management.Batch is here:
https://www.nuget.org/packages/Microsoft.Azure.Management.Batch/
And the following sample shows management plane operations in C#, although it is for non-application package operations:
https://github.com/Azure/azure-batch-samples/tree/master/CSharp/AccountManagement

Authorization failure when creating a Stream Analytics job

I've been trying (and failing) to create an Azure Stream Analytics job programatically. I was following this example originally:
https://azure.microsoft.com/en-gb/documentation/articles/stream-analytics-dotnet-management-sdk/
But it pops up a dialog for you to log in. I want to be able to do this server side. It looks like I need to use Azure AD to use the Resource Manager APIs. I've been working my way through this:
https://msdn.microsoft.com/en-us/library/azure/dn790557.aspx#bk_portal
And the code looks like this:
var authContext = new AuthenticationContext("https://login.microsoftonline.com/{tenant id}/oauth2/token");
var clientId = "{app client id}";
var appKey = "{app key}";
var subscriptionId = "{subscription id}";
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireToken("https://management.core.windows.net/", clientCredential);
var creds = new TokenCloudCredentials(subscriptionId, result.AccessToken);
var client = new StreamAnalyticsManagementClient(creds);
var jobCreateParameters = new JobCreateOrUpdateParameters
{
Job = new Job
{
Name = streamAnalyticsJobName,
Location = "North Europe",
Properties = new JobProperties
{
EventsOutOfOrderPolicy = EventsOutOfOrderPolicy.Adjust,
Sku = new Sku
{
Name = "Standard"
}
}
}
};
var jobCreateResponse = client.StreamingJobs.CreateOrUpdate(resourceGroupName, jobCreateParameters);
I can successfully acquire a token, but creating the job fails:
AuthorizationFailed: The client 'REDACTED' with object id 'REDACTED' does not have authorization to perform action 'Microsoft.StreamAnalytics/streamingjobs/write' over scope '/subscriptions/REDACTED/resourcegroups/REDACTED/providers/Microsoft.StreamAnalytics/streamingjobs/REDACTED'
Am I doing something wrong? The app has the delegated permissions set.
UPDATE - 08-Dec-2015
There's an easy way to assign roles to Service Principals now. Please see this link for more details: https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/.
Original Response
When you grant access to an application to your Azure subscription, behind the scenes a user is created with Service Principal user type in your Azure AD. The code you're using below assumes that you're using this Service Principal user when getting access token.
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireToken("https://management.core.windows.net/", clientCredential);
var creds = new TokenCloudCredentials(subscriptionId, result.AccessToken);
However by default this user is not granted any permissions (RBAC) on your subscription and that's why you're getting the authorization error.
To solve this problem, what you would need to do is grant appropriate role to this user in your subscription. Now you can use PowerShell to do so or you can do it via code using ADAL library + making some web requests.
What I did was I made use of ADAL library to get access tokens and then used Google Postman (or Fiddler) to do other stuff. In my case, it was a web application. Here's what I did:
I logged in into the application as Global Administrator (Subscription Owner) and got a code. Using that code and ADAL library, I got the access token (let's call it token1).
var authContext = new AuthenticationContext(string.Format("{0}/common", signinEndpoint));//signinEndpoint = https://login.windows.net
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, credential);
I copied the tenant id and access token returned to me in result above.
Next thing I did was I found out the object id of the Service Principal user using POSTMAN. This is the GET URL I executed there. For Authorization header, you would need to use Bearer {token1}.
https://graph.windows.net/{subscription-id}/servicePrincipals?api-version=1.5&$filter=appId eq '{app-client-id}'
After that I acquired another access token (let's call it token2) for Service Management API operation using the code below:
authContext = new AuthenticationContext(string.Format("{0}/{1}", signinEndpoint, result.TenantId));
result = await authContext.AcquireTokenSilentAsync(serviceManagementApiEndpoint, credential, new UserIdentifier(request.UserInfo.UniqueId, UserIdentifierType.UniqueId));//serviceManagementApiEndpoint = https://management.core.windows.net/
After that I listed the roles in my subscription and picked the role I wanted to assign to my Service Principal user. In my case, I wanted to assign a Reader role so I noted down the role's id. For Authorization header, you would need to use Bearer {token2}.
https://management.azure.com/subscriptions/{subscription-id}/providers/Microsoft.Authorization/roleDefinitions?api-version=2015-06-01
Next is assignment of this role to the user. For this I created a guid as role assignment id and used the following URL:
https://management.azure.com/subscriptions/{subscription-id}/providers/microsoft.authorization/roleassignments/{role-assignment-id}?api-version=2015-06-01
It's going to be a PUT request and this was the request body would be something like:
{
"properties":
{
"roleDefinitionId": "{role id of the selected role from step 5}",
"principalId": "{id of the service principal from step 3"
}
}
Please ensure that the content-type of the request is set as application/json;odata=verbose and not application/json.
That's pretty much it! After that your code should work just fine :)
Give it a try and see what happens.

Resources