Skip to content

Commit 1e340cc

Browse files
Josina20JosinaJoy
andauthored
Add cleanup for Endpoint only (#138)
* Add cleanup for Endpoint only * code review * remove duplicated code * revert * fix tests * fix comments --------- Co-authored-by: Josina Joy <josjoy@microsoft.com>
1 parent d9b1e1f commit 1e340cc

File tree

3 files changed

+536
-71
lines changed

3 files changed

+536
-71
lines changed

src/Microsoft.Agents.A365.DevTools.Cli/Commands/CleanupCommand.cs

Lines changed: 143 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,18 @@ private static Command CreateBlueprintCleanupCommand(
7171
new[] { "--verbose", "-v" },
7272
description: "Enable verbose logging");
7373

74+
var endpointOnlyOption = new Option<bool>(
75+
new[] { "--endpoint-only" },
76+
description: "Delete only the messaging endpoint, keep the blueprint application");
77+
7478
command.AddOption(configOption);
7579
command.AddOption(verboseOption);
80+
command.AddOption(endpointOnlyOption);
7681

77-
command.SetHandler(async (configFile, verbose) =>
82+
command.SetHandler(async (configFile, verbose, endpointOnly) =>
7883
{
7984
try
8085
{
81-
logger.LogInformation("Starting blueprint cleanup...");
82-
8386
var config = await LoadConfigAsync(configFile, logger, configService);
8487
if (config == null) return;
8588

@@ -89,8 +92,18 @@ private static Command CreateBlueprintCleanupCommand(
8992
agentBlueprintService.CustomClientAppId = config.ClientAppId;
9093
}
9194

95+
// If endpoint-only mode, only delete the messaging endpoint
96+
if (endpointOnly)
97+
{
98+
await ExecuteEndpointOnlyCleanupAsync(logger, config, botConfigurator);
99+
return;
100+
}
101+
102+
// Full blueprint cleanup (original behavior)
103+
logger.LogInformation("Starting blueprint cleanup...");
104+
92105
// Check if there's actually a blueprint to clean up
93-
if (string.IsNullOrEmpty(config.AgentBlueprintId))
106+
if (string.IsNullOrWhiteSpace(config.AgentBlueprintId))
94107
{
95108
logger.LogInformation("No blueprint application found to clean up");
96109
return;
@@ -127,27 +140,10 @@ private static Command CreateBlueprintCleanupCommand(
127140
// Blueprint deleted successfully
128141
logger.LogInformation("Agent blueprint application deleted successfully");
129142

130-
// Handle endpoint deletion if needed
131-
if (!string.IsNullOrEmpty(config.BotName))
143+
// Handle endpoint deletion if needed using shared helper
144+
if (!await DeleteMessagingEndpointAsync(logger, config, botConfigurator))
132145
{
133-
// Delete messaging endpoint
134-
logger.LogInformation("Deleting messaging endpoint registration...");
135-
var endpointName = EndpointHelper.GetEndpointName(config.BotName);
136-
137-
var endpointDeleted = await botConfigurator.DeleteEndpointWithAgentBlueprintAsync(
138-
endpointName,
139-
config.Location,
140-
config.AgentBlueprintId);
141-
142-
if (!endpointDeleted)
143-
{
144-
logger.LogWarning("Failed to delete blueprint messaging endpoint");
145-
return;
146-
}
147-
}
148-
else
149-
{
150-
logger.LogInformation("No blueprint messaging endpoint found in configuration");
146+
return;
151147
}
152148

153149
// Clear configuration after successful blueprint deletion
@@ -167,7 +163,7 @@ private static Command CreateBlueprintCleanupCommand(
167163
{
168164
logger.LogError(ex, "Blueprint cleanup failed");
169165
}
170-
}, configOption, verboseOption);
166+
}, configOption, verboseOption, endpointOnlyOption);
171167

172168
return command;
173169
}
@@ -207,7 +203,7 @@ private static Command CreateAzureCleanupCommand(
207203
logger.LogInformation("=========================");
208204
logger.LogInformation(" Web App: {WebAppName}", config.WebAppName);
209205
logger.LogInformation(" App Service Plan: {PlanName}", config.AppServicePlanName);
210-
if (!string.IsNullOrEmpty(config.BotId))
206+
if (!string.IsNullOrWhiteSpace(config.BotId))
211207
logger.LogInformation(" Azure Bot: {BotId}", config.BotId);
212208
logger.LogInformation(" Resource Group: {ResourceGroup}", config.ResourceGroup);
213209
logger.LogInformation("");
@@ -308,9 +304,9 @@ private static Command CreateInstanceCleanupCommand(
308304
logger.LogInformation("============================");
309305
logger.LogInformation("Will delete the following resources:");
310306

311-
if (!string.IsNullOrEmpty(config.AgenticAppId))
307+
if (!string.IsNullOrWhiteSpace(config.AgenticAppId))
312308
logger.LogInformation(" Agent Identity Application: {IdentityId}", config.AgenticAppId);
313-
if (!string.IsNullOrEmpty(config.AgenticUserId))
309+
if (!string.IsNullOrWhiteSpace(config.AgenticUserId))
314310
logger.LogInformation(" Agent User: {UserId}", config.AgenticUserId);
315311
logger.LogInformation(" Generated configuration file");
316312
logger.LogInformation("");
@@ -324,15 +320,15 @@ private static Command CreateInstanceCleanupCommand(
324320
}
325321

326322
// Delete agent identity application
327-
if (!string.IsNullOrEmpty(config.AgenticAppId))
323+
if (!string.IsNullOrWhiteSpace(config.AgenticAppId))
328324
{
329325
logger.LogInformation("Deleting agent identity application...");
330326
await executor.ExecuteAsync("az", $"ad app delete --id {config.AgenticAppId}", null, true, false, CancellationToken.None);
331327
logger.LogInformation("Agent identity application deleted");
332328
}
333329

334330
// Delete agent user
335-
if (!string.IsNullOrEmpty(config.AgenticUserId))
331+
if (!string.IsNullOrWhiteSpace(config.AgenticUserId))
336332
{
337333
logger.LogInformation("Deleting agent user...");
338334
await executor.ExecuteAsync("az", $"ad user delete --id {config.AgenticUserId}", null, true, false, CancellationToken.None);
@@ -420,19 +416,19 @@ private static async Task ExecuteAllCleanupAsync(
420416
logger.LogInformation("Complete Cleanup Preview:");
421417
logger.LogInformation("============================");
422418
logger.LogInformation("WARNING: ALL RESOURCES WILL BE DELETED:");
423-
if (!string.IsNullOrEmpty(config.AgentBlueprintId))
419+
if (!string.IsNullOrWhiteSpace(config.AgentBlueprintId))
424420
logger.LogInformation(" Blueprint Application: {BlueprintId}", config.AgentBlueprintId);
425-
if (!string.IsNullOrEmpty(config.AgenticAppId))
421+
if (!string.IsNullOrWhiteSpace(config.AgenticAppId))
426422
logger.LogInformation(" Agent Identity Application: {IdentityId}", config.AgenticAppId);
427-
if (!string.IsNullOrEmpty(config.AgenticUserId))
423+
if (!string.IsNullOrWhiteSpace(config.AgenticUserId))
428424
logger.LogInformation(" Agent User: {UserId}", config.AgenticUserId);
429-
if (!string.IsNullOrEmpty(config.WebAppName))
425+
if (!string.IsNullOrWhiteSpace(config.WebAppName))
430426
logger.LogInformation(" Web App: {WebAppName}", config.WebAppName);
431-
if (!string.IsNullOrEmpty(config.AppServicePlanName))
427+
if (!string.IsNullOrWhiteSpace(config.AppServicePlanName))
432428
logger.LogInformation(" App Service Plan: {PlanName}", config.AppServicePlanName);
433-
if (!string.IsNullOrEmpty(config.BotName))
429+
if (!string.IsNullOrWhiteSpace(config.BotName))
434430
logger.LogInformation(" Azure Messaging Endpoint: {BotName}", config.BotName);
435-
if (!string.IsNullOrEmpty(config.Location))
431+
if (!string.IsNullOrWhiteSpace(config.Location))
436432
logger.LogInformation(" Location: {Location}", config.Location);
437433
logger.LogInformation(" Generated configuration file");
438434
logger.LogInformation("");
@@ -452,7 +448,7 @@ private static async Task ExecuteAllCleanupAsync(
452448
logger.LogInformation("Starting complete cleanup...");
453449

454450
// 1. Delete agent blueprint application
455-
if (!string.IsNullOrEmpty(config.AgentBlueprintId))
451+
if (!string.IsNullOrWhiteSpace(config.AgentBlueprintId))
456452
{
457453
logger.LogInformation("Deleting agent blueprint application...");
458454
var deleted = await agentBlueprintService.DeleteAgentBlueprintAsync(
@@ -472,7 +468,7 @@ private static async Task ExecuteAllCleanupAsync(
472468
}
473469

474470
// 2. Delete agent identity application
475-
if (!string.IsNullOrEmpty(config.AgenticAppId))
471+
if (!string.IsNullOrWhiteSpace(config.AgenticAppId))
476472
{
477473
logger.LogInformation("Deleting agent identity application...");
478474

@@ -493,45 +489,25 @@ private static async Task ExecuteAllCleanupAsync(
493489
}
494490

495491
// 3. Delete agent user
496-
if (!string.IsNullOrEmpty(config.AgenticUserId))
492+
if (!string.IsNullOrWhiteSpace(config.AgenticUserId))
497493
{
498494
logger.LogInformation("Deleting agent user...");
499495
await executor.ExecuteAsync("az", $"ad user delete --id {config.AgenticUserId}", null, true, false, CancellationToken.None);
500496
logger.LogInformation("Agent user deleted");
501497
}
502498

503-
// 4. Delete bot messaging endpoint
504-
if (!string.IsNullOrEmpty(config.BotName))
499+
// 4. Delete bot messaging endpoint using shared helper
500+
if (!string.IsNullOrWhiteSpace(config.BotName))
505501
{
506-
logger.LogInformation("Deleting messaging endpoint registration...");
507-
if (string.IsNullOrEmpty(config.AgentBlueprintId))
502+
var endpointDeleted = await DeleteMessagingEndpointAsync(logger, config, botConfigurator);
503+
if (!endpointDeleted)
508504
{
509-
logger.LogError("Agent Blueprint ID not found. Agent Blueprint ID is required for deleting endpoint registration.");
510505
hasFailures = true;
511506
}
512-
else
513-
{
514-
var endpointName = EndpointHelper.GetEndpointName(config.BotName);
515-
516-
var endpointDeleted = await botConfigurator.DeleteEndpointWithAgentBlueprintAsync(
517-
endpointName,
518-
config.Location,
519-
config.AgentBlueprintId);
520-
521-
if (endpointDeleted)
522-
{
523-
logger.LogInformation("Messaging endpoint deleted successfully");
524-
}
525-
else
526-
{
527-
logger.LogWarning("Failed to delete messaging endpoint");
528-
hasFailures = true;
529-
}
530-
}
531507
}
532508

533509
// 5. Delete Azure resources (Web App and App Service Plan)
534-
if (!string.IsNullOrEmpty(config.WebAppName) && !string.IsNullOrEmpty(config.ResourceGroup))
510+
if (!string.IsNullOrWhiteSpace(config.WebAppName) && !string.IsNullOrWhiteSpace(config.ResourceGroup))
535511
{
536512
logger.LogInformation("Deleting Azure resources...");
537513

@@ -562,7 +538,7 @@ private static async Task ExecuteAllCleanupAsync(
562538
}
563539

564540
// Delete App Service Plan after web app is gone (with retry for conflicts)
565-
if (!string.IsNullOrEmpty(config.AppServicePlanName))
541+
if (!string.IsNullOrWhiteSpace(config.AppServicePlanName))
566542
{
567543
logger.LogInformation("Deleting App Service Plan: {PlanName}...", config.AppServicePlanName);
568544

@@ -663,6 +639,107 @@ private static async Task ExecuteAllCleanupAsync(
663639
}
664640
}
665641

642+
/// <summary>
643+
/// Shared helper method to delete a messaging endpoint.
644+
/// Validates configuration, gets endpoint name, and calls the bot configurator to delete.
645+
/// </summary>
646+
/// <param name="logger">Logger instance for diagnostic messages</param>
647+
/// <param name="config">Configuration containing endpoint and blueprint information</param>
648+
/// <param name="botConfigurator">Bot configurator service for endpoint operations</param>
649+
/// <returns>True if endpoint was deleted successfully; false otherwise</returns>
650+
private static async Task<bool> DeleteMessagingEndpointAsync(
651+
ILogger<CleanupCommand> logger,
652+
Agent365Config config,
653+
IBotConfigurator botConfigurator)
654+
{
655+
// Check if there's actually an endpoint to clean up
656+
if (string.IsNullOrWhiteSpace(config.BotName))
657+
{
658+
logger.LogInformation("No messaging endpoint found in configuration");
659+
return true; // No endpoint to delete = success
660+
}
661+
662+
// Check if blueprint ID exists (required for endpoint deletion)
663+
if (string.IsNullOrWhiteSpace(config.AgentBlueprintId))
664+
{
665+
logger.LogError("Agent Blueprint ID not found. Agent Blueprint ID is required for deleting endpoint registration.");
666+
return false;
667+
}
668+
669+
logger.LogInformation("Deleting messaging endpoint registration...");
670+
var endpointName = EndpointHelper.GetEndpointName(config.BotName);
671+
672+
var endpointDeleted = await botConfigurator.DeleteEndpointWithAgentBlueprintAsync(
673+
endpointName,
674+
config.Location,
675+
config.AgentBlueprintId);
676+
677+
if (endpointDeleted)
678+
{
679+
logger.LogInformation("Messaging endpoint deleted successfully");
680+
return true;
681+
}
682+
else
683+
{
684+
logger.LogWarning("Failed to delete messaging endpoint");
685+
return false;
686+
}
687+
}
688+
689+
/// <summary>
690+
/// Executes endpoint-only cleanup - deletes the messaging endpoint while preserving the blueprint application
691+
/// </summary>
692+
private static async Task ExecuteEndpointOnlyCleanupAsync(
693+
ILogger<CleanupCommand> logger,
694+
Agent365Config config,
695+
IBotConfigurator botConfigurator)
696+
{
697+
logger.LogInformation("Starting endpoint-only cleanup...");
698+
699+
// Check if there's actually an endpoint to clean up
700+
if (string.IsNullOrWhiteSpace(config.BotName))
701+
{
702+
logger.LogInformation("No messaging endpoint found to clean up");
703+
return;
704+
}
705+
706+
// Check if blueprint ID exists (required for endpoint deletion)
707+
if (string.IsNullOrWhiteSpace(config.AgentBlueprintId))
708+
{
709+
logger.LogError("Agent Blueprint ID not found. Blueprint ID is required for endpoint deletion.");
710+
logger.LogInformation("Please ensure blueprint is configured before attempting endpoint cleanup.");
711+
return;
712+
}
713+
714+
logger.LogInformation("");
715+
logger.LogInformation("Endpoint Cleanup Preview:");
716+
logger.LogInformation("============================");
717+
logger.LogInformation("Will delete messaging endpoint:");
718+
logger.LogInformation(" Endpoint Name: {BotName}", config.BotName);
719+
logger.LogInformation(" Location: {Location}", config.Location);
720+
logger.LogInformation("");
721+
722+
Console.Write("Continue with endpoint cleanup? (y/N): ");
723+
var response = Console.ReadLine()?.Trim().ToLowerInvariant();
724+
if (response != "y" && response != "yes")
725+
{
726+
logger.LogInformation("Cleanup cancelled by user");
727+
return;
728+
}
729+
730+
// Use shared helper to delete the endpoint
731+
var deleted = await DeleteMessagingEndpointAsync(logger, config, botConfigurator);
732+
733+
if (!deleted)
734+
{
735+
return;
736+
}
737+
738+
logger.LogInformation("");
739+
logger.LogInformation("Endpoint cleanup completed successfully!");
740+
logger.LogInformation("");
741+
}
742+
666743
private static async Task<Agent365Config?> LoadConfigAsync(
667744
FileInfo? configFile,
668745
ILogger<CleanupCommand> logger,

src/Microsoft.Agents.A365.DevTools.Cli/Commands/SetupSubcommands/BlueprintSubcommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ await RegisterEndpointAndSyncAsync(
174174
logger.LogError(ex, "Endpoint registration failed: {Message}", ex.Message);
175175
logger.LogError("");
176176
logger.LogError("To resolve this issue:");
177-
logger.LogError(" 1. If endpoint already exists, delete it: a365 cleanup azure");
177+
logger.LogError(" 1. If endpoint already exists, delete it: a365 cleanup blueprint --endpoint-only");
178178
logger.LogError(" 2. Verify your messaging endpoint configuration in a365.config.json");
179179
logger.LogError(" 3. Try registration again: a365 setup blueprint --endpoint-only");
180180
Environment.Exit(1);

0 commit comments

Comments
 (0)