diff --git a/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureAuthValidator.cs b/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureAuthValidator.cs index eef2e29..6c252b0 100644 --- a/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureAuthValidator.cs +++ b/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureAuthValidator.cs @@ -3,6 +3,7 @@ using System.Text.Json; using Microsoft.Extensions.Logging; +using Microsoft.Agents.A365.DevTools.Cli.Services.Helpers; namespace Microsoft.Agents.A365.DevTools.Cli.Services; @@ -47,8 +48,9 @@ public async Task ValidateAuthenticationAsync(string? expectedSubscription return false; } - // Parse the account information - var accountJson = JsonDocument.Parse(result.StandardOutput); + // Clean and parse the account information + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(result.StandardOutput); + var accountJson = JsonDocument.Parse(cleanedOutput); var root = accountJson.RootElement; var subscriptionId = root.GetProperty("id").GetString() ?? string.Empty; diff --git a/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureCliService.cs b/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureCliService.cs index fa5e473..4651c16 100644 --- a/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureCliService.cs +++ b/src/Microsoft.Agents.A365.DevTools.Cli/Services/AzureCliService.cs @@ -4,6 +4,7 @@ using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Agents.A365.DevTools.Cli.Models; +using Microsoft.Agents.A365.DevTools.Cli.Services.Helpers; namespace Microsoft.Agents.A365.DevTools.Cli.Services; @@ -51,7 +52,15 @@ public async Task IsLoggedInAsync() return null; } - var accountJson = JsonSerializer.Deserialize(result.StandardOutput); + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(result.StandardOutput); + + if (string.IsNullOrWhiteSpace(cleanedOutput)) + { + _logger.LogError("Azure CLI returned empty output"); + return null; + } + + var accountJson = JsonSerializer.Deserialize(cleanedOutput); return new AzureAccountInfo { @@ -89,7 +98,13 @@ public async Task> ListResourceGroupsAsync() return new List(); } - var resourceGroupsJson = JsonSerializer.Deserialize(result.StandardOutput); + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(result.StandardOutput); + if (string.IsNullOrWhiteSpace(cleanedOutput)) + { + return new List(); + } + + var resourceGroupsJson = JsonSerializer.Deserialize(cleanedOutput); return resourceGroupsJson?.Select(rg => new AzureResourceGroup { @@ -120,7 +135,13 @@ public async Task> ListAppServicePlansAsync() return new List(); } - var plansJson = JsonSerializer.Deserialize(result.StandardOutput); + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(result.StandardOutput); + if (string.IsNullOrWhiteSpace(cleanedOutput)) + { + return new List(); + } + + var plansJson = JsonSerializer.Deserialize(cleanedOutput); return plansJson?.Select(plan => new AzureAppServicePlan { @@ -153,7 +174,13 @@ public async Task> ListLocationsAsync() return new List(); } - var locationsJson = JsonSerializer.Deserialize(result.StandardOutput); + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(result.StandardOutput); + if (string.IsNullOrWhiteSpace(cleanedOutput)) + { + return new List(); + } + + var locationsJson = JsonSerializer.Deserialize(cleanedOutput); return locationsJson?.Select(loc => new AzureLocation { @@ -170,4 +197,4 @@ public async Task> ListLocationsAsync() return new List(); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Agents.A365.DevTools.Cli/Services/BotConfigurator.cs b/src/Microsoft.Agents.A365.DevTools.Cli/Services/BotConfigurator.cs index 1989eff..a2b098b 100644 --- a/src/Microsoft.Agents.A365.DevTools.Cli/Services/BotConfigurator.cs +++ b/src/Microsoft.Agents.A365.DevTools.Cli/Services/BotConfigurator.cs @@ -63,7 +63,8 @@ public async Task CreateEndpointWithAgentBlueprintAsync( return false; } - var subscriptionInfo = JsonSerializer.Deserialize(subscriptionResult.StandardOutput); + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(subscriptionResult.StandardOutput); + var subscriptionInfo = JsonSerializer.Deserialize(cleanedOutput); var tenantId = subscriptionInfo.GetProperty("tenantId").GetString(); if (string.IsNullOrEmpty(tenantId)) @@ -181,7 +182,8 @@ public async Task DeleteEndpointWithAgentBlueprintAsync( return false; } - var subscriptionInfo = JsonSerializer.Deserialize(subscriptionResult.StandardOutput); + var cleanedOutput = JsonDeserializationHelper.CleanAzureCliJsonOutput(subscriptionResult.StandardOutput); + var subscriptionInfo = JsonSerializer.Deserialize(cleanedOutput); var tenantId = subscriptionInfo.GetProperty("tenantId").GetString(); if (string.IsNullOrEmpty(tenantId)) diff --git a/src/Microsoft.Agents.A365.DevTools.Cli/Services/Helpers/JsonDeserializationHelper.cs b/src/Microsoft.Agents.A365.DevTools.Cli/Services/Helpers/JsonDeserializationHelper.cs index e685799..fb712df 100644 --- a/src/Microsoft.Agents.A365.DevTools.Cli/Services/Helpers/JsonDeserializationHelper.cs +++ b/src/Microsoft.Agents.A365.DevTools.Cli/Services/Helpers/JsonDeserializationHelper.cs @@ -63,4 +63,42 @@ public static class JsonDeserializationHelper return null; } } + + /// + /// Cleans JSON output from Azure CLI by removing control characters and non-JSON content. + /// Azure CLI on Windows can output control characters (like 0x0C - form feed) and warning messages + /// that need to be stripped before JSON parsing. + /// + /// The raw output from Azure CLI + /// Cleaned JSON string ready for parsing + public static string CleanAzureCliJsonOutput(string output) + { + if (string.IsNullOrWhiteSpace(output)) + { + return string.Empty; + } + + // Remove control characters (0x00-0x1F except \r, \n, \t) + // These characters can appear in Azure CLI output on Windows + var cleaned = new System.Text.StringBuilder(output.Length); + foreach (char c in output) + { + if (c >= 32 || c == '\n' || c == '\r' || c == '\t') + { + cleaned.Append(c); + } + } + + var result = cleaned.ToString().Trim(); + + // Find the first { or [ to locate JSON start + // This handles cases where Azure CLI outputs warnings or other text before the JSON + int jsonStart = result.IndexOfAny(new[] { '{', '[' }); + if (jsonStart > 0) + { + result = result.Substring(jsonStart); + } + + return result; + } }