6-routes-keepalived/main.tf (225 lines of code) (raw):

/** * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ locals { image = "debian-cloud/debian-11" machine_type = "e2-small" primary_instance_name = "nginx-primary" secondary_instance_name = "nginx-secondary" python_script = templatefile("cloud-function/main.py.tmpl", { route_name = var.route_name network_name = var.network_name floating_ip = var.floating_ip primary_instance = local.primary_instance_name secondary_instance = local.secondary_instance_name zone = var.zone }) } provider "google" { project = var.project_id region = var.region zone = var.zone } provider "archive" { } data "archive_file" "function_zip" { type = "zip" output_path = "${path.module}/function-switch.zip" source { content = local.python_script filename = "main.py" } source { content = file("cloud-function/requirements.txt") filename = "requirements.txt" } } resource "google_project_service" "required_api" { for_each = toset(["compute.googleapis.com", "cloudresourcemanager.googleapis.com", "cloudfunctions.googleapis.com", "cloudbuild.googleapis.com", "storage.googleapis.com"]) service = each.key disable_on_destroy = false } resource "google_service_account" "function_service_account" { account_id = "switcher-function" display_name = "Service Account that is able to create routes" } resource "google_service_account" "invoker_service_account" { account_id = "cf-invoker" display_name = "Service Account that is able to invoke the created functions" } resource "google_project_iam_member" "function_sa_membership" { project = var.project_id role = "roles/compute.networkAdmin" member = "serviceAccount:${google_service_account.function_service_account.email}" } resource "random_id" "bucketname_suffix" { byte_length = 4 } resource "google_storage_bucket" "function_bucket" { depends_on = [google_project_service.required_api] name = "${lower(var.project_id)}-function-${random_id.bucketname_suffix.hex}" location = var.bucket_location uniform_bucket_level_access = true force_destroy = true } resource "google_storage_bucket_object" "function_archive" { depends_on = [data.archive_file.function_zip] name = "function-switch.zip" bucket = google_storage_bucket.function_bucket.name source = "${path.module}/function-switch.zip" } resource "google_cloudfunctions_function" "function_switch" { depends_on = [google_project_service.required_api] name = "switch-route" description = "Function to switch route to primary or secondary VM" runtime = "python38" service_account_email = google_service_account.function_service_account.email available_memory_mb = 256 source_archive_bucket = google_storage_bucket.function_bucket.name source_archive_object = google_storage_bucket_object.function_archive.name trigger_http = true entry_point = "main" } # IAM entry for all users to invoke the function resource "google_cloudfunctions_function_iam_member" "invoker" { project = var.project_id region = google_cloudfunctions_function.function_switch.region cloud_function = google_cloudfunctions_function.function_switch.name role = "roles/cloudfunctions.invoker" member = "serviceAccount:${google_service_account.invoker_service_account.email}" } resource "google_compute_network" "failover_vpc" { depends_on = [google_project_service.required_api] name = var.network_name auto_create_subnetworks = false } resource "google_compute_subnetwork" "failover_subnet" { name = var.subnet_name ip_cidr_range = var.subnet_range network = google_compute_network.failover_vpc.id } resource "google_compute_firewall" "failover_firewall_http" { name = "failover-instances-http-traffic" allow { protocol = "tcp" ports = [80] } network = google_compute_network.failover_vpc.id source_tags = ["client"] target_tags = ["backend"] } resource "google_compute_firewall" "failover_firewall_ssh_iap" { name = "failover-ssh-iap" allow { protocol = "tcp" ports = ["22"] } network = google_compute_network.failover_vpc.id #IP range used by Identity-Aware-Proxy #See https://cloud.google.com/iap/docs/using-tcp-forwarding#create-firewall-rule source_ranges = ["35.235.240.0/20"] } resource "google_compute_firewall" "failover_firewall_vrrp" { name = "failover-vrrp" allow { #112 is VRRP IP protocol number required for keepalived communication protocol = "112" } network = google_compute_network.failover_vpc.id source_tags = ["backend"] target_tags = ["backend"] } resource "google_compute_instance" "nginx_primary_instance" { name = local.primary_instance_name machine_type = local.machine_type boot_disk { initialize_params { image = local.image } } metadata_startup_script = templatefile("startup-script.tmpl", { server_number = 1 function_url = google_cloudfunctions_function.function_switch.https_trigger_url target = "primary" floating_ip = var.floating_ip ip = var.primary_ip peer_ip = var.secondary_ip state = "MASTER" priority = 100 vrrp_password = var.vrrp_password }) can_ip_forward = true tags = ["backend"] network_interface { subnetwork = google_compute_subnetwork.failover_subnet.id access_config {} network_ip = var.primary_ip } service_account { email = google_service_account.invoker_service_account.email scopes = ["cloud-platform"] } allow_stopping_for_update = true } resource "google_compute_instance" "nginx_secondary_instance" { name = local.secondary_instance_name machine_type = local.machine_type boot_disk { initialize_params { image = local.image } } metadata_startup_script = templatefile("startup-script.tmpl", { server_number = 2 function_url = google_cloudfunctions_function.function_switch.https_trigger_url target = "secondary" floating_ip = var.floating_ip ip = var.secondary_ip peer_ip = var.primary_ip state = "BACKUP" priority = 50 vrrp_password = var.vrrp_password }) can_ip_forward = true tags = ["backend"] network_interface { subnetwork = google_compute_subnetwork.failover_subnet.id access_config {} network_ip = var.secondary_ip } service_account { email = google_service_account.invoker_service_account.email scopes = ["cloud-platform"] } allow_stopping_for_update = true } resource "google_compute_route" "floating_ip_route" { depends_on = [google_compute_subnetwork.failover_subnet] name = var.route_name dest_range = "${var.floating_ip}/32" network = google_compute_network.failover_vpc.name next_hop_instance = google_compute_instance.nginx_primary_instance.id priority = 1000 } resource "google_compute_instance" "client-vm" { name = "client" machine_type = local.machine_type tags = ["client"] boot_disk { initialize_params { image = local.image } } network_interface { subnetwork = google_compute_subnetwork.failover_subnet.name } allow_stopping_for_update = true }