From what I can see the resolving of the %token% value from Environment variables is done in Func.exe / the Host in Azure Functions.
The Metadata that is needed for the runner to register with all the queue etc is sent across via a GRPC call on start up.
This metadata is generated at design time by a Code Generator and is in a class which implements IFunctionMetadataProvider.
I have been able to create a new class that implements decorate the actual code generated class that generates the metadata:
internal class ConfigurableFunctionMetadataProvider(IFunctionMetadataProvider baseProvider, IConfiguration configuration, ILogger<ConfigurableFunctionMetadataProvider> logger) : IFunctionMetadataProvider
{
private static readonly Regex RegexExpression = new Regex("%([^%\"]*)%", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
public Task<ImmutableArray<IFunctionMetadata>> GetFunctionMetadataAsync(string directory) =>
baseProvider.GetFunctionMetadataAsync(directory).ContinueWith(task =>
{
var functionMetadataArray = task.Result;
foreach (var metadata in functionMetadataArray)
{
for (int i = 0; i < metadata.RawBindings.Count; i++)
{
Match matchResults = RegexExpression.Match(metadata.RawBindings[i]);
while (matchResults.Success)
{
var replacement = configuration.GetValue<string>(matchResults.Groups[1].Value);
if (replacement != null)
{
logger.LogInformation("Replacing token '{TokenName}' in '{FunctionName}' with '{Value}'", matchResults.Groups[0].Value, metadata.Name, replacement);
metadata.RawBindings[i] = metadata.RawBindings[i].Replace(matchResults.Groups[0].Value, replacement);
}
matchResults = matchResults.NextMatch();
}
}
}
return functionMetadataArray;
});
}
Then where you declare your dependency injection add the following line (using scrutor from nuget):
services.Decorate<IFunctionMetadataProvider, ConfigurableFunctionMetadataProvider>();
The code will also log out the fact it has made a substitution.