Use a Compute Engine resource policy with instance_schedule_policy, then attach it to the existing VMs you intend to schedule. Terraform manages the policy and attachments; Compute Engine performs the scheduled actions.
This revision replaces the earlier Python 3.8 Cloud Function example with a native instance schedule. It selects VMs explicitly, rather than discovering them by label. The historical label-based project is a different implementation, not the complete source for the revised example.
Prerequisites
Use Terraform, Application Default Credentials for a sandbox project, and existing eligible VMs in one region. The Compute Engine API must be enabled. Your deployment identity needs policy and attachment permissions; the Compute Engine Service Agent separately needs permission to start and stop instances.
Ask an administrator to review the Google Cloud instance-schedule requirements before deployment. Granting permissions only to your own user does not authorize scheduled execution. This guide does not alter project IAM automatically.
Define the policy and attachments
Save the complete configuration as main.tf in a dedicated directory:
terraform { required_version = ">= 1.5.0" required_providers { google = { source = "hashicorp/google" version = "7.10.0" } } } variable "project_id" { type = string } variable "instances" { type = map(object({ name = string, zone = string })) } provider "google" { project = var.project_id region = "us-central1" } resource "google_compute_resource_policy" "workday" { name = "workday-schedule" region = "us-central1" instance_schedule_policy { time_zone = "Europe/Amsterdam" vm_start_schedule { schedule = "0 8 * * 1-5" } vm_stop_schedule { schedule = "0 18 * * 1-5" } } } resource "google_compute_resource_policy_attachment" "scheduled" { for_each = var.instances name = google_compute_resource_policy.workday.name instance = each.value.name zone = each.value.zone }
The provider version is a reproducibility baseline, not a claim that it is the latest. Commit the generated .terraform.lock.hcl. The attachment's name is the policy name, not its self-link; consult the provider attachment reference.
Create terraform.tfvars with actual sandbox identifiers:
project_id = "YOUR_PROJECT_ID" instances = { development = { name = "YOUR_EXISTING_VM", zone = "us-central1-a" } }
Keep the VM zone in the policy's region. The map keys are Terraform identities; changing a key can produce a detach/attach operation even when the VM name stays the same. Do not manage the same attachment through another Terraform configuration or an instance resource at the same time.
Validate and review the plan
terraform init terraform fmt -check terraform validate terraform plan -out=schedule.tfplan
Check that the plan manages a policy and the intended attachments, without creating, replacing, or deleting VMs. Plan files can contain sensitive configuration; keep them out of version control. When the reviewed plan matches your intent, terraform apply schedule.tfplan attaches the schedule. The configuration passed local Terraform formatting and provider-schema validation against Google provider 7.10.0. No cloud plan or apply was performed for this revision.
Verify execution and understand limits
Check each VM's attached resource policy and examine audit logs after a scheduled operation. Use a timezone that matches your business schedule and consider daylight-saving changes. Native schedules are not a precise-to-the-second job runner and cannot guarantee capacity when a VM starts.
Not every VM configuration supports scheduled stopping. Check the service restrictions before attaching the policy to workloads with special storage or availability requirements. Stopping a VM does not necessarily stop charges for its disks or associated resources.
Remove the schedule safely
Review a destroy plan for this dedicated configuration before detaching the schedule. Removing the policy does not itself start or stop a VM, and does not delete the existing VM because this configuration never owns it. Check for attachments outside Terraform before deleting a shared policy.
For the configuration/state workflow, see what Terraform does. To compare a TypeScript infrastructure workflow, follow the Pulumi AWS and GCP exercise.
Quick Recap
Does this schedule discover VMs by label?
No. This revision attaches a native schedule to an explicit map of existing instances. Dynamic label discovery requires separate automation.
Does stopping a VM remove every cost?
No. Disks, reserved resources, addresses, and other attached services can remain billable. Review your project's resources and pricing.
Comments
Share your thoughts and questions below