I'm creating a new Azure Functions with a Queue trigger. A key requirement is for me to use my existing class libraries that I created in my ASP.NET Core app so that I can access my Repository methods. I also have my own clients that handle communication with some third party services.
I need help with creating instances of my clients and passing configuration to them which is IConfiguration.
Here's what my Startup.cs looks like in my Azure Functions project:
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MyApp.Infrastructure.Clients;
using MyApp.Infrastructure.Interface;
using MyApp.Infrastructure.Repositories;
[assembly: FunctionsStartup(typeof(MyTestFunction.Startup))]
namespace MyTestFunction
{
public class Startup : FunctionsStartup
{
public IConfiguration Configuration { get; }
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton(new MyApp.Infrastructure.Clients.MyClient123(Configuration));
builder.Services.AddTransient<ICommunicationsRepository, CommunicationsRepository>();
}
}
}
In my ASP.NET Core app's Startup.cs, I do have a constructor that handles the configuration -- see below -- but not sure how to handle this Azure Functions.
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
Looks like this is not a recommended approach:
https://github.com/Azure/azure-functions-host/issues/4464#issuecomment-513017446
I've decided to update my class libraries in my ASP.NET Core app so that I use Azure KeyVault in both my API and Azure Functions apps.
Related
I made an Azure Functions app that connects to a CosmosDB. I made the following class to retrieve a CosmosClient instance:
public static class CosmosClientContext
{
private static readonly CosmosClient CosmosClient = GetCosmosClient();
public static CosmosClient GetCosmosClient()
{
return CosmosClient
//?? new CosmosClient("AccountEndpoint=https://mycosmosdb.documents.azure.com:443/;AccountKey=JkLv....etc;");
?? new CosmosClient("AccountEndpoint=https://localhost:8081/;AccountKey=C2y6...etc");
}
}
So as you can see I'm currently hard coding the ConnectionString in the class, which is obviously not optimal.
I noticed that I have a local.settings.json file in my project. Is that the place to store to local connection string in? And if so, do I have to use a specific key name for this? Or how do I read from it?
And how does this then work when I publish my Azure Functions app?
So how can I make it so that it locally uses my local ConnectionString, and when published it automatically uses the remote ConnectionString?
You store these as environment variables. Locally these will be in the local.settings.json and on Azure they will be in the Application Settings tab under the Configuration blade of your function app in the Azure Portal.
The name of your variable is arbitrary. Your local.settings.json will look something like this:
{
"IsEncrypted": false,
"Values": {
"CosmosDbConnectionString": "[CONNECTION STRING HERE]"
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
Be sure it goes inside of the "Values" section. The you simply access it using the GetEnvironmentVariable method:
Environment.GetEnvironmentVariable("CosmosDbConnectionString");
So something like:
return new CosmosClient(Environment.GetEnvironmentVariable("CosmosDbConnectionString"));
You don't need any sort of logic to switch between dev and prod. Since the environment variables are different if each place, it will automatically pick up the correct connection string.
Note, in the Portal, make sure you use the "Application Settings" section and NOT the "Connection Strings" section. It's confusing, but the Connections String section is only used for Entity Framework on Functions.
If you prefer to access configuration in the idiomatic ASP.NET Core manner with dependency injection, you can use a Startup.cs that looks something like the below. The values are stored in local.settings.json or in hosted App Settings.
[assembly: FunctionsStartup(typeof(MyApp.Functions.Startup))]
namespace MyApp.Functions
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
IServiceCollection services = builder.Services;
// Read configuration
var config = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddEnvironmentVariables().Build();
// Pass configuration values to a custom IServiceCollection extension
services.AddCosmosDb(new CosmosDbOptions
{
ConnectionString = config["CosmosDb:ConnectionString"],
DatabaseId = config["CosmosDb:DatabaseId"]
});
// Configuration access
services.Configure<AzureStorageOptions>(config.GetSection("AzureStorage"));
// Other setup, add more things to services, etc
services.AddMemoryCache();
}
}
}
Then in any function, you can constructor-inject any services configured at Startup, including your Cosmos service instance.
My question has two parts but they are both about secure credential storage. The background is that I am using a basic hosting service with ftp to deploy my .net core 3.1 site. I don't have directory access beyond the root folder of where I deploy my files. I am using sql server and a few other third party services that require me to use API keys.
For development, all the documentation from the different third parties have me either use the windows credential storage or put a file somewhere on the file system outside of the solution folder. But that is not an option for deployment.
So my questions are, For deployment to this hosting service...
1. Is it safe to use appsettings.json to store my API keys?
2. Also, is it save to just put a flat text file in the root of my site so it's not reachable by the public and then just pull that in at runtime?
1.Is it safe to use appsettings.json to store my API keys?
NO
If you are using third party hosting service using FTP to a folder, it not safe to assume only you can see your deployment files.
Also, is it save to just put a flat text file in the root of my site so it's not reachable by the public and then just pull that in at runtime?
It is same as using appsettings.json to store API keys since appsettings files should not be exposed publicly too.
What I can suggest is to either use some third party secure tool like Azure Vault or Hashi Vault to store secrets(they are encrypted there) & consume in your application.
If you don't want to use third party service, you can create custom Configuration Builder where you can encrypt the secrets before saving & decrypt before using.
public class CustomConfigProvider : ConfigurationProvider
{
public CustomConfigProvider() { }
public override void Load()
{
Data = MyEncryptUtils.DecryptConfiguration();
}
}
public class CustomConfigurationSource : IConfigurationSource
{
public CustomConfigurationSource() { }
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new CustomConfigProvider();
}
}
public class CustomConfigurationSource : IConfigurationSource
{
public CustomConfigurationSource() { }
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new CustomConfigProvider();
}
}
Call in startup
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddCustomConfiguration()
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
Source: https://stormpath.com/blog/store-protect-sensitive-data-dotnet-core
Also there is alternate approach using bridge pattern & encryption by Steve here:
https://stevetalkscode.co.uk/configuration-bridging-part-4
Using .NET Core 2 in Azure App Service. Don't see my logs. In the actual application, the logging code looks like this:
using System.Diagnostics;
...
Trace.WriteLine("Logging works");
I expected to see the Trace logs in the Log Stream, but I don't. I do see general API logs. What am I doing wrong? My config below:
I had the same issue for .NET core azure web app: Trace.WriteLine method does not work(not write message to Application logs) for .NET core, but work for .NET framework , and I find the issue about that.
As a workaround for .NET core web application, I suggest you can use ILogger, which can write message to Application Logs.
In Startup.cs -> Configure method, re-write Configure method like below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//your other code
//add the following 2 lines of code.
loggerFactory.AddConsole();
loggerFactory.AddDebug();
app.UseStaticFiles();
//your other code
}
Then in HomeController.cs, add the following code:
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<HomeController>();
}
public IActionResult Index()
{
_logger.LogInformation("this is a information from ILogger...");
return View();
}
//other code
}
After publish to azure, and configure the Application Logging -> run the azure web app -> you can see the message is displayed in Application Logs:
I have an Azure web app with several web jobs in the back-end. Up until now, I have been using traditional .Net to create these web jobs. One of the core aspects of these web jobs is that they all access global configuration properties defined in the Azure portal, as shown below:
And I reference these settings in my traditional .Net app like so:
var appKey = ConfigurationManager.AppSettings["RingCentral_AppKey"];
I am now starting to transition my web jobs to .Net Core. However, the "best practice" for for managing/retrieving app settings like this seems to be in an appsettings.json file within each web job:
And I reference my settings in my .Net Core app like so:
class Program
{
public static IConfiguration StaticConfig { get; set; }
static void Main(string[] args)
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureAppConfiguration((hostContext, config) =>
{
var conf = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();
config.AddConfiguration(conf);
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
}
public class Functions
{
IConfiguration configuration;
public Functions(IConfiguration _configuration)
{
configuration = _configuration;
}
[NoAutomaticTrigger]
public void DoTrigger(ILogger logger, [Queue("messagestarterqueue")] ICollector<string> outputQueueMessage)
{
var appKey = configuration["RingCentral_AppKey"];
}
}
That's all good, but I have to have a separate appsettings.config file in each separate project, each with the same settings repeated. Is there a way to "centralize" theses settings for my .Net Core web jobs? Either create a "global" appsettings.config file that my Azure web app can reference? Or, is there some way that each .Net Core app can reference the "old style" application settings that are currently being used by the traditional .Net web jobs?
Allow me to suggest a bit different approach.
IMHO, the best way you can do it is by utilizing the Azure App Configuration service.
(Check it on GitHub)
It will allow you to call an API to get the configured values, while you can manage it conveniently through the portal.
Actually you could use Environment.GetEnvironmentVariable(your settings key) to get the settings.
After you set the settings in Configuration, they will be saved as environment variables, you could check them in https://yousitename.scm.azurewebsites.net/Env.cshtml.
So of course yo could get them GetEnvironmentVariable, it will show as below pic.
I'm trying to get a more user-friendly component name in Application Insights Application Map. I found examples but not for Service Fabric specifically.
How do you integrate that with the FabricTelemetryInitializer that is part of the Kestrel WebHostBuilder?
If you are not using the Application Insights Service Fabric nuget package, then you should use it to set your cloud role.
https://www.nuget.org/packages/Microsoft.ApplicationInsights.ServiceFabric
https://www.nuget.org/packages/Microsoft.ApplicationInsights.ServiceFabric.Native
Microsoft.ApplicationInsights.ServiceFabric.Native should be used if your application has references to service fabric runtime since this library is relevant to concepts like ServiceContext, Service Remoting, etc. Microsoft.ApplicationInsights.ServiceFabric should be used if your application runs in service fabric but has no reference to service fabric runtime.
Since you mentioned FabricTelemetryInitializer, I assume you are using these nuget packages already. You can see how FabricyTelemetryInitializer can be hooked up here:
https://github.com/microsoft/applicationinsights-servicefabric#net-core-1
If you don't have a service context object, then don't pass in one to the constructor and FabricTelemetryInitializer will just rely on environment variables.
See here for the actual logic:
https://github.com/Microsoft/ApplicationInsights-ServiceFabric/blob/master/src/ApplicationInsights.ServiceFabric/Shared/FabricTelemetryInitializer.cs#L81
I think you can use this code:
[DebuggerStepThrough]
public class ServiceNameInitializer : ITelemetryInitializer
{
/// <inheritdoc />
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = "ServiceA";
}
}
And in ConfigureServices add:
services.AddSingleton<ITelemetryInitializer, ServiceNameInitializer>();