TLDR
Can I automatically retrieve values from App Configuration through the Function App Configuration variables section similar to AWS retrieving App Config/Secret values
I come from an AWS background and one thing I am used to doing is storing my sensitive information in App Config/Secrets Manager and then directly referencing the sensitive info in the lambda environment variables.
I've been looking for a way to replicate this in Azure and I've been struggling as everything I've found so far seems to want me to change my application code to get the data from App Configuration when all I want to do is update my terraform configuration.
The closest I thought I'd gotten was this documentation since it says
Use App Configuration references for App Service and Azure Functions (preview)
and the format looks like something you could store as a value in the function app's ENV var configuration section.. but when I attempted to do this, I got an error because of invalid characters. Now I'm thinking that the docs I referenced above are also just another way to change my application code to reference this new location.
I'm probably missing something obvious here so I was hoping someone could point me in the right direction because I do not want to have to change dotnet code to do something as simple as
- dotnet code references 'ENV_VAR'
- Function App configuration blade has key 'ENV_VAR' w/ a value of something like APP_CONFIG(KEY)
- Value automatically retrieved from App Configuration and used in code
What I would like to avoid
- dotnet code changed to reference App Configuration
- when app runs it bypasses function app configuration and gets directly from app config
The reason I would like to avoid this is
There's no reason I should have to update application code when the end result that I need is to use an ENV variable and
there are some ENV variables that are required by Azure for a function app to work and they contain things like the the storage API Key, which I'd prefer to keep in a centralized location that I can have more restrictive access policies for
EDIT
I received this error when it attempted to retrieve the value
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
and
The request authorization key is not authorized for DEV-MyACCT-TEST.EASTUS-1.EVENTGRID.AZURE.NET. This is due to the reason: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters
EDIT 2
I verified that it's not attempting to get the secret from app configuration at all. Here's screenshots of what the environment variable is in the function and how i have it stored in app configuration
Returned ENV variable in response of request:
{
"message": "(Id: asdfasdfasdf) Env Var: #Microsoft.AppConfiguration(Endpoint=https://<my app config name>.azconfig.io; Key=EventGrid:Key:EVENTGRID_KEY)"
}
The issue was that while my Function App did have a user assigned managed identity which gave it permission to read the App Configuration, and i did have the app configuration as the target, I did not fully understand how permissions work in Azure, specifically that Granting access to a resource w/ a specific Role and then assigning that UAMI to a Function App is not enough to actually give it permission. I needed to do one more step which was to update a parameter for the keyvault, because even though I'm not using key vault, that is the parameter which uses the managed identity.
Additionally, Terraform's documentation hasn't been updated yet to indicate that you'd put the ID used for app configuration in the key vault ID section.
The last thing which threw me off is that instead of giving an error letting me know I didn't have access to App Configuration when attempting to access it, it seems it just didn't even try. It turns out that reason for that was because I had an extra ; at the end of the string before the parenthesis and instead of throwing an error, it just treated it like a plain string and didn't attempt to connect to App Configuration
Related
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.
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
I have .net core identity email verification endpoint setup like this:
/api/[controller]/{userId}/{emailVerificationCode}
and I encoded it in registration endpoint with Uri. EscapeDataString (decoding with Uri. UnescapeDataString but that's irrelevant here). So when I get email and I click the link, locally I hit endpoint and can debug it, but after deploying to azure (web app resource group) I get this response:
The resource you are looking for has been removed, had its name changed,
or is temporarily unavailable.
When I shorten code to not contain any special characters (which are now encoded so they are for example %2F, %3D etc) endpoint is hit (but ofc token is invalid).
Any idea what could be the case?
The code that is generated is Base64 encoded, and certain characters in Base64 are not allowed in the path segment of a URL by default, for security reasons, even when URL-encoded. While it's possible to change that, you should not, as the security concerns are valid, and you don't want to expose your app to exploits.
Instead, you can simply let the code be part of the query string. The same vulnerabilities do not exist for the query string portion of a URL, and the characters will be allowed there. Alternatively, you can use a different type of code. The token providers used by Identity for things like email confirmation and password resets can be customized.
Identity includes other token providers for the purposes of two-factor auth that you can switch out with, if you like. These use TOTP-based tokens (the 6-7 digit numbers you see all the time with 2FA). Or, you can create your own custom provider and handle it however you like. To change providers, you simply configure the Tokens member when setting up Identity:
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
// other options here like password reqs, etc.
o.Tokens.ChangeEmailTokenProvider = TokenOptions.DefaultEmailProvider;
o.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
o.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
}
The above will cause those three scenarios to generate tokens via EmailTokenProvider, which is one of the TOTP-based providers builtin.
If you want to use a custom provider, you simply create a class that implements IUserTwoFactorTokenProvider<TUser> and register that:
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
...
})
.AddTokenProvider<MyCustomTokenProvider<ApplicationUser>>("MyTokenProviderName");
The string you use as the "name" is what you would use to assign it as a token provider in the previous code above, i.e.:
o.Tokens.PasswordResetTokenProvider = "MyTokenProviderName";
I have an ARM template that creates, among other resources, a web site that hosts a webjob and a job that is part of a scheduler. I have managed to get everything configured through the ARM template except for the authentication.
When the job needs to run, it creates an HTTP request that should kick off the webjob. Unfortunately, the webjob is never started. If I go into the Azure portal and update the settings for the job (Action settings) and configure Basic authentication (with the deployment credentials) everything starts working as expected, but I'm not sure how I can retrieve those credentials from the ARM template. I could run it once, create the web site, get the credentials then update the ARM template, but that defeats the whole reason I'm building the ARM template in the first place.
I found an answer that got me most of the way there; you can set the Uri of the request to list(resourceId('Microsoft.Web/sites/config', variables('webSiteName'), 'publishingcredentials'), '2016-08-01').properties.scmUri. You will also need to concatinate the rest of the path (e.g. /api/triggeredwebjobs/{webjobname}/run)
The Uri produced by the above code includes the basic auth credentials, and that is parsed at some point and the username and password are taken out of the Uri so they aren't visible in the Azure portal and the authentication is set to 'Basic', and the credentials are set to the extracted values.
However, my Uri had query string appended to the end to pass parameters into the webjob. During the deployment process, the query string gets mangled (the question mark is escaped to %3F and if you have any escaped characters in your arguments value, they will get unescaped.
I managed to work around this by concatinating strings together to make up the Uri (NOT using the scmUri property), and then setting the authentication property, which is a sibling to the uri property to look like the following
"authentication": {
"type": "Basic",
"username": "[list(resourceId('Microsoft.Web/sites/config', variables('webSiteName'), 'publishingcredentials'), '2016-08-01').properties.publishingUserName]",
"password": "[list(resourceId('Microsoft.Web/sites/config', variables('webSiteName'), 'publishingcredentials'), '2016-08-01').properties.publishingPassword]"
}
I am trying to export an Azure package using the GetPackage method of Service Management API.
I have tried both calling the REST API directly using a WebClient, and by using the Windows Azure Service Management Library package (I have posted the code I used as an answer to that question).
However, no matter the method I tried and how I constructed and/or encoded the container URI, I am always getting the following error:
400 Bad Request
Parameter value '...' specified for parameter 'ContainerUriString' is invalid.
The parameter in question is of the following form:
https://something.blob.core.windows.net/somecontainer
I verified that the storage account exists and is accessible (tried both public and private containers), even tried calling HttpUtility.UrlEncode() on the container URI (even though the SDK does it automatically).
Any ideas how to get this resolved?
Please ensure that the storage account where you want the files to be copied belong to the same subscription as that of Cloud Service.