Search code examples
c#azure-cliazure-app-configurationazure-feature-manager

When I am running Azure CLI cmds through a C# console, an error is encountered stating: "ERROR: Filter parameter value must be a JSON escaped string."


When attempting to run Azure CLI commands through a C# console application, an error is encountered stating: "ERROR: Filter parameter value must be a JSON escaped string." Below is the C# Console code:

using System;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class AzureCliCommandRunner
{
    public static string RunAzureCliCommand(string command)
    {
        var processStartInfo = new ProcessStartInfo
        {
            FileName = "C:\\Program Files\\Microsoft SDKs\\Azure\\CLI2\\wbin\\az.cmd",
            Arguments = command,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            CreateNoWindow = true
        };

        using (var process = new Process { StartInfo = processStartInfo })
        {
            process.Start();

            // Read the output and error streams
            string output = process.StandardOutput.ReadToEnd();
            string error = process.StandardError.ReadToEnd();

            process.WaitForExit();

            if (process.ExitCode == 0)
            {
                return output;
            }
            else
            {
                // Handle the error
                throw new InvalidOperationException($"Command execution failed. Error: {error}");
            }
        }
    }
    public static void UpdateAppConfigFilter(string endpoint, string featureName)
    {
        // Run Azure CLI commands to update AppConfig filter
        string command1 = $"appconfig feature filter show --connection-string {endpoint} --feature {featureName} --filter-name Microsoft.Targeting";
        string jsondata = RunAzureCliCommand(command1);
        JArray jsonArray = JArray.Parse(jsondata);

        JObject audience = (JObject)jsonArray[0]["parameters"]["Audience"];
        var name = "User5";
        // Add a new user to the "Users" array
        ((JArray)audience["Users"]).Add(name);

        // Convert the modified JArray back to a JSON string
        string modifiedJson = jsonArray.ToString();

        JArray jsonArray1 = JArray.Parse(modifiedJson);
        JObject firstObject = (JObject)jsonArray1.First;

        // Access the "Audience" part
        JObject audienceObject = (JObject)firstObject["parameters"]["Audience"];


        string audienceJsonString = JsonConvert.SerializeObject(audienceObject);

        //string command4 = $"appconfig feature filter update --connection-string '{endpoint}' --feature {featureName} --filter-name Microsoft.Targeting --filter-parameters 'Audience={audienceJsonString}' --yes";
        string command4 = $"appconfig feature filter update --connection-string '{endpoint}' --feature {featureName} --filter-name Microsoft.Targeting --filter-parameters 'Audience={audienceJsonString}' --yes";
        RunAzureCliCommand(command4);
    }
    public static void Main()
    {
        // Replace <<EndPoint>> and <<FeatureName>> with your actual values
        string endpoint = "<<ConnectionString here>>";
        string featureName = "featureA";

        UpdateAppConfigFilter(endpoint, featureName);
    }
}

I am encountering an error with the above code. enter image description here

Note : Copying the output of the Command4 string from QuickWatch and executing the same command in the Azure portal Bash environment produces the expected result. However, when using the same input in C#, it results in the mentioned error.

Command4 output: appconfig feature filter update --connection-string <> --feature featureA --filter-name Microsoft.Targeting --filter-parameters 'Audience={"DefaultRolloutPercentage":50,"Groups":[],"Users":["user1","user2","user3","user4","User5"]}' --yes


Solution

  • You can use the Azure.Data.AppConfiguration package to update the Azure App Configuration filter using .NET code.

    Attempting to enhance the dynamism of this code to retrieve existing Audience details, append new users, and subsequently update the configuration in the portal.

    Code:In HomeController.cs

    using FinalFeatureFlagConfig.Models;
    using Microsoft.AspNetCore.Mvc;
    using System.Diagnostics;
    using Azure.Data.AppConfiguration;
    using CliWrap;
    using CliWrap.Buffered;
    using Newtonsoft.Json.Linq;
    using Newtonsoft.Json;
    using System;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Configuration.AzureAppConfiguration;
    using System.Text.Json.Nodes;
    using System.Collections.Generic;
    
    namespace FinalFeatureFlagConfig.Controllers
    {
        public class HomeController : Controller
        {
            private readonly ILogger<HomeController> _logger;
    
            public HomeController(ILogger<HomeController> logger)
            {
                _logger = logger;
            }
            public IActionResult Index()
            {
                try
                {
                    string connectionString = "<<EndPoint Here>>";
                    string featureFlag = "featureA";
                    var filterName = "Microsoft.Targeting";
    
                    var client = new ConfigurationClient(connectionString);
                    var test = client.GetConfigurationSetting(".appconfig.featureflag/" + featureFlag);
    
                    JObject jsonObject = JObject.Parse(test.Value.Value); 
    
                    JObject audience = (JObject)jsonObject["conditions"]["client_filters"][0]["parameters"]["Audience"];
                    var name = "user6";
                    ((JArray)audience["Users"]).Add(name);
                    Audience res=audience.ToObject<Audience>();
    
                    KeyValuePair<string, object>[] keyValuePairs = new KeyValuePair<string, object>[]
                    {
                        new KeyValuePair<string, object>("DefaultRolloutPercentage", res.DefaultRolloutPercentage),
                        new KeyValuePair<string, object>("Users", res.Users),
                        new KeyValuePair<string, object>("Groups", res.Groups)
                    };
                    IDictionary<string, object> dictionary = new Dictionary<string, object>();
                    dictionary.Add("Audience", new Dictionary<string, object>());
                    foreach (var kvp in keyValuePairs)
                    {
                        ((Dictionary<string, object>)dictionary["Audience"]).Add(kvp.Key, kvp.Value);
                    }
                    var featureFlagSetting = new FeatureFlagConfigurationSetting(featureFlag, isEnabled: true);
                    var featureflagfilter = new FeatureFlagFilter(filterName, dictionary);
                    featureFlagSetting.ClientFilters.Add(featureflagfilter);
                    client.SetConfigurationSetting(featureFlagSetting);
    
                    return View();
                }
                catch (Exception ex)
                {
                    return Content($"Error: {ex.Message}");
                }
            }
    
            public IActionResult Privacy()
            {
                return View();
            }
    
            [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
            public IActionResult Error()
            {
                return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
            }
        }
    }
    

    Create one class in Models with name Audience.cs Code :

    namespace FinalFeatureFlagConfig.Models
    {
        public class Audience
        {
            public int DefaultRolloutPercentage { get; set; }
            public string[] Groups { get; set; }
            public string[] Users { get; set; }
        }
    }
    

    Output : enter image description here