infra/main.tf (201 lines of code) (raw):

# Copyright 2023 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 { cluster_endpoint = "https://${google_container_cluster.jss_pos.endpoint}" cluster_ca_certificate = google_container_cluster.jss_pos.master_auth[0].cluster_ca_certificate kubernetes_service_account = "pos-access-sa" kubernetes_namespace = "default" } // Load the default client configuration used by the Google Cloud provider. data "google_client_config" "default" {} provider "google" { project = var.project_id region = var.region } provider "kubernetes" { host = local.cluster_endpoint token = data.google_client_config.default.access_token cluster_ca_certificate = base64decode(local.cluster_ca_certificate) } provider "helm" { kubernetes { host = local.cluster_endpoint token = data.google_client_config.default.access_token cluster_ca_certificate = base64decode(local.cluster_ca_certificate) } } // Enable all the Google Cloud APIs required for this solution module "enable_google_apis" { source = "terraform-google-modules/project-factory/google//modules/project_services" version = "~> 14.0" project_id = var.project_id disable_services_on_destroy = false activate_apis = [ "cloudresourcemanager.googleapis.com", "container.googleapis.com", "dns.googleapis.com", "gkehub.googleapis.com", "iam.googleapis.com", "monitoring.googleapis.com", "spanner.googleapis.com", ] } // Create a Google Service Account. This service account will be used by the // cluster autoscaler of the Google Kubernetes Engine cluster and by the Point // of sale application Pod, when accessing the Cloud Spanner instance. For the // latter use-case, Workload Identity is used with a Kubernetes Service Account resource "google_service_account" "jss_pos" { depends_on = [module.enable_google_apis] account_id = "jss-pos-${var.resource_name_suffix}" display_name = "jss-pos-${var.resource_name_suffix}" description = "Service Account used by the Dynamic Point-of-sale Java App Jump Start Solution" project = var.project_id } // Add the required roles to the Google Service Account to be used alongside the // Kubernetes Service Account to access Spanner via WorkloadIdentity resource "google_project_iam_member" "google_service_account_is_spanner_user" { project = var.project_id role = "roles/spanner.databaseUser" member = "serviceAccount:${google_service_account.jss_pos.email}" } resource "google_project_iam_member" "google_service_account_is_trace_agent" { project = var.project_id role = "roles/cloudtrace.agent" member = "serviceAccount:${google_service_account.jss_pos.email}" } resource "google_project_iam_member" "google_service_account_is_monitoring_agent" { project = var.project_id role = "roles/monitoring.metricWriter" member = "serviceAccount:${google_service_account.jss_pos.email}" } resource "google_project_iam_member" "google_service_account_is_logging_writer" { project = var.project_id role = "roles/logging.logWriter" member = "serviceAccount:${google_service_account.jss_pos.email}" } // Create a dedicated Virtual Private Cloud (VPC) network for this solution. // This network will be used for any network scoped resources in GCP like the // GKE cluster and any load balancers created by Kubernetes Services resource "google_compute_network" "jss_pos" { depends_on = [module.enable_google_apis] project = var.project_id name = "jss-pos-${var.resource_name_suffix}" auto_create_subnetworks = true } // A public external IP address that will be statically attached to the // Loadbalancer type Kubernetes Service created for the solution resource "google_compute_address" "jss_pos" { depends_on = [module.enable_google_apis] name = "jss-pos-${var.resource_name_suffix}" project = var.project_id region = var.region address_type = "EXTERNAL" } ######################################################################## # Google Kubernetes Engine Cluster resources ######################################################################## resource "google_container_cluster" "jss_pos" { depends_on = [ module.enable_google_apis, google_compute_address.jss_pos, ] name = "jss-pos-cluster-${var.resource_name_suffix}" project = var.project_id location = var.region network = google_compute_network.jss_pos.id enable_autopilot = true resource_labels = var.labels deletion_protection = false cluster_autoscaling { auto_provisioning_defaults { service_account = google_service_account.jss_pos.email } } ip_allocation_policy { # Need an empty ip_allocation_policy to overcome an error related to autopilot node pool constraints. # Workaround from https://github.com/hashicorp/terraform-provider-google/issues/10782#issuecomment-1024488630 } } resource "kubernetes_service_account" "jss_pos" { depends_on = [google_container_cluster.jss_pos] metadata { name = local.kubernetes_service_account namespace = local.kubernetes_namespace annotations = { "iam.gke.io/gcp-service-account" = google_service_account.jss_pos.email } } } resource "google_project_iam_member" "jss_pos_role_metric_writer" { project = var.project_id role = "roles/monitoring.metricWriter" member = "serviceAccount:${google_service_account.jss_pos.email}" } resource "google_project_iam_member" "jss_pos_role_log_writer" { project = var.project_id role = "roles/logging.logWriter" member = "serviceAccount:${google_service_account.jss_pos.email}" } resource "google_project_iam_member" "jss_pos_role_monitoring_viewer" { project = var.project_id role = "roles/monitoring.viewer" member = "serviceAccount:${google_service_account.jss_pos.email}" } resource "google_project_iam_member" "jss_pos_role_stackdriver_writer" { project = var.project_id role = "roles/stackdriver.resourceMetadata.writer" member = "serviceAccount:${google_service_account.jss_pos.email}" } #----------------------------------------------------------------------- resource "google_service_account_iam_member" "jss_poss_impersonate_google_sa" { depends_on = [kubernetes_service_account.jss_pos] service_account_id = google_service_account.jss_pos.name role = "roles/iam.workloadIdentityUser" member = "serviceAccount:${var.project_id}.svc.id.goog[${local.kubernetes_namespace}/${local.kubernetes_service_account}]" } ######################################################################## # Google Spanner resources ######################################################################## resource "google_spanner_instance" "jss_pos" { depends_on = [module.enable_google_apis] config = "regional-us-central1" display_name = "jss-pos" project = var.project_id num_nodes = 1 labels = {} } resource "google_spanner_database" "jss_pos" { instance = google_spanner_instance.jss_pos.name project = var.project_id name = "pos_db" database_dialect = "GOOGLE_STANDARD_SQL" version_retention_period = "3d" deletion_protection = false ddl = [ file("${path.module}/sql-schema/items.sql"), file("${path.module}/sql-schema/payments.sql"), file("${path.module}/sql-schema/payment_units.sql"), ] } #----------------------------------------------------------------------- ######################################################################## # Helm release resource to deploy the application into the GKE cluster ######################################################################## resource "helm_release" "jss_point_of_sale" { depends_on = [ google_container_cluster.jss_pos, google_spanner_database.jss_pos, google_compute_address.jss_pos, ] name = "jss-point-of-sale" chart = "${path.module}/charts" timeout = 600 values = [ file("${path.module}/charts/values.yaml"), ] set { name = "loadbalancer_ip" value = google_compute_address.jss_pos.address } set { name = "service_account" value = local.kubernetes_service_account } set { name = "project_id" value = var.project_id } set { name = "spanner_id" value = google_spanner_instance.jss_pos.name } set { name = "spanner_database" value = google_spanner_database.jss_pos.name } } #-----------------------------------------------------------------------