in src/Cli/func/Actions/HostActions/StartHostAction.cs [733:800]
internal static async Task CheckNonOptionalSettings(IEnumerable<KeyValuePair<string, string>> secrets, string scriptPath, bool skipAzureWebJobsStorageCheck = false)
{
string storageConnectionKey = "AzureWebJobsStorage";
try
{
// FirstOrDefault returns a KeyValuePair<string, string> which is a struct so it can't be null.
var azureWebJobsStorage = secrets.FirstOrDefault(pair => pair.Key.Equals(storageConnectionKey, StringComparison.OrdinalIgnoreCase)).Value;
var functionJsonFiles = await FileSystemHelpers
.GetDirectories(scriptPath)
.Select(d => Path.Combine(d, "function.json"))
.Where(FileSystemHelpers.FileExists)
.Select(async f => (filePath: f, content: await FileSystemHelpers.ReadAllTextFromFileAsync(f)))
.WhenAll();
var functionsJsons = functionJsonFiles
.Select(t => (filePath: t.filePath, jObject: JsonConvert.DeserializeObject<JObject>(t.content)))
.Where(b => b.jObject["bindings"] != null);
var allNonStorageTriggers = functionsJsons
.Select(b => b.jObject["bindings"])
.SelectMany(i => i)
.Where(b => b?["type"] != null)
.Select(b => b["type"].ToString())
.Where(b => b.IndexOf("Trigger", StringComparison.OrdinalIgnoreCase) != -1)
.All(t => Constants.TriggersWithoutStorage.Any(tws => tws.Equals(t, StringComparison.OrdinalIgnoreCase)));
if (!skipAzureWebJobsStorageCheck && string.IsNullOrWhiteSpace(azureWebJobsStorage) &&
!ConnectionExists(secrets, storageConnectionKey) && !allNonStorageTriggers)
{
throw new CliException($"Missing value for AzureWebJobsStorage in {SecretsManager.AppSettingsFileName}. " +
$"This is required for all triggers other than {string.Join(", ", Constants.TriggersWithoutStorage)}. "
+ $"You can run 'func azure functionapp fetch-app-settings <functionAppName>', specify a connection string in {SecretsManager.AppSettingsFileName}, or use managed identity to authenticate.");
}
foreach ((var filePath, var functionJson) in functionsJsons)
{
foreach (JObject binding in functionJson["bindings"])
{
foreach (var token in binding)
{
if (token.Key == "connection" || token.Key == "apiKey" || token.Key == "accountSid" || token.Key == "authToken")
{
var appSettingName = token.Value.ToString();
if (string.IsNullOrWhiteSpace(appSettingName))
{
ColoredConsole.WriteLine(WarningColor($"Warning: '{token.Key}' property in '{filePath}' is empty."));
}
else if ((token.Key == "connection" && !ConnectionExists(secrets, appSettingName)) ||
(token.Key != "connection" && !secrets.Any(v => v.Key.Equals(appSettingName, StringComparison.OrdinalIgnoreCase))))
{
ColoredConsole
.WriteLine(WarningColor($"Warning: Cannot find value named '{appSettingName}' in {SecretsManager.AppSettingsFileName} that matches '{token.Key}' property set on '{binding["type"]?.ToString()}' in '{filePath}'. " +
$"You can run 'func azure functionapp fetch-app-settings <functionAppName>' or specify a connection string in {SecretsManager.AppSettingsFileName}."));
}
}
}
}
}
}
catch (Exception e) when (!(e is CliException))
{
ColoredConsole.WriteLine(WarningColor($"Warning: unable to verify all settings from {SecretsManager.AppSettingsFileName} and function.json files."));
if (StaticSettings.IsDebug)
{
ColoredConsole.WriteLine(e);
}
}
}