Deallocate Azure VMs automatically and save money

Stopped vs Deallocated

As you might already know an Azure Virtual Machines that are simply Stopped and not Deallocated still cost you money. Virtual Machines that have the status “VM Stopped” will occur in CPU and Memory allocation charges.

This post helps you implement a PS Script to run on a schedule in a Runbook that will go over all the virtual machines in the subscriptions that are Stopped but not Deallocated and will deallocate them for you. As the Virtual Machine is already Stopped running this brings no risk.

Creating a Runbook

Navite to Azure Automation Accounts. Use the Search to find it faster

If you already have an Automation Account to use skip this step.

In the automaton account page click on + Add

Give it a name, select a subscripion, resource group and location. Select Yes for “Create A Run As Account”

The Run As Account will create a Service Principal into your Azure Active Directory. Make sure you have permission for it

Acess the automation account you just created. In the account’s blade, first we need to do is add the Az.Compute and Az.Account module. For that, click on Modules gallery and search for Az.Accounts and Az.Compute. Click on it and then Import. It can take sometime to finish the importation.

Now click on Runbooks and hen Create a runbook

Give it a name, select PowerShell for type and a optional description

In the Edit PowerShell Runbook, copy the Script below and paste there.

#Ensures you do not inherit an AzContext in your runbook
    Disable-AzContextAutosave –Scope Process

    $connection = Get-AutomationConnection -Name AzureRunAsConnection
#Wrap authentication in retry logic for transient network failures
    $logonAttempt = 0
    while(!($connectionResult) -And ($logonAttempt -le 10))
#Logging in to Azure...
    $connectionResult =    Add-AzAccount `
                               -ServicePrincipal `
                               -Tenant $connection.TenantID `
                               -ApplicationId $connection.ApplicationID `

    Start-Sleep -Seconds 10
    $ErrorActionPreference = "Stop"
        #Ingest all subscriptions
        [array]$AzureSubscriptions = (Get-AzContext).Subscription     
#Loop Subscriptions
    ForEach($AzureSubscription in $AzureSubscriptions) {
        $Script:ActiveSubscriptionName = $AzureSubscription.Name
        $Script:ActiveSubscriptionID = $AzureSubscription.Id

#Set AzContext as we are in a ForEach Loop

        Set-AzContext -SubscriptionId $AzureSubscription.Id

#Get VMs Status
       [array]$StoppedAzVMs = Get-AzVM -Status

       if($StoppedAzVMs) {
#Loop through each VM in this Resource Group
           ForEach($StoppedAzVM in $StoppedAzVMs) {
#If PowerState is VM stopped
                   if($StoppedAzVM.PowerState -eq "VM stopped") {
       Stop-AzVM -Name $StoppedAzVM.Name -ResourceGroupName $StoppedAzVM.ResourceGroupName -Force
This image has an empty alt attribute; its file name is image-68.png

Click on Test Pane an then Start to validate the Script. Bear in mind that even during the validation, if the script finds Stopped VMs it will deallocate them.

The Output should be as follow and any Stopped machines should now be Stopped Deallocated

Now that you have tested the script, go back to the Script Editor and click Publish.

To run it on a schedule, you have to either link it to an existent schedule or create a new one.

That should be it. Please give feedback and comments below.