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
2 changes: 1 addition & 1 deletion src/Compute/Compute/Az.Compute.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ PrivateData = @{
* Added ''-AddProxyAgentExtension'' parameter (Bool) to ''Set-AzVMProxyAgentSetting'' and ''Set-AzVmssProxyAgentSetting'''

# Prerelease string of this module
# Prerelease = ''
Prerelease = 'preview'

# Flag to indicate whether the module requires explicit user acceptance for install/update/save
# RequireLicenseAcceptance = $false
Expand Down
1 change: 1 addition & 0 deletions src/Compute/Compute/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

-->
## Upcoming Release
* Supported `-DryRun` and `-ExportBicep`
* Improved user experience and consistency. This may introduce breaking changes. Please refer to [here](https://go.microsoft.com/fwlink/?linkid=2340249).
* Updated Azure.Core from 1.45.0 to 1.47.3
* Added `-EnableAutomaticUpgrade` and `-TreatFailureAsDeploymentFailure` parameters (Bool) to `New-AzVmGalleryApplication` and `New-AzVmssGalleryApplication` cmdlets.
Expand Down
753 changes: 753 additions & 0 deletions src/Compute/Compute/Common/ComputeClientBaseCmdlet.cs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/Compute/Compute/Compute.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
<PackageReference Include="System.Management" Version="4.5.0" />
<!-- Include the following DLLs for security issue -->
<PackageReference Include="System.Security.Cryptography.Xml" Version="4.7.1" />
<PackageReference Include="Azure.Identity" Version="1.11.4" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary? I thought azure identity will be brought in by az.accounts?

</ItemGroup>

<!-- Include shared WhatIf library -->
<ItemGroup>
<Compile Include="..\..\shared\WhatIf\**\*.cs" LinkBase="Shared\WhatIf" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using CM = Microsoft.Azure.Commands.Compute.Models;
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.Azure.Management.Compute.Models;
using System.Text.RegularExpressions;

namespace Microsoft.Azure.Commands.Compute
{
Expand Down Expand Up @@ -154,67 +155,43 @@ public class AddAzureVMDataDiskCommand : ComputeClientBaseCmdlet

public override void ExecuteCmdlet()
{
if (this.ParameterSetName.Equals(VmNormalDiskParameterSet))
// DryRun interception: build preview only, do not mutate the provided VM object
if (this.DryRun.IsPresent && TryHandleDryRun(BuildDryRunPreviewScript()))
{
var storageProfile = this.VM.StorageProfile;

if (storageProfile == null)
{
storageProfile = new StorageProfile();
}
return;
}

if (storageProfile.DataDisks == null)
{
storageProfile.DataDisks = new List<DataDisk>();
}
if (this.ParameterSetName.Equals(VmNormalDiskParameterSet))
{
var storageProfile = this.VM.StorageProfile ?? new StorageProfile();
storageProfile.DataDisks = storageProfile.DataDisks ?? new List<DataDisk>();

storageProfile.DataDisks.Add(new DataDisk
{
Name = this.Name,
Caching = this.Caching,
DiskSizeGB = this.DiskSizeInGB,
Lun = this.Lun.GetValueOrDefault(),
Vhd = string.IsNullOrEmpty(this.VhdUri) ? null : new VirtualHardDisk
{
Uri = this.VhdUri
},
Vhd = string.IsNullOrEmpty(this.VhdUri) ? null : new VirtualHardDisk { Uri = this.VhdUri },
CreateOption = this.CreateOption,
Image = string.IsNullOrEmpty(this.SourceImageUri) ? null : new VirtualHardDisk
{
Uri = this.SourceImageUri
},
Image = string.IsNullOrEmpty(this.SourceImageUri) ? null : new VirtualHardDisk { Uri = this.SourceImageUri },
DeleteOption = this.DeleteOption,
SourceResource = string.IsNullOrEmpty(this.SourceResourceId) ? null : new ApiEntityReference
{
Id = this.SourceResourceId
}
SourceResource = string.IsNullOrEmpty(this.SourceResourceId) ? null : new ApiEntityReference { Id = this.SourceResourceId }
});

this.VM.StorageProfile = storageProfile;

WriteObject(this.VM);
}
else
else // Managed disk parameter set
{
if (!string.IsNullOrEmpty(this.Name) && !string.IsNullOrEmpty(this.ManagedDiskId))
if (!string.IsNullOrEmpty(this.Name) && !string.IsNullOrEmpty(this.ManagedDiskId) &&
!this.Name.Equals(GetDiskNameFromId(this.ManagedDiskId)))
{
if (!this.Name.Equals(GetDiskNameFromId(this.ManagedDiskId)))
{
ThrowInvalidArgumentError("Disk name, {0}, does not match with given managed disk ID", this.Name);
}
ThrowInvalidArgumentError("Disk name, {0}, does not match with given managed disk ID", this.Name);
}

var storageProfile = this.VM.StorageProfile;

if (storageProfile == null)
{
storageProfile = new StorageProfile();
}

if (storageProfile.DataDisks == null)
{
storageProfile.DataDisks = new List<DataDisk>();
}
var storageProfile = this.VM.StorageProfile ?? new StorageProfile();
storageProfile.DataDisks = storageProfile.DataDisks ?? new List<DataDisk>();

storageProfile.DataDisks.Add(new DataDisk
{
Expand All @@ -226,16 +203,61 @@ public override void ExecuteCmdlet()
ManagedDisk = SetManagedDisk(this.ManagedDiskId, this.DiskEncryptionSetId, this.StorageAccountType),
WriteAcceleratorEnabled = this.WriteAccelerator.IsPresent,
DeleteOption = this.DeleteOption,
SourceResource = string.IsNullOrEmpty(this.SourceResourceId) ? null : new ApiEntityReference
{
Id = this.SourceResourceId
}
SourceResource = string.IsNullOrEmpty(this.SourceResourceId) ? null : new ApiEntityReference { Id = this.SourceResourceId }
});

this.VM.StorageProfile = storageProfile;

WriteObject(this.VM);
}
}

private string BuildDryRunPreviewScript()
{
try
{
string vmVar = GetVmVariableNameFromInvocation(); // Variable name used in -VM parameter
string previewVar = vmVar; // Construct preview variable name

var lines = new List<string>();
lines.Add("# DryRun preview: resulting " + vmVar + " (simplified) after Add-AzVMDataDisk would execute");

string vmName = VM?.Name ?? "<null>";
string rgName = VM?.ResourceGroupName ?? "<null>";
string vmId = VM?.Id ?? "<null>";

// Build preview object
lines.Add(previewVar + " = [PSCustomObject]@{");
lines.Add(" Name='" + vmName + "'");
lines.Add(" ResourceGroupName='" + rgName + "'");
lines.Add(" Id='" + vmId + "'");
lines.Add("}" );
lines.Add("# You can inspect $" + previewVar + " to see the projected state.");
lines.Add("# Original invocation follows:");

return string.Join(Environment.NewLine, lines);
}
catch
{
return null;
}
}

private string GetVmVariableNameFromInvocation()
{
try
{
var line = this.MyInvocation?.Line;
if (!string.IsNullOrWhiteSpace(line))
{
var m = Regex.Match(line, @"-VM\s*[:=]?\s*(\$[a-zA-Z_][a-zA-Z0-9_]*)");
if (m.Success)
{
return m.Groups[1].Value;
}
}
}
catch { }
return "$vm"; // default fallback
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
using System.Management.Automation;
using Microsoft.Azure.Commands.Compute.Common;
using Microsoft.Azure.Commands.Compute.Models;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Microsoft.Azure.Commands.Compute
{
[Cmdlet("Remove", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "VMDataDisk",SupportsShouldProcess = true),OutputType(typeof(PSVirtualMachine))]
public class RemoveAzureVMDataDiskCommand : Microsoft.Azure.Commands.ResourceManager.Common.AzureRMCmdlet
public class RemoveAzureVMDataDiskCommand : ComputeClientBaseCmdlet
{
[Alias("VMProfile")]
[Parameter(
Expand Down Expand Up @@ -51,49 +53,141 @@ public class RemoveAzureVMDataDiskCommand : Microsoft.Azure.Commands.ResourceMan

public override void ExecuteCmdlet()
{
// DryRun interception (preview only, no mutation of the live VM object)
if (this.DryRun.IsPresent && TryHandleDryRun(BuildDryRunPreviewScript()))
{
return;
}

if (this.ShouldProcess("DataDisk", VerbsCommon.Remove))
{
var storageProfile = this.VM.StorageProfile;
ApplyRemoval();
}
}

if (storageProfile != null && storageProfile.DataDisks != null)
{
var disks = storageProfile.DataDisks.ToList();
var comp = StringComparison.OrdinalIgnoreCase;
private void ApplyRemoval()
{
var storageProfile = this.VM.StorageProfile;

if (this.ForceDetach != true) {
if (DataDiskNames == null){
disks.Clear();
}
else{
foreach (var diskName in DataDiskNames){
disks.RemoveAll(d => string.Equals(d.Name, diskName, comp));
}
if (storageProfile != null && storageProfile.DataDisks != null)
{
var disks = storageProfile.DataDisks.ToList();
var comp = StringComparison.OrdinalIgnoreCase;

if (!this.ForceDetach.IsPresent)
{
if (DataDiskNames == null || DataDiskNames.Length == 0)
{
disks.Clear();
}
else
{
foreach (var diskName in DataDiskNames)
{
disks.RemoveAll(d => string.Equals(d.Name, diskName, comp));
}
}
else{
if (this.DataDiskNames == null){
foreach (var disk in disks){
disk.DetachOption = "ForceDetach";
disk.ToBeDetached = true;
}
}
else
{
if (this.DataDiskNames == null || DataDiskNames.Length == 0)
{
foreach (var disk in disks)
{
disk.DetachOption = "ForceDetach";
disk.ToBeDetached = true;
}
else
}
else
{
foreach (var disk in disks)
{
foreach (var disk in disks){
if (DataDiskNames.Contains(disk.Name)){
disk.ToBeDetached = true;
disk.DetachOption = "ForceDetach";
}
if (DataDiskNames.Contains(disk.Name, StringComparer.OrdinalIgnoreCase))
{
disk.ToBeDetached = true;
disk.DetachOption = "ForceDetach";
}
}
}

storageProfile.DataDisks = disks;
}
this.VM.StorageProfile = storageProfile;

WriteObject(this.VM);
storageProfile.DataDisks = disks;
}
this.VM.StorageProfile = storageProfile;

WriteObject(this.VM);
}

private string BuildDryRunPreviewScript()
{
try
{
string vmVar = GetVmVariableNameFromInvocation(); // Variable name used by user in -VM parameter (e.g. $myvm)
string previewVar = vmVar; // Name of the constructed preview object variable

var lines = new List<string>();
lines.Add("# DryRun preview: resulting " + vmVar + " (simplified) after Remove-AzVMDataDisk would execute");

string vmName = VM?.Name ?? "<null>";
string rgName = VM?.ResourceGroupName ?? "<null>";
string vmId = VM?.Id ?? "<null>";

// Build preview PowerShell object
lines.Add(previewVar + " = [PSCustomObject]@{");
lines.Add(" Name='" + vmName + "'");
lines.Add(" ResourceGroupName='" + rgName + "'");
lines.Add(" Id='" + vmId + "'");
lines.Add("}" );
lines.Add("# You can inspect" + previewVar + " to see the projected state.");
lines.Add("# Original invocation follows:");

return string.Join(Environment.NewLine, lines);
}
catch
{
return null;
}
}

private string GetVmVariableNameFromInvocation()
{
try
{
var line = this.MyInvocation?.Line;
if (!string.IsNullOrWhiteSpace(line))
{
// Match variations like: -VM $myvm OR -VM:$myvm OR -VM $myvm -OtherParam
var m = Regex.Match(line, @"-VM\s*[:=]?\s*(\$[a-zA-Z_][a-zA-Z0-9_]*)");
if (m.Success)
{
return m.Groups[1].Value;
}
}
}
catch { }
return "$vm"; // Fallback default if parsing fails
}

private static Microsoft.Azure.Management.Compute.Models.DataDisk CloneDisk(Microsoft.Azure.Management.Compute.Models.DataDisk d)
{
if (d == null) return null;
// Only copy fields needed for the preview
return new Microsoft.Azure.Management.Compute.Models.DataDisk
{
Name = d.Name,
Lun = d.Lun,
CreateOption = d.CreateOption,
DiskSizeGB = d.DiskSizeGB,
Caching = d.Caching,
Vhd = d.Vhd,
Image = d.Image,
ManagedDisk = d.ManagedDisk,
WriteAcceleratorEnabled = d.WriteAcceleratorEnabled,
ToBeDetached = d.ToBeDetached,
DetachOption = d.DetachOption,
DeleteOption = d.DeleteOption,
SourceResource = d.SourceResource
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,11 @@ public class NewAzureVMCommand : VirtualMachineBaseCmdlet

public override void ExecuteCmdlet()
{
// Handle DryRun early (before any real logic)
if (DryRun.IsPresent && TryHandleDryRun())
{
return;
}

switch (ParameterSetName)
{
Expand Down
Loading