In some cases VMs have to be started / stopped in a certain order. Often, certain services have to run before the next VM can start. It may be necessary to set waiting times between the start of two VMs.

Such scenarios can be implemented well with Azure Automation. Azure offers with the Start-Stop-Solution the possibility for different scenarios. Among other things, the start / stop is also possible in a specific order. The sequence is defined in the form of sequencestart and sequencestop tags. But a waiting time between the individual VMs cannot be defined. In addition, the actual sequence is no longer easy to see and difficult to maintain due to distribution over tags.

Therefore, these two scripts were created, based on Start-AzureV2VMs and Stop-AzureV2VMs. It accepts a sequence of VM names and waiting times in seconds as a comma-separated string. For example, the string VM1, VM2, 60, VM3, 30, VM4 starts VM1 and then VM2, then waiting 60 seconds, start VM3, waiting again 30 seconds and finally start VM4.

You can see in the Start-script:

<#PSScriptInfo
.VERSION 1.0
#>

#Requires -Module Az.Account
#Requires -Module Az.Compute

<#
.SYNOPSIS
  Connects to Azure and starts of all VMs in the specified Azure subscription or resource group on given weekdays

.DESCRIPTION
  Based on Start-AzureV2Vs (https://github.com/azureautomation/runbooks/blob/master/Utility/Start-AzureV2VMs.ps1).

.PARAMETER Sequence    
  A sequence of VM names of a resource group and waiting times in seconds. The informations are separated with commas.
  Example: vm1, vm2, 60, vm3
  This starts vm1, then vm2, then waiting 1 minute and afterward will vm3 be started.

#>

# Returns strings with status messages
[OutputType([String])]

param (
    [Parameter(Mandatory=$false)] 
    [String]  $AzureConnectionAssetName = "AzureRunAsConnection",

    [Parameter(Mandatory=$true)] 
    [String] $ResourceGroupName,
	
    [Parameter(Mandatory=$true)] 
    [String]  $Sequence
)

try {
    # Connect to Azure using service principal auth
    $ServicePrincipalConnection = Get-AutomationConnection -Name $AzureConnectionAssetName         

    Write-Output "Logging in to Azure..."

    $Null = Add-AzAccount `
        -ServicePrincipal `
        -TenantId $ServicePrincipalConnection.TenantId `
        -ApplicationId $ServicePrincipalConnection.ApplicationId `
        -CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint 
}
catch {
    if(!$ServicePrincipalConnection) {
        throw "Connection $AzureConnectionAssetName not found."
    }
    else {
        throw $_.Exception
    }
}

$elems = $Sequence.Split(",")
foreach($elem in $elems){
    $elem = $elem.Trim()
    [int]$seconds = 0
    [bool]$result = [int]::TryParse($elem, [ref]$seconds)
    if($result -eq $true)
    {
        Write-Output "Sleeping $seconds ..."
        Start-Sleep $seconds
    }else
    {
        Write-Host "Starting VM: $elem"
		$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $elem
		$StartRtn = $VM | Start-AzVM -ErrorAction Continue

		if ($StartRtn.Status -ne "Succeeded") {
			# The VM failed to start, so send notice
			Write-Output ($VM.Name + " failed to start")
			Write-Error ($VM.Name + " failed to start. Error was:") -ErrorAction Continue
			Write-Error (ConvertTo-Json $StartRtn) -ErrorAction Continue
		}
		else {
			# The VM started, so send notice
			Write-Output ($VM.Name + " has been started")
		}
    }
}

that the provided sequence (line 33) are split by commas (line 57). The TryParse is used to decide whether the current position is a number or a VM name. A wait will be performed or the corresponding VM will be started.

You can download the Start-VMsInSequence.ps1 and Stop-VMsInSequence.ps1 from github.