diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index c6f25abd3..a1d9018e1 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -11,6 +11,9 @@ + + + diff --git a/src/ApiService/ApiService/FeatureFlags.cs b/src/ApiService/ApiService/FeatureFlags.cs new file mode 100644 index 000000000..a55a3dad1 --- /dev/null +++ b/src/ApiService/ApiService/FeatureFlags.cs @@ -0,0 +1,5 @@ +namespace Microsoft.OneFuzz.Service; + +public static class FeatureFlagConstants { + public const string EnableScribanOnly = "EnableScribanOnly"; +} diff --git a/src/ApiService/ApiService/Functions/AgentCanSchedule.cs b/src/ApiService/ApiService/Functions/AgentCanSchedule.cs index af41c3efe..8d74f2dec 100644 --- a/src/ApiService/ApiService/Functions/AgentCanSchedule.cs +++ b/src/ApiService/ApiService/Functions/AgentCanSchedule.cs @@ -8,6 +8,7 @@ public class AgentCanSchedule { private readonly IEndpointAuthorization _auth; private readonly IOnefuzzContext _context; + public AgentCanSchedule(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) { _log = log; _auth = auth; diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index 7db6db60f..00b3d686a 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -13,8 +13,10 @@ using Azure.Identity; using Microsoft.ApplicationInsights.DependencyCollector; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Middleware; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.FeatureManagement; using Microsoft.Graph; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; @@ -47,6 +49,13 @@ public class Program { using var host = new HostBuilder() + .ConfigureAppConfiguration(builder => { + var _ = builder.AddAzureAppConfiguration(options => { + var _ = options + .Connect(new Uri(configuration.AppConfigurationEndpoint!), new DefaultAzureCredential()) + .UseFeatureFlags(ffOptions => ffOptions.CacheExpirationInterval = TimeSpan.FromMinutes(1)); + }); + }) .ConfigureFunctionsWorkerDefaults(builder => { builder.UseMiddleware(); builder.AddApplicationInsights(options => { @@ -54,6 +63,8 @@ public class Program { }); }) .ConfigureServices((context, services) => { + services.AddAzureAppConfiguration(); + _ = services.AddFeatureManagement(); services.Configure(options => { options = EntityConverter.GetJsonSerializerOptions(); }); diff --git a/src/ApiService/ApiService/ServiceConfiguration.cs b/src/ApiService/ApiService/ServiceConfiguration.cs index d535cc7a7..7dca0b427 100644 --- a/src/ApiService/ApiService/ServiceConfiguration.cs +++ b/src/ApiService/ApiService/ServiceConfiguration.cs @@ -16,6 +16,7 @@ public interface IServiceConfig { public string? ApplicationInsightsAppId { get; } public string? ApplicationInsightsInstrumentationKey { get; } + public string? AppConfigurationEndpoint { get; } public string? AzureSignalRConnectionString { get; } public string? AzureSignalRServiceTransportType { get; } @@ -82,6 +83,8 @@ public class ServiceConfiguration : IServiceConfig { public string? ApplicationInsightsAppId => GetEnv("APPINSIGHTS_APPID"); public string? ApplicationInsightsInstrumentationKey => GetEnv("APPINSIGHTS_INSTRUMENTATIONKEY"); + public string? AppConfigurationEndpoint => GetEnv("APPCONFIGURATION_ENDPOINT"); + public string? AzureSignalRConnectionString => GetEnv("AzureSignalRConnectionString"); public string? AzureSignalRServiceTransportType => GetEnv("AzureSignalRServiceTransportType"); diff --git a/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs b/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs index 01c9e6dc7..ffda56aaa 100644 --- a/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs +++ b/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs @@ -1,8 +1,11 @@ using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; + namespace Microsoft.OneFuzz.Service; +using Microsoft.Extensions.Configuration.AzureAppConfiguration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.FeatureManagement; public interface IOnefuzzContext { IAutoScaleOperations AutoScaleOperations { get; } @@ -46,6 +49,9 @@ public interface IOnefuzzContext { ITeams Teams { get; } IGithubIssues GithubIssues { get; } IAdo Ado { get; } + + IFeatureManagerSnapshot FeatureManagerSnapshot { get; } + IConfigurationRefresher ConfigurationRefresher { get; } } public class OnefuzzContext : IOnefuzzContext { @@ -95,4 +101,8 @@ public class OnefuzzContext : IOnefuzzContext { public ITeams Teams => _serviceProvider.GetRequiredService(); public IGithubIssues GithubIssues => _serviceProvider.GetRequiredService(); public IAdo Ado => _serviceProvider.GetRequiredService(); + + public IFeatureManagerSnapshot FeatureManagerSnapshot => _serviceProvider.GetRequiredService(); + + public IConfigurationRefresher ConfigurationRefresher => _serviceProvider.GetRequiredService().Refreshers.First(); } diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs b/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs index c727617ba..8ae8d7287 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs @@ -34,6 +34,7 @@ public abstract class NotificationsBase { private readonly Uri _targetUrl; private readonly Uri _inputUrl; private readonly Uri _reportUrl; + private readonly bool _scribanOnly; public static async Async.Task ConstructRenderer( IOnefuzzContext context, @@ -66,6 +67,9 @@ public abstract class NotificationsBase { inputUrl = new Uri(context.Containers.AuthDownloadUrl(report.InputBlob.Container, report.InputBlob.Name)); } + await context.ConfigurationRefresher.TryRefreshAsync().IgnoreResult(); + var scribanOnly = await context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.EnableScribanOnly); + return new Renderer( container, filename, @@ -74,7 +78,8 @@ public abstract class NotificationsBase { checkedJob, targetUrl, inputUrl!, // TODO: incorrect - reportUrl); + reportUrl, + scribanOnly); } public Renderer( Container container, @@ -84,7 +89,8 @@ public abstract class NotificationsBase { Job job, Uri targetUrl, Uri inputUrl, - Uri reportUrl) { + Uri reportUrl, + bool scribanOnly) { _report = report; _container = container; _filename = filename; @@ -93,13 +99,17 @@ public abstract class NotificationsBase { _reportUrl = reportUrl; _targetUrl = targetUrl; _inputUrl = inputUrl; + _scribanOnly = scribanOnly; } // TODO: This function is fallible but the python // implementation doesn't have that so I'm trying to match it. // We should probably propagate any errors up public async Async.Task Render(string templateString, Uri instanceUrl) { - templateString = JinjaTemplateAdapter.IsJinjaTemplate(templateString) ? JinjaTemplateAdapter.AdaptForScriban(templateString) : templateString; + if (!_scribanOnly && JinjaTemplateAdapter.IsJinjaTemplate(templateString)) { + templateString = JinjaTemplateAdapter.AdaptForScriban(templateString); + } + var template = Template.Parse(templateString); if (template != null) { return await template.RenderAsync(new { diff --git a/src/ApiService/ApiService/packages.lock.json b/src/ApiService/ApiService/packages.lock.json index b033ab012..82f60a9de 100644 --- a/src/ApiService/ApiService/packages.lock.json +++ b/src/ApiService/ApiService/packages.lock.json @@ -157,6 +157,16 @@ "resolved": "0.12.2", "contentHash": "JgMAGj8ekeAzKkagubXqf1UqgfHq89GyA1UQYWbkAe441uRr2Rh2rktkx5Z0LPwmD/aOqu9cxjekD2GZjP8rbw==" }, + "Microsoft.Azure.Functions.Extensions": { + "type": "Direct", + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "zYKtQQoS1fdzufxFApuMFiFtoi9QAGH6McXxntpylwLKgKjmCMWdgUd1dcekzTKNR9DPSDPRLiulvukqXnpWrQ==", + "dependencies": { + "Microsoft.Azure.WebJobs": "3.0.18", + "Microsoft.Extensions.DependencyInjection": "2.1.0" + } + }, "Microsoft.Azure.Functions.Worker": { "type": "Direct", "requested": "[1.10.0, )", @@ -262,6 +272,31 @@ "System.Net.Http": "4.3.0" } }, + "Microsoft.Extensions.Configuration.AzureAppConfiguration": { + "type": "Direct", + "requested": "[5.1.0, )", + "resolved": "5.1.0", + "contentHash": "FoAfgvT/rjL/+c7BP7q0LrJIdc4Hu6SH56BTIUbwCwVjHoUw4dpgGtLQULi5GmMjdbdAxyLQSnbwpOEWuBy+RA==", + "dependencies": { + "Azure.Data.AppConfiguration": "1.2.0", + "Azure.Messaging.EventGrid": "4.7.0", + "Azure.Security.KeyVault.Secrets": "4.0.1", + "Microsoft.Extensions.Configuration": "3.1.18", + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.18", + "Microsoft.Extensions.Logging": "3.1.18", + "System.Text.Json": "4.6.0" + } + }, + "Microsoft.FeatureManagement": { + "type": "Direct", + "requested": "[2.5.1, )", + "resolved": "2.5.1", + "contentHash": "ERbRjk0etZs4d5Pv17unfogO4iBwV2c/HoBt4jqIJmfbKbmTLV+GbjBPYzidIg2RgYIFi8yA+EoEapSAIOp19g==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.1.10", + "Microsoft.Extensions.Logging": "2.1.1" + } + }, "Microsoft.Graph": { "type": "Direct", "requested": "[4.37.0, )", @@ -355,6 +390,16 @@ "resolved": "2.0.0", "contentHash": "rXkSI9t4vP2EaPhuchsWiD3elcLNth3UOZAlGohGmuckpkiOr57oMHuzM5WDzz7MJd+ZewE27/WfrZhhhFDHzA==" }, + "Azure.Data.AppConfiguration": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "KA1dAM9TuDsq0CRFd+3cJTYUAzA2z9N8t9/xKdDbP9URuReq/NDFcKYr7GW2W9xzVGDtCHlD5j5am/+zLLBdSg==", + "dependencies": { + "Azure.Core": "1.20.0", + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Text.Json": "4.6.0" + } + }, "Azure.Storage.Common": { "type": "Transitive", "resolved": "12.12.0", @@ -567,6 +612,33 @@ "Microsoft.CodeAnalysis.CSharp": "3.11.0" } }, + "Microsoft.Azure.WebJobs": { + "type": "Transitive", + "resolved": "3.0.18", + "contentHash": "aYJ76yjPkIpsafqFp1Xz1sA06RvhUwqJnk4AqX4I0teuRjPyig9Sv7LTzxUMAppKXc4JyR/Asos2At/LMiblqg==", + "dependencies": { + "Microsoft.Azure.WebJobs.Core": "3.0.18", + "Microsoft.Extensions.Configuration": "2.1.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0", + "Microsoft.Extensions.Configuration.Json": "2.1.0", + "Microsoft.Extensions.Hosting": "2.1.0", + "Microsoft.Extensions.Logging": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Configuration": "2.1.0", + "Newtonsoft.Json": "11.0.2", + "System.Threading.Tasks.Dataflow": "4.8.0" + } + }, + "Microsoft.Azure.WebJobs.Core": { + "type": "Transitive", + "resolved": "3.0.18", + "contentHash": "ajYI8pPzPn4qq7FL8C2tz9WmFEG5PorUlkw8W9CF5M+5egnFJaF7yH48WYC+zBoQIzv2vHmFq0zhQpnv+O8v5Q==", + "dependencies": { + "System.ComponentModel.Annotations": "4.4.0", + "System.Diagnostics.TraceSource": "4.3.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "6.0.0", @@ -1381,6 +1453,22 @@ "System.Runtime": "4.3.0" } }, + "System.Diagnostics.TraceSource": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, "System.Diagnostics.Tracing": { "type": "Transitive", "resolved": "4.3.0", @@ -2077,6 +2165,11 @@ "System.Runtime": "4.3.0" } }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "PSIdcgbyNv7FZvZ1I9Mqy6XZOwstYYMdZiXuHvIyc0gDyPjEhrrP9OvTGDHp+LAHp1RNSLjPYssyqox9+Kt9Ug==" + }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", diff --git a/src/ApiService/IntegrationTests/Fakes/TestContext.cs b/src/ApiService/IntegrationTests/Fakes/TestContext.cs index 60d2af0ca..9106dfc0d 100644 --- a/src/ApiService/IntegrationTests/Fakes/TestContext.cs +++ b/src/ApiService/IntegrationTests/Fakes/TestContext.cs @@ -1,7 +1,9 @@ using System; using System.Linq; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Configuration.AzureAppConfiguration; using Microsoft.Extensions.Options; +using Microsoft.FeatureManagement; using Microsoft.OneFuzz.Service; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using Async = System.Threading.Tasks; @@ -120,6 +122,7 @@ public sealed class TestContext : IOnefuzzContext { public ITeams Teams => throw new NotImplementedException(); public IGithubIssues GithubIssues => throw new NotImplementedException(); public IAdo Ado => throw new NotImplementedException(); + public IFeatureManagerSnapshot FeatureManagerSnapshot => throw new NotImplementedException(); - + public IConfigurationRefresher ConfigurationRefresher => throw new NotImplementedException(); } diff --git a/src/ApiService/IntegrationTests/Fakes/TestServiceConfiguration.cs b/src/ApiService/IntegrationTests/Fakes/TestServiceConfiguration.cs index 9ab9250f4..20f91bbe9 100644 --- a/src/ApiService/IntegrationTests/Fakes/TestServiceConfiguration.cs +++ b/src/ApiService/IntegrationTests/Fakes/TestServiceConfiguration.cs @@ -63,4 +63,5 @@ public sealed class TestServiceConfiguration : IServiceConfig { public string? OneFuzzResourceGroup => throw new NotImplementedException(); public string? OneFuzzAllowOutdatedAgent => throw new NotImplementedException(); + public string? AppConfigurationEndpoint => throw new NotImplementedException(); } diff --git a/src/ApiService/IntegrationTests/packages.lock.json b/src/ApiService/IntegrationTests/packages.lock.json index 2a98dad36..83c59842a 100644 --- a/src/ApiService/IntegrationTests/packages.lock.json +++ b/src/ApiService/IntegrationTests/packages.lock.json @@ -68,6 +68,16 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "Azure.Data.AppConfiguration": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "KA1dAM9TuDsq0CRFd+3cJTYUAzA2z9N8t9/xKdDbP9URuReq/NDFcKYr7GW2W9xzVGDtCHlD5j5am/+zLLBdSg==", + "dependencies": { + "Azure.Core": "1.20.0", + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Text.Json": "4.6.0" + } + }, "Azure.Data.Tables": { "type": "Transitive", "resolved": "12.5.0", @@ -365,6 +375,15 @@ "resolved": "5.0.8", "contentHash": "ZI9S2NGjuOKXN3PxJcF8EKVwd1cqpWyUSqiVoH8gqq5tlHaXULwPmoR0DBOFON4sEFETRWI69f5RQ3tJWw205A==" }, + "Microsoft.Azure.Functions.Extensions": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "zYKtQQoS1fdzufxFApuMFiFtoi9QAGH6McXxntpylwLKgKjmCMWdgUd1dcekzTKNR9DPSDPRLiulvukqXnpWrQ==", + "dependencies": { + "Microsoft.Azure.WebJobs": "3.0.18", + "Microsoft.Extensions.DependencyInjection": "2.1.0" + } + }, "Microsoft.Azure.Functions.Worker": { "type": "Transitive", "resolved": "1.10.0", @@ -519,6 +538,33 @@ "System.Net.Http": "4.3.0" } }, + "Microsoft.Azure.WebJobs": { + "type": "Transitive", + "resolved": "3.0.18", + "contentHash": "aYJ76yjPkIpsafqFp1Xz1sA06RvhUwqJnk4AqX4I0teuRjPyig9Sv7LTzxUMAppKXc4JyR/Asos2At/LMiblqg==", + "dependencies": { + "Microsoft.Azure.WebJobs.Core": "3.0.18", + "Microsoft.Extensions.Configuration": "2.1.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0", + "Microsoft.Extensions.Configuration.Json": "2.1.0", + "Microsoft.Extensions.Hosting": "2.1.0", + "Microsoft.Extensions.Logging": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Configuration": "2.1.0", + "Newtonsoft.Json": "11.0.2", + "System.Threading.Tasks.Dataflow": "4.8.0" + } + }, + "Microsoft.Azure.WebJobs.Core": { + "type": "Transitive", + "resolved": "3.0.18", + "contentHash": "ajYI8pPzPn4qq7FL8C2tz9WmFEG5PorUlkw8W9CF5M+5egnFJaF7yH48WYC+zBoQIzv2vHmFq0zhQpnv+O8v5Q==", + "dependencies": { + "System.ComponentModel.Annotations": "4.4.0", + "System.Diagnostics.TraceSource": "4.3.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "6.0.0", @@ -598,6 +644,20 @@ "Microsoft.Extensions.Primitives": "5.0.0" } }, + "Microsoft.Extensions.Configuration.AzureAppConfiguration": { + "type": "Transitive", + "resolved": "5.1.0", + "contentHash": "FoAfgvT/rjL/+c7BP7q0LrJIdc4Hu6SH56BTIUbwCwVjHoUw4dpgGtLQULi5GmMjdbdAxyLQSnbwpOEWuBy+RA==", + "dependencies": { + "Azure.Data.AppConfiguration": "1.2.0", + "Azure.Messaging.EventGrid": "4.7.0", + "Azure.Security.KeyVault.Secrets": "4.0.1", + "Microsoft.Extensions.Configuration": "3.1.18", + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.18", + "Microsoft.Extensions.Logging": "3.1.18", + "System.Text.Json": "4.6.0" + } + }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", "resolved": "5.0.0", @@ -856,6 +916,15 @@ "resolved": "5.0.1", "contentHash": "5WPSmL4YeP7eW+Vc8XZ4DwjYWBAiSwDV9Hm63JJWcz1Ie3Xjv4KuJXzgCstj48LkLfVCYa7mLcx7y+q6yqVvtw==" }, + "Microsoft.FeatureManagement": { + "type": "Transitive", + "resolved": "2.5.1", + "contentHash": "ERbRjk0etZs4d5Pv17unfogO4iBwV2c/HoBt4jqIJmfbKbmTLV+GbjBPYzidIg2RgYIFi8yA+EoEapSAIOp19g==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.1.10", + "Microsoft.Extensions.Logging": "2.1.1" + } + }, "Microsoft.Graph": { "type": "Transitive", "resolved": "4.37.0", @@ -2238,6 +2307,11 @@ "System.Runtime": "4.3.0" } }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "PSIdcgbyNv7FZvZ1I9Mqy6XZOwstYYMdZiXuHvIyc0gDyPjEhrrP9OvTGDHp+LAHp1RNSLjPYssyqox9+Kt9Ug==" + }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -2419,6 +2493,7 @@ "Azure.Storage.Blobs": "[12.13.0, )", "Azure.Storage.Queues": "[12.11.0, )", "Faithlife.Utility": "[0.12.2, )", + "Microsoft.Azure.Functions.Extensions": "[1.1.0, )", "Microsoft.Azure.Functions.Worker": "[1.10.0, )", "Microsoft.Azure.Functions.Worker.ApplicationInsights": "[1.0.0-preview3, )", "Microsoft.Azure.Functions.Worker.Extensions.EventGrid": "[2.1.0, )", @@ -2429,6 +2504,8 @@ "Microsoft.Azure.Functions.Worker.Sdk": "[1.7.0, )", "Microsoft.Azure.Management.Monitor": "[0.28.0-preview, )", "Microsoft.Azure.Management.OperationalInsights": "[0.24.0-preview, )", + "Microsoft.Extensions.Configuration.AzureAppConfiguration": "[5.1.0, )", + "Microsoft.FeatureManagement": "[2.5.1, )", "Microsoft.Graph": "[4.37.0, )", "Microsoft.Identity.Client": "[4.46.2, )", "Microsoft.Identity.Web.TokenCache": "[1.23.1, )", diff --git a/src/ApiService/Tests/packages.lock.json b/src/ApiService/Tests/packages.lock.json index edddf0818..715810521 100644 --- a/src/ApiService/Tests/packages.lock.json +++ b/src/ApiService/Tests/packages.lock.json @@ -87,6 +87,16 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "Azure.Data.AppConfiguration": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "KA1dAM9TuDsq0CRFd+3cJTYUAzA2z9N8t9/xKdDbP9URuReq/NDFcKYr7GW2W9xzVGDtCHlD5j5am/+zLLBdSg==", + "dependencies": { + "Azure.Core": "1.20.0", + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Text.Json": "4.6.0" + } + }, "Azure.Data.Tables": { "type": "Transitive", "resolved": "12.5.0", @@ -414,6 +424,15 @@ "resolved": "5.0.8", "contentHash": "ZI9S2NGjuOKXN3PxJcF8EKVwd1cqpWyUSqiVoH8gqq5tlHaXULwPmoR0DBOFON4sEFETRWI69f5RQ3tJWw205A==" }, + "Microsoft.Azure.Functions.Extensions": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "zYKtQQoS1fdzufxFApuMFiFtoi9QAGH6McXxntpylwLKgKjmCMWdgUd1dcekzTKNR9DPSDPRLiulvukqXnpWrQ==", + "dependencies": { + "Microsoft.Azure.WebJobs": "3.0.18", + "Microsoft.Extensions.DependencyInjection": "2.1.0" + } + }, "Microsoft.Azure.Functions.Worker": { "type": "Transitive", "resolved": "1.10.0", @@ -568,6 +587,33 @@ "System.Net.Http": "4.3.0" } }, + "Microsoft.Azure.WebJobs": { + "type": "Transitive", + "resolved": "3.0.18", + "contentHash": "aYJ76yjPkIpsafqFp1Xz1sA06RvhUwqJnk4AqX4I0teuRjPyig9Sv7LTzxUMAppKXc4JyR/Asos2At/LMiblqg==", + "dependencies": { + "Microsoft.Azure.WebJobs.Core": "3.0.18", + "Microsoft.Extensions.Configuration": "2.1.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0", + "Microsoft.Extensions.Configuration.Json": "2.1.0", + "Microsoft.Extensions.Hosting": "2.1.0", + "Microsoft.Extensions.Logging": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Configuration": "2.1.0", + "Newtonsoft.Json": "11.0.2", + "System.Threading.Tasks.Dataflow": "4.8.0" + } + }, + "Microsoft.Azure.WebJobs.Core": { + "type": "Transitive", + "resolved": "3.0.18", + "contentHash": "ajYI8pPzPn4qq7FL8C2tz9WmFEG5PorUlkw8W9CF5M+5egnFJaF7yH48WYC+zBoQIzv2vHmFq0zhQpnv+O8v5Q==", + "dependencies": { + "System.ComponentModel.Annotations": "4.4.0", + "System.Diagnostics.TraceSource": "4.3.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "6.0.0", @@ -647,6 +693,20 @@ "Microsoft.Extensions.Primitives": "5.0.0" } }, + "Microsoft.Extensions.Configuration.AzureAppConfiguration": { + "type": "Transitive", + "resolved": "5.1.0", + "contentHash": "FoAfgvT/rjL/+c7BP7q0LrJIdc4Hu6SH56BTIUbwCwVjHoUw4dpgGtLQULi5GmMjdbdAxyLQSnbwpOEWuBy+RA==", + "dependencies": { + "Azure.Data.AppConfiguration": "1.2.0", + "Azure.Messaging.EventGrid": "4.7.0", + "Azure.Security.KeyVault.Secrets": "4.0.1", + "Microsoft.Extensions.Configuration": "3.1.18", + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.18", + "Microsoft.Extensions.Logging": "3.1.18", + "System.Text.Json": "4.6.0" + } + }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", "resolved": "5.0.0", @@ -905,6 +965,15 @@ "resolved": "5.0.1", "contentHash": "5WPSmL4YeP7eW+Vc8XZ4DwjYWBAiSwDV9Hm63JJWcz1Ie3Xjv4KuJXzgCstj48LkLfVCYa7mLcx7y+q6yqVvtw==" }, + "Microsoft.FeatureManagement": { + "type": "Transitive", + "resolved": "2.5.1", + "contentHash": "ERbRjk0etZs4d5Pv17unfogO4iBwV2c/HoBt4jqIJmfbKbmTLV+GbjBPYzidIg2RgYIFi8yA+EoEapSAIOp19g==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.1.10", + "Microsoft.Extensions.Logging": "2.1.1" + } + }, "Microsoft.Graph": { "type": "Transitive", "resolved": "4.37.0", @@ -2333,6 +2402,11 @@ "System.Runtime": "4.3.0" } }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "PSIdcgbyNv7FZvZ1I9Mqy6XZOwstYYMdZiXuHvIyc0gDyPjEhrrP9OvTGDHp+LAHp1RNSLjPYssyqox9+Kt9Ug==" + }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -2546,6 +2620,7 @@ "Azure.Storage.Blobs": "[12.13.0, )", "Azure.Storage.Queues": "[12.11.0, )", "Faithlife.Utility": "[0.12.2, )", + "Microsoft.Azure.Functions.Extensions": "[1.1.0, )", "Microsoft.Azure.Functions.Worker": "[1.10.0, )", "Microsoft.Azure.Functions.Worker.ApplicationInsights": "[1.0.0-preview3, )", "Microsoft.Azure.Functions.Worker.Extensions.EventGrid": "[2.1.0, )", @@ -2556,6 +2631,8 @@ "Microsoft.Azure.Functions.Worker.Sdk": "[1.7.0, )", "Microsoft.Azure.Management.Monitor": "[0.28.0-preview, )", "Microsoft.Azure.Management.OperationalInsights": "[0.24.0-preview, )", + "Microsoft.Extensions.Configuration.AzureAppConfiguration": "[5.1.0, )", + "Microsoft.FeatureManagement": "[2.5.1, )", "Microsoft.Graph": "[4.37.0, )", "Microsoft.Identity.Client": "[4.46.2, )", "Microsoft.Identity.Web.TokenCache": "[1.23.1, )", diff --git a/src/deployment/azuredeploy.bicep b/src/deployment/azuredeploy.bicep index e8ac1acc0..20568e5eb 100644 --- a/src/deployment/azuredeploy.bicep +++ b/src/deployment/azuredeploy.bicep @@ -40,23 +40,27 @@ var roleAssignmentsParams = [ } { suffix: '-storage' - role:'17d1049b-9a84-46fb-8f53-869881c3d3ab' //StorageAccountContributor + role: '17d1049b-9a84-46fb-8f53-869881c3d3ab' //StorageAccountContributor } { suffix: '-network' - role: '4d97b98b-1d4f-4787-a291-c67834d212e7'//NetworkContributor + role: '4d97b98b-1d4f-4787-a291-c67834d212e7' //NetworkContributor } { suffix: '-logs' - role: '92aaf0da-9dab-42b6-94a3-d43ce8d16293'//LogAnalyticsContributor + role: '92aaf0da-9dab-42b6-94a3-d43ce8d16293' //LogAnalyticsContributor } { suffix: '-user_managed_identity' - role: 'f1a07417-d97a-45cb-824c-7a7467783830'//ManagedIdentityOperator + role: 'f1a07417-d97a-45cb-824c-7a7467783830' //ManagedIdentityOperator } { suffix: '-contributor' - role: 'b24988ac-6180-42a0-ab88-20f7382dd24c'//Contributor + role: 'b24988ac-6180-42a0-ab88-20f7382dd24c' //Contributor + } + { + suffix: '-app_config_reader' + role: '516239f1-63e1-4d78-a4de-a74fb236a071' //App Configuration Data Reader } ] resource scalesetIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { @@ -162,7 +166,7 @@ module autoscaleSettings 'bicep-templates/autoscale-settings.bicep' = { module eventGrid 'bicep-templates/event-grid.bicep' = { name: 'event-grid' - params:{ + params: { location: location storageFuzzId: storage.outputs.FuzzId storageFuncId: storage.outputs.FuncId @@ -184,6 +188,7 @@ resource roleAssignments 'Microsoft.Authorization/roleAssignments@2020-10-01-pre eventGrid keyVault serverFarm + featureFlags ] }] @@ -198,10 +203,10 @@ resource roleAssignmentsNet 'Microsoft.Authorization/roleAssignments@2020-10-01- eventGrid keyVault serverFarm + featureFlags ] }] - // try to make role assignments to deploy as late as possible in order to have principalId ready resource readBlobUserAssignment 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = { name: guid('${resourceGroup().id}-user_managed_idenity_read_blob') @@ -213,9 +218,16 @@ resource readBlobUserAssignment 'Microsoft.Authorization/roleAssignments@2020-10 eventGrid keyVault serverFarm - ] + featureFlags + ] } +module featureFlags 'bicep-templates/feature-flags.bicep' = { + name: 'featureFlags' + params: { + location: location + } +} module function 'bicep-templates/function.bicep' = { name: 'function' @@ -278,45 +290,46 @@ module functionSettings 'bicep-templates/function-settings.bicep' = { monitor_account_name: operationalInsights.outputs.monitorAccountName multi_tenant_domain: multi_tenant_domain enable_profiler: enable_profiler + app_config_endpoint: featureFlags.outputs.AppConfigEndpoint functions_disabled: '0' agent_function_names: [ - 'AgentCanSchedule' //0 - 'AgentCommands' //1 - 'AgentEvents' //2 - 'AgentRegistration' //3 - 'Containers' //4 - 'Download' //5 - 'Info' //6 - 'InstanceConfig' //7 - 'Jobs' //8 - 'JobTemplates' //9 - 'JobTemplatesManage' //10 - 'Negotiate' //11 - 'Node' //12 - 'NodeAddSshKey' //13 - 'Notifications' //14 - 'Pool' //15 - 'Proxy' //16 - 'QueueFileChanges' //17 - 'QueueNodeHeartbeat' //18 - 'QueueProxyUpdate' //19 - 'QueueSignalrEvents' //20 - 'QueueTaskHeartbeat' //21 - 'QueueUpdates' //22 - 'QueueWebhooks' //23 - 'ReproVms' //24 - 'Scaleset' //25 - 'Tasks' //26 - 'TimerDaily' //27 - 'TimerProxy' //28 - 'TimerRepro' //29 - 'TimerRetention' //30 - 'TimerTasks' //31 - 'TimerWorkers' //32 - 'Tools' //33 - 'Webhooks' //34 - 'WebhooksLogs' //35 - 'WebhooksPing' //36 + 'AgentCanSchedule' //0 + 'AgentCommands' //1 + 'AgentEvents' //2 + 'AgentRegistration' //3 + 'Containers' //4 + 'Download' //5 + 'Info' //6 + 'InstanceConfig' //7 + 'Jobs' //8 + 'JobTemplates' //9 + 'JobTemplatesManage' //10 + 'Negotiate' //11 + 'Node' //12 + 'NodeAddSshKey' //13 + 'Notifications' //14 + 'Pool' //15 + 'Proxy' //16 + 'QueueFileChanges' //17 + 'QueueNodeHeartbeat' //18 + 'QueueProxyUpdate' //19 + 'QueueSignalrEvents' //20 + 'QueueTaskHeartbeat' //21 + 'QueueUpdates' //22 + 'QueueWebhooks' //23 + 'ReproVms' //24 + 'Scaleset' //25 + 'Tasks' //26 + 'TimerDaily' //27 + 'TimerProxy' //28 + 'TimerRepro' //29 + 'TimerRetention' //30 + 'TimerTasks' //31 + 'TimerWorkers' //32 + 'Tools' //33 + 'Webhooks' //34 + 'WebhooksLogs' //35 + 'WebhooksPing' //36 ] } dependsOn: [ @@ -324,7 +337,6 @@ module functionSettings 'bicep-templates/function-settings.bicep' = { ] } - module netFunctionSettings 'bicep-templates/function-settings.bicep' = { name: 'netFunctionSettings' params: { @@ -344,45 +356,46 @@ module netFunctionSettings 'bicep-templates/function-settings.bicep' = { monitor_account_name: operationalInsights.outputs.monitorAccountName multi_tenant_domain: multi_tenant_domain enable_profiler: enable_profiler + app_config_endpoint: featureFlags.outputs.AppConfigEndpoint functions_disabled: '1' agent_function_names: [ - 'AgentCanSchedule' //0 - 'AgentCommands' //1 - 'AgentEvents' //2 - 'AgentRegistration' //3 - 'Containers' //4 - 'Download' //5 - 'Info' //6 - 'InstanceConfig' //7 - 'Jobs' //8 - 'JobTemplates' //9 - 'JobTemplatesManage' //10 - 'Negotiate' //11 - 'Node' //12 - 'NodeAddSshKey' //13 - 'Notifications' //14 - 'Pool' //15 - 'Proxy' //16 - 'QueueFileChanges' //17 - 'QueueNodeHeartbeat' //18 - 'QueueProxyUpdate' //19 - 'QueueSignalrEvents' //20 - 'QueueTaskHeartbeat' //21 - 'QueueUpdates' //22 - 'QueueWebhooks' //23 - 'ReproVms' //24 - 'Scaleset' //25 - 'Tasks' //26 - 'TimerDaily' //27 - 'TimerProxy' //28 - 'TimerRepro' //29 - 'TimerRetention' //30 - 'TimerTasks' //31 - 'TimerWorkers' //32 - 'Tools' //33 - 'Webhooks' //34 - 'WebhookLogs' //35 - 'WebhookPing' //36 + 'AgentCanSchedule' //0 + 'AgentCommands' //1 + 'AgentEvents' //2 + 'AgentRegistration' //3 + 'Containers' //4 + 'Download' //5 + 'Info' //6 + 'InstanceConfig' //7 + 'Jobs' //8 + 'JobTemplates' //9 + 'JobTemplatesManage' //10 + 'Negotiate' //11 + 'Node' //12 + 'NodeAddSshKey' //13 + 'Notifications' //14 + 'Pool' //15 + 'Proxy' //16 + 'QueueFileChanges' //17 + 'QueueNodeHeartbeat' //18 + 'QueueProxyUpdate' //19 + 'QueueSignalrEvents' //20 + 'QueueTaskHeartbeat' //21 + 'QueueUpdates' //22 + 'QueueWebhooks' //23 + 'ReproVms' //24 + 'Scaleset' //25 + 'Tasks' //26 + 'TimerDaily' //27 + 'TimerProxy' //28 + 'TimerRepro' //29 + 'TimerRetention' //30 + 'TimerTasks' //31 + 'TimerWorkers' //32 + 'Tools' //33 + 'Webhooks' //34 + 'WebhookLogs' //35 + 'WebhookPing' //36 ] } dependsOn: [ diff --git a/src/deployment/bicep-templates/feature-flags.bicep b/src/deployment/bicep-templates/feature-flags.bicep new file mode 100644 index 000000000..49742cd72 --- /dev/null +++ b/src/deployment/bicep-templates/feature-flags.bicep @@ -0,0 +1,14 @@ +param location string + +var suffix = uniqueString(resourceGroup().id) +var appConfigName = 'app-config-${suffix}' + +resource featureFlags 'Microsoft.AppConfiguration/configurationStores@2022-05-01' = { + name: appConfigName + location: location + sku:{ + name: 'standard' + } +} + +output AppConfigEndpoint string = 'https://${appConfigName}.azconfig.io' diff --git a/src/deployment/bicep-templates/function-settings.bicep b/src/deployment/bicep-templates/function-settings.bicep index af42bb629..739681f26 100644 --- a/src/deployment/bicep-templates/function-settings.bicep +++ b/src/deployment/bicep-templates/function-settings.bicep @@ -13,6 +13,8 @@ param multi_tenant_domain string @secure() param signal_r_connection_string string +param app_config_endpoint string + param func_storage_resource_id string param fuzz_storage_resource_id string @@ -67,6 +69,7 @@ resource functionSettings 'Microsoft.Web/sites/config@2021-03-01' = { AzureWebJobsDisableHomepage: 'true' AzureSignalRConnectionString: signal_r_connection_string AzureSignalRServiceTransportType: 'Transient' + APPCONFIGURATION_ENDPOINT: app_config_endpoint ONEFUZZ_INSTANCE_NAME: instance_name ONEFUZZ_INSTANCE: 'https://${instance_name}.azurewebsites.net' ONEFUZZ_RESOURCE_GROUP: resourceGroup().id