Using Automation to shut down Azure VMs

Azure is great. For a test lab environment, why bother messing with virtual machines on your laptop when you can run them in the cloud instead? But, as anyone who has made this mistake will know, forgetting to shut down your Azure VMs can hit you in the wallet. The good news is that the Azure framework provides all the tools you need to make sure that it never happens again. All you need to do is set up an automated job (just like a scheduled task) that will shut down the VMs on schedule.

There are other posts and scripts on this subject but most use Azure automation certificates, which can be tricky to get working. My way is much simpler and uses Azure Active Directory. And compared to other solutions you get more control. The script can be set up to exclude certain VMs or Cloud Services, as you might want to shut down part of your Azure infrastructure but keep the rest online.

The two Azure components required to do this are:

  • Automation – hosting the runbook containing a schedule and the PowerShell scripts to run
  • Azure Active Directory – containing credentials used to authenticate with Azure during script execution

Creating the User Context

This step covers:

  • Creating a user under which whose context the PowerShell script to shut down the VMs is executed
  • Granting the user permissions over the Azure VM estate

Creating a User

First load the Azure portal and click on the Active Directory tab.


Select the Default Directory then click “Add User” at the bottom.


In the dialog that is displayed set the type of user as ‘New user in your organization’ and set the username to ScriptUser. Click Next.


Configure the new user with a name and display name and set the role to ‘User’.


Azure will assign the new user a temporary password – click the Create button to display it


Note down the password shown.


The password must be changed so log out of the Portal, sign in using your new account (the user name will be ScriptUser@{youraccount} and set a new password. Then log back in using the Azure Administrator account.

Granting Permissions

Next, the account needs to be set as a co-administrator. This grants it permissions to perform the shutdown operation. Go to Settings > Administrators then click Add.


Enter the full username of the ScriptUser user and click the green tick.


The user is now set up to facilitate automation.

Configuring Automation

This step covers:

  • Creating the automation account
  • Creating a runbook, where the actual PowerShell script is stored
  • Defining a schedule which specifies how often the script is run

Creating the Automation Account

First, go to the Automation tab and create a new Automation Account.


To link our newly-created Automation Account to the user we just created, open the Automation Account, click the Assets tab then click New then select the “Add Credential” option.


Choose the ‘Windows PowerShell Credential’ credential type, input the ScriptUser username and a description. Click Next.


On the Define Credential page, enter the username again and the account’s password, then click the tick.


Creating a Runbook

Next a Runbook must be created. The Runbook contains the PowerShell code to control the VMs and a schedule defining how frequently the script should be run.

Give the runbook a name and description and configure it to use the automation account created in the previous step.


Azure will create the runbook – click the Edit Runbook link to load up the runbook properties.


Open the new Runbook then click the Author tab. This displays a simple PowerShell script editor.


Input the script below.

    Azure VM Shutdown Script
    Author: Robin Watkins <>
    Inspired by script written by Peter Selch Dahl

workflow StopAzureVMs
    #Script configuration
    $AutomationCredential = "scriptuser@{YourFullDomain}" #Credential used for authentication
    $AzureSubscription = "{Your Subscription}" #Name of Azure subscription
    $ServiceNamesToIgnore = @() #Servicenames to *never* shut down
    $HostNamesToIgnore = @("") #Hostnames to *never* shut down
   	$Cred = Get-AutomationPSCredential -Name $AutomationCredential

	# Connect to Azure (ignore output)
    $output = Add-AzureAccount -Credential $Cred 

    # Select subscription
    Select-AzureSubscription -SubscriptionName $AzureSubscription

    Write-Output "-------------------------------------------------------------------------"

    Write-Output "Starting Azure VM shutdown"

    Get-AzureVM | ? -Filterscript { $_.Status -eq 'ReadyRole' `
                        -and $ServiceNamesToIgnore -notcontains $_.ServiceName `
                        -and $HostNamesToIgnore -notcontains $_.Name } |
           ForEach-Object {
                Write-Output "Shutting down VM : $($_.Name), ServiceName $($_.ServiceName)"
                $null = Stop-AzureVM -Name $_.Name -ServiceName $_.ServiceName -Force 
     Write-Output "Shutdown complete"

    Write-Output "-------------------------------------------------------------------------"

Click the Publish button to save and publish the Runbook script.


Defining a Schedule

Next a schedule needs to be defined on the runbook. This controls how often the runbook is executed. Click the Schedule link at the top of the runbook page.


Create a new schedule and give it a name and description.


Configure the occurrence of the schedule – I want my VMs to shut down at 11pm every night.


The schedule is now visible on the runbook page.



Start some VMs.


Go to the runbook and click the “Start” button at the bottom of the window.


The runbook will begin and start to shut down the Azure VMs. Within a couple of minutes all your VMs will be off and there’s more money in your pocket to spend on coffee.


Other Notes

Something I learned writing this post is that positional Powershell parameters do not work when writing Azure workflows! You need to be more specific with parameters, for example using the -Filterscript parameter.

This entry was posted in Azure, PowerShell, Scripting. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *