diff --git a/src/Aspire.Hosting.Azure.Kusto/AzureKustoBuilderExtensions.cs b/src/Aspire.Hosting.Azure.Kusto/AzureKustoBuilderExtensions.cs
index 02ccb63f5de..2776afad215 100644
--- a/src/Aspire.Hosting.Azure.Kusto/AzureKustoBuilderExtensions.cs
+++ b/src/Aspire.Hosting.Azure.Kusto/AzureKustoBuilderExtensions.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
#pragma warning disable AZPROVISION001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+#pragma warning disable ASPIREINTERACTION001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
@@ -11,7 +12,9 @@
using Kusto.Data;
using Kusto.Data.Common;
using Kusto.Data.Net.Client;
+using Kusto.Data.Utils;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
namespace Aspire.Hosting;
@@ -36,7 +39,7 @@ public static class AzureKustoBuilderExtensions
///
///
/// By default references to the Azure Data Explorer database resources will be assigned the following roles:
- ///
+ ///
/// -
///
///
@@ -88,6 +91,7 @@ public static IResourceBuilder AddAzureKustoCluster(t
var resourceBuilder = builder.AddResource(resource);
AddKustoHealthChecksAndLifecycleManagement(resourceBuilder);
+ AddKustoCustomCommands(resourceBuilder);
return resourceBuilder
.WithAnnotation(new DefaultRoleAssignmentsAnnotation(new HashSet()));
@@ -307,4 +311,100 @@ private static KustoConnectionStringBuilder GetConnectionStringBuilder(AzureKust
return builder;
}
+
+ private static void AddKustoCustomCommands(IResourceBuilder resourceBuilder)
+ {
+ resourceBuilder.WithCommand(
+ name: "open-kusto-explorer-desktop",
+ displayName: "Open in Kusto Explorer (Desktop)",
+ executeCommand: context => OnOpenInKustoExplorerDesktop(resourceBuilder, context),
+ commandOptions: new CommandOptions
+ {
+ UpdateState = UpdateStateDesktop,
+ IconName = "DatabaseSearch"
+ });
+
+ resourceBuilder.WithCommand(
+ name: "open-kusto-explorer-web",
+ displayName: "Open in Kusto Explorer (Web)",
+ executeCommand: context => OnOpenInKustoExplorerWeb(resourceBuilder, context),
+ commandOptions: new CommandOptions
+ {
+ UpdateState = context => UpdateStateWeb(resourceBuilder, context),
+ IconName = "DatabaseSearch"
+ });
+
+ static ResourceCommandState UpdateStateDesktop(UpdateCommandStateContext context)
+ {
+ // The Desktop Kusto.Explorer is only available on Windows, so don't show the command on other platforms.
+ if (!OperatingSystem.IsWindows())
+ {
+ return ResourceCommandState.Hidden;
+ }
+
+ return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy ? ResourceCommandState.Enabled : ResourceCommandState.Disabled;
+ }
+
+ static ResourceCommandState UpdateStateWeb(IResourceBuilder resourceBuilder, UpdateCommandStateContext context)
+ {
+ // The web explorer will only auto-connect to allowed domains, so do not show the command when running as an emulator.
+ if (resourceBuilder.Resource.IsEmulator)
+ {
+ return ResourceCommandState.Hidden;
+ }
+
+ return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy ? ResourceCommandState.Enabled : ResourceCommandState.Disabled;
+ }
+
+ static async Task OnOpenInKustoExplorerDesktop(IResourceBuilder resourceBuilder, ExecuteCommandContext context)
+ {
+ var connectionString = await resourceBuilder
+ .Resource
+ .ConnectionStringExpression
+ .GetValueAsync(context.CancellationToken)
+ .ConfigureAwait(false) ??
+ throw new DistributedApplicationException($"Connection string for Kusto resource '{resourceBuilder.Resource.Name}' is not set.");
+
+ var launcher = new KustoClientToolLauncher();
+ var result = launcher.TryLaunchKustoExplorer(title: "", resourceBuilder.Resource.Name, connectionString, requestText: "");
+
+ return result ? CommandResults.Success() : CommandResults.Failure("Failed to launch Kusto Explorer");
+ }
+
+ static async Task OnOpenInKustoExplorerWeb(IResourceBuilder resourceBuilder, ExecuteCommandContext context)
+ {
+ var connectionString = await resourceBuilder
+ .Resource
+ .ConnectionStringExpression
+ .GetValueAsync(context.CancellationToken)
+ .ConfigureAwait(false) ??
+ throw new DistributedApplicationException($"Connection string for Kusto resource '{resourceBuilder.Resource.Name}' is not set.");
+
+ var launcher = new KustoClientToolLauncher();
+ var result = launcher.TryLaunchKustoWebExplorer(title: "", resourceBuilder.Resource.Name, connectionString, requestText: "");
+
+ if (!result)
+ {
+ // If the launcher fails (which may mean we're in a remote session or can't detect a browser),
+ // show a notification with a clickable link to the Kusto Web Explorer
+ var interactionService = context.ServiceProvider.GetRequiredService();
+ if (interactionService.IsAvailable)
+ {
+ _ = await interactionService.PromptMessageBoxAsync(
+ title: "Kusto Web Explorer",
+ message: $"Could not automatically open Kusto Web Explorer for resource '{resourceBuilder.Resource.Name}'. Click [{connectionString}]({connectionString}) to manually open the Web Explorer.",
+ new MessageBoxInteractionOptions
+ {
+ Intent = MessageIntent.Information,
+ EnableMessageMarkdown = true,
+ PrimaryButtonText = "Dismiss",
+ ShowSecondaryButton = false,
+ },
+ context.CancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ return CommandResults.Success();
+ }
+ }
}