Edit 2017: With the change to AzureRM this approach need some tweaking, see this new GIST for how to allow AzureRM to execute commands in Powershell jobs. The script itself is used to remove deployment history for a resource group but can easily be edited to work on VMs.
So I’ve got a demo that I’ve put together that uses a LOAD of Azure VM’s to demonstrate scripting against them.
To make sure I don’t get charged for them while I’m not using them I’ve got the following powershell to start and stop them:
Get-AzureVM | Stop-AzureVM –Force
Get-AzureVM | Start-AzureVM
I notice that this took a longer than I was expecting and so I started to do some digging. It turn out the PowerShell pipeline is executing these API requests in a synchronous way. Firing off the first request, waiting for the response and then, when completed, moving on to the next. This wouldn’t be a problem if I didn’t have loads of VM’s!
So I wrote the following, it uses PowerShell Jobs (think Task<T> if you’re a C# person), to kick these off as background jobs that run in parallel. (nb. You have to pass the variables in as arguments to the script block)
$vms = Get-azurevm | ?{ $_.Status -eq "StoppedDeallocated"} | |
$jobs = @() | |
foreach ($vm in $vms) | |
{ | |
$params = @($vm.Name, $vm.ServiceName) | |
$job = Start-Job -ScriptBlock { | |
param($ComputerName, $ServiceName) | |
start-Azurevm -Name $ComputerName -ServiceName $ServiceName | |
} -ArgumentList $params | |
$jobs = $jobs + $job | |
} | |
# Wait for it all to complete | |
Wait-Job -Job $jobs | |
# Getting the information back from the jobs | |
Get-Job | Receive-Job |
Now we get this lovely output. These all run as individual jobs and complete asynchronously, we then wait for all of them (think WaitAll() for C#). Now I can start all of my machines in the demo really quickly!
This is really useful in other scenarios too, think of any time you’re waiting on azure management API and the actions could be done simultaneously.
Id Name PSJobTypeName State HasMoreData Location Command
— —- ————- —– ———– ——– ——-
22 Job22 BackgroundJob Running True localhost …
24 Job24 BackgroundJob Running True localhost …
26 Job26 BackgroundJob Running True localhost …
28 Job28 BackgroundJob Running True localhost …
30 Job30 BackgroundJob Running True localhost …
RunspaceId : 88465362-c8ce-45de-866d-9ced85cc3e40
OperationDescription : Start-AzureVM
OperationId : 60712d20-311b-6cd5-b0df-9a750f024d0c
OperationStatus : Succeeded
RunspaceId : 3a9e93cf-7d16-4a2c-aeb7-4493e8ec8abb
OperationDescription : Start-AzureVM
OperationId : fb494c0c-3728-62d4-b288-b8d391ad32d7
OperationStatus : Succeeded
RunspaceId : c1144e74-ba7c-4aa3-bcbb-aa9f2bd72fdf
OperationDescription : Start-AzureVM
OperationId : 194254da-be36-6c7d-a147-67a980816c8e
OperationStatus : Succeeded
RunspaceId : 44af74a2-fc11-4e9b-8158-d9d84dd49d9e
OperationDescription : Start-AzureVM
OperationId : 86059d5d-22ee-6a55-871d-28bcf52241a2
OperationStatus : Succeeded
RunspaceId : a44b51e9-11cc-4d31-875e-5e0fd16b935a
OperationDescription : Start-AzureVM
OperationId : d4e2fcce-b270-6d6e-ad0c-8322e2c4df23
OperationStatus : Succeeded
Just a tip: concurrent stop/start vm in the same cloud service does not work, because that operation requires an exclusive access to the cloud service resources, and you will end up with just one job running, and the others in Completed state, returning the error “An exception occurred when calling the ServiceManagement API. HTTP Status Code: 409. Service Management Error Code: ConflictError. Message: Windows Azure is currently
performing an operation with x-ms-requestid xxxxxxxxx on this deployment that requires exclusive access…”
In this case I just run a command like this:
Start-Job -ScriptBlock { Get-AzureVM | ?{ $_.Status -eq “StoppedDeallocated” } | Start-AzureVM }
and to stop:
Start-Job -ScriptBlock { Get-AzureVM | ?{ $_.Status -eq “StoppedDeallocated” } | Stop-AzureVM -Force }
this will run in background, but will do a sequential bootstrap, avoiding the conflict error, and of course is much slower than the other approach, but it could be changed to run this per cloud service, so at least per cloud service could be fired out a new job, and parallelize per cloud service.
Thanks, awesome tip! Will update the gist when I have a chance.
Pingback: End of Month Research Roundup – June 2014 | endjin blog
Pingback: Les cmdlets PowerShell pour Azure | Nouvelles Chroniques d'Amethyste
great post thx !!!