deployment/mystudies.hcl (1,103 lines of code) (raw):

# Copyright 2020 Google LLC # # Use of this source code is governed by an MIT-style # license that can be found in the LICENSE file or at # https://opensource.org/licenses/MIT. # # This is the solution template for MyStudies. Deployment specific # values are to be filled in ./deployment.hcl. # {{$recipes := "git://github.com/GoogleCloudPlatform/healthcare-data-protection-suite//templates/tfengine/recipes"}} # {{$ref := "ref=templates-v0.4.0"}} data = { parent_type = "folder" parent_id = "{{.folder_id}}" billing_account = "{{.billing_account}}" state_bucket = "{{.prefix}}-{{.env}}-terraform-state" # Default locations for resources. Can be overridden in individual templates. bigquery_location = "us-east1" # BigQuery is not available in "us-central1" cloud_sql_region = "{{.default_location}}" cloud_sql_zone = "{{.default_zone}}" compute_region = "{{.default_location}}" compute_zone = "{{.default_zone}}" gke_region = "{{.default_location}}" healthcare_location = "{{.default_location}}" storage_location = "{{.default_location}}" secret_locations = ["{{.default_location}}"] } # Central devops project for Terraform state management and CI/CD. template "devops" { recipe_path = "{{$recipes}}/devops.hcl?{{$ref}}" output_path = "./devops" data = { # During Step 1, set to `true` and re-run the engine after generated devops module has been deployed. # Run `terraform init` in the devops module to backup its state to GCS. enable_gcs_backend = false admins_group = "{{.prefix}}-{{.env}}-folder-admins@{{.domain}}" project = { project_id = "{{.prefix}}-{{.env}}-devops" owners = [ "group:{{.prefix}}-{{.env}}-devops-owners@{{.domain}}", ] apis = [ "container.googleapis.com", "firebase.googleapis.com", "iap.googleapis.com", "secretmanager.googleapis.com", ] } } } template "cicd" { recipe_path = "{{$recipes}}/cicd.hcl?{{$ref}}" output_path = "./cicd" data = { project_id = "{{.prefix}}-{{.env}}-devops" github = { owner = "{{.github_owner}}" name = "{{.github_repo}}" } branch_name = "{{.github_branch}}" terraform_root = "deployment/terraform" # Prepare and enable default triggers. triggers = { validate = {} plan = {} apply = {} } # IAM members to give the roles/cloudbuild.builds.viewer permission so they can see build results. build_viewers = [ "group:{{.prefix}}-{{.env}}-cicd-viewers@{{.domain}}", ] managed_dirs = [ "devops", // NOTE: CICD service account can only update APIs on the devops project. "audit", "{{.prefix}}-{{.env}}-secrets", "{{.prefix}}-{{.env}}-networks", "{{.prefix}}-{{.env}}-apps", "{{.prefix}}-{{.env}}-firebase", "{{.prefix}}-{{.env}}-data", ] } } template "audit" { recipe_path = "{{$recipes}}/audit.hcl?{{$ref}}" output_path = "./audit" data = { auditors_group = "{{.prefix}}-{{.env}}-auditors@{{.domain}}" project = { project_id = "{{.prefix}}-{{.env}}-audit" } logs_bigquery_dataset = { dataset_id = "{{.prefix}}_{{.env}}_1yr_audit_logs" } logs_storage_bucket = { name = "{{.prefix}}-{{.env}}-7yr-audit-logs" } additional_filters = [ "logName=\\\"logs/application-audit-log\\\"", ] } } # Central secrets project and resources. # NOTE: Any secret in this deployment that is not automatically filled in with # a value must be filled manually in the GCP console secret manager page before # any deployment can access its value. template "project_secrets" { recipe_path = "{{$recipes}}/project.hcl?{{$ref}}" output_path = "./{{.prefix}}-{{.env}}-secrets" data = { project = { project_id = "{{.prefix}}-{{.env}}-secrets" apis = [ "secretmanager.googleapis.com" ] } resources = { secrets = [ { secret_id = "manual-mystudies-email-address" }, { secret_id = "manual-mystudies-email-password" }, { secret_id = "manual-mystudies-from-email-address" }, { secret_id = "manual-mystudies-contact-email-address" }, { secret_id = "manual-mystudies-from-email-domain" }, { secret_id = "manual-mystudies-smtp-hostname" }, { secret_id = "manual-mystudies-smtp-use-ip-allowlist" }, { secret_id = "manual-log-path" }, { secret_id = "manual-org-name" }, { secret_id = "manual-terms-url" }, { secret_id = "manual-privacy-url" }, { secret_id = "manual-fcm-api-url" }, { secret_id = "manual-ios-deeplink-url" }, { secret_id = "manual-android-deeplink-url" }, { secret_id = "manual-region-id" }, { secret_id = "manual-consent-enabled" }, { secret_id = "manual-fhir-enabled" }, { secret_id = "manual-discard-fhir" }, { secret_id = "manual-ingest-data-to-bigquery" }, { secret_id = "auto-mystudies-sql-default-user-password" secret_data = "$${random_password.passwords[\"mystudies_sql_default_user_password\"].result}" }, { secret_id = "auto-hydra-db-password" secret_data = "$${random_password.passwords[\"hydra_db_password\"].result}" }, { secret_id = "auto-hydra-db-user" secret_data = "$${random_string.strings[\"hydra_db_user\"].result}" }, { secret_id = "auto-hydra-system-secret" secret_data = "$${random_password.system_secrets[\"hydra_system_secret\"].result}" }, { secret_id = "auto-auth-server-db-user" secret_data = "$${random_string.strings[\"auth_server_db_user\"].result}" }, { secret_id = "auto-auth-server-db-password" secret_data = "$${random_password.passwords[\"auth_server_db_password\"].result}" }, { secret_id = "auto-auth-server-client-id" secret_data = "$${random_string.strings[\"auth_server_client_id\"].result}" }, { secret_id = "auto-auth-server-secret-key" secret_data = "$${random_password.passwords[\"auth_server_secret_key\"].result}" }, { secret_id = "auto-auth-server-encryptor-password" secret_data = "$${random_password.passwords[\"auth_server_encryptor_password\"].result}" }, { secret_id = "auto-response-datastore-db-user" secret_data = "$${random_string.strings[\"response_datastore_db_user\"].result}" }, { secret_id = "auto-response-datastore-db-password" secret_data = "$${random_password.passwords[\"response_datastore_db_password\"].result}" }, { secret_id = "auto-response-datastore-client-id" secret_data = "$${random_string.strings[\"response_datastore_client_id\"].result}" }, { secret_id = "auto-response-datastore-secret-key" secret_data = "$${random_password.passwords[\"response_datastore_secret_key\"].result}" }, { secret_id = "auto-study-builder-db-user" secret_data = "$${random_string.strings[\"study_builder_db_user\"].result}" }, { secret_id = "auto-study-builder-db-password" secret_data = "$${random_password.passwords[\"study_builder_db_password\"].result}" }, { secret_id = "auto-study-builder-client-id" secret_data = "$${random_string.strings[\"study_builder_client_id\"].result}" }, { secret_id = "auto-study-builder-secret-key" secret_data = "$${random_password.passwords[\"study_builder_secret_key\"].result}" }, { secret_id = "auto-study-datastore-db-user" secret_data = "$${random_string.strings[\"study_datastore_db_user\"].result}" }, { secret_id = "auto-study-datastore-db-password" secret_data = "$${random_password.passwords[\"study_datastore_db_password\"].result}" }, { secret_id = "auto-study-datastore-client-id" secret_data = "$${random_string.strings[\"study_datastore_client_id\"].result}" }, { secret_id = "auto-study-datastore-secret-key" secret_data = "$${random_password.passwords[\"study_datastore_secret_key\"].result}" }, { secret_id = "auto-participant-consent-datastore-db-user" secret_data = "$${random_string.strings[\"participant_consent_datastore_db_user\"].result}" }, { secret_id = "auto-participant-consent-datastore-db-password" secret_data = "$${random_password.passwords[\"participant_consent_datastore_db_password\"].result}" }, { secret_id = "auto-participant-consent-datastore-client-id" secret_data = "$${random_string.strings[\"participant_consent_datastore_client_id\"].result}" }, { secret_id = "auto-participant-consent-datastore-secret-key" secret_data = "$${random_password.passwords[\"participant_consent_datastore_secret_key\"].result}" }, { secret_id = "auto-participant-enroll-datastore-db-user" secret_data = "$${random_string.strings[\"participant_enroll_datastore_db_user\"].result}" }, { secret_id = "auto-participant-enroll-datastore-db-password" secret_data = "$${random_password.passwords[\"participant_enroll_datastore_db_password\"].result}" }, { secret_id = "auto-participant-enroll-datastore-client-id" secret_data = "$${random_string.strings[\"participant_enroll_datastore_client_id\"].result}" }, { secret_id = "auto-participant-enroll-datastore-secret-key" secret_data = "$${random_password.passwords[\"participant_enroll_datastore_secret_key\"].result}" }, { secret_id = "auto-participant-user-datastore-db-user" secret_data = "$${random_string.strings[\"participant_user_datastore_db_user\"].result}" }, { secret_id = "auto-participant-user-datastore-db-password" secret_data = "$${random_password.passwords[\"participant_user_datastore_db_password\"].result}" }, { secret_id = "auto-participant-user-datastore-client-id" secret_data = "$${random_string.strings[\"participant_user_datastore_client_id\"].result}" }, { secret_id = "auto-participant-user-datastore-secret-key" secret_data = "$${random_password.passwords[\"participant_user_datastore_secret_key\"].result}" }, { secret_id = "auto-participant-manager-datastore-db-user" secret_data = "$${random_string.strings[\"participant_manager_datastore_db_user\"].result}" }, { secret_id = "auto-participant-manager-datastore-db-password" secret_data = "$${random_password.passwords[\"participant_manager_datastore_db_password\"].result}" }, { secret_id = "auto-participant-manager-datastore-client-id" secret_data = "$${random_string.strings[\"participant_manager_datastore_client_id\"].result}" }, { secret_id = "auto-participant-manager-datastore-secret-key" secret_data = "$${random_password.passwords[\"participant_manager_datastore_secret_key\"].result}" }, { secret_id = "auto-sd-response-datastore-token" secret_data = "$${random_password.tokens[\"sd_response_datastore_token\"].result}" }, { secret_id = "auto-sd-response-datastore-id" secret_data = "$${random_string.strings[\"sd_response_datastore_id\"].result}" }, { secret_id = "auto-sd-android-token" secret_data = "$${random_password.tokens[\"sd_android_token\"].result}" }, { secret_id = "auto-sd-android-id" secret_data = "$${random_string.strings[\"sd_android_id\"].result}" }, { secret_id = "auto-sd-ios-token" secret_data = "$${random_password.tokens[\"sd_ios_token\"].result}" }, { secret_id = "auto-sd-ios-id" secret_data = "$${random_string.strings[\"sd_ios_id\"].result}" }, ] } terraform_addons = { raw_config = <<EOF locals { apps = [ "auth_server", "response_datastore", "study_builder", "study_datastore", "participant_consent_datastore", "participant_enroll_datastore", "participant_user_datastore", "participant_manager_datastore", ] } resource "random_string" "strings" { for_each = toset(concat( [ "hydra_db_user", "sd_response_datastore_id", "sd_android_id", "sd_ios_id", ], formatlist("%s_db_user", local.apps), formatlist("%s_client_id", local.apps)) ) length = 16 special = false } resource "random_password" "passwords" { for_each = toset(concat( [ "mystudies_sql_default_user_password", "hydra_db_password", "auth_server_encryptor_password" ], formatlist("%s_db_password", local.apps), formatlist("%s_secret_key", local.apps)) ) length = 16 special = false } resource "random_password" "tokens" { for_each = toset([ "sd_response_datastore_token", "sd_android_token", "sd_ios_token", ]) length = 16 special = false } resource "random_password" "system_secrets" { for_each = toset([ "hydra_system_secret", ]) length = 32 special = false } EOF } } } # Central networks host project and resources. template "project_networks" { recipe_path = "{{$recipes}}/project.hcl?{{$ref}}" output_path = "./{{.prefix}}-{{.env}}-networks" data = { project = { project_id = "{{.prefix}}-{{.env}}-networks" is_shared_vpc_host = true apis = [ "compute.googleapis.com", "container.googleapis.com", "iap.googleapis.com", "servicenetworking.googleapis.com", "sqladmin.googleapis.com", ] } resources = { compute_networks = [{ name = "{{.prefix}}-{{.env}}-network" subnets = [ { name = "{{.prefix}}-{{.env}}-bastion-subnet" # 10.0.128.0 --> 10.0.128.255 ip_range = "10.0.128.0/24" }, { name = "{{.prefix}}-{{.env}}-gke-subnet" # 10.0.0.0 --> 10.0.127.255 ip_range = "10.0.0.0/17" secondary_ranges = [ { name = "{{.prefix}}-{{.env}}-pods-range" ip_range = "172.16.0.0/14" }, { name = "{{.prefix}}-{{.env}}-services-range" ip_range = "172.20.0.0/14" } ] }, ] cloud_sql_private_service_access = {} # Enable SQL private service access. }] # To connect to the CloudSQL instance via the bastion VM: # $ gcloud compute ssh bastion-vm --zone={{.default_location}}-{{.default_zone}} --project={{.prefix}}-{{.env}}-networks # $ cloud_sql_proxy -instances={{.prefix}}-{{.env}}-data:{{.default_location}}:mystudies=tcp:3306 # $ mysql -u default -p --host 127.0.0.1 bastion_hosts = [{ name = "bastion-vm" network = "$${module.{{.prefix}}_{{.env}}_network.network.network.self_link}" subnet = "$${module.{{.prefix}}_{{.env}}_network.subnets[\"{{.default_location}}/{{.prefix}}-{{.env}}-bastion-subnet\"].self_link}" image_family = "ubuntu-2004-lts" image_project = "ubuntu-os-cloud" members = [ "group:{{.prefix}}-{{.env}}-bastion-accessors@{{.domain}}", ] startup_script = <<EOF sudo apt-get -y update sudo apt-get -y install mysql-client sudo wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O /usr/local/bin/cloud_sql_proxy sudo chmod +x /usr/local/bin/cloud_sql_proxy EOF }] compute_routers = [{ name = "{{.prefix}}-{{.env}}-router" network = "$${module.{{.prefix}}_{{.env}}_network.network.network.self_link}" nats = [{ name = "{{.prefix}}-{{.env}}-nat" source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" subnetworks = [ { name = "$${module.{{.prefix}}_{{.env}}_network.subnets[\"{{.default_location}}/{{.prefix}}-{{.env}}-bastion-subnet\"].self_link}" source_ip_ranges_to_nat = ["PRIMARY_IP_RANGE"] secondary_ip_range_names = [] }, { name = "$${module.{{.prefix}}_{{.env}}_network.subnets[\"{{.default_location}}/{{.prefix}}-{{.env}}-gke-subnet\"].self_link}" source_ip_ranges_to_nat = ["ALL_IP_RANGES"] secondary_ip_range_names = [] }, ] }] }] } terraform_addons = { raw_config = <<EOF resource "google_compute_firewall" "fw_allow_k8s_ingress_lb_health_checks" { name = "fw-allow-k8s-ingress-lb-health-checks" description = "GCE L7 firewall rule" network = module.{{.prefix}}_{{.env}}_network.network.network.self_link project = module.project.project_id allow { protocol = "tcp" ports = ["30000-32767"] } allow { protocol = "tcp" ports = ["4444"] } allow { protocol = "tcp" ports = ["80"] } allow { protocol = "tcp" ports = ["8080"] } allow { protocol = "tcp" ports = ["10256"] } # Load Balancer Health Check IP ranges. source_ranges = [ "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "35.191.0.0/16", ] target_tags = [ "gke-{{.prefix}}-{{.env}}-gke-cluster", "gke-{{.prefix}}-{{.env}}-gke-cluster-default-node-pool", ] } EOF } } } # Apps project and resources. template "project_apps" { recipe_path = "{{$recipes}}/project.hcl?{{$ref}}" output_path = "./{{.prefix}}-{{.env}}-apps" data = { project = { project_id = "{{.prefix}}-{{.env}}-apps" apis = [ "binaryauthorization.googleapis.com", "compute.googleapis.com", "container.googleapis.com", "dns.googleapis.com", ] shared_vpc_attachment = { host_project_id = "{{.prefix}}-{{.env}}-networks" subnets = [{ name = "{{.prefix}}-{{.env}}-gke-subnet" }] } } resources = { gke_clusters = [{ name = "{{.prefix}}-{{.env}}-gke-cluster" network_project_id = "{{.prefix}}-{{.env}}-networks" network = "{{.prefix}}-{{.env}}-network" subnet = "{{.prefix}}-{{.env}}-gke-subnet" ip_range_pods_name = "{{.prefix}}-{{.env}}-pods-range" ip_range_services_name = "{{.prefix}}-{{.env}}-services-range" master_ipv4_cidr_block = "192.168.0.0/28" {{hclField . "master_authorized_networks"}} }] # Terraform-generated service account for use by the GKE apps. service_accounts = [ { account_id = "auth-server-gke-sa" }, { account_id = "hydra-gke-sa" }, { account_id = "response-datastore-gke-sa" }, { account_id = "study-builder-gke-sa" }, { account_id = "study-datastore-gke-sa" }, { account_id = "consent-datastore-gke-sa" }, { account_id = "enroll-datastore-gke-sa" }, { account_id = "user-datastore-gke-sa" }, { account_id = "participant-manager-gke-sa" }, { account_id = "triggers-pubsub-handler-gke-sa" }, ] iam_members = { # Adding Logs Writer permission to service accounts for application level audit logs "roles/logging.logWriter" = [ "serviceAccount:$${google_service_account.auth_server_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.hydra_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.response_datastore_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.study_builder_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.study_datastore_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.consent_datastore_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.enroll_datastore_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.user_datastore_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.participant_manager_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:$${google_service_account.triggers_pubsub_handler_gke_sa.account_id}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] # BigQuery Permissions "roles/bigquery.admin" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] } # Binary Authorization resources. # Simple configuration for now. Future # See https://cloud.google.com/binary-authorization/docs/overview binary_authorization = { admission_whitelist_patterns = [{ name_pattern = "gcr.io/cloudsql-docker/*" }, { name_pattern = "gcr.io/gke-release/istio/*" }, { name_pattern = "docker.io/prom/*" } ] } # DNS sets up nameservers to connect to the GKE clusters. dns_zones = [{ name = "{{.prefix}}-{{.env}}" domain = "{{.prefix}}-{{.env}}.{{.domain}}." type = "public" record_sets = [{ name = "participants" type = "A" ttl = 30 records = [ "$${google_compute_global_address.ingress_static_ip.address}", ] }, { name = "studies" type = "A" ttl = 30 records = [ "$${google_compute_global_address.ingress_static_ip.address}", ] }] }] } terraform_addons = { raw_config = <<EOF # Reserve a static external IP for the Ingress. resource "google_compute_global_address" "ingress_static_ip" { name = "{{.prefix}}-{{.env}}-ingress-ip" description = "Reserved static external IP for the GKE cluster Ingress and DNS configurations." address_type = "EXTERNAL" # This is the default, but be explicit because it's important. project = module.project.project_id } # Step 7: Cloud Build for Apps Project # ***NOTE***: First follow # https://cloud.google.com/cloud-build/docs/automating-builds/create-github-app-triggers#installing_the_cloud_build_app # to install the Cloud Build app and connect your GitHub repository to your Cloud project. # # The following content should be initially commented out if the above manual step is not completed. #7# locals { #7# apps_dependencies = { #7# "study-builder" = ["study-builder/**"] #7# "study-datastore" = ["study-datastore/**"] #7# "hydra" = ["hydra/**"] #7# "auth-server" = ["auth-server/**", "common-modules/**"] #7# "response-datastore" = ["response-datastore/**", "common-modules/**"] #7# "participant-datastore/consent-mgmt-module" = ["participant-datastore/consent-mgmt-module/**", "common-modules/**"] #7# "participant-datastore/user-mgmt-module" = ["participant-datastore/user-mgmt-module/**", "common-modules/**"] #7# "participant-datastore/enroll-mgmt-module" = ["participant-datastore/enroll-mgmt-module/**", "common-modules/**"] #7# "participant-manager-datastore" = ["participant-manager-datastore/**", "common-modules/**"] #7# "participant-manager" = ["participant-manager/**"] #7# } #7# } #7# resource "google_cloudbuild_trigger" "server_build_triggers" { #7# for_each = local.apps_dependencies #7# provider = google-beta #7# project = module.project.project_id #7# name = replace(each.key, "/", "-") #7# included_files = each.value #7# #7# github { #7# owner = "{{.github_owner}}" #7# name = "{{.github_repo}}" #7# push { branch = "^{{.github_branch}}$" } #7# } #7# filename = "$${each.key}/cloudbuild.yaml" #7# } EOF } } } # Firebase project and resources. template "project_firebase" { recipe_path = "{{$recipes}}/project.hcl?{{$ref}}" output_path = "./{{.prefix}}-{{.env}}-firebase" data = { project = { project_id = "{{.prefix}}-{{.env}}-firebase" apis = [ "firebase.googleapis.com", ] } } } # Data project and resources. template "project_data" { recipe_path = "{{$recipes}}/project.hcl?{{$ref}}" output_path = "./{{.prefix}}-{{.env}}-data" data = { project = { project_id = "{{.prefix}}-{{.env}}-data" apis = [ "bigquery.googleapis.com", "compute.googleapis.com", "servicenetworking.googleapis.com", "sqladmin.googleapis.com", "healthcare.googleapis.com", ] shared_vpc_attachment = { host_project_id = "{{.prefix}}-{{.env}}-networks" } } # Step 5.1: uncomment and re-run the engine once all previous steps have been completed. #5# terraform_addons = { #5# raw_config = <<EOF #5# locals { #5# apps = [ #5# "auth-server", #5# "response-datastore", #5# "study-builder", #5# "study-datastore", #5# "participant-consent-datastore", #5# "participant-enroll-datastore", #5# "participant-user-datastore", #5# "participant-manager-datastore", #5# "hydra", #5# ] #5# } #5# data "google_secret_manager_secret_version" "db_secrets" { #5# provider = google-beta #5# project = "{{.prefix}}-{{.env}}-secrets" #5# secret = each.key #5# for_each = toset(concat( #5# ["auto-mystudies-sql-default-user-password"], #5# formatlist("auto-%s-db-user", local.apps), #5# formatlist("auto-%s-db-password", local.apps)) #5# ) #5# } #5# resource "google_sql_user" "db_users" { #5# for_each = toset(local.apps) #5# name = data.google_secret_manager_secret_version.db_secrets["auto-$${each.key}-db-user"].secret_data #5# instance = module.mystudies.instance_name #5# host = "%" #5# password = data.google_secret_manager_secret_version.db_secrets["auto-$${each.key}-db-password"].secret_data #5# project = module.project.project_id #5# } #5# EOF #5# } resources = { # Step 5.2: uncomment and re-run the engine once all previous steps have been completed. #5# cloud_sql_instances = [{ #5# name = "mystudies" #5# type = "mysql" #5# network_project_id = "{{.prefix}}-{{.env}}-networks" #5# network = "{{.prefix}}-{{.env}}-network" #5# user_password = "$${data.google_secret_manager_secret_version.db_secrets[\"auto-mystudies-sql-default-user-password\"].secret_data}" #5# }] iam_members = { "roles/cloudsql.client" = [ "serviceAccount:bastion@{{.prefix}}-{{.env}}-networks.iam.gserviceaccount.com", "serviceAccount:auth-server-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:hydra-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:study-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:consent-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:enroll-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:user-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:participant-manager-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:triggers-pubsub-handler-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] # HEALTH CARE API Roles/Permissions "roles/healthcare.consentArtifactEditor" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:consent-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:enroll-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:user-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.consentEditor" = [ "serviceAccount:consent-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:enroll-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:user-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.consentArtifactReader" = [ "serviceAccount:participant-manager-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.consentReader" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.consentStoreViewer" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.datasetAdmin" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.consentStoreAdmin" = [ "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.fhirStoreAdmin" = [ "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/healthcare.fhirResourceEditor" = [ "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] # BigQuery Permissions #6# "roles/bigquery.admin" = [ #6# "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", #6# "serviceAccount:user-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", #6# "serviceAccount:service-$${module.project.project_number}@gcp-sa-healthcare.iam.gserviceaccount.com", #6# ] "roles/bigquery.dataEditor" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] "roles/datastore.user" = [ "serviceAccount:response-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", "serviceAccount:user-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com", ] #6# "roles/storage.objectAdmin" = [ #6# "serviceAccount:service-$${module.project.project_number}@gcp-sa-healthcare.iam.gserviceaccount.com", #6# ] } storage_buckets = [ { name = "{{.prefix}}-{{.env}}-mystudies-consent-documents" #6# iam_members = [ #6# { #6# role = "roles/storage.objectAdmin" #6# member = "serviceAccount:consent-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" #6# }, #6# { #6# role = "roles/storage.objectAdmin" #6# member = "serviceAccount:participant-manager-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" #6# }, #6# { #6# role = "roles/storage.objectAdmin" #6# member = "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" #6# }, # HEALTHCARE API SA role bindling to consent bucket #6# { #6# role = "roles/storage.objectViewer" #6# member = "serviceAccount:service-$${module.project.project_number}@gcp-sa-healthcare.iam.gserviceaccount.com" #6# }, #6# ] }, { name = "{{.prefix}}-{{.env}}-mystudies-study-resources" iam_members = [{ role = "roles/storage.objectAdmin" member = "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" }, { role = "roles/storage.objectAdmin" member = "serviceAccount:study-datastore-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" }, { role = "roles/storage.objectAdmin" member = "serviceAccount:participant-manager-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" }] }, { name = "{{.prefix}}-{{.env}}-mystudies-sql-import" # Step 6: uncomment and re-run the engine once all previous steps have been completed. #6# iam_members = [{ #6# role = "roles/storage.objectViewer" #6# member = "serviceAccount:$${module.mystudies.instance_service_account_email_address}" #6# }, #6# { #6# role = "roles/storage.objectAdmin" #6# member = "serviceAccount:study-builder-gke-sa@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" #6# }] }, ] } } } # Kubernetes Terraform deployment. This should be deployed manually as Cloud # Build cannot access the GKE cluster and should be deployed after the GKE # Cluster has been deployed. template "kubernetes" { recipe_path = "{{$recipes}}/deployment.hcl?{{$ref}}" output_path = "./kubernetes" data = { state_path_prefix = "kubernetes" terraform_addons = { raw_config = <<EOF data "google_client_config" "default" {} data "google_container_cluster" "gke_cluster" { name = "{{.prefix}}-{{.env}}-gke-cluster" location = "{{.default_location}}" project = "{{.prefix}}-{{.env}}-apps" } provider "kubernetes" { version = "1.13.3" load_config_file = false token = data.google_client_config.default.access_token host = data.google_container_cluster.gke_cluster.endpoint client_certificate = base64decode(data.google_container_cluster.gke_cluster.master_auth.0.client_certificate) client_key = base64decode(data.google_container_cluster.gke_cluster.master_auth.0.client_key) cluster_ca_certificate = base64decode(data.google_container_cluster.gke_cluster.master_auth.0.cluster_ca_certificate) } locals { # hydra is treated separately. apps = [ "auth-server", "response-datastore", "study-builder", "study-datastore", "participant-consent-datastore", "participant-enroll-datastore", "participant-user-datastore", "participant-manager-datastore", ] apps_db_names = { "auth-server" = "oauth_server_hydra" "response-datastore" = "mystudies_response_server" "study-builder" = "fda_hphc" "study-datastore" = "fda_hphc" "participant-consent-datastore" = "mystudies_participant_datastore" "participant-enroll-datastore" = "mystudies_participant_datastore" "participant-user-datastore" = "mystudies_participant_datastore" "participant-manager-datastore" = "mystudies_participant_datastore" } service_account_ids = [ "auth-server-gke-sa", "hydra-gke-sa", "response-datastore-gke-sa", "study-builder-gke-sa", "study-datastore-gke-sa", "consent-datastore-gke-sa", "enroll-datastore-gke-sa", "user-datastore-gke-sa", "participant-manager-gke-sa", ] } # Data sources from Secret Manager. data "google_secret_manager_secret_version" "secrets" { provider = google-beta project = "{{.prefix}}-{{.env}}-secrets" secret = each.key for_each = toset(concat( [ "manual-mystudies-email-address", "manual-mystudies-email-password", "manual-mystudies-contact-email-address", "manual-mystudies-from-email-address", "manual-mystudies-from-email-domain", "manual-mystudies-smtp-hostname", "manual-mystudies-smtp-use-ip-allowlist", "manual-log-path", "manual-org-name", "manual-terms-url", "manual-privacy-url", "manual-fcm-api-url", "manual-ios-deeplink-url", "manual-android-deeplink-url", "manual-region-id", "manual-consent-enabled", "manual-fhir-enabled", "manual-discard-fhir", "manual-ingest-data-to-bigquery", "auto-auth-server-encryptor-password", "auto-hydra-db-password", "auto-hydra-db-user", "auto-hydra-system-secret", "auto-sd-response-datastore-token", "auto-sd-response-datastore-id", "auto-sd-android-token", "auto-sd-android-id", "auto-sd-ios-token", "auto-sd-ios-id", ], formatlist("auto-%s-db-user", local.apps), formatlist("auto-%s-db-password", local.apps), formatlist("auto-%s-client-id", local.apps), formatlist("auto-%s-secret-key", local.apps)) ) } # Shared secrets. resource "kubernetes_secret" "shared_secrets" { metadata { name = "shared-secrets" } data = { consent_bucket_name = "{{.prefix}}-{{.env}}-mystudies-consent-documents" study_resources_bucket_name = "{{.prefix}}-{{.env}}-mystudies-study-resources" study_export_import_bucket_name = "{{.prefix}}-{{.env}}-mystudies-sql-import" institution_resources_bucket_name = "{{.prefix}}-{{.env}}-mystudies-institution-resources" base_url = "https://participants.{{.prefix}}-{{.env}}.{{.domain}}" studies_base_url = "https://studies.{{.prefix}}-{{.env}}.{{.domain}}" data_project_id = "{{.prefix}}-{{.env}}-data" firestore_project_id = "{{.prefix}}-{{.env}}-firebase" log_path = data.google_secret_manager_secret_version.secrets["manual-log-path"].secret_data org_name = data.google_secret_manager_secret_version.secrets["manual-org-name"].secret_data terms_url = data.google_secret_manager_secret_version.secrets["manual-terms-url"].secret_data privacy_url = data.google_secret_manager_secret_version.secrets["manual-privacy-url"].secret_data fcm_api_url = data.google_secret_manager_secret_version.secrets["manual-fcm-api-url"].secret_data region_id = data.google_secret_manager_secret_version.secrets["manual-region-id"].secret_data consent_enabled = data.google_secret_manager_secret_version.secrets["manual-consent-enabled"].secret_data fhir_enabled = data.google_secret_manager_secret_version.secrets["manual-fhir-enabled"].secret_data discard_fhir = data.google_secret_manager_secret_version.secrets["manual-discard-fhir"].secret_data ingest_data_to_bigquery = data.google_secret_manager_secret_version.secrets["manual-ingest-data-to-bigquery"].secret_data } } # App credentials. resource "kubernetes_secret" "apps_credentials" { for_each = toset(local.apps) metadata { name = "$${each.key}-credentials" } data = { dbusername = data.google_secret_manager_secret_version.secrets["auto-$${each.key}-db-user"].secret_data dbpassword = data.google_secret_manager_secret_version.secrets["auto-$${each.key}-db-password"].secret_data client_id = data.google_secret_manager_secret_version.secrets["auto-$${each.key}-client-id"].secret_data secret_key = data.google_secret_manager_secret_version.secrets["auto-$${each.key}-secret-key"].secret_data dbname = local.apps_db_names[each.key] } } # Client-side credentials. resource "kubernetes_secret" "client_side_credentials" { metadata { name = "client-side-credentials" } data = { client_id = data.google_secret_manager_secret_version.secrets["auto-auth-server-client-id"].secret_data secret_key = data.google_secret_manager_secret_version.secrets["auto-auth-server-secret-key"].secret_data } } # Auth-server secrets. resource "kubernetes_secret" "auth_server_secrets" { metadata { name = "auth-server-secrets" } data = { encryptor_password = data.google_secret_manager_secret_version.secrets["auto-auth-server-encryptor-password"].secret_data ios_deeplink_url = data.google_secret_manager_secret_version.secrets["manual-ios-deeplink-url"].secret_data android_deeplink_url = data.google_secret_manager_secret_version.secrets["manual-android-deeplink-url"].secret_data } } # Hydra credentials. resource "kubernetes_secret" "hydra_credentials" { metadata { name = "hydra-credentials" } data = { dbusername = data.google_secret_manager_secret_version.secrets["auto-hydra-db-user"].secret_data dbpassword = data.google_secret_manager_secret_version.secrets["auto-hydra-db-password"].secret_data system_secret = data.google_secret_manager_secret_version.secrets["auto-hydra-system-secret"].secret_data dbname = "hydra" } } # Study datastore connect credentials. resource "kubernetes_secret" "study_datastore_connect_credentials" { metadata { name = "study-datastore-connect-credentials" } data = { response_datastore_id = data.google_secret_manager_secret_version.secrets["auto-sd-response-datastore-id"].secret_data response_datastore_token = data.google_secret_manager_secret_version.secrets["auto-sd-response-datastore-token"].secret_data android_id = data.google_secret_manager_secret_version.secrets["auto-sd-android-id"].secret_data android_token = data.google_secret_manager_secret_version.secrets["auto-sd-android-token"].secret_data ios_id = data.google_secret_manager_secret_version.secrets["auto-sd-ios-id"].secret_data ios_token = data.google_secret_manager_secret_version.secrets["auto-sd-ios-token"].secret_data } } # Email credentials. resource "kubernetes_secret" "email_credentials" { metadata { name = "email-credentials" } data = { email_address = data.google_secret_manager_secret_version.secrets["manual-mystudies-email-address"].secret_data email_password = data.google_secret_manager_secret_version.secrets["manual-mystudies-email-password"].secret_data contact_email_address = data.google_secret_manager_secret_version.secrets["manual-mystudies-contact-email-address"].secret_data from_email_address = data.google_secret_manager_secret_version.secrets["manual-mystudies-from-email-address"].secret_data from_email_domain = data.google_secret_manager_secret_version.secrets["manual-mystudies-from-email-domain"].secret_data smtp_hostname = data.google_secret_manager_secret_version.secrets["manual-mystudies-smtp-hostname"].secret_data smtp_use_ip_allowlist = data.google_secret_manager_secret_version.secrets["manual-mystudies-smtp-use-ip-allowlist"].secret_data } } # gcloud keys from service accounts resource "google_service_account_key" "apps_service_account_keys" { for_each = toset(local.service_account_ids) service_account_id = "$${each.key}@{{.prefix}}-{{.env}}-apps.iam.gserviceaccount.com" } resource "kubernetes_secret" "apps_gcloud_keys" { for_each = toset(local.service_account_ids) metadata { name = "$${each.key}-gcloud-key" } data = { "key.json" = base64decode(google_service_account_key.apps_service_account_keys[each.key].private_key) } } EOF } } }