Updating appsettings.json values from KeyVault in Azure Function - azure

I am using an appsettings files in my Azure Function using a Startup class. In my appsettings files, there are keyvault references. But I cant seem to get it to work as the keyvault references never get replaced by actual strings from the keyvault. What am I doing wrong? My sample uses a client id and secret since I needed to test locally but will get switched out to using the managed identity in Prod.
Startup has following function:
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
try
{
FunctionsHostBuilderContext context = builder.GetContext();
var configurationBuilder = builder.ConfigurationBuilder
.SetBasePath(context.ApplicationRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.ConfigurationBuilder
.SetBasePath(context.ApplicationRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddAzureKeyVault(configurationBuilder["vaultUri"], "<clientid>", "<clientsecret>");
} catch (Exception e)
{
throw new Exception(e.StackTrace + " " + e.Message);
}
}
However the configuration object still has the keyvault reference strings even after using the "AddAzureKeyVault" method. What am I doing wrong?
My appsettings file looks like this
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
},
"MySection": {
"MyUrl": "#Microsoft.KeyVault(SecretUri=https://mykv.vault.azure.net/secrets/MySection-MyUrl/)"
},
"vaultUri": "https://mykv.vault.azure.net/"
}
Shouldnt the MyUrl key get updated with actual values from the keyvault?
UPDATE: So I realized I was using the wrong nuget pacakge for Keyvault. So instead switched to Azure.Extensions.AspNetCore.Configuration.Secrets. Code is now changed to using Managed Identity. But somehow the values still dont update :(
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
try
{
FunctionsHostBuilderContext context = builder.GetContext();
var configurationBuilder = builder.ConfigurationBuilder
.SetBasePath(context.ApplicationRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
string userAssignedClientId = configurationBuilder["userAssignedClientId"];
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
var options = new AzureKeyVaultConfigurationOptions { ReloadInterval = TimeSpan.FromHours(24) };
builder.ConfigurationBuilder
.SetBasePath(context.ApplicationRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
.AddAzureKeyVault(new Uri(configurationBuilder["vaultUri"]), credential, options)
.Build();
} catch (Exception e)
{
throw new Exception(e.StackTrace + " " + e.Message);
}
}

This is because you are:
Not using app setting keyvault references correctly -- they must be set in your App Service's app configuration section, not in a configuration file.
Trying to combine app settings references with direct application keyvault integration. You only need one of the two, not both.
Basically, remove this:
"MySection": {
"MyUrl": "#Microsoft.KeyVault(SecretUri=https://mykv.vault.azure.net/secrets/MySection-MyUrl/)"
}
It's not loading the secret from keyvault because you're already defining the value.
Also, for what it's worth, this code is not doing anything useful and should be removed:
catch (Exception e)
{
throw new Exception(e.StackTrace + " " + e.Message);
}
The exception you're catching already has a stack trace and message. You're not accomplishing anything slightly reformatting the contents and throwing a new exception.

Related

Unable to read Azure SignalR Connection String from Azure App Service Configuration Application Settings

I am working on an Azure SignalR application and everything is working fine on my local machine when I set the following section in my appsettings.json:
"Azure": {
"SignalR": {
"ConnectionString": "XXXXX"
}
}
And then initialize in my startup.cs as follows:
services.AddSignalR().AddAzureSignalR();
However when I create the same environment variable in my Azure App Service using App Service>Configration>ApplicationSettings:
My application is unable to start and I get the following application error:
System.ArgumentException: Connection string missing required properties endpoint and accesskey. (Parameter 'connectionString')
at Microsoft.Azure.SignalR.ConnectionStringParser.Parse(String connectionString)
at Microsoft.Azure.SignalR.ServiceEndpoint..ctor(String connectionString, EndpointType type, String name)
When I hardcore my connection string onto the AddAzureSignalR() connectionstring parameter and deploy everything works fine.
It would seem that azuresignalR is unable to pickup this environment variable, despite also being able to see it via the Kudo Appsettings page as Azure:SginalR:ConnectionString.
You need to initiate like this,
string azureSignalrConnectionString = configuration["Azure:SignalR:ConnectionString"];
services.AddSignalR().AddNewtonsoftJsonProtocol().AddAzureSignalR(options =>
{
options.ConnectionString = azureSignalrConnectionString;
});
So I asked this same question on the Azure SignalR github page and below was the answer I got:
EnvironmentVariablesConfigurationProvider automatically replaces __
with : . So when you configures connection string via environment
variables, you should use Azure__SignalR__ConnectionString as the key.
When you configures it via JSON file, you should use the origin key
Azure:SignalR:ConnectionString.
Once I changed it to the double underscores all worked
For net core 5, use:
string azureSignalrConnectionString = Configuration["Azure:SignalR:ConnectionString"];
services.AddSignalR()
.AddAzureSignalR(options =>
{
options.ConnectionString = azureSignalrConnectionString;
});
appsettings.json, like:
{
"Azure": {
"SignalR": {
"ConnectionString": "<your-connection-string>"
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

Unable to connect to Cosmos DB while creating Azure function through Visual Studio

I am new to both Azure Functions and Cosmos DB. I am trying to set up an Azure function in my local machine that gets triggered on updates on Cosmos DB through Visual Studio.
Initially, I was getting the error Unable to resolve app setting for property CosmosDBTriggerAttribute.ConnectionStringSetting but on following this solution I am able to resolve that error, but now getting:
The listener for function 'Function1' was unable to start. System.Net.Http: No such host is known. System.Private.CoreLib: No such host is known.
Here are my local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"connectionString": "AccountEndpoint=https://<collectionName>.documents.azure.com:443/;AccountKey=<key>"
}
}
This is my run method if useful:
public static void Run([CosmosDBTrigger(
databaseName: "DB-Name",
collectionName: "CollectionName",
ConnectionStringSetting = "String I used while setting up the project in VS",
LeaseCollectionName = "leases")]IReadOnlyList<Document> input, ILogger log)
{
if (input != null && input.Count > 0)
{
log.LogInformation("Documents modified " + input.Count);
log.LogInformation("First document Id " + input[0].Id);
}
}
Can someone help? My other question is what is Account Key in this case? I am currently using the app name in my Cosmos DB account.
You can find the AccountKey from Settings -> ConnectionString -> Primary Password when you click on your cosmosDB resource
and also your connectionString should be something like below,
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"CosmosDB": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
}
}

Azure Functions settings not consistent betwen portal and local

Im working with Azure functions and have a problem.
I declared a local.settings.json file with my variables as follows:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"TopicEndpoint": "my endpoint"
}
}
This allows my azure function to read the settings using:
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var myTopic = config["Values:TopicEndpoint"];
This allows me to publish and export my variables to the portal via:
func azure functionapp
publish myfunctionapp --publish-local-settings -i
However, upon publishing and verifying that the value is in the 'Application Settings' in the portal, the "Values:TopicEndpoint" doesn't exist.
In order to be able to access its value i have to put my variables directly under the json root:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
}
"TopicEndpoint": "my endpoint"
}
That way I can safely use config['TopicEndpoint'] both in my local development environment, as well as on Azure. However, this defeats the purpose of the --publish-local-settings -i as it only exports the values found under the 'Values' key, so I have to create all my settings manually.
Do you know why this happens or if maybe Im missing something?
I guess you use it a bit in a different way it should be. The thing is that the values from the local.settings.json come as environment variables automatically, so you don't have to worry about working with the file at all. That's how we do it on our project and it works on all environments.
// see, just env variables
var configuration = new ConfigurationBuilder().AddEnvironmentVariables().Build();
// and then use
var value = configuration["TopicEndpoint"];
I found the issue.
The problem was that I was adding a separate secret.settings.json as suggested here
https://www.tomfaltesek.com/azure-functions-local-settings-json-and-source-control/
Therefore I was loading my configurations like this:
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddJsonFile("secret.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
My local settings file looked like this:
{
"Values": {
"TopicEndpoint": "my endpoint"
}
}
And my secret settings like this:
{
"Values": {
"TopicKey": "my key"
}
}
This caused a conflict so I needed to remove the 'Values' key from the secret.settings.json so that it looked like this:
{
"TopicKey": "my key"
}
This way I am able to both use func azure functionapp publish myfunctionapp --publish-local-settings -i to deploy the values inside my local settings file and use both files as environment variables.

How to set environment variables or inputs in timerTrigger Azure Functions?

I am trying to setup a timerTrigger azure function
My function.json:
{
"disabled": false,
"bindings": [
{
"type": "timerTrigger",
"direction": "in",
"name": "sampleCronTrigger",
"schedule": "*/5 * * * * *",
}
],
"entryPoint": "sampleCron",
"scriptFile": "index.js"
}
In this I need to set an environment variable, but I am not able to do so. I tried looking for some documentation but couldn't find anything which doesn't require some setup on the Azure console?
I can I define environment variables? Or If here is any way I can pass an input to the function, that works too.
App settings in a function app contain global configuration options that affect all functions for that function app. When you run locally, these settings are accessed as local environment variables.
Local settings file
The file local.settings.json stores app settings, connection strings, and settings for Azure Functions Core Tools. Settings in the local.settings.json file are only used by Functions tools when running locally. By default, these settings are not migrated automatically when the project is published to Azure. Use the --publish-local-settings switch when you publish to make sure these settings are added to the function app in Azure.
In Functions, app settings, such as service connection strings, are exposed as environment variables during execution. You can access these settings using process.env, as shown here in the GetEnvironmentVariable function:
module.exports = function (context, myTimer) {
var timeStamp = new Date().toISOString();
context.log('Node.js timer trigger function ran!', timeStamp);
context.log(GetEnvironmentVariable("AzureWebJobsStorage"));
context.log(GetEnvironmentVariable("WEBSITE_SITE_NAME"));
context.done();
};
function GetEnvironmentVariable(name)
{
return name + ": " + process.env[name];
}
There are several ways that you can add, update, and delete function app settings:
From Azure portal.
By using the Azure CLI.
When running locally, app settings are read from the local.settings.json project file.
References:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings
https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node#environment-variables
https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node#environment-variables
Additionally for retrieving values from local.settings.json, another way is to create an IConfigurationRoot object using ExecutionContext executionContext.
ExecutionContext can be added to function definition:
[FunctionName("FunctionName")]
public static async Task Run(
[ServiceBusTrigger(...)]
SomeMessage msg,
ILogger log,
ExecutionContext executionContext)
{
}
After that, you can instantiate an IConfigurationRoot instance, which you instruct to optionally load local.appsettings.json.
var configurationRoot = new ConfigurationBuilder()
.SetBasePath(executionContext.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
With the configurationRoot object, you can retrieve configuration values:
var value = configurationRoot["SomeKey"];
Example local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "...",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"SomeKey": "Value",
},
"Host": {
"LocalHttpPort": "7071"
}
}

AspNetCore Httpsys configure useURLS from webapi Appsettings file

I am building a AspNetCore webapi application for internal corporate use and I need to enable Windows Authentication.
So I am creating a httpsys server to listen at a specific endpoint:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseHttpSys(options =>
{
options.Authentication.Schemes =
AuthenticationSchemes.NTLM | AuthenticationSchemes.Negotiate;
options.Authentication.AllowAnonymous = true;
options.UrlPrefixes.Add("http://localhost:16000");
}).UseUrls("http://localhost:16000");
so while this obviously works fine, I want to be able to configure it in the config file.
Earlier in the project I was using Kestrel, so I just added these settings to the application config:
"Kestrel": {
"EndPoints": {
"HttpsInlineCertStore": {
"Url": "https://*:16000",
"Certificate": {
"Subject": "localhost",
"Store": "My",
"Location": "LocalMachine",
"AllowInvalid": "true"
}
} ...
Now I understand perfectly that HttpSYS can be configured by the registry etc, so I am not interested in those kinds of responses.
My Specific question is: For a NetCoreApi web api application, is it possible to use the IConfiguration inside the (static) CreateWebHostBuilder method?
I am injecting the IConfiguration into the startup class, but it appears the limitation is in the framework preventing access to it in the CreateWebHostBuilder method. Have I missed something?
For a NetCoreApi web api application, is it possible to use the
IConfiguration inside the (static) CreateWebHostBuilder method?
Yes, you will be able to access it inside ConfigureServices, which is enough to make your configurations. The overloaded of UseHttpSys actually does the exact same thing.
So basically you just have to configure your HttpSysOptions.
For netcoreapp2.1 :
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseHttpSys()
.ConfigureServices((context, services) =>
{
// Option 1. Set options manually.
services.Configure<HttpSysOptions>(options =>
{
// Use context.Configuration to access your config.
var url = context.Configuration.GetSection("MySection")["Url"];
options.UrlPrefixes.Add(url);
});
// Option 2. Build options from settings.
services.Configure<HttpSysOptions>(context.Configuration.GetSection("WebSys"));
});
For netcoreapp3.1:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureServices((context, services) =>
{
// Option 1. Set options manually.
services.Configure<HttpSysOptions>(options =>
{
// Use context.Configuration to access your config.
var url = context.Configuration.GetSection("MySection")["Url"];
options.UrlPrefixes.Add(url);
});
// Option 2. Build options from settings.
services.Configure<HttpSysOptions>(context.Configuration.GetSection("HttpSys"));
});
webBuilder.UseHttpSys(options =>
{
// Verify that your options is correct here.
});
});
In case you want to use option 2, your appsettings.json should look something like this:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"HttpSys": {
"AllowSynchronousIO": false,
"RequestQueueLimit": 2,
"MaxAccepts": 3
},
"AllowedHosts": "*"
}
Note that the property UrlPrefixes in HttpSysOptions is a rather complex object, so I'm not sure if you will be able to serialize it correctly from appsettings. However, you can simply set the field as urls in your config, as mentioned here. Then HttpSys will pick it up as long as your Configuration is correct.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"urls": "http://*:5005;",
"HttpSys": {
"AllowSynchronousIO": false,
"RequestQueueLimit": 2,
"MaxAccepts": 3
},
"AllowedHosts": "*"
}

Resources