Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changes:
- section: "Breaking Changes"
description: "Removed unused parameters from Azure Backup tools."
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@

using System.Net;
using Azure.Mcp.Core.Commands.Subscription;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Backup;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.AzureBackup.Commands.Backup;

Expand All @@ -31,44 +29,23 @@ and the Azure region (location) where the resource exists.
ReadOnly = true,
Secret = false,
LocalRequired = false)]
public sealed class BackupStatusCommand(ILogger<BackupStatusCommand> logger, IAzureBackupService azureBackupService) : SubscriptionCommand<BackupStatusOptions>()
public sealed class BackupStatusCommand(ILogger<BackupStatusCommand> logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
: SubscriptionCommand<BackupStatusOptions, BackupStatusCommand.BackupStatusCommandResult>(subscriptionResolver)
{
private readonly ILogger<BackupStatusCommand> _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;

protected override void RegisterOptions(Command command)
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, BackupStatusOptions options, CancellationToken cancellationToken)
{
base.RegisterOptions(command);
command.Options.Add(AzureBackupOptionDefinitions.DatasourceId.AsRequired());
command.Options.Add(AzureBackupOptionDefinitions.Location.AsRequired());
}

protected override BackupStatusOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.DatasourceId = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.DatasourceId.Name);
options.Location = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.Location.Name);
return options;
}

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, "status-check");

try
{
var result = await _azureBackupService.GetBackupStatusAsync(
options.DatasourceId!,
options.DatasourceId,
options.Subscription!,
options.Location!,
options.Location,
options.Tenant,
options.RetryPolicy,
cancellationToken);
Expand Down Expand Up @@ -97,5 +74,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};

internal record BackupStatusCommandResult(BackupStatusResult Status);
public sealed record BackupStatusCommandResult(BackupStatusResult Status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,24 @@

using System.Diagnostics.CodeAnalysis;
using Azure.Mcp.Core.Commands.Subscription;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Options;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.AzureBackup.Commands;

public abstract class BaseAzureBackupCommand<
[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] T>
: SubscriptionCommand<T>
where T : BaseAzureBackupOptions, new()
public abstract class BaseAzureBackupCommand<[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions, TResult>(ISubscriptionResolver subscriptionResolver)
: SubscriptionCommand<TOptions, TResult>(subscriptionResolver) where TOptions : BaseAzureBackupOptions
{
protected override void RegisterOptions(Command command)
public override void ValidateOptions(TOptions options, ValidationResult validationResult)
{
base.RegisterOptions(command);
command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired());
command.Options.Add(AzureBackupOptionDefinitions.Vault.AsRequired());
command.Options.Add(AzureBackupOptionDefinitions.VaultType);
command.Validators.Add(commandResult =>
{
if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.VaultType.Name))
{
var value = commandResult.GetValue<string>(AzureBackupOptionDefinitions.VaultType.Name);
if (!string.IsNullOrEmpty(value) &&
!value.Equals("rsv", StringComparison.OrdinalIgnoreCase) &&
!value.Equals("dpp", StringComparison.OrdinalIgnoreCase))
{
commandResult.AddError("--vault-type must be 'rsv' (Recovery Services vault) or 'dpp' (Backup vault).");
}
}
});
}
base.ValidateOptions(options, validationResult);

protected override T BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.ResourceGroup ??= parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.ResourceGroup.Name);
options.Vault = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.Vault.Name);
options.VaultType = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.VaultType.Name);
return options;
if (!string.IsNullOrEmpty(options.VaultType) &&
!options.VaultType.Equals("rsv", StringComparison.OrdinalIgnoreCase) &&
!options.VaultType.Equals("dpp", StringComparison.OrdinalIgnoreCase))
{
validationResult.Errors.Add("--vault-type must be 'rsv' (Recovery Services vault) or 'dpp' (Backup vault).");
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
// Licensed under the MIT License.

using System.Net;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.AzureBackup.Commands.DisasterRecovery;

Expand All @@ -24,28 +23,22 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.DisasterRecovery;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
public sealed class DisasterRecoveryEnableCrrCommand(ILogger<DisasterRecoveryEnableCrrCommand> logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand<BaseAzureBackupOptions>()
public sealed class DisasterRecoveryEnableCrrCommand(ILogger<DisasterRecoveryEnableCrrCommand> logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
: BaseAzureBackupCommand<BaseAzureBackupOptions, DisasterRecoveryEnableCrrCommand.DisasterRecoveryEnableCrrCommandResult>(subscriptionResolver)
{
private readonly ILogger<DisasterRecoveryEnableCrrCommand> _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, BaseAzureBackupOptions options, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);

try
{
var result = await _azureBackupService.ConfigureCrossRegionRestoreAsync(
options.Vault!,
options.ResourceGroup!,
options.Vault,
options.ResourceGroup,
options.Subscription!,
options.VaultType,
options.Tenant,
Expand Down Expand Up @@ -81,5 +74,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};

internal record DisasterRecoveryEnableCrrCommandResult(OperationResult Result);
public sealed record DisasterRecoveryEnableCrrCommandResult(OperationResult Result);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
// Licensed under the MIT License.

using Azure.Mcp.Core.Commands.Subscription;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Governance;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;

Expand All @@ -28,37 +26,14 @@ Scans the subscription to find Azure resources that are not currently protected
ReadOnly = true,
Secret = false,
LocalRequired = false)]
public sealed class GovernanceFindUnprotectedCommand(ILogger<GovernanceFindUnprotectedCommand> logger, IAzureBackupService azureBackupService) : SubscriptionCommand<GovernanceFindUnprotectedOptions>()
public sealed class GovernanceFindUnprotectedCommand(ILogger<GovernanceFindUnprotectedCommand> logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
: SubscriptionCommand<GovernanceFindUnprotectedOptions, GovernanceFindUnprotectedCommand.GovernanceFindUnprotectedCommandResult>(subscriptionResolver)
{
private readonly ILogger<GovernanceFindUnprotectedCommand> _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;

protected override void RegisterOptions(Command command)
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, GovernanceFindUnprotectedOptions options, CancellationToken cancellationToken)
{
base.RegisterOptions(command);
command.Options.Add(AzureBackupOptionDefinitions.ResourceTypeFilter);
command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsOptional());
command.Options.Add(AzureBackupOptionDefinitions.TagFilter);
}

protected override GovernanceFindUnprotectedOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.ResourceTypeFilter = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.ResourceTypeFilter.Name);
options.ResourceGroup = parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.ResourceGroup.Name);
options.TagFilter = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.TagFilter.Name);
return options;
}

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, "scan");

Expand Down Expand Up @@ -86,5 +61,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
return context.Response;
}

internal record GovernanceFindUnprotectedCommandResult(List<UnprotectedResourceInfo> Resources);
public sealed record GovernanceFindUnprotectedCommandResult(List<UnprotectedResourceInfo> Resources);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
// Licensed under the MIT License.

using System.Net;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Governance;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;

Expand All @@ -28,47 +26,27 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
public sealed class GovernanceImmutabilityCommand(ILogger<GovernanceImmutabilityCommand> logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand<GovernanceImmutabilityOptions>()
public sealed class GovernanceImmutabilityCommand(ILogger<GovernanceImmutabilityCommand> logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
: BaseAzureBackupCommand<GovernanceImmutabilityOptions, GovernanceImmutabilityCommand.GovernanceImmutabilityCommandResult>(subscriptionResolver)
{
private readonly ILogger<GovernanceImmutabilityCommand> _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;

protected override void RegisterOptions(Command command)
public override void ValidateOptions(GovernanceImmutabilityOptions options, ValidationResult validationResult)
{
base.RegisterOptions(command);
command.Options.Add(AzureBackupOptionDefinitions.ImmutabilityState.AsRequired());
command.Validators.Add(commandResult =>
{
if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.ImmutabilityState.Name))
{
var value = commandResult.GetValue<string>(AzureBackupOptionDefinitions.ImmutabilityState.Name);
if (!string.IsNullOrEmpty(value) &&
!value.Equals("Disabled", StringComparison.OrdinalIgnoreCase) &&
!value.Equals("Enabled", StringComparison.OrdinalIgnoreCase) &&
!value.Equals("Locked", StringComparison.OrdinalIgnoreCase))
{
commandResult.AddError("--immutability-state must be 'Disabled', 'Enabled', or 'Locked'. Warning: 'Locked' is irreversible.");
}
}
});
}

protected override GovernanceImmutabilityOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.ImmutabilityState = parseResult.GetValueOrDefault<string>(AzureBackupOptionDefinitions.ImmutabilityState.Name);
return options;
}
base.ValidateOptions(options, validationResult);

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
if (!string.IsNullOrEmpty(options.ImmutabilityState) &&
!options.ImmutabilityState.Equals("Disabled", StringComparison.OrdinalIgnoreCase) &&
!options.ImmutabilityState.Equals("Enabled", StringComparison.OrdinalIgnoreCase) &&
!options.ImmutabilityState.Equals("Locked", StringComparison.OrdinalIgnoreCase))
{
return context.Response;
validationResult.Errors.Add("--immutability-state must be 'Disabled', 'Enabled', or 'Locked'. Warning: 'Locked' is irreversible.");
}
}

var options = BindOptions(parseResult);

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, GovernanceImmutabilityOptions options, CancellationToken cancellationToken)
{
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);

Expand Down Expand Up @@ -109,5 +87,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};

internal record GovernanceImmutabilityCommandResult(OperationResult Result);
public sealed record GovernanceImmutabilityCommandResult(OperationResult Result);
}
Loading