22

I build an Azure Function App (v2). Configuration tasks necessary for all functions are done in a Setup class that is structured like the following:

[assembly: WebJobsStartup(typeof(Startup))]

internal class Startup : IWebJobsStartup
{
  public void Configure(IWebJobsBuilder builder)
  {
    Configuration = new ConfigurationBuilder()
                      .SetBasePath(<functionAppDirectory>)
                      .AddJsonFile("local.settings.json")
                      .Build();
    builder.AddDependencyInjection(ConfigureServices);  
  }

  public IConfiguration Configuration { get; set; }

  private void ConfigureServices(IServiceCollection services)
  {
    var connection = Configuration.GetConnectionString("<myconnection-string>");
    ...
  }
}

In ConfigureServices I want to read a connection string from a configuration file. For that the function app base folder has be specified with SetBasePath. But I found no way to get access to this path. According to https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function an ExecutionContext can be injected in a function, which contains the path need. But how do I access ExecutionContext in my Startup class?

Liam
  • 27,717
  • 28
  • 128
  • 190
  • I think System.Environment.CurrentDirectory will give you what you need. – Tim Heikell Jan 02 '19 at 19:44
  • 1
    No System.Environment.CurrentDirectory does not work in Azure. Same error: The configuration file 'config.json' was not found and is not optional. The physical path is '/config.json'. It works though locally. – Michael Chudinov Mar 20 '19 at 20:24
  • 1
    Did you manage to work this out? I am in the exact same boat at the moment in regard to needing the base path for the appsettings.json. – nagrom97 Apr 11 '19 at 15:30

6 Answers6

21

You can use this piece of code in your startup file. I have just tested it today for my project and it works on both cloud and local.

var executioncontextoptions = builder.Services.BuildServiceProvider()
    .GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
Logemann
  • 2,767
  • 33
  • 53
  • Solid. Thank you! (fake internet points for you! ;) – Jim Speaker Oct 18 '20 at 23:46
  • I referenced your answer here: https://stackoverflow.com/questions/57360238/using-configurationbuilder-in-functionsstartup/64419725#64419725 And, added some additional sample code and my opinion on why this has its place in addition to the use of environment variables. Thanks again for the good answer. – Jim Speaker Oct 19 '20 at 00:52
  • 1
    You can inject `IOptions` right into the ctor of any class that registered with the DI container and it works fine. – Mx.Wolf Feb 07 '21 at 13:44
8

TL;DR: just use Environment.GetEnvironmentVariable.

The ConfigurationBuilder approach shows up in a lot of blog posts, and worked up until we started doing DI. But there is no context parameter, so ConfigurationBuilder immediately starts to cause some strain.

I think people went this direction because in Azure Functions 2, we switched to ASP.NET Core configuration which caused ConfigurationManager to stop working. ConfigurationBuilder was a reasonable place to land. It felt congruent with MVC, and worked fine up until the introduction of DI.

But now that we are doing DI, it's becoming clear that Environment.GetEnvironmentVariable might have been the better choice all along for this platform... There's less code overhead, and it maps cleanly to the configuration model of Azure Functions: in dev, it picks up items in the local.settings.json > Values array, and in production it picks up your environment variables, and it just works.

It is different than what we do in MVC. Until these platforms come into closer parity, however, we should do what makes sense in Functions, rather than trying to force solutions from MVC.

So:

[assembly: WebJobsStartup(typeof(StartUp))]
namespace Keystone.AzureFunctions
{

    public class StartUp : IWebJobsStartup
    {
        public void Configure(IWebJobsBuilder builder)
        {           
            var connectionString = Environment.GetEnvironmentVariable("KeystoneDB");

            // Configure EF
            builder.Services.AddDbContext<KeystoneDB>(options => options.UseSqlServer(connectionString));
        }
    }
}

And your local.settings.json might look like this:

{
    "IsEncrypted": false,
    "Values": {
        "KeystoneDB": "[CONNECTION STRING HERE]"
        "FUNCTIONS_WORKER_RUNTIME": "dotnet"
    }
}

You can also use Key Vault with Environment. It works great.

Brian MacKay
  • 31,133
  • 17
  • 86
  • 125
4

Greeting,
I found a solution that works in the Startup :

var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
string path = fileInfo.Directory.Parent.FullName;
var configuration = new ConfigurationBuilder()
    .SetBasePath(Environment.CurrentDirectory)
    .SetBasePath(path)
    .AddJsonFile("appsettings.json", false)
    .Build();
Laurenz Albe
  • 209,280
  • 17
  • 206
  • 263
DrDivX59
  • 41
  • 1
3

You might want to use FunctionsStartupAttribute and IFunctionsHostBuilder from Microsoft.Azure.Functions.Extensions, for example:

[assembly:FunctionsStartup(typeof(SampleFunction.FunctionsAppStartup))]

namespace SampleFunction
{
  public class FunctionsAppStartup : FunctionsStartup
  {
    public override void Configure(IFunctionsHostBuilder builder)
    {
      string appRootPath = builder.GetContext().ApplicationRootPath;

      // ...
    }
  }
}
Marek Stój
  • 4,075
  • 6
  • 49
  • 50
2

The only workaround I found for configuration builder in Startup() method is to use hardcoded path "/home/site/wwwroot/"

var config = new ConfigurationBuilder()
                .SetBasePath("/home/site/wwwroot/")
                .AddJsonFile("config.json", optional: false)
                .Build();

System.Environment.CurrentDirectory does not work in Azure. Though it works locally. But in Azure it gives an error: The configuration file 'config.json' was not found and is not optional. The physical path is '/config.json'. And function does not start.

Michael Chudinov
  • 2,620
  • 28
  • 43
-3

Try using Environment.CurrentDirectory

Nate
  • 35
  • 2
  • 1
    No, this does not work. Got error: The configuration file 'config.json' was not found and is not optional. The physical path is '/config.json'. – Michael Chudinov Mar 20 '19 at 20:19
  • 2
    Can confirm this is not correct, this goes to like `D:\Program Files (x86)\SiteExtensions\Functions\2.0.12493\64bit` not wwwroot – Glenn Watson Jun 03 '19 at 09:47