From d1c56a24dcffd5cfbd19b3af48f8449b7983b8f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:39:51 +0000 Subject: [PATCH 1/2] Initial plan From 053c05d1949006a7e10703b59d82712eeffde4d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:06:52 +0000 Subject: [PATCH 2/2] Add deprecation warning when deploying VM/VMSS with deprecated marketplace images Co-authored-by: audreyttt <225061541+audreyttt@users.noreply.github.com> Agent-Logs-Url: https://github.com/Azure/azure-powershell/sessions/afb992b3-fe48-4f15-93e9-461fc35a9b92 --- .../StrategiesVirtualMachineTests.cs | 7 +++ .../StrategiesVirtualMachineTests.ps1 | 37 ++++++++++++ .../ScenarioTests/StrategiesVmssTests.cs | 7 +++ .../ScenarioTests/StrategiesVmssTests.ps1 | 38 ++++++++++++ src/Compute/Compute/ChangeLog.md | 3 + ...tualMachineScaleSetCreateOrUpdateMethod.cs | 24 ++++++++ ...tualMachineScaleSetCreateOrUpdateMethod.cs | 31 ++++++++++ .../Strategies/ComputeRp/ImageAndOsType.cs | 5 +- .../Compute/Strategies/ComputeRp/ImageEx.cs | 3 +- .../Operation/NewAzureVMCommand.cs | 59 +++++++++++++++++++ 10 files changed, 212 insertions(+), 2 deletions(-) diff --git a/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.cs b/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.cs index b3864ab3f5ce..215bd19f50ab 100644 --- a/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.cs +++ b/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.cs @@ -190,5 +190,12 @@ public void TestSimpleGalleryCrossTenant() { TestRunner.RunTestScript("Test-SimpleGalleryCrossTenant"); } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void TestSimpleNewVmDeprecatedImageWarning() + { + TestRunner.RunTestScript("Test-SimpleNewVmDeprecatedImageWarning"); + } } } diff --git a/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.ps1 b/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.ps1 index 7fd5c438db88..d0718f9cc0f2 100644 --- a/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.ps1 +++ b/src/Compute/Compute.Test/ScenarioTests/StrategiesVirtualMachineTests.ps1 @@ -695,3 +695,40 @@ function Test-SimpleGalleryCrossTenant Clean-ResourceGroup $vmname } } + + +<# +.SYNOPSIS +Test that creating a VM with a deprecated marketplace image emits a deprecation warning +and still successfully creates the VM. +#> +function Test-SimpleNewVmDeprecatedImageWarning +{ + # Setup + $vmname = Get-ResourceName + + try + { + $username = "admin01" + $password = Get-PasswordForVM | ConvertTo-SecureString -AsPlainText -Force + $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password + [string]$domainNameLabel = "$vmname-$vmname".tolower(); + $stnd = "Standard"; + + # Use a specific image version format (publisher:offer:sku:version) to exercise the + # deprecation check code path. The image used here should be a deprecated image + # to verify that a warning is emitted but VM creation proceeds successfully. + # Note: Replace the image below with a known deprecated image when recording this test. + $image = "MicrosoftWindowsServer:WindowsServer:2022-datacenter:latest" + + $x = New-AzVM -Name $vmname -Credential $cred -DomainNameLabel $domainNameLabel -Image $image -SecurityType $stnd + + Assert-AreEqual $vmname $x.Name; + Assert-NotNull $x.Id; + } + finally + { + # Cleanup + Clean-ResourceGroup $vmname + } +} diff --git a/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.cs b/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.cs index dcda2e463a44..0ccc8483cf9b 100644 --- a/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.cs +++ b/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.cs @@ -137,5 +137,12 @@ public void SimpleNewVmssSkipExtOverprovision() { TestRunner.RunTestScript("Test-SimpleNewVmssSkipExtOverprovision"); } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void TestSimpleNewVmssDeprecatedImageWarning() + { + TestRunner.RunTestScript("Test-SimpleNewVmssDeprecatedImageWarning"); + } } } diff --git a/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.ps1 b/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.ps1 index 459adf55c85a..2c77beee0e1d 100644 --- a/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.ps1 +++ b/src/Compute/Compute.Test/ScenarioTests/StrategiesVmssTests.ps1 @@ -551,3 +551,41 @@ function Test-SimpleNewVmssSkipExtOverprovision Clean-ResourceGroup $vmssname } } + +<# +.SYNOPSIS +Test that creating a VMSS with a deprecated marketplace image emits a deprecation warning +and still successfully creates the VMSS. +#> +function Test-SimpleNewVmssDeprecatedImageWarning +{ + # Setup + $vmssname = Get-ResourceName + + try + { + $lbName = $vmssname + "LoadBalancer" + $username = "admin01" + $password = Get-PasswordForVM | ConvertTo-SecureString -AsPlainText -Force + $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password + [string]$domainNameLabel = "$vmssname$vmssname".tolower(); + $stnd = "Standard"; + + # Use a specific image version format (publisher:offer:sku:version) to exercise the + # deprecation check code path. The image used here should be a deprecated image + # to verify that a warning is emitted but VMSS creation proceeds successfully. + # Note: Replace the image below with a known deprecated image when recording this test. + $image = "MicrosoftWindowsServer:WindowsServer:2022-datacenter:latest" + + $x = New-AzVmss -Name $vmssname -Credential $cred -DomainNameLabel $domainNameLabel -LoadBalancerName $lbName -ImageName $image -SecurityType $stnd + + Assert-AreEqual $vmssname $x.Name; + Assert-AreEqual $vmssname $x.ResourceGroupName; + Assert-NotNull $x.Id; + } + finally + { + # Cleanup + Clean-ResourceGroup $vmssname + } +} diff --git a/src/Compute/Compute/ChangeLog.md b/src/Compute/Compute/ChangeLog.md index 14e253d71429..dae93238dafa 100644 --- a/src/Compute/Compute/ChangeLog.md +++ b/src/Compute/Compute/ChangeLog.md @@ -20,6 +20,9 @@ --> ## Upcoming Release +* Added deprecation warning for `New-AzVM` and `New-AzVmss` when the specified marketplace image is deprecated or scheduled for deprecation + - A warning is emitted when the image state is 'Deprecated' or 'ScheduledForDeprecation' + - VM and VMSS creation proceeds normally after the warning ## Version 11.4.0 * Added `-DiskIOPSReadWrite` and `-DiskMBpsReadWrite` parameters to `Add-AzVMDataDisk` cmdlet diff --git a/src/Compute/Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs b/src/Compute/Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs index e4dca274e627..f79bddb96cbb 100644 --- a/src/Compute/Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs +++ b/src/Compute/Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs @@ -342,6 +342,7 @@ private VirtualMachineScaleSet BuildVirtualMachineScaleSetParameters() ComputeAutomationAutoMapperProfile.Mapper.Map(this.VirtualMachineScaleSet, parameters); CheckImageVersionWarning(parameters); + CheckAndWarnMarketplaceImageDeprecation(); SetDefaultOrchestrationMode(parameters); ConfigureFlexibleOrchestrationMode(parameters); ConfigureSecuritySettings(parameters); @@ -349,6 +350,29 @@ private VirtualMachineScaleSet BuildVirtualMachineScaleSetParameters() return parameters; } + private void CheckAndWarnMarketplaceImageDeprecation() + { + var imageRef = this.VirtualMachineScaleSet?.VirtualMachineProfile?.StorageProfile?.ImageReference; + if (imageRef == null + || string.IsNullOrEmpty(imageRef.Publisher) + || string.IsNullOrEmpty(imageRef.Offer) + || string.IsNullOrEmpty(imageRef.Sku) + || string.IsNullOrEmpty(imageRef.Version)) + { + return; + } + + try + { + var imgResponse = retrieveSpecificImageFromNotId(); + WarnIfImageDeprecated(imgResponse?.Body?.ImageDeprecationStatus); + } + catch + { + // Ignore errors during deprecation check to not block VMSS creation + } + } + private void CheckImageVersionWarning(VirtualMachineScaleSet parameters) { if (parameters?.VirtualMachineProfile?.StorageProfile?.ImageReference?.Version?.ToLower() != ImageVersions.Latest) diff --git a/src/Compute/Compute/Manual/VirtualMachineScaleSetCreateOrUpdateMethod.cs b/src/Compute/Compute/Manual/VirtualMachineScaleSetCreateOrUpdateMethod.cs index 13da3d8188ad..d3b47a2e3363 100644 --- a/src/Compute/Compute/Manual/VirtualMachineScaleSetCreateOrUpdateMethod.cs +++ b/src/Compute/Compute/Manual/VirtualMachineScaleSetCreateOrUpdateMethod.cs @@ -432,6 +432,7 @@ private async Task> SimpleParameterSetNor ImageAndOsType = await _client.UpdateImageAndOsTypeAsync( ImageAndOsType, _cmdlet.ResourceGroupName, _cmdlet.ImageName, Location); + _cmdlet.WarnIfImageDeprecated(ImageAndOsType); // generate a domain name label if it's not specified. _cmdlet.DomainNameLabel = await PublicIPAddressStrategy.UpdateDomainNameLabelAsync( @@ -604,6 +605,8 @@ private async Task> SimpleParameterSetOrc ImageAndOsType = await _client.UpdateImageAndOsTypeAsync( ImageAndOsType, _cmdlet.ResourceGroupName, _cmdlet.ImageName, Location); + _cmdlet.WarnIfImageDeprecated(ImageAndOsType); + // generate a domain name label if it's not specified. _cmdlet.DomainNameLabel = await PublicIPAddressStrategy.UpdateDomainNameLabelAsync( domainNameLabel: _cmdlet.DomainNameLabel, @@ -886,5 +889,33 @@ private VirtualMachineScaleSetIdentity GetVmssIdentityFromArgs() } : null; } + + /// + /// Emits a warning if the provided ImageAndOsType indicates the image is deprecated or scheduled for deprecation. + /// + internal void WarnIfImageDeprecated(ImageAndOsType imageAndOsType) + { + WarnIfImageDeprecated(imageAndOsType?.ImageDeprecationStatus); + } + + /// + /// Emits a warning if the provided ImageDeprecationStatus indicates the image is deprecated or scheduled for deprecation. + /// + internal void WarnIfImageDeprecated(ImageDeprecationStatus deprecationStatus) + { + if (deprecationStatus == null) + { + return; + } + + if (string.Equals(deprecationStatus.ImageState, ImageState.Deprecated, StringComparison.OrdinalIgnoreCase)) + { + WriteWarning("This image is deprecated. VM created from this image might not be supported and VM creation might be blocked in the near future. Please select the latest image that is supported instead."); + } + else if (string.Equals(deprecationStatus.ImageState, ImageState.ScheduledForDeprecation, StringComparison.OrdinalIgnoreCase)) + { + WriteWarning("This image is scheduled for deprecation. VM created from this image might not be supported in the near future. Please consider selecting the latest supported image instead."); + } + } } } \ No newline at end of file diff --git a/src/Compute/Compute/Strategies/ComputeRp/ImageAndOsType.cs b/src/Compute/Compute/Strategies/ComputeRp/ImageAndOsType.cs index f7f5fe807135..168e0254715b 100644 --- a/src/Compute/Compute/Strategies/ComputeRp/ImageAndOsType.cs +++ b/src/Compute/Compute/Strategies/ComputeRp/ImageAndOsType.cs @@ -25,11 +25,14 @@ sealed class ImageAndOsType public IList DataDiskLuns { get; } - public ImageAndOsType(OperatingSystemTypes osType, ImageReference image, IList dataDiskLuns) + public ImageDeprecationStatus ImageDeprecationStatus { get; } + + public ImageAndOsType(OperatingSystemTypes osType, ImageReference image, IList dataDiskLuns, ImageDeprecationStatus imageDeprecationStatus = null) { OsType = osType; Image = image; DataDiskLuns = dataDiskLuns; + ImageDeprecationStatus = imageDeprecationStatus; } } } diff --git a/src/Compute/Compute/Strategies/ComputeRp/ImageEx.cs b/src/Compute/Compute/Strategies/ComputeRp/ImageEx.cs index 98c536880ad4..ccec3329e063 100644 --- a/src/Compute/Compute/Strategies/ComputeRp/ImageEx.cs +++ b/src/Compute/Compute/Strategies/ComputeRp/ImageEx.cs @@ -86,7 +86,8 @@ public static async Task UpdateImageAndOsTypeAsync( return new ImageAndOsType( imageModel.OsDiskImage.OperatingSystem, image, - imageModel.DataDiskImages.GetLuns()); + imageModel.DataDiskImages.GetLuns(), + imageModel.ImageDeprecationStatus); } else if (imageName.Contains("/")) { diff --git a/src/Compute/Compute/VirtualMachine/Operation/NewAzureVMCommand.cs b/src/Compute/Compute/VirtualMachine/Operation/NewAzureVMCommand.cs index aaf8fb3c1e08..8f2cacf82078 100644 --- a/src/Compute/Compute/VirtualMachine/Operation/NewAzureVMCommand.cs +++ b/src/Compute/Compute/VirtualMachine/Operation/NewAzureVMCommand.cs @@ -585,6 +585,7 @@ public async Task> CreateConfigAsync() { ImageAndOsType = await _client.UpdateImageAndOsTypeAsync( ImageAndOsType, _cmdlet.ResourceGroupName, _cmdlet.Image, Location); + _cmdlet.WarnIfImageDeprecated(ImageAndOsType); } _cmdlet.DomainNameLabel = await PublicIPAddressStrategy.UpdateDomainNameLabelAsync( @@ -968,6 +969,9 @@ public void DefaultExecuteCmdlet() SetDefaultUefiSettings(); } + // Check if the marketplace image is deprecated and warn the user + CheckAndWarnMarketplaceImageDeprecation(); + if (ShouldProcess(this.VM.Name, VerbsCommon.New)) { ExecuteClientAction(() => @@ -1751,5 +1755,60 @@ private void ValidateVmParameters() ValidateSshKeyConfiguration(); ValidateEncryptionIdentityConfiguration(); } + + /// + /// Emits a warning if the provided ImageAndOsType indicates the image is deprecated or scheduled for deprecation. + /// + internal void WarnIfImageDeprecated(ImageAndOsType imageAndOsType) + { + WarnIfImageDeprecated(imageAndOsType?.ImageDeprecationStatus); + } + + /// + /// Emits a warning if the provided ImageDeprecationStatus indicates the image is deprecated or scheduled for deprecation. + /// + internal void WarnIfImageDeprecated(ImageDeprecationStatus deprecationStatus) + { + if (deprecationStatus == null) + { + return; + } + + if (string.Equals(deprecationStatus.ImageState, ImageState.Deprecated, StringComparison.OrdinalIgnoreCase)) + { + WriteWarning("This image is deprecated. VM created from this image might not be supported and VM creation might be blocked in the near future. Please select the latest image that is supported instead."); + } + else if (string.Equals(deprecationStatus.ImageState, ImageState.ScheduledForDeprecation, StringComparison.OrdinalIgnoreCase)) + { + WriteWarning("This image is scheduled for deprecation. VM created from this image might not be supported in the near future. Please consider selecting the latest supported image instead."); + } + } + + /// + /// Checks the marketplace image reference for deprecation status and emits a warning if deprecated. + /// Used for the DefaultParameterSet where the image is specified via VM.StorageProfile.ImageReference. + /// + private void CheckAndWarnMarketplaceImageDeprecation() + { + var imageRef = this.VM?.StorageProfile?.ImageReference; + if (imageRef == null + || string.IsNullOrEmpty(imageRef.Publisher) + || string.IsNullOrEmpty(imageRef.Offer) + || string.IsNullOrEmpty(imageRef.Sku) + || string.IsNullOrEmpty(imageRef.Version)) + { + return; + } + + try + { + var imgResponse = retrieveSpecificImageFromNotId(); + WarnIfImageDeprecated(imgResponse?.Body?.ImageDeprecationStatus); + } + catch + { + // Ignore errors during deprecation check to not block VM creation + } + } } } \ No newline at end of file