Terraform/Opentofu VM Provisioning in Google Cloud Platform

A not-so-comprehensive guide to provisioning a VM in Google Cloud Platform—because my bank account has… boundaries.

Cloud can get expensive real fast. If you’re using your company’s account, go wild. Live your best cloud life. Spin up instances like there’s no tomorrow.

But if you’re like me—personally funding this adventure—then welcome. We’ll be keeping things practical, efficient, and, most importantly, affordable.

For now, I’ll cover the basics and the stuff that actually matters. I’ll update this guide if I ever stumble upon a pot of gold.

Table of Contents

Free Tier Account

If your just testing GCP, then I advice you use the free tier account.

Check this link.

Service Account

Create service account that have permission to create vm compute engine.

Account

If you already have account in GCP, then you already have a default project. Create new or keep using the default-make sure to use the project-id (e.g. myproject-123456).

1gcloud iam service-accounts create <your-tf-tofu-service-account> \
2    --display-name=<service account description> \
3    --project=<project-id>

The full service account email will look like:

1my-service-account@my-sample-project-123.iam.gserviceaccount.com

Role/Permission

If you don’t care about security, RBAC and don’t want to worry about permission error that may arise.

1gcloud projects add-iam-policy-binding <project-id> \
2    --member=serviceAccount:tofu-sa@coltrane-492510.iam.gserviceaccount.com \
3    --role=roles/roles/compute.admin

This is overkill for most real-world use cases.

Instead of roles/compute.admin, use more specific roles:

 1#!/bin/bash
 2
 3PROJECT_ID="my-sample-project-123"
 4SA="my-service-account@$PROJECT_ID.iam.gserviceaccount.com"
 5
 6for ROLE in \
 7  roles/compute.instanceAdmin.v1 \
 8  roles/compute.networkAdmin \
 9  roles/iam.serviceAccountUser 
10do
11  gcloud projects add-iam-policy-binding $PROJECT_ID \
12    --member="serviceAccount:$SA" \
13    --role="$ROLE"
14done

Export Service Account

1gcloud iam service-accounts keys create ./service-account.json \
2    --iam-account=<full_service_account_email> \
3    --project=<project-id>

Service

Enable GCP service, this is usually disabled by default.

1gcloud services enable cloudresourcemanager.googleapis.com --project=<project-id>
2gcloud services enable compute.googleapis.com --project=<project-id>
3gcloud services enable networkmanagement.googleapis.com --project=<project-id>
4gcloud services enable iap.googleapis.com --project=<project-id>
5gcloud services enable monitoring.googleapis.com --project=<project-id>

Basic VM

This will create VM:

  • create e2-micro
  • internal IP/subnet
  • ssh through IAP tunnel only
  • no extra disk
  • no scaling

provider.tf

 1terraform {
 2  required_providers {
 3    google = {
 4      source  = "hashicorp/google"
 5      version = "7.26.0"
 6    }
 7  }
 8}
 9
10provider "google" {
11  credentials = file("${path.module}/service-account.json")
12  project = var.project_id
13  region  = var.region
14}

variables.tf

 1variable "project_id" {
 2  type    = string
 3}
 4variable "region" {
 5  type        = string
 6  description = "Region"
 7}
 8variable "zone" {
 9  type        = string
10  description = "Zone"
11}
12variable "compute_instance_name" {
13  type        = string
14  description = "Compute Instance Name"
15}
16variable "machine_type" {
17  type        = string
18  description = "Machine Type"
19} 
20variable "hostname" {
21  type        = string
22  description = "VM Hostname"
23} 
24variable "image_type" {
25  type        = string
26  description = "Image Type"
27} 
28variable "network_name" {
29  type        = string
30  description = "Network Name"
31} 

terraform.tfvars

 1project_id = "yourproject-123456"
 2
 3region = "us-west1"
 4zone = "us-west1-c"
 5
 6compute_instance_name = "vm-name"
 7machine_type = "e2-micro"
 8image_type = "debian-cloud/debian-13"
 9network_name = "vm-network"
10
11hostname = "vm-hostname"

main.tf

 1resource "google_compute_network" "vm_network" {
 2  name                    = var.network_name
 3  auto_create_subnetworks = false
 4}
 5
 6resource "google_compute_subnetwork" "vm_sub_network" {
 7  name          = "my-subnet"
 8  ip_cidr_range = "10.0.1.0/24"
 9  region        = var.region
10  network       = google_compute_network.vm_network.id
11}
12
13
14resource "google_compute_instance" "vm_instance" {
15  name         = var.compute_instance_name
16  tags         = ["vm-test", "os-login"]
17  zone         = var.zone
18  machine_type = var.machine_type
19  network_interface {
20    network    = google_compute_network.vm_network.id
21    subnetwork = google_compute_subnetwork.vm_sub_network.id
22  }
23  boot_disk {
24    initialize_params {
25      image = var.image_type
26    }
27  }
28  hostname = var.hostname # if not set, default to vm google_compute_instance.name
29}

Provision using tf or tofu.

1tofu plan
2tofu apply

List VM instances.

1gcloud compute instances list
2NAME      ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP     STATUS
3miles-vm  us-west1-c  e2-micro                   10.0.1.2                     RUNNING

Network

Before we can SSH to the server we must first define the network.

Firewall

Allow SSH.

  • “35.235.240.0/20” - IAP Subnet
  • “0.0.0.0/0” - allow public access
 1resource "google_compute_firewall" "allow_ssh" {
 2  name    = "allow-ssh-iap"
 3  network = google_compute_network.vm_network.id
 4
 5  source_ranges = ["35.235.240.0/20", "0.0.0.0/0"]
 6
 7  allow {
 8    protocol = "tcp"
 9    ports    = ["22"]
10  }
11
12  target_tags = ["vm-test"]
13}

Make sure that target_tags is the same tag set in vm_tags.

Public IP

Skip this if you don’t want to expose the VM in public domain. You can only login using IAP tunnel.

 1resource "google_compute_address" "vm_address" {
 2  name   = "external-test-ip"
 3  region = var.region
 4}
 5
 6resource "google_compute_instance" "vm_instance" {
 7  name         = var.compute_instance_name
 8  tags         = ["vm-test", "os-login"]
 9  zone         = var.zone
10  machine_type = var.machine_type
11  network_interface {
12    network    = google_compute_network.vm_network.id
13    subnetwork = google_compute_subnetwork.vm_sub_network.id
14
15    # acquire public/external ip
16    access_config {
17      nat_ip = google_compute_address.vm_address.address
18    }
19  }
20}

Login Method

IAM-Based

First enable osloginapi.

 1# compute engine api
 2resource "google_project_service" "project" {
 3  service            = "oslogin.googleapis.com"
 4  disable_on_destroy = false
 5}
 6
 7data "google_project" "project" {
 8}
 9resource "google_project_iam_member" "os_login_admin_users" {
10  project = data.google_project.project.project_id
11  role    = "roles/compute.osAdminLogin"
12  member  = "serviceAccount:service-${data.google_project.project.number}@compute-system.iam.gserviceaccount.com"
13}

If you want to enable IAM-Based login to all the VM then define the metadata resources outside the compute engine bracket/resources.

1resource "google_compute_project_metadata" "default" {
2  metadata = {
3    enable-oslogin = "TRUE"
4  }
5}

If not and explicit to specific VM.

 1resource "google_compute_instance" "vm_instance" {
 2  name         = var.compute_instance_name
 3  tags         = ["vm-test", "os-login"]
 4  zone         = var.zone
 5  machine_type = var.machine_type
 6  network_interface {
 7    network    = google_compute_network.vm_network.id
 8    subnetwork = google_compute_subnetwork.vm_sub_network.id
 9  }
10  boot_disk {
11    initialize_params {
12      image = var.image_type
13    }
14  }
15  metadata = {
16    enable-oslogin : "TRUE"
17  }
18}

To login to the vm/server.

1gcloud compute ssh --zone "zone" "vm-name" --tunnel-through-iap --project "project-id"

SSH-Based

This is the traditional method-you would need to assign public IP to the VM.

Using file (public-key), this will apply to all the VM created.

1resource "google_os_login_ssh_public_key" "default" {
2  user = data.google_client_openid_userinfo.me.email
3  key  = file("id_rsa.pub") 
4}

Using manifest, this will apply to all VM created.

1resource "google_compute_project_metadata" "default" {
2  metadata = {
3    ssh-keys = <<EOF
4      dev:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILg6UtHDNyMNAh0GjaytsJdrUxjtLy3APXqZfNZhvCeT dev
5      test:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILg6UtHDNyMNAh0GjaytsJdrUxjtLy3APXqZfNZhvCeT test
6    EOF
7  }
8}

Or apply metadata inside compute engine resources, this will apply only to this VM.

 1resource "google_compute_instance" "vm_instance" {
 2  name         = var.compute_instance_name
 3  tags         = ["vm-test", "os-login"]
 4  zone         = var.zone
 5  machine_type = var.machine_type
 6  network_interface {
 7    network    = google_compute_network.vm_network.id
 8    subnetwork = google_compute_subnetwork.vm_sub_network.id
 9
10    # acquire public/external ip
11    access_config {
12      nat_ip = google_compute_address.vm_address.address
13    }
14  }
15  boot_disk {
16    initialize_params {
17      image = var.image_type
18    }
19  }
20  metadata = {
21    "ssh-keys" = var.ssh_keys
22  }
23}

List VM instances.

1gcloud compute instances list
2NAME      ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP     STATUS
3miles-vm  us-west1-c  e2-micro                   10.0.1.2     136.109.82.172  RUNNING

Login to the VM through External IP.

1ssh <user>@<external_ip>

Extra Disk

Persistent Storage

By default the Disk limit is 10gb, if you are using free tier set limit to 30gb and disk type to pd-standard.

 1resource "google_compute_instance" "vm_instance" {
 2  name         = var.compute_instance_name
 3  tags         = ["vm-test", "os-login"]
 4  zone         = var.zone
 5  machine_type = var.machine_type
 6  network_interface {
 7    network    = google_compute_network.vm_network.id
 8    subnetwork = google_compute_subnetwork.vm_sub_network.id
 9  }
10  boot_disk {
11    initialize_params {
12      image = var.image_type
13      size = 30
14      type = "pd-standard"
15    }
16  }
17  metadata = {
18    "ssh-keys" = var.ssh_keys
19  }
20}
Disk Type Free Tier Speed
pd-standard free slow
pd-balanced paid medium
pd-ssd paid fast

List disk.

 1mcbtaguiad@miles:~$ sudo fdisk -l
 2Disk /dev/sda: 30 GiB, 32212254720 bytes, 62914560 sectors
 3Disk model: PersistentDisk  
 4Units: sectors of 1 * 512 = 512 bytes
 5Sector size (logical/physical): 512 bytes / 4096 bytes
 6I/O size (minimum/optimal): 4096 bytes / 4096 bytes
 7Disklabel type: gpt
 8Disk identifier: A2056751-613C-B043-B7CA-DDB01D9057BD
 9
10Device      Start      End  Sectors  Size Type
11/dev/sda1  262144 62912512 62650369 29.9G Linux root (x86-64)
12/dev/sda14   2048     8191     6144    3M BIOS boot
13/dev/sda15   8192   262143   253952  124M EFI System
14
15Partition table entries are not in disk order.

To add extra persistent disk.

 1resource "google_compute_disk" "extra_disk" {
 2  name = "extra-disk"
 3  type = "pd-standard"
 4  zone = var.zone
 5  size = "15"
 6}
 7resource "google_compute_instance" "vm_instance" {
 8  name         = var.compute_instance_name
 9  tags         = ["vm-test"]
10  zone         = var.zone
11  machine_type = var.machine_type
12  network_interface {
13    network    = google_compute_network.vm_network.id
14    subnetwork = google_compute_subnetwork.vm_sub_network.id
15
16    # acquire public/external ip
17    access_config {
18      nat_ip = google_compute_address.vm_address.address
19    }
20  }
21  boot_disk {
22    initialize_params {
23      image = var.image_type
24      size = var.disk_size
25      type = var.disk_type
26    }
27  }
28  attached_disk {
29    source      = google_compute_disk.extra_disk.id
30    device_name = google_compute_disk.extra_disk.name
31  }
32  metadata = {
33    "ssh-keys" = var.ssh_keys
34  }
35  hostname = var.hostname # if not set, default to vm google_compute_instance.name
36}

List disk.

 1mcbtaguiad@miles:~$ sudo fdisk -l
 2Disk /dev/sda: 15 GiB, 16106127360 bytes, 31457280 sectors
 3Disk model: PersistentDisk  
 4Units: sectors of 1 * 512 = 512 bytes
 5Sector size (logical/physical): 512 bytes / 4096 bytes
 6I/O size (minimum/optimal): 4096 bytes / 4096 bytes
 7Disklabel type: gpt
 8Disk identifier: A2056751-613C-B043-B7CA-DDB01D9057BD
 9
10Device      Start      End  Sectors  Size Type
11/dev/sda1  262144 31455232 31193089 14.9G Linux root (x86-64)
12/dev/sda14   2048     8191     6144    3M BIOS boot
13/dev/sda15   8192   262143   253952  124M EFI System
14
15Partition table entries are not in disk order.
16
17
18Disk /dev/sdb: 15 GiB, 16106127360 bytes, 31457280 sectors
19Disk model: PersistentDisk  
20Units: sectors of 1 * 512 = 512 bytes
21Sector size (logical/physical): 512 bytes / 4096 bytes
22I/O size (minimum/optimal): 4096 bytes / 4096 bytes

Ephemeral Storage

Use for cache or temp data, this uses NVME or SCSI (and money hahaha).

 1resource "google_compute_instance" "default" {
 2  name         = "my-vm-instance-with-scratch"
 3  machine_type = "n2-standard-8"
 4  zone         = "us-central1-a"
 5
 6  boot_disk {
 7    initialize_params {
 8      image = "debian-cloud/debian-13"
 9    }
10  }
11
12  # Local SSD interface type; NVME for image with optimized NVMe drivers or SCSI
13  # Local SSD are 375 GiB in size
14  scratch_disk {
15    interface = "SCSI"
16  }
17
18  network_interface {
19    network = "default"
20    access_config {}
21  }
22}

Snapshots

Manual

List the disk created.

1gcloud compute disks list
2NAME        LOCATION    LOCATION_SCOPE  SIZE_GB  TYPE         STATUS
3extra-disk  us-west1-c  zone            15       pd-standard  READY
4miles-vm    us-west1-c  zone            10       pd-standard  READY

Manual run snapshot.

1gcloud compute disks snapshot miles-vm \
2  --snapshot-names=snapshot-$(date +%Y%m%d-%H%M%S) \
3  --zone=us-west1-c
4
5Creating snapshot(s) snapshot-20260408-140531...done.     

List snapshots.

1gcloud compute snapshots list             
2NAME                      DISK_SIZE_GB  SRC_DISK                     STATUS
3snapshot-20260408-140531  15            us-west1-c/disks/miles-vm    READY

To delete.

1gcloud compute snapshots delete SNAPSHOT_NAME

Automatic/Scheduled

Create google_compute_disk for boot.

1resource "google_compute_disk" "boot_disk" {
2  name = "boot-disk"
3  type = "pd-standard"
4  zone = var.zone
5  size = var.disk_size
6  image = var.image_type
7}

Create google_compute_snapshot. Note that you would also need to create for other disk present.

1resource "google_compute_snapshot" "boot_disk_snapshot" {
2  name        = "vm-snapshot-extra-disk"
3  source_disk = google_compute_disk.boot_disk.name
4  zone        = var.zone
5
6  depends_on = [google_compute_disk.boot_disk]
7}

Create snapshot_policy.

 1resource "google_compute_resource_policy" "snapshot_policy" {
 2  name   = "daily-snapshot-policy"
 3  region = var.region
 4
 5  snapshot_schedule_policy {
 6    schedule {
 7      daily_schedule {
 8        days_in_cycle = 1
 9        start_time    = "04:00"
10      }
11    }
12
13    retention_policy {
14      max_retention_days    = 3
15      on_source_disk_delete = "KEEP_AUTO_SNAPSHOTS"
16    }
17  }
18}

If you are not broke, also add regional clone.

 1resource "google_compute_region_disk" "regiondisk" {
 2  name                      = "region-disk-name"
 3  snapshot                  = google_compute_snapshot.boot_disk_snapshot.id
 4  type                      = "pd-ssd"
 5  region                    = "us-central1"
 6  physical_block_size_bytes = 4096
 7  size                      = 11
 8
 9  replica_zones = ["us-central1-a", "us-central1-f"]
10}

Asynchronous Disk Replication

This replicate boot_disk to a regional/secondary disk replication_disk.

Create google_compute_disk.

 1resource "google_compute_disk" "boot_disk" {
 2  name = "boot-disk"
 3  type = "pd-standard"
 4  zone = var.zone
 5  size = var.disk_size
 6  image = var.image_type
 7}
 8
 9resource "google_compute_disk" "replication_disk" {
10  name = "replication-disk"
11  type = "pd-ssd"
12  zone = "europe-west3-a"
13
14  async_primary_disk {
15    disk = google_compute_disk.boot_disk.id
16  }
17
18  physical_block_size_bytes = 4096
19}

Create google_compute_disk_async_replication.

1resource "google_compute_disk_async_replication" "repli_disk" {
2  primary_disk = google_compute_disk.boot_disk.id
3  secondary_disk {
4    disk = google_compute_disk.replication_disk.id
5  }
6}

Autoscaler

This defines the blueprint for the instances that will be part of the managed instance group.

 1resource "google_compute_instance_template" "default" {
 2  name           = "my-instance-template"
 3  machine_type   = "e2-medium"
 4  can_ip_forward = false
 5
 6  tags = ["vm-test"]
 7
 8  disk {
 9    source_image = data.google_compute_image.debian_13.id
10  }
11
12  network_interface {
13    network = "default"
14  }
15
16  metadata = {
17    name = "value"
18  }
19
20  service_account {
21    scopes = ["userinfo-email", "compute-ro", "storage-ro"]
22  }
23}

Fetches the latest Debian 11 image from debian-cloud.

1data "google_compute_image" "debian_11" {
2  family  = "debian-11"
3  project = "debian-cloud"
4}

Creates a Managed Instance Group (MIG) in a specific zone.

  • handle creating, deleting, and updating instances automatically based on the template.
 1resource "google_compute_instance_group_manager" "default" {
 2  name = "my-igm"
 3  zone = "us-central1-f"
 4
 5  version {
 6    instance_template = google_compute_instance_template.default.id
 7    name              = "primary"
 8  }
 9
10  base_instance_name = "autoscaler-sample"
11}

Automatically scales the MIG based on CPU usage, load, or schedule.

  • minimum 1 vm
  • maximum 5 vm
  • automatically ensures 2 instances every weekday starting 7AM New York time for 12 hours (43,200 seconds).
  • outside this period, it can scale down to the min (1 instance).
 1resource "google_compute_autoscaler" "default" {
 2  provider = google-beta
 3  name     = "my-autoscaler"
 4  zone     = "us-central1-f"
 5  target   = google_compute_instance_group_manager.default.id
 6
 7  autoscaling_policy {
 8    max_replicas    = 5
 9    min_replicas    = 1
10    cooldown_period = 60
11
12    scaling_schedules {
13      name                  = "every-weekday-morning"
14      description           = "Increase to 2 every weekday at 7AM for 12 hours."
15      min_required_replicas = 2
16      schedule              = "0 7 * * MON-FRI"
17      time_zone             = "America/New_York"
18      duration_sec          = 43200
19    }
20  }
21}

Full Example

  • SSH and IAM based login
  • with external/public IP
  • firewall rule
  • extra disk attached
  • boot disk - async replication
  • boot disk - automatic snapshot

variables.tf

  1variable "project_id" {
  2  type    = string
  3}
  4
  5variable "region" {
  6  type        = string
  7  description = "Region"
  8}
  9
 10variable "zone" {
 11  type        = string
 12  description = "Zone"
 13}
 14
 15variable "compute_instance_name" {
 16  type        = string
 17  description = "Compute Instance Name"
 18}
 19
 20variable "machine_type" {
 21  type        = string
 22  description = "Machine Type"
 23} 
 24
 25variable "hostname" {
 26  type        = string
 27  description = "VM Hostname"
 28} 
 29
 30variable "image_type" {
 31  type        = string
 32  description = "Image Type"
 33} 
 34
 35variable "network_name" {
 36  type        = string
 37  description = "Network Name"
 38} 
 39
 40variable "subnetwork_name" {
 41  type        = string
 42  description = "Sub Network Name"
 43} 
 44
 45variable "address_name" {
 46  type        = string
 47  description = "Address Name"
 48} 
 49
 50variable "service_account" {
 51  type        = string
 52  description = "Service Account"
 53} 
 54
 55variable "user_email" {
 56  type        = string
 57  description = "User Email"
 58}
 59
 60variable "ssh_keys" {
 61  description = "SSH Keys"
 62  type        = string
 63}
 64
 65variable "boot_disk_name" {
 66  type        = string
 67  description = "Boot Disk Name"
 68}
 69
 70variable "boot_disk_size" {
 71  type        = string
 72  description = "Boot Disk Size"
 73}
 74
 75variable "boot_disk_type" {
 76  type        = string
 77  description = "Boot Disk Type"
 78}
 79
 80variable "extra_disk_name" {
 81  type        = string
 82  description = "Extra Disk Name"
 83}
 84
 85variable "extra_disk_size" {
 86  type        = string
 87  description = "Disk Size"
 88}
 89
 90variable "extra_disk_type" {
 91  type        = string
 92  description = "Disk Type"
 93}
 94
 95variable "replication_disk_name" {
 96  type        = string
 97  description = "Replication Disk Name"
 98}
 99
100variable "replication_disk_size" {
101  type        = string
102  description = "Replication Disk Size"
103}
104
105variable "replication_disk_type" {
106  type        = string
107  description = "Replication Disk Type"
108}
109
110variable "replication_disk_zone" {
111  type        = string
112  description = "Replication Disk Zone"
113}

terraform.tfvars

 1project_id = "project-123456"
 2
 3region = "us-west1"
 4zone = "us-west1-c"
 5
 6compute_instance_name = "vm-name"
 7machine_type = "e2-micro"
 8image_type = "debian-cloud/debian-13"
 9network_name = "vm-network"
10subnetwork_name = "vm-subnet"
11address_name = "external-test-ip"
12
13hostname = "vm.example.com"
14
15boot_disk_name = "boot-disk"
16boot_disk_size = 10
17boot_disk_type = "pd-standard"
18
19
20extra_disk_name = "extra-disk"
21extra_disk_size = 10
22extra_disk_type = "pd-standard"
23
24replication_disk_name = "replication-disk"
25replication_disk_size = 10
26replication_disk_type = "pd-ssd"
27replication_disk_zone = "us-west1-c"
28
29service_account = "tofu-sa@project-123456.iam.gserviceaccount.com"
30user_email = "youremail@gmail.com"
31
32ssh_keys = <<EOF
33admin:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtf3e9lQR1uAypz4nrq2nDj0DvZZGONku5wO+M87wUVTistrY8REsWO admin
34dev:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtf3e9lQR1uAypz4nrq2nDj0DvZZGONku5wO+M87wUVTistrY8REsWO dev
35EOF

main.tf

  1resource "google_compute_network" "vm_network" {
  2  name                    = var.network_name
  3  auto_create_subnetworks = false
  4}
  5
  6resource "google_compute_subnetwork" "vm_sub_network" {
  7  name          = var.subnetwork_name
  8  ip_cidr_range = "10.0.1.0/24"
  9  region        = var.region
 10  network       = google_compute_network.vm_network.id
 11}
 12
 13resource "google_compute_address" "vm_address" {
 14  name   = var.address_name
 15  region = var.region
 16}
 17
 18resource "google_compute_firewall" "allow_ssh" {
 19  name    = "allow-ssh-iap"
 20  network = google_compute_network.vm_network.id
 21
 22  source_ranges = ["35.235.240.0/20", "0.0.0.0/0"]
 23
 24  allow {
 25    protocol = "tcp"
 26    ports    = ["22"]
 27  }
 28
 29  target_tags = ["vm-test"]
 30}
 31
 32resource "google_project_service" "project" {
 33  service            = "oslogin.googleapis.com"
 34  disable_on_destroy = false
 35}
 36
 37resource "google_compute_project_metadata" "default" {
 38  metadata = {
 39    enable-oslogin = "TRUE"
 40  }
 41}
 42
 43data "google_project" "project" {
 44}
 45resource "google_project_iam_member" "os_login_admin_users" {
 46  project = data.google_project.project.project_id
 47  role    = "roles/compute.osAdminLogin"
 48  member  = "serviceAccount:service-${data.google_project.project.number}@compute-system.iam.gserviceaccount.com"
 49}
 50
 51resource "google_compute_disk" "boot_disk" {
 52  name = var.boot_disk_name
 53  type = var.boot_disk_type
 54  zone = var.zone
 55  size = var.boot_disk_size
 56  image = var.image_type
 57}
 58
 59resource "google_compute_disk" "replication_disk" {
 60  name = var.replication_disk_name
 61  type = var.replication_disk_type
 62  zone = var.replication_disk_zone
 63
 64  async_primary_disk {
 65    disk = google_compute_disk.boot_disk.id
 66  }
 67
 68  physical_block_size_bytes = 4096
 69}
 70
 71resource "google_compute_disk" "extra_disk" {
 72  name = var.extra_disk_name
 73  type = var.extra_disk_type
 74  zone = var.zone
 75  size = var.extra_disk_size
 76}
 77
 78resource "google_compute_disk_async_replication" "async_replication_disk" {
 79  primary_disk = google_compute_disk.boot_disk.id
 80  secondary_disk {
 81    disk = google_compute_disk.replication_disk.id
 82  }
 83}
 84
 85resource "google_compute_snapshot" "boot_disk_snapshot" {
 86  name        = "vm-snapshot-extra-disk"
 87  source_disk = google_compute_disk.boot_disk.name
 88  zone        = var.zone
 89
 90  depends_on = [google_compute_disk.boot_disk]
 91}
 92
 93resource "google_compute_resource_policy" "snapshot_policy" {
 94  name   = "daily-snapshot-policy"
 95  region = var.region
 96
 97  snapshot_schedule_policy {
 98    schedule {
 99      daily_schedule {
100        days_in_cycle = 1
101        start_time    = "04:00"
102      }
103    }
104
105    retention_policy {
106      max_retention_days    = 3
107      on_source_disk_delete = "KEEP_AUTO_SNAPSHOTS"
108    }
109  }
110}
111
112resource "google_compute_instance" "vm_instance" {
113  name         = var.compute_instance_name
114  tags         = ["vm-test"]
115  zone         = var.zone
116  machine_type = var.machine_type
117  network_interface {
118    network    = google_compute_network.vm_network.id
119    subnetwork = google_compute_subnetwork.vm_sub_network.id
120
121    # acquire public/external ip
122    access_config {
123      nat_ip = google_compute_address.vm_address.address
124    }
125  }
126  boot_disk {
127    source = google_compute_disk.boot_disk.id
128    device_name = google_compute_disk.boot_disk.name
129  }
130  attached_disk {
131    source      = google_compute_disk.extra_disk.id
132    device_name = google_compute_disk.extra_disk.name
133  }
134  metadata = {
135    "ssh-keys" = var.ssh_keys
136    enable-oslogin : "TRUE"
137  }
138  hostname = var.hostname # if not set, default to vm google_compute_instance.name
139}