Skip to content

Commit

Permalink
Support secretless extensions (#2652)
Browse files Browse the repository at this point in the history
* add check to config section for conn string

* how to simulate managed identity?

* added unit test

* edit error message

* PR changes

* space between tests
  • Loading branch information
madelinegordon authored Jul 22, 2021
1 parent 735920b commit 5e114c9
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 5 deletions.
30 changes: 27 additions & 3 deletions src/Azure.Functions.Cli/Actions/HostActions/StartHostAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,11 @@ private bool IsPreCompiledFunctionApp()

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("AzureWebJobsStorage", StringComparison.OrdinalIgnoreCase)).Value;
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"))
Expand All @@ -400,11 +401,12 @@ internal static async Task CheckNonOptionalSettings(IEnumerable<KeyValuePair<str
.Where(b => b.IndexOf("Trigger", StringComparison.OrdinalIgnoreCase) != -1)
.All(t => Constants.TriggersWithoutStorage.Any(tws => tws.Equals(t, StringComparison.OrdinalIgnoreCase)));

if (!skipAzureWebJobsStorageCheck && string.IsNullOrWhiteSpace(azureWebJobsStorage) && !allNonStorageTriggers)
if (!skipAzureWebJobsStorageCheck && string.IsNullOrWhiteSpace(azureWebJobsStorage) &&
!StorageConnectionExists(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>' or specify a connection string in {SecretsManager.AppSettingsFileName}.");
+ $"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)
Expand Down Expand Up @@ -441,6 +443,28 @@ internal static async Task CheckNonOptionalSettings(IEnumerable<KeyValuePair<str
}
}

internal static bool StorageConnectionExists(IEnumerable<KeyValuePair<string, string>> secrets, string connectionStringKey)
{
// convert secrets into IConfiguration object, check for storage connection in config section
var convertedEnv = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var kvp in secrets)
{
var convertedKey = kvp.Key.Replace("__", ":");
if (!convertedEnv.ContainsKey(convertedKey))
{
convertedEnv.Add(convertedKey, kvp.Value);
}
}

var configuration = new ConfigurationBuilder().AddInMemoryCollection(convertedEnv).Build();
var connectionStringSection = configuration?.GetSection("ConnectionStrings").GetSection(connectionStringKey);
if (!connectionStringSection.Exists())
{
connectionStringSection = configuration?.GetSection(connectionStringKey);
}
return connectionStringSection.Exists();
}

private async Task<(Uri listenUri, Uri baseUri, X509Certificate2 cert)> Setup()
{
var protocol = UseHttps ? "https" : "http";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Azure.Functions.Cli.Tests.ActionsTests
public class StartHostActionTests : IDisposable
{
[SkippableFact]
public async Task CheckNonOptionalSettingsThrowsOnMissingAzureWebJobsStorage()
public async Task CheckNonOptionalSettingsThrowsOnMissingAzureWebJobsStorageAndManagedIdentity()
{
Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows),
reason: "Environment.CurrentDirectory throws in linux in test cases for some reason. Revisit this once we figure out why it's failing");
Expand Down Expand Up @@ -48,6 +48,37 @@ public async Task CheckNonOptionalSettingsThrowsOnMissingAzureWebJobsStorage()
$"This is required for all triggers other than {string.Join(", ", Constants.TriggersWithoutStorage)}.");
}

[Fact]
public async Task CheckNonOptionalSettingsDoesntThrowMissingStorageUsingManagedIdentity()
{
Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows),
reason: "Environment.CurrentDirectory throws in linux in test cases for some reason. Revisit this once we figure out why it's failing");
var fileSystem = GetFakeFileSystem(new[]
{
("x:\\folder1", "{'bindings': [{'type': 'blobTrigger'}]}"),
("x:\\folder2", "{'bindings': [{'type': 'httpTrigger'}]}")
});

var secrets = new Dictionary<string, string>()
{
{ "AzureWebJobsStorage:blobServiceUri", "myuri" },
{ "AzureWebJobsStorage__queueServiceUri", "queueuri" }
};

FileSystemHelpers.Instance = fileSystem;

Exception exception = null;
try
{
await StartHostAction.CheckNonOptionalSettings(secrets, "x:\\");
}
catch (Exception e)
{
exception = e;
}
exception.Should().BeNull();
}

[Fact]
public async Task CheckNonOptionalSettingsDoesntThrowOnMissingAzureWebJobsStorage()
{
Expand Down Expand Up @@ -99,7 +130,7 @@ public async Task CheckNonOptionalSettingsDoesntThrowOnMissingAzureWebJobsStorag
public async Task CheckNonOptionalSettingsPrintsWarningForMissingSettings()
{
Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows),
reason: "Environment.CurrentDirectory throws in linux in test cases for some reason. Revisit this once we figure out why it's failling");
reason: "Environment.CurrentDirectory throws in linux in test cases for some reason. Revisit this once we figure out why it's failing");

var fileSystem = GetFakeFileSystem(new[]
{
Expand Down

0 comments on commit 5e114c9

Please sign in to comment.