Here is how I instantiate the client in my Configure method:
services.AddSingleton<ServiceBusClient>(x => new ServiceBusClient(configuration.GetSection("ServiceBus:ConnectionString").Value, serviceBusClientOptions));
And this how my appsettings looks like:
{
"ServiceBus:ConnectionString": "#Microsoft.KeyVault(VaultName=MyVaultName;SecretName=MySecretName)"
}
However, I am getting the following exception:
The connection string used for an Service Bus client must specify the Service Bus namespace host and either a Shared Access Key (both the name and value) OR a Shared Access Signature to be valid. (Parameter 'connectionString'
What am I missing here?
Have you created a managed identity for you application and added access policies such that your app can GET this secret value from key vault?
Check out the official documentaion for this here : https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references
Also on a side note, have you tried directly adding the secret value as the appsetting value instead of referencing it from KV and see if that worked? (if yes then definitely its a permissions issue and NOT a problem with your C# app code.
Related
I'm migrating a servicebus client application from Microsoft.Azure.ServiceBus to use the current library Azure.Messaging.ServiceBus.
The application is a Worker Process running on a virtual machine in windows azure.
The VM has a system assigned managed identity which grants it access to service bus and we have been using it successfully with the old library for over a year.
On the old library we created a client using this connection string
Endpoint=sb://MyNamespace.servicebus.windows.net/;Authentication=Managed Identity
When I put that connection string into the constructor of Azure.Messaging.ServiceBus.ServiceBusClient I get the following error
The connection string used for an Service Bus client must specify the Service Bus namespace host and either a Shared Access Key (both the name and value) OR a Shared Access Signature to be valid. (Parameter 'connectionString')
I've been trawling through documents for some time now with no progress.
Is there anyway to make this work?
Ideally I would continue to use the connection string - developer machines do not have system assigned ID's so we develop with key based connection strings and let devops swap in the correct prod connection string.
UPDATE
Following on from Jesse's answer managed identity has to go trough a separate constructor which requires a namespace instead of an endpoint and an instance of ManagedIdentityCredential.
As I mentioned not all environments where we deploy have managed aged identities, some require a SharedAccessKey based connection string.
Instead introducing new "identity type" configuration parameters into our build process I've used a factory method to parse the connection string and call the correct constructor overload. Where its a managed identity It extracts the namespace from the endpoint setting.
I Hope its useful for others
private static ServiceBusClient CreateServiceBusClient(string connectionString)
{
var cs = new DbConnectionStringBuilder();
cs.ConnectionString = connectionString;
if (cs.ContainsKey("Authentication") &&
"Managed Identity".Equals(cs["Authentication"].ToString(), StringComparison.OrdinalIgnoreCase))
{
string endpoint = cs["Endpoint"].ToString() ?? String.Empty;
if (endpoint.StartsWith(#"sb://", StringComparison.OrdinalIgnoreCase)) endpoint = endpoint.Substring(5);
if (endpoint.EndsWith(#"/")) endpoint = endpoint.Substring(0, endpoint.Length - 1);
return new ServiceBusClient(endpoint, new ManagedIdentityCredential());
}
return new ServiceBusClient(connectionString);
}
it needs the Azure.Identity package and the namespace System.Data.Common for the connection string builder.
The clients in the Azure.Messaging.ServiceBus package support connection strings only in the format that the Azure portal returns them.
The ;Authentication=Managed Identity token that you've included in your connection string is not a known token and is ignored, so the client does not have the information needed to perform authorization. A managed identity cannot be specified via connection string.
To use a managed identity, you'll use one of the constructor overloads that accepts a fully qualified namespace and a TokenCredential. An example can be found in the package Overview docs. Any of the Azure.Identity credentials can be used; you may want to take take a look at the managed identity section of the Azure.Identity overview.
I am creating a script using Azure CLI that will automatically generate an App Registration (service principal), and then use that App Registration to create a secret that will be stored in Azure Key Vault.
However, I am getting the following error:
The user, group or application 'appid=04b07795-8ddb-461a-bbee-02f9e1bf7b46;oid=0ec2b0e8-daeb-46a8-b627-0d4f61f87157;numgroups=134;iss=https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/' does not have secrets set permission on key vault 'asakeyabcfelaqpgsfnxcy;location=eastus'. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125287
Can anyone provide guidance on what this ID is and how to resolve this error? This is not my App Registration Object ID or App ID.
I think there're 2 points you're now concerning, one is you failed to add secret, another is the app id in the error message is not the one you registered.
I think you've followed the document to execute the cli command, so I just want to explain the second point. Pls allow me show you a code sample for a better explanation.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
public async Task<IActionResult> PrivacyAsync()
{
var kvUri = "https://your_vault_name.vault.azure.net/";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
_ = await client.SetSecretAsync("test0311", "hello");
return View();
}
When we want to add key vault secret to azure, we need to provide a credential so that our operations are authenticated. This is the DefaultAzureCredential() here, and it has several sources to get the authentication like screenshot below.
That means if someone sets the environment variables for authentication, then it will cover the information you entered when executing cli command, this may usually cause the issue that the app is different from what you set. I think you may follow this document to check all your configurations and try again, or you can directly add environment variables with the app you registered on your computer.
By the way, pls don't forget to add access policy in azure portal for the azure ad app you registered.
In my Startup.cs I add key vault clients as such:
services.AddAzureClients(s =>
{
var keyVaultUri = new Uri(Configuration["KeyVault:Uri"]);
s.AddCertificateClient(keyVaultUri);
s.AddSecretClient(keyVaultUri);
s.UseCredential(new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
VisualStudioTenantId = Configuration["AzureAd:TenantId"]
}));
});
Locally, I can authenticatie with the VisualStudioCredential. All works fine. When deployed I run into the following error:
The key vault returns a 401 even though I successfully got a token. I am using Access Control on the key vault and it my Managed Identity is a Key Vault Administrator.
What could be going on here and how can I debug this further? I have been stuck on this for a while now.
EDIT:
Title is misleading and what I thought was happening was not happening. There was no authentication issue but it looked as such in the logs due to another exception I was getting. This was occurring due to the fact my App Service Plan didn't have SSL/TLS functionality required to work with certificates, I needed a more expensive App Service Plan.
Please see this GitHub issue: https://github.com/dotnet/runtime/issues/30658#issuecomment-523987878
You need to give your Managed Identity an access policy on the Key Vault, not an RBAC access.
RBAC in this case is for management plane operation. Access policies are for data plane operations - which is what you want.
https://learn.microsoft.com/en-us/azure/key-vault/general/secure-your-key-vault
I am pretty new to Azure and I am trying to simulate a real production environment.
So, I've deployed a .netcore 3.1 app to an AppService resource. This web app is able to get the configuration from two other services - KeyVault and App Configuration. I already have configuraed the AppService identity.
I can get the keys from KeyVault using the following code:
var settings = config.Build();
var web = settings["KeyVault:Name"];
var clientId = settings["KeyVault:ClientId"];
var clientSecret = settings["KeyVault:ClientSecret"];
config.AddAzureKeyVault($"https://{web}.vault.azure.net/",
clientId,
clientSecret);
As I can get the keys from the App Configuration:
var settings = config.Build();
config.AddAzureAppConfiguration(settings["AppSettings:Endpoint"]);
And I am able to use them both at the same time through identities and AppConfiguration's key reference
var settings = config.Build();
config.AddAzureAppConfiguration(options =>
{
options
.Connect(settings["AppSettings:Endpoint"])
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
});
So, my question is about the code snippet #3: is it safe to store the AppSettings:Endpoint value the appconfig.json in a production environment? To be clear, I am referring to this value:
Moreover, I found here and explanation on how to use the two services together. But first of all, that solution doesn't work for me - I get an Azure.Identity.CredentialUnavailableException: 'ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.'.
On the second hand, he's not clear on where to store the AppSettings:AppConfiguration:Endpoint and AppSettings:Identity:ClientId values, it doesn't answer to my first question then.
Thanks in advance
Short answer - the endpoint https://[your_app_name].azconfig.io is safe to leave anywhere. It does nothing without the other parts that make up the connection string.
Long answer - you can and probably should store the parts that are sensitive, connection strings and their parts, in Key Vault as secrets. If your code needs those values you have options on how to get the values to it. Consider using the web config (it's equivalent as a place that would ordinarily have the secret values but put Key Vault references there instead. Your way works also. Keep in mind that your way may require a code change if your config shifts away from what you've coded.
Be sure that your access policy on KV is using the service principal of your app. I repeat, be sure that your access policy on KV is using the service principal of your app. It will likely only need permissions "list" and "get" for secrets and NOTHING ELSE.
Do not store secrets in your web config or its equivalent. Key Vault is almost free, it's so cheap. I don't deploy apps without them getting their own vault as part of the solution.
Leave comments if this is unclear or needs web references.
You should be able to use Azure Marange Identity to connect to APP Configuration. Use Azure.Identity preview version.
c# Example
https://github.com/nishanperera/Azure-App-Configuration-With-Key-Vault
As I understand, so long as I setup grants to KeyVault, my function should be able to read from it by using
#Microsoft.KeyVault(SecretUri=MYSECRETFULLURL), and I assume this would be transformed at run-time?
Any idea how I would debug this?
Currently, as thats getting to my function, is the above, with nothing transformed.
Running as system-managed.
If I debug, this is all I get:
However I can see my audit on azure key vault its being hit.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
var accountToMoveFrom = System.Environment.GetEnvironmentVariable("accountToMonitor");
log.LogCritical(accountToMoveFrom);
var accessToken = System.Environment.GetEnvironmentVariable("accessToken");
}
Adding the final resolution:
Make sure you do not have the "authorized application" or applicationId settings configured.
From the documentation
Create an access policy in Key Vault for the application identity you created earlier. Enable the "Get" secret permission on this policy. Do not configure the "authorized application" or applicationId settings, as this is not compatible with a managed identity.
Note: Does your code actually work? Logging the value of a key is intercepted and is displayed as
#Microsoft.KeyVault(SecretUri=MYSECRETFULLURL)
in logs to avoid sensitive configuration from KeyVault ending up in
log files that may reach a wider audience.
It works fine as per the docs (extract below), also double check you have:
Managed Service Identity (MSI) configured on the function app
Restarted your function app after adding the function's app setting
The function's MSI is given access to to the relevant KeyVault, not to the Management Plane but on the Access Policies.
If you are running/debugging locally in Visual Studio, you need to give the account signed in to Visual Studio rights on the Key Vault since because it is the identity presented.
Sourcing Application Settings from Key Vault The Key Vault references feature makes it so that your app can work as if it were
using App Settings as they have been, meaning no code changes are
required. You can get all of the details from our Key Vault reference
documentation, but I’ll outline the basics here.
This feature requires a system-assigned managed identity for your app.
Later in this post I’ll be talking about user-assigned identities, but
we’re keeping these previews separate for now.
You’ll then need to configure an access policy on your Key Vault which
gives your application the GET permission for secrets. Learn how to
configure an access policy.
Lastly, set the value of any application setting to a reference of the
following format:
#Microsoft.KeyVault(SecretUri=secret_uri_with_version)
Where secret_uri_with_version is the full URI for a secret in Key
Vault. For example, this would be something like:
https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931