5
Get-AzureRmVM -ResourceGroupName RG-VNETS | 
    ForEach-Object { 
        Get-AzureRmVM -ResourceGroupName RG-VNETS -Name $_.Name -Status 
    } | 
    ForEach-Object { 
        if (-Not ($_.Statuses[1].DisplayStatus -like "*deallocated*")) { 
            Stop-AzureRmVM -ResourceGroupName RG-VNETS -Name $_.Name -Force 
        } 
    }

I've got this script that stops all my Azure VMs, the catch here is that this script shuts down one VM at a time.

i.e. if I have three VMs: VM1, VM2, VM3

The script doesn't shut down VM2 until VM1 is fully shutdown and so on. I don't know if there's a way to tell PowerShell not to wait for each VM to be fully shutdown to proceed with the following one.

DAXaholic
  • 33,312
  • 6
  • 76
  • 74
miticoluis
  • 511
  • 2
  • 15
  • 32

2 Answers2

3

There is already a feature request on GitHub for doing such operations asynchronously which should be implemented in the near future.

In the meantime you could do a workaround like the following using the PoshRSJob module - just replace temp4so with your resource group name

# Install PoshRSJob if necessary
#
# Install-Module PoshRSJob

Login-AzureRmAccount    

$start = Get-Date
$jobs = Get-AzureRmVM -ResourceGroupName temp4so | 
    % { 
        Get-AzureRmVM -ResourceGroupName temp4so -Name $_.Name -Status 
    } | 
    % { 
        if (-Not ($_.Statuses[1].DisplayStatus -like "*deallocated*")) { 
            $vm = $_
            Start-RSJob {               
                Stop-AzureRmVM -ResourceGroupName temp4so -Name ($using:vm).Name -Force
            }
        }
    }
$jobs | Wait-RSJob | Receive-RSJob
$jobs | Remove-RSJob
$end = Get-Date
Write-Host ("Stopping took {0}" -f ($end - $start))

which in my test case with 3 VMs resulted in output similar to the following which shows that the operations where done in parallel

OperationId : 
Status      : Succeeded
StartTime   : 24.09.2016 18:49:10
EndTime     : 24.09.2016 18:51:32
Error       : 

OperationId : 
Status      : Succeeded
StartTime   : 24.09.2016 18:49:11
EndTime     : 24.09.2016 18:51:22
Error       : 

OperationId : 
Status      : Succeeded
StartTime   : 24.09.2016 18:49:11
EndTime     : 24.09.2016 18:51:22
Error       : 

Stopping took 00:02:32.9115538

Note: You cannot simply use the standard Start-Job to offload the sync. operations to a background job as the newly created PowerShell instances in the background do not share the context with your initial session and therefore would require you to authenticate again for each of those sessions. As PoshRSJob uses PowerShell runspaces within the initial PowerShell instance it does not require to authenticate again.

DAXaholic
  • 33,312
  • 6
  • 76
  • 74
  • Thanks a lot, it does exactly what I needed. I've just tested it and it works – miticoluis Sep 25 '16 at 14:25
  • This script only stops first 5 VMs in my case, no matter how many times I run it. Anyway to shutdown all VMs? – WinBoss Sep 03 '17 at 13:57
  • @WinBoss, There are two reasons (that I can see) for why you're only seeing some of your VMs shutdown. 1. Do you have VMs that are in different ResourceGroups? This script only targets one resource group at a time. 2. Are you using a classic deployments vs resource manager deployments? If so, the example scripts are using RM modules. Just switch them over to the classic modules which are Get-AzureVM, Stop-AzureVM and etc. More on this here: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-deployment-model – GrayDwarf Jan 12 '18 at 00:59
  • Yeah, I'm using multiple Resource Groups with RM model. Any way to stop all VMs in all Resource Groups? – WinBoss Jan 12 '18 at 05:34
3

They implemented -AsJob per the GitHub feature request DAXaholic linked. Here's an example taken from the GitHub comments section.

$VMList = Get-AzureRmVM -ResourceGroupName $resourceGroupName
$JobList = @()
foreach ($vm in $VMList) {
  $JobList += Stop-AzureRmVm -ResourceGroupName $resourceGroupName -Name $vm -Force -AsJob | Add-Member -MemberType NoteProperty -Name VMName -Value $vm.Name -PassThru
}

and if you want to wait for the jobs to finish with cleanup:

$JobList | Wait-Job | Receive-Job
$JobList | Remove-Job
GrayDwarf
  • 2,469
  • 2
  • 20
  • 22