Determine client id of user-assigned managed identity at runtime? - azure-web-app-service

How do I determine the Client Id of a user-assigned managed identity to an app service or function running on Azure? It's possible to assign multiple user-assigned managed identities and I'd like to get a list of the ones assigned to my app at runtime.
The goal of this is to avoid having to store the client id of the managed user identity in configuration for use in creating a DefaultAzureCredential for accessing KeyVault and other resources.
Right now my code looks like the following:
string managedIdentityClientId = Environment.GetEnvironmentVariable("ManagedIdentityClientId", EnvironmentVariableTarget.Process);
var options = new DefaultAzureCredentialOptions { ManagedIdentityClientId = managedIdentityClientId };
var keyVaultCredentials = new DefaultAzureCredential(options);
I'd like to avoid storing the ManagedIdentityClientId in app settings and simply read it from the configured app service if possible.

There is no way to get the client id of the user-assigned managed identity at runtime without credentials.
Even if you can use another way e.g. call the REST API in the code to get them, you will also need to use another credential(e.g. service principal), means you also need to expose the client id and secret in the code or store them in the app setting, this makes no sense.
So in your case, I think storing the client id in the app setting is the most feasible way to use MSI.

Since you can have multiple user-assigned managed identities on your Azure resource, there is no way to directly infer which user-assigned identity should be used and thus you need to specify it.
For system-assigned managed identities, you don't need to specify the clientId because you can only have 1 system-assigned per resource.
"I'd like to avoid storing the ManagedIdentityClientId in app settings and simply read it from the configured app service if possible."
Even if you could enlist all the user-assigned assigned to a resource, you'd have to have logic to pick the right one from the list. It is just easier to be explicitly specify the clientId via Config.

Related

Is there a way to bypass key/token-authentication in ManagedOnlineEndpoint (SDK-v2)?

According to SDK-v2's ManagedOnlineEndpoint documentation, auth_mode must be provided thru key or aml_token. This means, once the endpoint is deployed, we need to include the api key or token when we send POST requests to the API. I'm wondering if there's a way to bypass providing these authentication methods, e.g., thru Azure ManagedIdentity?
My motivation for bypassing the key/token authentication is that, I have an ML model (deployed via ManagedOnlineEndpoint) that needs to call multiple other secondary ManagedOnlineEndpoints and then use their outputs as input-features in the (main) model. It would be nice to not have to manage API-keys for those secondary model endpoints.
I have a solution that works, but I'm curious if there's an alternative or more elegant way of doing it.
The current solution is, I'm storing APIkeys of the secondary models in Azure KeyVault and then retrieve them when needed, i.e., defined in the scoring script of the (main) model.
Use managed identity for seamless access to your resources. Here is the Document to access Azure resources from an online endpoint with a managed identity.
The identity for an endpoint is immutable. During endpoint creation, you can associate it with a system-assigned identity (default) or a user-assigned identity. You can't change the identity after the endpoint has been created.

Azure KeyVaultClient Secret for GetKey

I have this code snippet:
I need to get the parameters:
keyIdentifier is easy enough:
clientId is also easy to find:
clientSecret - where do I get this??
Your screenshot is of a Managed Identity, there is no client secret that you can use.
It uses certificates internally, but that is not really relevant here.
My recommendation would be to use the Azure.Identity library to acquire tokens with DefaultAzureCredential.
And also use the newer KeyVault libraries e.g.:
https://www.nuget.org/packages/Azure.Security.KeyVault.Secrets/4.1.0
https://www.nuget.org/packages/Azure.Security.KeyVault.Keys/4.1.0
DefaultAzureCredential can use a system-assigned Managed Identity automatically and your user account in local development environments.
It tries multiple approaches and uses the one that works.

Is it possible to create KeyVault using ARM, generate password and use the password in other ARM resources?

I would like to create ARM Template
create a resource group that contains KeyVault;
generate new secret with predefined name, e.g AdminPassword.
Use the password in other resources, e.g Master password when creating a SQL Database.
When redeploying the template and KeyVault and the AdminPassword secret already exist, existing secret should be used.
I have found samples where KeyVault secret is used as a parameter, however this is different, because KeyVault does not exist at the time parameters are resolved.
Can you write sample ARM teplate that creates KeyVault and then uses sectets from it?
if you generate a secret in an arm template - it makes no sense to retrieve it from the key vault, if you pass the secret to the template - again it makes no sense to retrieve it, just use it. either way, if you are really keen on making your life harder you can probably hack something in the arm template using conditions and nested templates
It depends on how secure you want the password to be...
If you want a subsequent deployment to use the same password value, then it has to be deterministic. If it's deterministic then anyone with access to the deployment can determine the password.
If you want the password to be random, then the template will generate a random one each time so a subsequent deployment would create a new password.
You could use a user provided seed for the password generation as a parameter (and use uniqueString() which is idempotent) and then only someone who knows the seed and has access to the deployment could determine the password. Note that your seed would have to be a secureString parameter type. But at this point a better practice would be to separate the steps of password generation and resource deployment.
You can generate the password in the ARM Template using uniqueString.
Then create your KeyVault and the Secret. On the outputs of the KeyVault template you can then get the URI of the Secret which can be injected into the App Configuration of another resource such as a Web App. https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references
"outputs": {
"dbSecretUri": {
"value": "[reference(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), 'yourSecretName')).secretUriWithVersion]"
}
}
Your web app will need Managed Identity enabled and the KeyVault will need to have an Access Policy that allows that app to connect to the vault.

How do you reference a Key from Azure Key Vault in a function app?

I've used quite a few secrets for Azure Functions in the past, and now I am trying to use a Key, RSA encrypted , 4096 bits. It's a private PuTTy .ppk key, which I've exported to a .pem type to match Azure's requirements. I have a Python 3.7 App in which I have deployed the needed function and I have tried to reference in the Application Settings the needed key with the following statement :
#Microsoft.KeyVault(SecretUri=https://{thevault-address}.vault.azure.net/keys/{thekey-name}/)
as well as
#Microsoft.KeyVault(SecretUri=https://{thevault-address}.vault.azure.net/keys/{thekey-name}/latestVersionHash)
But when I try to save it , it says that the reference could not be resolved.
The application has Identity On and the Vault has access policies for the Function configured for secrets as Get and List and for Keys as everything (initially just Get and List but added the rest just in case, because I thought that it might be an 'encription/decription' issue).
And idea why am I getting the Status:InvalidSyntax and Error Details:
Key Vault reference was not able to be resolved because invalid Key Vault reference syntax found ? Thanks !
The process you described above is perfectly correct to reference a Secret.That is why in the string #Microsoft.KeyVault(SecretUri= you set SecretUri equals to...
For Keys thow, that would not be possible. A Key cannot be referenced by an azure web app.
To clear any confusion in order to reference a secret to Azure KeyVault from a web app you need three points
You application identity On
Set policies in in Keyvault so your app would be able to access the value
In your web app, in Configuration tab you set the value of your variable as #Microsoft.KeyVault(SecretUri={Secret Identifier}).
Secret identifier can be found if you go to your Keyvault resource and click your secret
The identity value needs to be identical.
Unfortunately App Services / Azure Functions only support referencing secrets, not keys or certificates. In fact, Key Vault won't let you access the private half of a key at all, so you'll need to store it as a secret if you need to access it, or use the Key Vault SDK if you just want to do encryption/decryption using the saved key.

Install Certificate in IIS CurrentUser/Personal Store

We are hosting some websites on azure and some on a vm in azure. We want to reuse as much code as possible. In azure application services (websites) the installed certificates can be found in the CurrentUser/Personal store using this snippet:
using (var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
certStore.Open(OpenFlags.ReadOnly);
var certCollection =
certStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbprint,
false);
}
I want to use the exact same snippet on IIS as well. The application pool identity is set to ApplicationPoolIdentity. I already tried to install the certificate in various places but I am not able to retrieve the desired certificate ... I have also tried to install the certificate in the LocalComputer/Personal store and grant permissions to the private key
Which Identity shall I use in the MMC Snap-In? I cannot find the ApplicationPoolIdentity user account there. There is only an Application Identity account which does not solve my issue ...
If you wanted to use the Local Computer store instead of the personal store of a named user you would need to use StoreLocation.LocalMachine in your call to the X509Store constructor, the Enum consists of two values and in your code above you're using the other - CurrentUser. If you are using the certificate's private key the IIS AppUser\ApplicationPoolIdentity user would need access to the relevant certificates in the Local Computer store, this can be granted by adding the DefaultAppPool user to the allowed users in the relevant certificate's private key permissions dialog. This is accessible from the certificate's context menu, under All Tasks and then Manage Private Keys.
I am not currently sure if it is possible to install certificates for the ApplicationPoolIdentity user, I'm currently trying to find the answer to this myself. According to Microsoft's IIS documentation the ApplicationPoolIdentity account is a virtual account created for the life of the pool, so I am starting to conclude it may not be possible to use the CurrentUser store for this type of user. If I find out definitively I will come back and update my answer.
For the time being I am creating a new user, adding the certificate to the users My store and using that user as app pool identity. Still, other solutions are very welcome!

Resources