Here's a simple question: Why are your dev and test VMs running at 3am on a Sunday?
Unless you have overnight batch jobs or a global team, the answer is usually "because nobody turned them off." That's expensive.
The Math
A typical dev environment might have 10 VMs costing £50/month each = £500/month.
Running 24/7: £500/month
Running business hours only (10hrs x 5 days): £500 x (50/168) = ~£150/month
Saving: £350/month = £4,200/year
And that's just one environment. Most organisations have multiple dev, test, UAT, and staging environments.
Option 1: Auto-Shutdown (Built-in)
Azure has a simple auto-shutdown feature built into every VM:
- Go to the VM → Auto-shutdown in the left menu
- Toggle to Enabled
- Set your shutdown time (e.g., 7:00 PM)
- Choose your timezone
- Optionally add a notification email
- Save
This handles the shutdown. But you still need to start them in the morning.
Option 2: Start/Stop VMs v2 (Azure Solution)
Microsoft provides a more complete solution called Start/Stop VMs v2:
- Schedule both start and stop times
- Apply to VMs based on tags or resource groups
- Includes a Logic App for orchestration
- Free (you pay for the underlying resources)
Deploy it from the Azure Marketplace: search for "Start/Stop VMs during off-hours."
Option 3: Azure Automation (Most Flexible)
For full control, use Azure Automation with PowerShell runbooks:
# Get all VMs with the "AutoShutdown" tag
$vms = Get-AzVM | Where-Object { $_.Tags.AutoShutdown -eq "true" }
foreach ($vm in $vms) {
$status = (Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status).Statuses[1].Code
if ($status -eq "PowerState/running") {
Write-Output "Stopping $($vm.Name)"
Stop-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Force
}
}
Create a similar runbook for starting VMs, then schedule both with Automation Schedules.
Option 4: Azure DevOps Pipeline
If your dev environments are created by pipelines, destroy them with pipelines:
# Scheduled pipeline to stop dev VMs
schedules:
- cron: "0 19 * * 1-5" # 7pm Mon-Fri
displayName: Stop Dev VMs
branches:
include:
- main
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'your-connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az vm deallocate --ids $(az vm list --resource-group rg-dev --query "[].id" -o tsv)
Important: Deallocate, Don't Just Shut Down
There's a difference:
- Stop (from inside the VM): VM is off but you're still paying for compute
- Deallocate (Stop from Azure): Compute charges stop, you only pay for storage
Always use the Azure portal, CLI, or API to stop VMs. The auto-shutdown feature does this correctly.
What About Databases?
Azure SQL and other PaaS services don't have the same on/off model. Options:
Azure SQL:
- Use serverless tier for dev (auto-pauses after inactivity)
- Scale down DTUs/vCores outside business hours
Cosmos DB:
- Reduce RU/s to minimum outside business hours
- Use autoscale with low maximum
Tagging Strategy
Use tags to control which VMs get shut down:
AutoShutdown: true
Environment: dev
ShutdownTime: 19:00
StartTime: 08:00
Your automation can then target VMs based on these tags, making it easy to add or exclude machines.
The Exceptions
Some dev/test resources should run 24/7:
- Shared services (AD, DNS) used by multiple environments
- Integration test environments used by offshore teams
- Long-running build agents
Tag these as AutoShutdown: false so your automation skips them.
Want to know how much you could save by scheduling your dev environments? Our free savings snapshot analyses your non-production spend.