I have an Azure web application with a key vault. I want to store secrets in the key vault using a C# application.
- I have two subscriptions: A development subscription and a production subscription.
- Each contains a key vault.
- Both key vaults use the "Azure role-based control" permission model.
- My account has the "Owner" and "Key Vault Secrets Officer" role for both key vaults.
- I can upload secrets fine from the Azure GUI or azure-cli (
az keyvault secret set
), to both environments.
My C# application can upload secrets to development, but not to production, no matter what credentials I give it. In production it always throws the following error:
Azure.RequestFailedException: Service request failed.
Status: 403 (Forbidden)
Content:
{"error":{"code":"Forbidden","message":
"Caller is not authorized to perform action on resource.\r\n
If role assignments, deny assignments or role definitions were changed recently, please observe propagation time.\r\n
Caller: appid=<GUID>;oid=<GUID>;iss=https://sts.windows.net/<GUID>/\r\n
Action: 'Microsoft.KeyVault/vaults/secrets/setSecret/action'\r\n
Resource: '<path to my secret>'\r\n
Assignment: (not found)\r\n
Vault: <name>;location=westeurope\r\n",
"innererror":{"code":"ForbiddenByRbac"}}}
The error message says it has to do with RBAC. I don't understand where it gets its rights from. I am calling the application from a PowerShell prompt. I thought it would pick up my permissions from PowerShell, but it makes no difference whether I do az login
or az logout
or az account set
. Whatever I do, it uploads fine to development but not to production.
I use the class Azure.Security.KeyVault.Secrets.SecretClient
. I create it like this:
private SecretClient CreateSecretClient() =>
new(
new Uri($"https://{_keyVaultName}.vault.azure.net/"),
new DefaultAzureCredential(),
new SecretClientOptions
{
Retry =
{
Delay = TimeSpan.FromSeconds(2),
MaxDelay = TimeSpan.FromSeconds(16),
MaxRetries = 5,
Mode = RetryMode.Exponential
}
});
I write the secret like this:
var keyVaultSecret = new KeyVaultSecret(secretName, value);
await _secretClient.SetSecretAsync(keyVaultSecret, ct);
I have also tried to give it a managed identity (which should have access to production and NOT to development):
new DefaultAzureCredential(
new DefaultAzureCredentialOptions
{
ManagedIdentityClientId = <GUID>
})
Still the same result. It is willing to write to development, not to production. I even tried to give it a ManagedIdentityClientId
that is nonsense and doesn't point to any existing managed identity. Same result.
It looks to me as though my SecretClient
ignores both its assigned credential object and the rights in the PowerShell session. Does it conjure its permissions out of thin air? How is it able to write to my development environment no matter what I do? And how do I get it to access my other environment?
CodePudding user response:
I found one fix that works. I can use InteractiveBrowserCredential
instead of DefaultAzureCredential
.
private SecretClient CreateSecretClient() =>
new(
new Uri($"https://{_keyVaultName}.vault.azure.net/"),
new InteractiveBrowserCredential(),
new SecretClientOptions
{
Retry =
{
Delay = TimeSpan.FromSeconds(2),
MaxDelay = TimeSpan.FromSeconds(16),
MaxRetries = 5,
Mode = RetryMode.Exponential
}
});
I can probably use other kinds of TokenCredential
as well, as listed here: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet