Shutting down VMs on schedule in Azure


I thought it was time for a post on the quintessential automation task:  shutting down VMs & starting them on a schedule.

UPDATE (Nov-27th 2016):  this particular task is now available directly in the VM portal (at least the shut down part of it).  This article remains interesting to look at a complete example of Azure Automation.

This is a perfect job for Azure Automation / Runbook and will allow us to discover plenty of details.

I took some inspiration from Noah Stahl on automys.com.  His approach was quite original as he uses the Azure Tags to input a schedule for a VM to be up / down.

I kept the tag idea but left the scheduling to Azure Automation as it does it much better and more robustly.

Functional

So the way this is gona work is that we’re going to have a runbook running a PowerShell workflow.  This script will scan your entire subscription for VMs with a specified tag on them.  For those, it will shut them down.

A similar runbook will start-up those VMs.

Azure Automation

Please see Azure Runbook – A complete (simple) example in order to create an Automation account.

Credentials

Once created, we should create a credential with a Service Principal.  Please read through Using Azure Active Directory Service Principal in order to create a Service Principal.

image

Click Add a credential.

image

And fill the fields the following way:

  • Name:  principal
  • Description:  something like “Principal used to start / shutdown VMs”
  • User Name:  the Client ID of the AAD Application (Service Principal)
  • Password:  the key of the AAD Application

Then we’re going to give some access to this principal so it can see VMs and start / shut them down.

The easiest way is to go in a Resource Group containing a VM and adding access.

image

Add a user, give it the Virtual Machine Contributor role and type the name of the AAD application for user name.

Shutdown Runbook

At the center of this runbook is the Stop-AzureRmVm cmdlet.

workflow Shutdown-VMs
{
	Param
    (
        [parameter(Mandatory=$true)]
        [String] $tagKey="env",
        [parameter(Mandatory=$true)]
        [String] $tagValue="dev"
    )

	#	Refered to the credential stored in the Azure Automation account
	$credentialAssetName = "principal"
	$cred = Get-AutomationPSCredential -Name $credentialAssetName
	
    if(!$cred)
	{
        Throw "Could not find an Automation Credential Asset named '${credentialAssetName}'. Make sure you have created one in this Automation Account."
    }

    #	Connect to your Azure subscription
    $account = Add-AzureRmAccount -ServicePrincipal -Credential $cred -Tenant "72f988bf-86f1-41af-91ab-2d7cd011db47"
    if(!$account)
	{
        Throw "Could not authenticate to Azure using the credential asset '${credentialAssetName}'. Make sure the user name and password are correct."
    }

	#	Get all the VMs in the subscription (the ones the principal is able to see)
	#	Filter on the ones having the tag specified in the runbook parameter
	$vms = Get-AzureRmVM | Where-Object {$_.Tags[$tagKey] -eq $tagValue}

	#	Shutdown-VMs in parallel	
	ForEach -Parallel ($v in $vms)
	{
		Write-Output "Stopping $($v.ResourceGroupName).$($v.Name)"
		$ops = Stop-AzureRmVM -ResourceGroupName $v.ResourceGroupName -Name $v.Name -Force
		
		if($ops.IsSuccessStatusCode -ine $true)
		{
			Write-Output "Failure to stop $($v.ResourceGroupName).$($v.Name)"
		}
		else
		{
			Write-Output "$($v.ResourceGroupName).$($v.Name) Stopped"
		}
	}	
}

You can save, test and publish the runbook.

Start Runbook

At the center of this other runbook is the Start-AzureRmVm cmdlet.  It is basically identical to the other one except for this cmdlet.

workflow Start-VMs
{
	Param
    (
        [parameter(Mandatory=$true)]
        [String] $tagKey="env",
        [parameter(Mandatory=$true)]
        [String] $tagValue="dev"
    )

	#	Refered to the credential stored in the Azure Automation account
	$credentialAssetName = "principal"
	$cred = Get-AutomationPSCredential -Name $credentialAssetName
	
    if(!$cred)
	{
        Throw "Could not find an Automation Credential Asset named '${credentialAssetName}'. Make sure you have created one in this Automation Account."
    }

    #	Connect to your Azure subscription
    $account = Add-AzureRmAccount -ServicePrincipal -Credential $cred -Tenant "72f988bf-86f1-41af-91ab-2d7cd011db47"
    if(!$account)
	{
        Throw "Could not authenticate to Azure using the credential asset '${credentialAssetName}'. Make sure the user name and password are correct."
    }

	#	Get all the VMs in the subscription (the ones the principal is able to see)
	#	Filter on the ones having the tag specified in the runbook parameter
	$vms = Get-AzureRmVM | Where-Object {$_.Tags[$tagKey] -eq $tagValue}

	#	Start-VMs in parallel	
	ForEach -Parallel ($v in $vms)
	{
		Write-Output "Starting $($v.ResourceGroupName).$($v.Name)"
		$ops = Start-AzureRmVM -ResourceGroupName $v.ResourceGroupName -Name $v.Name
		
		if($ops.IsSuccessStatusCode -ine $true)
		{
			Write-Output "Failure to start $($v.ResourceGroupName).$($v.Name)"
			Write-Output $ops
		}
		else
		{
			Write-Output "$($v.ResourceGroupName).$($v.Name) Started"
		}
	}	
}

Scheduling

Now that we have both runbooks we can give them a schedule by creating a job for each.

Conclusion

We’ve seen how to create 2 simple runbooks to start & shut down VMs with an Azure Service Principal.

You could create multiple jobs using different schedule and different tags.  For instance, you might want your developer VMs to be off during a certain schedule but your QA to be off on a different schedule.  By assigning different tags to different VMs, this would be easily accomplished.

UPDATE (05-02-2016):  In some cases, your VM might fail to restart.  Read Allocation Failure and Remediation to understand why.  The solution to that would be to destroy the VMs (keep the VHDs) (instead of simply shutting them down) and recreating them.  A colleague of mine wrote the post On & Off – Done Right on Azure to explain how to go about it.  You could hook up those scripts in the automation built here and have a failure-resistant automation.  Those script wouldn’t be generic though:  they would depend on your VM configuration.

 

5 thoughts on “Shutting down VMs on schedule in Azure

  1. Alexandre Brisebois

    In the case where you need an arm template to recreate your VM, you can use tags to help identify which template to execute. Consequently, we have the opportunity to generalize the automation Runbook.

    Reply
  2. Pingback: Recreating VMs in Azure | Vincent-Philippe Lauzon's blog

  3. Mark Smith

    Hi there,

    Nice article, just worth noting that we have developed some software that does all this without powershell.

    You can also use the Azure Virtual Machine scheduler from SmiKar Software. AVMS as it is known will connect to your Azure Subscriptions and allow you to select the VMs and a power on or down schedule to suit.

    Works with V1 and V2 Azure Virtual Machines

    http://www.smikar.com/automate

    Reply
  4. Pingback: Azure Automation and parallel processing – Thinking Cloud

  5. Pingback: I tortured Azure in the Week-End | Vincent-Philippe Lauzon's blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s