This post covers how to build Google Cloud Platform infrastructure using Terraform .
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 one main.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/
gcloud CLI setup. Once all the variables are set, run:
$ terraform init
Initialize modules 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
Create an execution plan The following will create the infrastructure as it's been defined:
$ terraform apply
Apply changes Configurations for GCP Infrastructure Let's get into what our actual Terraform configurations look like. We start with some general configuration in main.tf :
locals {
database_version = "" # "POSTGRES_11"
network = "" # Network name
region = "" # us-east1
project_id = "" # GCP Project ID
subnetwork = "" # Subnetwork name
}
// Configure the Google Cloud provider
provider "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
}
main.tf Next we need to make Terraform aware of our credentials file in backend.tf :
data "terraform_remote_state" "backend" {
backend = "gcs"
config = {
bucket = ""
prefix = "terraform"
credentials = file("CREDENTIALS_FILE.json")
}
}
backend.tf # GCP variables
variables.tf 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
}
modules/cloudsql/main.tf 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"
}
modules/cloudsql/variables.tf 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 = []
}
}
modules/gke/main.tf 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"
}
modules/gke/variables.tf 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
}
modules/memorystore/main.tf 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"
}
modules/memorystore/variables.tf 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"
},
]
}
}
modules/vpc/main.tf 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"
}
modules/vpc/variables.tf