I am trying to use a User Assigned Managed Identity in one of our applications. I also read about the differences between System Assigned Managed Identity and User Assigned Managed Identity.
It is very clear to me that a System Assigned Managed Identity cannot be used locally as there you're assigning an identity to an Azure Resource.
However I am not clear if a User Assigned Managed Identity can be used locally. Only thing I could find is the following:
In my scenario, I would like to read some secrets from an Azure Key Vault. I have created a User Assigned Managed Identity and configured access policies on the Key Vault to give necessary permissions to this identity. Considering I am using this identity to access Azure Key Vault (which is an Azure resource), my expectation is that it should work regardless of the location (using JetBrains Rider as my IDE) from where my code is running.
However when I try to do something like:
var managedIdentityCredential = new ManagedIdentityCredential("managed-identity-id");
SecretClient secretClient = new(new Uri("https://mykeyvault.vault.azure.net/"), managedIdentityCredential);
KeyVaultSecret secret = await secretClient.GetSecretAsync(key);
I get the Azure.Identity.CredentialUnavailableException with ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found error message when I run the code locally:
Azure.Identity.CredentialUnavailableException: ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.
at Azure.Identity.ManagedIdentityClient.AuthenticateAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
at Azure.Identity.CredentialDiagnosticScope.FailWrapAndThrow(Exception ex)
at Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityCredential.GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
at Azure.Security.KeyVault.ChallengeBasedAuthenticationPolicy.AuthenticateRequestAsync(HttpMessage message, Boolean async, AuthenticationChallenge challenge)
at Azure.Security.KeyVault.ChallengeBasedAuthenticationPolicy.ProcessCoreAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
at Azure.Core.Pipeline.RetryPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
at Azure.Core.Pipeline.RetryPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
at Azure.Core.Pipeline.HttpPipeline.SendRequestAsync(Request request, CancellationToken cancellationToken)
at Azure.Security.KeyVault.KeyVaultPipeline.SendRequestAsync(Request request, CancellationToken cancellationToken)
at Azure.Security.KeyVault.KeyVaultPipeline.SendRequestAsync[TResult](RequestMethod method, Func`1 resultFactory, CancellationToken cancellationToken, String[] path)
at Azure.Security.KeyVault.Certificates.CertificateClient.GetCertificateAsync(String certificateName, CancellationToken cancellationToken)
Any insights into this will be highly appreciated.
No. User managed identity is also not supported with ManagedIdentityCredential in the local environment.
You should use DefaultAzureCredential for the code to work in local environment.
See the Note tip here.
Note
The ManagedIdentityCredential works only in Azure environments of
services that support managed identity authentication. It doesn't work
in the local environment. Use DefaultAzureCredential for the code
to work in both local and Azure environments as it will fall back to a
few authentication options including managed identity.
In case you want to use a user-asigned managed identity with the
DefaultAzureCredential when deployed to Azure, specify the
clientId.
Related
We have an app service that needs to access a key vault on Azure. We have 2 subscriptions, and this is working fine in one of them. The other one is new and we're getting this error (C# code in app service):
Microsoft.Azure.Management.ApiManagement.Models.ErrorResponseException:
Operation returned an invalid status code 'Forbidden'
at Microsoft.Azure.Management.ApiManagement.CertificateOperations.DeleteWithHttpMessagesAsync
(String resourceGroupName,
String serviceName,
String certificateId,
String ifMatch,
Dictionary`2 customHeaders,
CancellationToken cancellationToken)
at Microsoft.Azure.Management.ApiManagement.CertificateOperationsExtensions.DeleteAsync
(ICertificateOperations operations,
String resourceGroupName,
String serviceName,
String certificateId,
String ifMatch,
CancellationToken cancellationToken)
We already compared pretty much all resources, settings, keys, permissions, access policies, managed identities, everything that we think could be causing this issue, but it is still not working. The code is the same, so it does not come from there. Must be something obvious but this error message does absolutely not tell us what the root cause is.
Anyone could point us to the setting we're missing? If this is related to the account running the app service, how/where can we know what the account is on Azure?
In our case, the issues were:
Client certificates were actually stored in APIM and not in KV.
Service needed API Management Service Contributor permission instead of Contributor to be able to perform operations on the certificates stored in APIM.
Documentation:
https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
Contributor
-> Grants full access to manage all resources
API Management Service Contributor
-> Can manage service and the APIs
I am trying to use a User Assigned Managed Identity in one of our applications. I also read about the differences between System Assigned Managed Identity and User Assigned Managed Identity.
It is very clear to me that a System Assigned Managed Identity cannot be used locally as there you're assigning an identity to an Azure Resource.
However I am not clear if a User Assigned Managed Identity can be used locally. Only thing I could find is the following:
In my scenario, I would like to read some secrets from an Azure Key Vault. I have created a User Assigned Managed Identity and configured access policies on the Key Vault to give necessary permissions to this identity. Considering I am using this identity to access Azure Key Vault (which is an Azure resource), my expectation is that it should work regardless of the location (using JetBrains Rider as my IDE) from where my code is running.
However when I try to do something like:
var managedIdentityCredential = new ManagedIdentityCredential("managed-identity-id");
SecretClient secretClient = new(new Uri("https://mykeyvault.vault.azure.net/"), managedIdentityCredential);
KeyVaultSecret secret = await secretClient.GetSecretAsync(key);
I get the Azure.Identity.CredentialUnavailableException with ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found error message when I run the code locally:
Azure.Identity.CredentialUnavailableException: ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.
at Azure.Identity.ManagedIdentityClient.AuthenticateAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
at Azure.Identity.CredentialDiagnosticScope.FailWrapAndThrow(Exception ex)
at Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityCredential.GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
at Azure.Security.KeyVault.ChallengeBasedAuthenticationPolicy.AuthenticateRequestAsync(HttpMessage message, Boolean async, AuthenticationChallenge challenge)
at Azure.Security.KeyVault.ChallengeBasedAuthenticationPolicy.ProcessCoreAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
at Azure.Core.Pipeline.RetryPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
at Azure.Core.Pipeline.RetryPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
at Azure.Core.Pipeline.HttpPipeline.SendRequestAsync(Request request, CancellationToken cancellationToken)
at Azure.Security.KeyVault.KeyVaultPipeline.SendRequestAsync(Request request, CancellationToken cancellationToken)
at Azure.Security.KeyVault.KeyVaultPipeline.SendRequestAsync[TResult](RequestMethod method, Func`1 resultFactory, CancellationToken cancellationToken, String[] path)
at Azure.Security.KeyVault.Certificates.CertificateClient.GetCertificateAsync(String certificateName, CancellationToken cancellationToken)
Any insights into this will be highly appreciated.
No. User managed identity is also not supported with ManagedIdentityCredential in the local environment.
You should use DefaultAzureCredential for the code to work in local environment.
See the Note tip here.
Note
The ManagedIdentityCredential works only in Azure environments of
services that support managed identity authentication. It doesn't work
in the local environment. Use DefaultAzureCredential for the code
to work in both local and Azure environments as it will fall back to a
few authentication options including managed identity.
In case you want to use a user-asigned managed identity with the
DefaultAzureCredential when deployed to Azure, specify the
clientId.
I have an Asp.Net Core 3.1 application that reads its configuration from a Key Vault using the following code:
var keyVaultEndpoint = builtConfig["ProductKeyVaultUri"];
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient,
new DefaultKeyVaultSecretManager());
}
It uses the most recent version of the Microsoft.Azure.Services.AppAuthentication package to date - 1.4.0
The application is deployed to an Azure App Service with a System Managed Identity (MI for short), which can read secrets from the relevant Key Vault. It works.
Indeed when I remove the System MI from the Key Vault access policy and restart the App Service, I get this:
Unhandled exception. Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: Access denied. Caller was not found on any access policy.
Caller: appid=6f215b10-33a1-4e5d-b3b7-20e8f3d3b587;oid=3d6af26c-af56-4cef-a832-41c2303a8cbe;numgroups=0;iss=https://sts.windows.net/2...b/
Vault: a...v;location=eastus2
at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable`1 maxresults, Dictionary`2 customHeaders, CancellationToken cancellationToken)
at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable`1 maxresults, CancellationToken cancellationToken)
at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at Gateway.Program.Main(String[] args)
/opt/startup/init_container.sh: line 20: 10 Aborted (core dumped) dotnet Gateway.dll
(I scrubbed the tenantId and the key vault name)
It gives us the AppId (6f215b10-33a1-4e5d-b3b7-20e8f3d3b587) and the ObjectId (3d6af26c-af56-4cef-a832-41c2303a8cbe) which indeed match the System MI.
So far so good.
Now, I replace the System MI with a User Assigned MI, which has the access to the secrets in the same KV. However, restarting the App Service does not yield any good. The web app fails to read the secrets from the Key Vault which aborts the start of the container. Here is what docker logs tell me:
Unhandled exception. Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. Received a non-retryable error. MSI ResponseCode: BadRequest, Response: {"statusCode":400,"message":"Unable to load requested managed identity.","correlationId":"1b0ee635-0805-4438-8ae8-747e9f6dd7c2"}
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Environment variable LOCALAPPDATA not set.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. /bin/bash: az: No such file or directory
at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAuthResultAsyncImpl(String resource, String authority, CancellationToken cancellationToken)
at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.<get_KeyVaultTokenCallback>b__8_0(String authority, String resource, String scope)
at Microsoft.Azure.KeyVault.KeyVaultCredential.PostAuthenticate(HttpResponseMessage response)
at Microsoft.Azure.KeyVault.KeyVaultCredential.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable`1 maxresults, Dictionary`2 customHeaders, CancellationToken cancellationToken)
at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable`1 maxresults, CancellationToken cancellationToken)
at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at Gateway.Program.Main(String[] args)
/opt/startup/init_container.sh: line 20: 10 Aborted (core dumped) dotnet Gateway.dll
From which I conclude that the docker must be told explicitly the name of the User Assigned MI. It kind of makes sense, because an App Service may have many User Assigned MIs, but only one System MI.
So, the question is - can we use User Assigned MI at all in this scenario?
The issue might be that you are not telling AzureServiceTokenProvider the id of the user-assigned MI.
Here in the docs you can see samples of the connection string syntax: https://learn.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support.
For your case, specify a connection string like:
RunAs=App;AppId={ClientId of user-assigned identity}
And use that as a constructor argument for the token provider.
By default the provider tries only system-assigned MI.
I should start out by mentioning that I am very new to Azure functions, so I don't know the idiosyncrasies inherent to this particular service. I think the issue is probably that I'm not looking at this in a very "functionesque" way.
I am trying to access storage account from an Azure Function using System Assigned service identities. However, no matter how I configure it, it throws an exception saying "This request is not authorized to perform this operation using this permission."
I know that I can access KeyVault by giving it access and using #Microsoft.KeyVault, where I could store the connection string, but I want to be able to auto-rotate the storage connection string without having to update the app settings of my function. I've seen using storage as an input binding, but these seem to also require the use of an App Setting anyways, identifying the connection string. I just wanted to be able to take advantage of assigning an identity to the function so that I don't need to store this information in an app setting.
Here is how I am currently attempting it:
Function:
public static class Function1
{
[FunctionName("WebHook-Func")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://storage.azure.com/");
TokenCredential creds = new TokenCredential(accessToken);
log.LogInformation($"Token: {accessToken}");
StorageCredentials storageCreds = new StorageCredentials(creds);
try
{
CloudBlobClient client = new CloudBlobClient(new StorageUri(new Uri("https://<storageAccount>.blob.core.windows.net")), storageCreds);
CloudBlobContainer container = client.GetContainerReference("fltd");
CloudBlockBlob blob = container.GetBlockBlobReference("shopping.txt");
string content = await blob.DownloadTextAsync();
return (ActionResult)new OkObjectResult($"File contents: {content}");
}catch(Exception ex)
{
return new BadRequestObjectResult($"Exception when calling web hook: {ex.StackTrace} {ex.Message}");
}
}
}
I have enabled System Assigned Identity
I have added this to the storage account as a contributor:
And the container I am trying to access exists:
However it keeps throwing an exception saying I am not authorized. Note the exception is happening at DownloadTextAsync, meaning it is able to retrieve the reference for the container and blob:
Exception when calling web hook: at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteAsyncInternal[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token)
at Microsoft.WindowsAzure.Storage.Blob.CloudBlob.DownloadRangeToStreamAsync(Stream target, Nullable`1 offset, Nullable`1 length, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext, IProgress`1 progressHandler, CancellationToken cancellationToken)
at Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob.DownloadTextAsync(Encoding encoding, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext, IProgress`1 progressHandler, CancellationToken cancellationToken)
at codeupdated.Function1.Run(HttpRequest req, ILogger log) in C:\...\webhook-func.cs:line 40 This request is not authorized to perform this operation using this permission.
Is there any reason this doesn't work as is? I could set up the storage account to be managed by the keyvault and request SAS tokens using the KeyVault reference bindings, but this seems like an extra step and defeats the purpose and ease of use of maintaining a system assigned service identity. Furthermore, without actually setting this up I don't know if it would just return the same error.
Thanks for any help you can offer.
The Contributor just permits your MSI to manage the storage account, it will not provide access to the blob data within that account.
To solve the issue, just need to add the MSI(System Assigned Identity) to the storage account as a Storage Blob Data Owner / Storage Blob Data Contributor, then it will work fine.
It seems you haven't assign the right permission to your function in azure key vault. Please have a try by following the steps below:
Go to your azure key vault and click "Access policies" --> "Add Access Policy.
Then select the permission which you want in "Key permissions", "Secret permissions" and "Certificate permissions" select boxes. And select your azure function to the "Select principal" box.
Hope it would be helpful to your problem~
This is a cross post from git.
I have a console app using "Microsoft.Azure.Services.AppAuthentication" Version="1.2.0-preview" and "Microsoft.Azure.KeyVault" Version="3.0.2".
I have a user assigned managed identity on my Windows 2019 VM.
The managed identity has been given the contributor role assignment on my keyvault and read to the resource group it lives in.
I am using a user assigned managed identity as the intention is to run a similar app in a kubernetes pod (with aad-pod identity). I have a connection string environment variable set as AzureServicesAuthConnectionString RunAs=App;<clientId of my user assigned MI>;TenantId=<tenantId>
When I try to connect to the keyvault to retrieve a secret I get a Forbidden exception:
Unhandled Exception: System.AggregateException: One or more errors
occurred. (Operation returned an invalid status code 'Forbidden') --->
Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: Operation
returned an invalid status code 'Forbidden' at
Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretWithHttpMessagesAsync(String
vaultBaseUrl, String secretName, String secretVersion, Dictionary`2
customHeaders, CancellationToken cancellationToken) at
Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretAsync(IKeyVaultClient
operations, String secretIdentifier, CancellationToken
cancellationToken) at msiauth.AzureStuff.Run() in
C:\Users\aiadmin\src\azure\msiauth\tests.cs:line 50
--- End of inner exception stack trace --- at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout,
CancellationToken cancellationToken) at
System.Threading.Tasks.Task.Wait() at msiauth.Program.Main(String[]
args) in C:\Users\aiadmin\src\azure\msiauth\Program.cs:line 10
When I try to create a Resource Group using "Microsoft.Azure.Management.ResourceManager" Version="2.0.0-preview" it works fine so the user assigned MI works for that.
I tried the exact same keyvault code but with a system assigned managed identity and it worked correctly so permissions are fine and the code works.
Has anybody been able to do this in .net core?
Reading or writing secrets from or to the KeyVault is an operation on the Key Vault itself and is allowed (or not) based on the Access Policies set.
The contributor role you are referring belongs to the Azure Resource Manager RBAC system and allows to manage the KeyVault (f.e. assign access policies).
Most likely the identity that does work has both an RBAC assignment and an access policy. The one that doesn't work will need an access policy (and doesn't need an RBAC assignment)
For more on access policies see: https://learn.microsoft.com/en-us/azure/key-vault/key-vault-get-started#authorize