如何通过Terraform实现Azure虚拟机非工作时段自动启停?
Let me start by clearing up that earlier misconception: you absolutely can build this auto-start/stop workflow with Terraform. The claim that runbook parameters block this isn’t true—we just need to properly configure the right automation resources, runbooks, variables, and schedules together. I’ve implemented this exact setup for multiple clients, so let’s walk through a working, modular solution that matches what you’ve used in the Azure portal.
Core Prerequisites
- Make sure you’re using the latest
azurermTerraform provider (v3.x+ recommended) - You’ll need permissions to create Automation Accounts, Runbooks, Variables, and Job Schedules in your Azure subscription
- Grab the pre-built PowerShell runbook code for starting/stopping VMs from the Azure portal’s Runbook Gallery (search for "Start Azure VMs" and "Stop Azure VMs" to copy their code)
Terraform Configuration Breakdown
Step 1: Create the Automation Account & Resource Group
This is the foundation for hosting all your scheduling resources:
resource "azurerm_resource_group" "automation_rg" { name = "vm-auto-schedule-rg" location = "eastus" # Swap with your preferred region } resource "azurerm_automation_account" "vm_scheduler" { name = "vm-start-stop-automation" location = azurerm_resource_group.automation_rg.location resource_group_name = azurerm_resource_group.automation_rg.name sku_name = "Basic" # Basic tier is fully sufficient for this use case }
Step 2: Deploy the Start/Stop Runbooks
Instead of linking to external galleries (to avoid external dependencies), we’ll use local script files. Create a scripts folder in your Terraform workspace, paste the official runbook code into Start-AzureVMs.ps1 and Stop-AzureVMs.ps1, then reference them in Terraform:
# Stop VMs Runbook resource "azurerm_automation_runbook" "stop_vms" { name = "StopVMs" location = azurerm_resource_group.automation_rg.location resource_group_name = azurerm_resource_group.automation_rg.name automation_account_name = azurerm_automation_account.vm_scheduler.name runbook_type = "PowerShell" content = file("./scripts/Stop-AzureVMs.ps1") } # Start VMs Runbook resource "azurerm_automation_runbook" "start_vms" { name = "StartVMs" location = azurerm_resource_group.automation_rg.location resource_group_name = azurerm_resource_group.automation_rg.name automation_account_name = azurerm_automation_account.vm_scheduler.name runbook_type = "PowerShell" content = file("./scripts/Start-AzureVMs.ps1") }
Step 3: Configure Runbook Parameters with Automation Variables
The runbooks need details like which VMs to target and your subscription ID. Storing these as Automation Variables makes them easy to update later without modifying runbook code:
# Comma-separated list of VM resource IDs to manage resource "azurerm_automation_variable_string" "target_vms" { name = "TargetVMs" resource_group_name = azurerm_resource_group.automation_rg.name automation_account_name = azurerm_automation_account.vm_scheduler.name value = "/subscriptions/your-sub-id/resourceGroups/vm-rg/providers/Microsoft.Compute/virtualMachines/vm1,/subscriptions/your-sub-id/resourceGroups/vm-rg/providers/Microsoft.Compute/virtualMachines/vm2" description = "List of VMs to start/stop (comma-separated resource IDs)" } # Your Azure Subscription ID resource "azurerm_automation_variable_string" "subscription_id" { name = "SubscriptionID" resource_group_name = azurerm_resource_group.automation_rg.name automation_account_name = azurerm_automation_account.vm_scheduler.name value = "your-subscription-id" }
Step 4: Create Schedules & Link to Runbooks
Now we’ll set up daily schedules for off-hours stops (e.g., 7 PM local time) and morning starts (e.g., 7 AM local time). Adjust the time zone and start time to match your team’s working hours:
# Daily schedule to stop VMs at 7 PM Eastern Time resource "azurerm_automation_schedule" "stop_vms_schedule" { name = "StopVMs-Daily-1900EST" resource_group_name = azurerm_resource_group.automation_rg.name automation_account_name = azurerm_automation_account.vm_scheduler.name frequency = "Day" interval = 1 start_time = "2024-01-01T19:00:00Z" # Convert local time to UTC if needed time_zone = "Eastern Standard Time" } # Daily schedule to start VMs at 7 AM Eastern Time resource "azurerm_automation_schedule" "start_vms_schedule" { name = "StartVMs-Daily-0700EST" resource_group_name = azurerm_resource_group.automation_rg.name automation_account_name = azurerm_automation_account.vm_scheduler.name frequency = "Day" interval = 1 start_time = "2024-01-01T07:00:00Z" time_zone = "Eastern Standard Time" } # Link stop schedule to the StopVMs runbook with parameters resource "azurerm_automation_job_schedule" "stop_vms_job" { automation_account_name = azurerm_automation_account.vm_scheduler.name resource_group_name = azurerm_resource_group.automation_rg.name runbook_name = azurerm_automation_runbook.stop_vms.name schedule_name = azurerm_automation_schedule.stop_vms_schedule.name parameters = { "VMResourceIDs" = azurerm_automation_variable_string.target_vms.value "SubscriptionID" = azurerm_automation_variable_string.subscription_id.value } } # Link start schedule to the StartVMs runbook with parameters resource "azurerm_automation_job_schedule" "start_vms_job" { automation_account_name = azurerm_automation_account.vm_scheduler.name resource_group_name = azurerm_resource_group.automation_rg.name runbook_name = azurerm_automation_runbook.start_vms.name schedule_name = azurerm_automation_schedule.start_vms_schedule.name parameters = { "VMResourceIDs" = azurerm_automation_variable_string.target_vms.value "SubscriptionID" = azurerm_automation_variable_string.subscription_id.value } }
Critical Post-Deployment Step: Grant Permissions
Your Automation Account needs permission to manage VMs. Create a system-assigned managed identity for the account, then assign the Virtual Machine Contributor role to it:
# Enable system-assigned managed identity for the Automation Account resource "azurerm_automation_account_identity" "vm_scheduler_identity" { automation_account_name = azurerm_automation_account.vm_scheduler.name resource_group_name = azurerm_resource_group.automation_rg.name type = "SystemAssigned" } # Assign VM Contributor role to the identity resource "azurerm_role_assignment" "automation_vm_access" { scope = "/subscriptions/your-sub-id" # Limit to specific resource groups if preferred role_definition_name = "Virtual Machine Contributor" principal_id = azurerm_automation_account_identity.vm_scheduler_identity.principal_id }
Troubleshooting Tips
- Test Runbooks First: After applying the config, manually trigger the runbooks from the Azure portal to verify they can start/stop your VMs before relying on the schedule.
- Parameter Names: Double-check that the parameter names in your
azurerm_automation_job_schedulematch exactly what the runbook expects (you can view these in the runbook’s "Parameters" tab in the portal). - Time Zone Gotchas: Ensure your
start_timealigns with thetime_zoneyou set—use UTC as a fallback if you’re unsure about time zone conversions.
Alternative: Deploy the Full Portal Solution via Terraform
If you want to replicate the exact dashboard and pre-configured resources from the portal’s Start/Stop solution, you can use Terraform to deploy the official ARM template. Save the template content from Azure’s documentation as a local JSON file, then use:
resource "azurerm_resource_group_template_deployment" "vm_start_stop_solution" { name = "vm-start-stop-solution" resource_group_name = azurerm_resource_group.automation_rg.name template_content = file("./start-stop-vms-solution.json") parameters = { "automationAccountName" = azurerm_automation_account.vm_scheduler.name "vmList" = azurerm_automation_variable_string.target_vms.value "stopTime" = "19:00" "startTime" = "07:00" "timeZone" = "Eastern Standard Time" "enableSchedule" = "true" } }
内容的提问来源于stack exchange,提问作者atefeh

