Build it with Terraform
Provision Google Cloud infrastructure using Hashicorp Terraform. Spin up instances of CloudSQL, Redis, Kubernetes and more.


This post covers how to build Google Cloud Platform infrastructure usingTerraform.
The files you need include:
- main.tf
- variables.tf
- backend.tf (Optional)
The configurations are referenced from inside modules in this implementation, but you could have everything defined in onemain.tf.
Note: Google Cloud Platform requires APIs to be enabled, and you will need account credentials with sufficient permission to build the following resources. The project will need to be linked to a billing account.
GCP Setup via CLI
Here's what you'll need to do to get set up via the CLI:
$ export PROJECT_ID=my_gcp_project$ export ACCOUNT_ID=$(gcloud beta billing accounts list | grep True | cut -d ' ' -f1)$ gcloud auth login$ gcloud projects create $PROJECT_ID$ gcloud config set compute/region us-east1$ gcloud config set project $PROJECT_ID$ gcloud beta billing projects link $PROJECT_ID --billing-account=$ACCOUNT_ID# enable apis$ gcloud services enable \ cloudapis.googleapis.com \ cloudresourcemanager.googleapis.com \ container.googleapis.com \ containerregistry.googleapis.com \ iam.googleapis.com \ redis.googleapis.com \ servicenetworking.googleapis.com \ sqladmin.googleapis.com# If you want to use gcs for remote storage$ gsutil mb -c standard -l us-east1 gs://$PROJECT_ID# Create a service account for terraform$ gcloud iam service-accounts create terraform \ --description="Terraform Service Account" \ --display-name="Terraform"$ gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:terraform@$PROJECT_ID.iam.gserviceaccount.com \ --role roles/owner$ gcloud iam service-accounts keys create CREDENTIALS_FILE.json --iam-account=terraform@$PROJECT_ID.iam.gserviceaccount.com --project $PROJECT_ID$ mv CREDENTIALS_FILE.json terraform/
Setting up Terraform
Once all the variables are set, run:
$ terraform init
This will download the necessary files to./terraform/
.
Next we'll create a dry-run execution plan to see what infrastructure will be built, and decide if any changes are necessary:
$ terraform plan
The following will create the infrastructure as it's been defined:
$ terraform apply
Configurations for GCP Infrastructure
Let's get into what our actual Terraform configurations look like. We start with some general configuration inmain.tf:
locals { database_version = "" # "POSTGRES_11" network = "" # Network name region = "" # us-east1 project_id = "" # GCP Project ID subnetwork = "" # Subnetwork name}// Configure the Google Cloud providerprovider "google" { credentials = file("CREDENTIALS_FILE.json") project = local.project_id region = local.region}provider "google-beta" { credentials = file("CREDENTIALS_FILE.json") project = local.project_id region = local.region}module "cloudsql" { source = "./modules/cloudsql" network = local.network private_ip_name = "" # Private IP Name project = local.project_id region = local.region}module "gke" { source = "./modules/gke" cluster = "" # Cluster Name network = local.network project = local.project_id region = local.region subnetwork = local.subnetwork zones = "" # ["us-east1-b", "us-east1-c", "us-east1-d"]}module "memorystore" { source = "./modules/memorystore" display_name = "" # Display Name ip_range = "" # location = "" # Zone name = "" # Instance name network = local.network project = local.project_id redis_version = "" # 5.0 region = local.region size = "" # 1 tier = "" # STANDARD}module "vpc" { source = "./modules/vpc" project = local.project_id network = local.network region = local.region subnetwork = local.subnetwork}
Next we need to make Terraform aware of our credentials file inbackend.tf:
data "terraform_remote_state" "backend" { backend = "gcs" config = { bucket = "" prefix = "terraform" credentials = file("CREDENTIALS_FILE.json") }}
# GCP variables
CloudSQL Configuration
locals { network = join("/", ["projects", var.project, "global", "networks", var.network])}resource "google_compute_global_address" "private_ip_address" { provider = google-beta name = var.private_ip_name purpose = "VPC_PEERING" address_type = "INTERNAL" prefix_length = 16 network = local.network depends_on = [local.network]}resource "google_service_networking_connection" "private_vpc_connection" { provider = google-beta network = local.network service = "servicenetworking.googleapis.com" reserved_peering_ranges = [google_compute_global_address.private_ip_address.name] depends_on = [local.network]}resource "random_id" "db_name_suffix" { byte_length = 4}resource "google_sql_database_instance" "instance" { provider = google-beta name = "private-instance-${random_id.db_name_suffix.hex}" database_version = var.database_version region = var.region depends_on = [google_service_networking_connection.private_vpc_connection] settings { tier = "db-custom-1-3840" ip_configuration { ipv4_enabled = false private_network = local.network } }}resource "google_sql_user" "users" { name = var.user_name instance = google_sql_database_instance.instance.name password = var.user_password}
variable "database_version" { description = "The database version"}variable "network" { description = "The name of the network being created"}variable "private_ip_name" { description = "The name of the private ip address being created"}variable "project" { description = "Project ID"}variable "region" { description = "Region"}variable "user_name" { default = "DB_USER"}variable "user_password" { default = "DB_PASSWORD"}
Kubernetes Configuration
module "gke" { source = "terraform-google-modules/kubernetes-engine/google" project_id = var.project name = var.cluster region = var.region zones = var.zones network = var.network subnetwork = var.subnetwork ip_range_pods = join("-",[var.subnetwork,"pods"]) ip_range_services = join("-",[var.subnetwork,"services"]) http_load_balancing = "true" horizontal_pod_autoscaling = "true" network_policy = "true" maintenance_start_time = "05:00" remove_default_node_pool = "true" node_pools = [ { name = "pool-1" machine_type = "n1-standard-2" min_count = 1 max_count = 10 local_ssd_count = 0 disk_size_gb = 100 disk_type = "pd-standard" image_type = "COS" auto_repair = "true" auto_upgrade = "true" preemptible = "true" initial_node_count = 1 } ] node_pools_oauth_scopes = { all = [ "https://www.googleapis.com/auth/cloud-platform", ] } node_pools_labels = { all = {} } node_pools_metadata = { all = {} } node_pools_tags = { all = [] }}
variable "cluster" { description = "Cluster name"}variable "kubernetes_version" { description = "The Kubernetes version of the masters. If set to 'latest' it will pull latest available version in the selected region." type = string default = "latest"}variable "network" { description = "The name of the network being created"}variable "project" { description = "Project"}variable "region" { description = "Region of resources"}variable "subnetwork" { description = "The name of the subnetwork being created"}variable "zones" { description = "Zones"}
Redis Configuration
resource "google_redis_instance" "cache" { authorized_network = var.network display_name = var.display_name name = var.name memory_size_gb = var.size location_id = var.location project = var.project redis_version = var.redis_version region = var.region reserved_ip_range = var.ip_range tier = var.tier}
variable "display_name" { description = "Instance Name"}variable "ip_range" { description = "IP Range"}variable "location" { description = "Zone"}variable "name" { description = "Instance Name"}variable "network" { description = "Authorized Network"}variable "project" { description = "Project ID"}variable "redis_version" { description = "Redis Version"}variable "region" { description = "Region"}variable "size" { description = "Memory Size in GB"}variable "tier" { description = "Service Tier"}
VPC Configuration
module "vpc" { source = "terraform-google-modules/network/google" version = "~> 2.3" project_id = var.project network_name = var.network routing_mode = "GLOBAL" subnets = [ { subnet_name = var.subnetwork subnet_ip = "10.183.0.0/20" subnet_region = var.region subnet_private_access = "true" subnet_flow_logs = "true" description = var.subnet_description } ] secondary_ranges = { (var.subnetwork) = [ { range_name = join("-", [var.subnetwork, "pods"]) ip_cidr_range = "10.184.0.0/14" }, { range_name = join("-", [var.subnetwork, "services"]) ip_cidr_range = "10.188.0.0/20" }, ] }}
variable "auto_create_subnetworks" { type = bool description = "When set to true, the network is created in 'auto subnet mode' and it will create a subnet for each region automatically across the 10.128.0.0/9 address range. When set to false, the network is created in 'custom subnet mode' so the user can explicitly connect subnetwork resources." default = false}variable "description" { type = string description = "An optional description of this resource. The resource must be recreated to modify this field." default = "Toptal VPC network"}variable "network" { description = "The name of the network being created"}variable "project" { description = "Toptal Project"}variable "region" { description = "Region of resources"}variable "routing_mode" { type = string default = "GLOBAL" description = "The network routing mode (default 'GLOBAL')"}variable "subnet_description" { type = string description = "An optional description of this resource. The resource must be recreated to modify this field." default = "Toptal VPC subnetwork"}variable "shared_vpc_host" { type = bool description = "Makes this project a Shared VPC host if 'true' (default 'false')" default = false}variable "subnetwork" { description = "The name of the subnetwork being created"}