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
Expand Up @@ -102,9 +102,10 @@ function New-AzStackHCIVMImage{

[Parameter(ParameterSetName='GalleryImage', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Body')]
[System.String]
# Local path of image that the image should be created from.
[System.Security.SecureString]
# Local path of image that the image should be created from (as SecureString).
# This parameter is required for non marketplace images.
# Use: ConvertTo-SecureString -String "path\to\image.vhdx" -AsPlainText -Force
${ImagePath},

[Parameter(ParameterSetName='GalleryImage', Mandatory)]
Expand Down Expand Up @@ -333,12 +334,14 @@ function New-AzStackHCIVMImage{
Start-Sleep -Seconds 5
if ($image.ProvisioningStatus -eq "Failed") {
Break
}
}
if (($PercentCompleted -ne 100) -and ($image.ProvisioningStatus -ne "Failed")) {
Start-Sleep -Seconds 5
}
Comment on lines 334 to +340
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

This introduces a double sleep in the common case (first unconditional 5s, then another 5s when not complete), which will slow down provisioning polling by ~2x. Consider sleeping only once per loop iteration (e.g., sleep at the end when still in progress), and rely on a single, consistent delay.

Copilot uses AI. Check for mistakes.
}
if ($image.ProvisioningStatus -eq "Failed"){
Write-Error $image.StatusErrorMessage -ErrorAction Stop
}

} catch {
$e = $_
if ($e.FullyQualifiedErrorId -match "MissingAzureKubernetesMapping" ){
Expand Down Expand Up @@ -416,6 +419,7 @@ function New-AzStackHCIVMImage{
if ($PSCmdlet.ParameterSetName -eq "GalleryImage")
{
try{
# ImagePath is already a SecureString, no conversion needed
Az.StackHCIVM.internal\New-AzStackHCIVMGalleryImage -ErrorAction Stop @PSBoundParameters
} catch {
$e = $_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ function New-AzStackHCIVMVirtualMachine {

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Body')]
[System.String]
# AdminPassword - admin password
[System.Security.SecureString]
# AdminPassword - admin password (as SecureString)
${AdminPassword},

[Parameter()]
Expand Down Expand Up @@ -657,6 +657,7 @@ function New-AzStackHCIVMVirtualMachine {
if ($ComputerName){
$PSBoundParameters.Add('ComputerName', $ComputerName)
}
# Handle credentials - either from Credential parameter or direct AdminUsername/AdminPassword
if ($AdminUsername){
$PSBoundParameters.Add('AdminUsername', $AdminUsername)
}
Expand All @@ -675,7 +676,7 @@ function New-AzStackHCIVMVirtualMachine {
if ($EnableTpm.IsPresent){
$PSBoundParameters.Add('EnableTpm', $EnableTpm)
}
if($SecureBootEnabled.IsPresent){
if ($SecureBootEnabled.IsPresent){
$PSBoundParameters.Add('SecureBootEnabled', $SecureBootEnabled)
}
$null = $PSBoundParameters.Remove("Name")
Expand All @@ -688,7 +689,6 @@ function New-AzStackHCIVMVirtualMachine {
$null = $PSBoundParameters.Remove("Location")
$null = $PSBoundParameters.Remove("OSType")
$null = $PSBoundParameters.Remove("IdentityType")

try{
Az.StackHCIVM.internal\New-AzStackHCIVMVirtualMachine -ErrorAction Stop @PSBoundParameters
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@

# ----------------------------------------------------------------------------------
#
# Copyright Microsoft Corporation
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------------

<#
.Synopsis
The operation to save a virtual machine instance.
.Description
The operation to save a virtual machine instance.

.Outputs
Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Models.IVirtualMachineInstance
.Notes
COMPLEX PARAMETER PROPERTIES


.Link
https://learn.microsoft.com/powershell/module/az.stackhcivm/Save-AzStackHCIVMVirtualMachine
#>
function Save-AzStackHCIVMVirtualMachine {
[OutputType([Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Models.IVirtualMachineInstance])]
[CmdletBinding( PositionalBinding=$false, SupportsShouldProcess, ConfirmImpact='Medium')]
Comment on lines +31 to +33
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

This PR adds/changes cmdlet surface area (new Save/Suspend cmdlets and parameter type changes). The module ChangeLog (src/StackHCIVM/StackHCIVM/ChangeLog.md) currently has an empty "Upcoming Release" section—please add an entry describing these changes (past tense) and regenerate markdown help so published docs reflect the new cmdlets/parameters.

Copilot uses AI. Check for mistakes.
param(

[Parameter(ParameterSetName='ByName', Mandatory)]
[Alias('VirtualMachineName')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[System.String]
# Name of the virtual machine
${Name},

[Parameter(ParameterSetName='ByName', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[System.String]
# The name of the resource group.
# The name is case insensitive.
${ResourceGroupName},

[Parameter(ParameterSetName='ByName', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Runtime.DefaultInfo(Script='(Get-AzContext).Subscription.Id')]
[System.String]
# The ID of the target subscription.
${SubscriptionId},

[Parameter(ParameterSetName='ByResourceId', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[System.String]
# The ARM Resource ID of the virtual machine.
${ResourceId},

[Parameter()]
[Alias('AzureRMContext', 'AzureCredential')]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Azure')]
[System.Management.Automation.PSObject]
# The credentials, account, tenant, and subscription used for communication with Azure.
${DefaultProfile},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Run the command as a job
${AsJob},

[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Wait for .NET debugger to attach
${Break},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Runtime.SendAsyncStep[]]
# SendAsync Pipeline Steps to be appended to the front of the pipeline
${HttpPipelineAppend},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Runtime.SendAsyncStep[]]
# SendAsync Pipeline Steps to be prepended to the front of the pipeline
${HttpPipelinePrepend},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Run the command asynchronously
${NoWait},

[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Uri]
# The URI for the proxy server to use
${Proxy},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.PSCredential]
# Credentials for a proxy server to use for the remote call
${ProxyCredential},

[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Use the default credentials for the proxy
${ProxyUseDefaultCredentials}

)
if (($resourceId -match $vmRegex) -or ($name -and $resourceGroupName -and $subscriptionId)){
if ($resourceId -match $vmRegex){
$subscriptionId = $($Matches['subscriptionId'])
$resourceGroupName = $($Matches['resourceGroupName'])
$name = $($Matches['machineName'])
}
$resourceUri = "/subscriptions/" + $subscriptionId + "/resourceGroups/" + $resourceGroupName + "/providers/Microsoft.HybridCompute/machines/" + $name
$PSBoundParameters.Add("ResourceUri", $resourceUri)
$null = $PSBoundParameters.Remove("SubscriptionId")
$null = $PSBoundParameters.Remove("ResourceGroupName")
$null = $PSBoundParameters.Remove("ResourceId")
$null = $PSBoundParameters.Remove("Name")

if ($PSCmdlet.ShouldProcess($resourceUri, "Save virtual machine instance")) {
try{
Az.StackHCIVM.internal\Save-AzStackHCIVMVirtualMachine -ErrorAction Stop @PSBoundParameters
} catch {
$e = $_
if ($e.FullyQualifiedErrorId -match "MissingAzureKubernetesMapping" ){
Write-Error "An older version of the Arc VM cluster extension is installed on your cluster. Please downgrade the Az.StackHCIVm version to 1.0.1 to proceed." -ErrorAction Stop
} else {
Write-Error -ErrorRecord $e -ErrorAction Stop
}
}
}

} else {
Write-Error "One or more input parameters are invalid. Resource ID is: $resourceId, name is $name, resource group name is $resourceGroupName, subscription id is $subscriptionId" -ErrorAction Stop
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@

# ----------------------------------------------------------------------------------
#
# Copyright Microsoft Corporation
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------------

<#
.Synopsis
The operation to Suspend a virtual machine instance.
.Description
The operation to Suspend a virtual machine instance.

.Outputs
Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Models.IVirtualMachineInstance
.Notes
COMPLEX PARAMETER PROPERTIES


.Link
https://learn.microsoft.com/powershell/module/az.stackhcivm/Suspend-AzStackHCIVMVirtualMachine
#>
function Suspend-AzStackHCIVMVirtualMachine {
[OutputType([Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Models.IVirtualMachineInstance])]
[CmdletBinding( PositionalBinding=$false, SupportsShouldProcess, ConfirmImpact='Medium')]
param(

[Parameter(ParameterSetName='ByName', Mandatory)]
[Alias('VirtualMachineName')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[System.String]
# Name of the virtual machine
${Name},

[Parameter(ParameterSetName='ByName', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[System.String]
# The name of the resource group.
# The name is case insensitive.
${ResourceGroupName},

[Parameter(ParameterSetName='ByName', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Runtime.DefaultInfo(Script='(Get-AzContext).Subscription.Id')]
[System.String]
# The ID of the target subscription.
${SubscriptionId},

[Parameter(ParameterSetName='ByResourceId', Mandatory)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Path')]
[System.String]
# The ARM Resource ID of the virtual machine.
${ResourceId},

[Parameter()]
[Alias('AzureRMContext', 'AzureCredential')]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Azure')]
[System.Management.Automation.PSObject]
# The credentials, account, tenant, and subscription used for communication with Azure.
${DefaultProfile},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Run the command as a job
${AsJob},

[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Wait for .NET debugger to attach
${Break},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Runtime.SendAsyncStep[]]
# SendAsync Pipeline Steps to be appended to the front of the pipeline
${HttpPipelineAppend},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Runtime.SendAsyncStep[]]
# SendAsync Pipeline Steps to be prepended to the front of the pipeline
${HttpPipelinePrepend},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Run the command asynchronously
${NoWait},

[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Uri]
# The URI for the proxy server to use
${Proxy},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.PSCredential]
# Credentials for a proxy server to use for the remote call
${ProxyCredential},

[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.StackHCIVM.Category('Runtime')]
[System.Management.Automation.SwitchParameter]
# Use the default credentials for the proxy
${ProxyUseDefaultCredentials}

)
if (($ResourceId -match $vmRegex) -or ($name -and $resourceGroupName -and $subscriptionId)){
if ($ResourceId -match $vmRegex){
$subscriptionId = $($Matches['subscriptionId'])
$resourceGroupName = $($Matches['resourceGroupName'])
$name = $($Matches['machineName'])
}
$resourceUri = "/subscriptions/" + $subscriptionId + "/resourceGroups/" + $resourceGroupName + "/providers/Microsoft.HybridCompute/machines/" + $name
$PSBoundParameters.Add("ResourceUri", $resourceUri)
$null = $PSBoundParameters.Remove("SubscriptionId")
$null = $PSBoundParameters.Remove("ResourceGroupName")
$null = $PSBoundParameters.Remove("ResourceId")
$null = $PSBoundParameters.Remove("Name")

if ($PSCmdlet.ShouldProcess($resourceUri, "Suspend virtual machine")) {
try{
Az.StackHCIVM.internal\Suspend-AzStackHCIVMVirtualMachine -ErrorAction Stop @PSBoundParameters
} catch {
$e = $_
if ($e.FullyQualifiedErrorId -match "MissingAzureKubernetesMapping" ){
Write-Error "An older version of the Arc VM cluster extension is installed on your cluster. Please downgrade the Az.StackHCIVm version to 1.0.1 to proceed." -ErrorAction Stop
} else {
Write-Error -ErrorRecord $e -ErrorAction Stop
}
}
}

} else {
Write-Error "One or more input parameters are invalid. Resource ID is: $ResourceId, name is $name, resource group name is $resourceGroupName, subscription id is $subscriptionid" -ErrorAction Stop
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ if(($null -eq $TestName) -or ($TestName -contains 'AzStackHCIVMImageNew'))

Describe 'AzStackHCIVMImageNew' {
It 'Create Image ' {
New-AzStackHCIVMImage -Name $env.imageName -ImagePath $env.imagePath -SubscriptionId $env.newSubscriptionId -ResourceGroupName $env.newResourceGroupName -CustomLocationId $env.newCustomLocationId -Location $env.location -OSType $env.osTypeLinux | Select-Object -Property ProvisioningState | Should -BeExactly "@{ProvisioningState=Succeeded}"
$secureImagePath = ConvertTo-SecureString -String $env.imagePath -AsPlainText -Force
New-AzStackHCIVMImage -Name $env.imageName -ImagePath $secureImagePath -SubscriptionId $env.newSubscriptionId -ResourceGroupName $env.newResourceGroupName -CustomLocationId $env.newCustomLocationId -Location $env.location -OSType $env.osTypeLinux | Select-Object -Property ProvisioningState | Should -BeExactly "@{ProvisioningState=Succeeded}"
}
It 'Create MarketplaceImage'
{
Expand Down
Loading
Loading