diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
index 8ca456a1..cba9dc1b 100644
--- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
+++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
@@ -146,7 +146,7 @@ public AzureAppConfigurationProvider(IConfigurationClientManager configClientMan
string requestTracingDisabled = null;
try
{
- requestTracingDisabled = Environment.GetEnvironmentVariable(RequestTracingConstants.RequestTracingDisabledEnvironmentVariable);
+ requestTracingDisabled = Environment.GetEnvironmentVariable(EnvironmentVariables.DisableRequestTracing);
}
catch (SecurityException) { }
diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/RequestTracingConstants.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/RequestTracingConstants.cs
index 612e1bcc..979ea9ad 100644
--- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/RequestTracingConstants.cs
+++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/RequestTracingConstants.cs
@@ -5,7 +5,6 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
{
internal class RequestTracingConstants
{
- public const string RequestTracingDisabledEnvironmentVariable = "AZURE_APP_CONFIGURATION_TRACING_DISABLED";
public const string AzureFunctionEnvironmentVariable = "FUNCTIONS_EXTENSION_VERSION";
public const string AzureWebAppEnvironmentVariable = "WEBSITE_SITE_NAME";
public const string ContainerAppEnvironmentVariable = "CONTAINER_APP_NAME";
diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EnvironmentVariables.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EnvironmentVariables.cs
new file mode 100644
index 00000000..2a223280
--- /dev/null
+++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EnvironmentVariables.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+//
+namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
+{
+ ///
+ /// Environment variables used to configure Azure App Configuration provider behavior.
+ ///
+ internal static class EnvironmentVariables
+ {
+ ///
+ /// Environment variable to disable Feature Management schema compatibility.
+ /// The value of this variable is a boolean string, e.g. "true" or "false".
+ /// When set to "true", schema compatibility checks for feature flags are disabled,
+ /// and all feature flags will be interpreted using the Microsoft Feature Flags schema.
+ ///
+ public const string DisableFmSchemaCompatibility = "AZURE_APP_CONFIGURATION_FM_SCHEMA_COMPATIBILITY_DISABLED";
+
+ ///
+ /// Environment variable to disable request tracing.
+ /// The value of this variable is a boolean string, e.g. "true" or "false".
+ ///
+ public const string DisableRequestTracing = "AZURE_APP_CONFIGURATION_TRACING_DISABLED";
+ }
+}
diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
index 13408385..1b1adfb9 100644
--- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
+++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
@@ -9,6 +9,7 @@
using System.Linq;
using System.Net.Mime;
using System.Security.Cryptography;
+using System.Security;
using System.Text;
using System.Text.Json;
using System.Threading;
@@ -20,10 +21,21 @@ internal class FeatureManagementKeyValueAdapter : IKeyValueAdapter
{
private FeatureFlagTracing _featureFlagTracing;
private int _featureFlagIndex = 0;
+ private bool _fmSchemaCompatibilityDisabled = false;
public FeatureManagementKeyValueAdapter(FeatureFlagTracing featureFlagTracing)
{
_featureFlagTracing = featureFlagTracing ?? throw new ArgumentNullException(nameof(featureFlagTracing));
+
+ string fmSchemaCompatibilityDisabled = null;
+
+ try
+ {
+ fmSchemaCompatibilityDisabled = Environment.GetEnvironmentVariable(EnvironmentVariables.DisableFmSchemaCompatibility);
+ }
+ catch (SecurityException) { }
+
+ _fmSchemaCompatibilityDisabled = bool.TryParse(fmSchemaCompatibilityDisabled, out bool disabled) ? disabled : false;
}
public Task>> ProcessKeyValue(ConfigurationSetting setting, Uri endpoint, Logger logger, CancellationToken cancellationToken)
@@ -33,7 +45,10 @@ public Task>> ProcessKeyValue(Configura
var keyValues = new List>();
// Check if we need to process the feature flag using the microsoft schema
- if ((featureFlag.Variants != null && featureFlag.Variants.Any()) || featureFlag.Allocation != null || featureFlag.Telemetry != null)
+ if (_fmSchemaCompatibilityDisabled ||
+ (featureFlag.Variants != null && featureFlag.Variants.Any()) ||
+ featureFlag.Allocation != null ||
+ featureFlag.Telemetry != null)
{
keyValues = ProcessMicrosoftSchemaFeatureFlag(featureFlag, setting, endpoint);
}
diff --git a/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs
index 929bcbc5..21d4e92e 100644
--- a/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs
+++ b/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs
@@ -2291,6 +2291,75 @@ public void ThrowsOnIncorrectJsonTypes()
}
}
+ [Fact]
+ public void EnvironmentVariableForcesMicrosoftSchemaForAllFlags()
+ {
+ var mixedSchemaFlags = new List
+ {
+ _kv,
+ _variantFeatureFlagCollection[0]
+ };
+
+ var mockResponse = new Mock();
+ var mockClient = new Mock(MockBehavior.Strict);
+
+ mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny()))
+ .Returns(new MockAsyncPageable(mixedSchemaFlags));
+
+ try
+ {
+ // Act - Set environment variable to force Microsoft schema
+ Environment.SetEnvironmentVariable(EnvironmentVariables.DisableFmSchemaCompatibility, "true");
+
+ var config = new ConfigurationBuilder()
+ .AddAzureAppConfiguration(options =>
+ {
+ options.ClientManager = TestHelpers.CreateMockedConfigurationClientManager(mockClient.Object);
+ options.UseFeatureFlags();
+ })
+ .Build();
+
+ // Assert - Both flags should be in Microsoft schema format
+ // First flag (would be in .NET schema without environment variable) should now be in Microsoft schema
+ Assert.Equal("Beta", config["feature_management:feature_flags:0:id"]);
+ Assert.Equal("True", config["feature_management:feature_flags:0:enabled"]);
+ Assert.Equal("Browser", config["feature_management:feature_flags:0:conditions:client_filters:0:name"]);
+ Assert.Equal("Firefox", config["feature_management:feature_flags:0:conditions:client_filters:0:parameters:AllowedBrowsers:0"]);
+
+ // Second flag (always Microsoft schema) should still be in Microsoft schema
+ Assert.Equal("VariantsFeature1", config["feature_management:feature_flags:1:id"]);
+ Assert.Equal("True", config["feature_management:feature_flags:1:enabled"]);
+ Assert.Equal("Big", config["feature_management:feature_flags:1:variants:0:name"]);
+ Assert.Equal("600px", config["feature_management:feature_flags:1:variants:0:configuration_value"]);
+
+ // Verify .NET schema paths are NOT present
+ Assert.Null(config["FeatureManagement:myFeature:EnabledFor:0:Name"]);
+ Assert.Null(config["FeatureManagement:VariantsFeature1:EnabledFor:0:Name"]);
+ }
+ finally
+ {
+ // Cleanup - Reset environment variable
+ Environment.SetEnvironmentVariable("AZURE_APP_CONFIGURATION_FM_SCHEMA_COMPATIBILITY_DISABLED", null);
+ }
+
+ // Act - Verify normal behavior when environment variable is not set
+ var configWithoutEnvVar = new ConfigurationBuilder()
+ .AddAzureAppConfiguration(options =>
+ {
+ options.ClientManager = TestHelpers.CreateMockedConfigurationClientManager(mockClient.Object);
+ options.UseFeatureFlags();
+ })
+ .Build();
+
+ // First flag (no variants) should be in .NET schema
+ Assert.Equal("Browser", configWithoutEnvVar["FeatureManagement:Beta:EnabledFor:0:Name"]);
+ Assert.Equal("Firefox", configWithoutEnvVar["FeatureManagement:Beta:EnabledFor:0:Parameters:AllowedBrowsers:0"]);
+
+ // Second flag (has variants) should be in Microsoft schema
+ Assert.Equal("VariantsFeature1", configWithoutEnvVar["feature_management:feature_flags:0:id"]);
+ Assert.Equal("Big", configWithoutEnvVar["feature_management:feature_flags:0:variants:0:name"]);
+ }
+
Response GetIfChanged(ConfigurationSetting setting, bool onlyIfChanged, CancellationToken cancellationToken)
{
return Response.FromValue(FirstKeyValue, new MockResponse(200));
diff --git a/tests/Tests.AzureAppConfiguration/Unit/Tests.cs b/tests/Tests.AzureAppConfiguration/Unit/Tests.cs
index 1f2ab5fa..7e68f1cf 100644
--- a/tests/Tests.AzureAppConfiguration/Unit/Tests.cs
+++ b/tests/Tests.AzureAppConfiguration/Unit/Tests.cs
@@ -273,7 +273,7 @@ public void TestTurnOffRequestTracing()
var options = new AzureAppConfigurationOptions();
options.ClientOptions.Transport = mockTransport;
- Environment.SetEnvironmentVariable(RequestTracingConstants.RequestTracingDisabledEnvironmentVariable, "True");
+ Environment.SetEnvironmentVariable(EnvironmentVariables.DisableRequestTracing, "True");
var clientManager = TestHelpers.CreateMockedConfigurationClientManager(options);
var config = new ConfigurationBuilder()
@@ -296,7 +296,7 @@ public void TestTurnOffRequestTracing()
options.ClientOptions.Transport = mockTransport;
// Delete the request tracing environment variable
- Environment.SetEnvironmentVariable(RequestTracingConstants.RequestTracingDisabledEnvironmentVariable, null);
+ Environment.SetEnvironmentVariable(EnvironmentVariables.DisableRequestTracing, null);
Environment.SetEnvironmentVariable(RequestTracingConstants.AzureFunctionEnvironmentVariable, "v1.0");
var clientManager1 = TestHelpers.CreateMockedConfigurationClientManager(options);