terraform google cloud run add cloud sql connection

I am using terraform to create my infrastucture on google cloud. I use tfvars file to hold my variables such as database password. I deploy my app to cloud run and create a database in sql. Problem is i need to add this database to cloud run's Cloud SQL connections. Because otherwise this app can't connect to database. How can i do this with terraform ?

My tf file:

variable "database_password" {
type = string
}
variable "database_user" {
    type = string
}
variable "project_name" {
    type=string
}
variable "jwt_key" {
    type = string
}
provider "google"{
    credentials=file("credentials.json")
    project=var.project_name
    region="us-west1"
}
resource "google_sql_database_instance" "instance" {
    name="socialmediadatabase"
    region="us-central1"
    database_version="POSTGRES_13"
    deletion_protection = false
    settings{
        tier="db-f1-micro"
    }
}
resource "google_sql_database" "database"{
    name="socialmedia"
    instance=google_sql_database_instance.instance.name
}
resource "google_sql_user" "database-user" {
    name = var.database_user
    instance = google_sql_database_instance.instance.name
    password = var.database_password
}
resource "google_cloud_run_service" "run" {
    name="socialmedia"
    location = "us-central1"
    template {
        spec {
            containers {
                image = "gcr.io/${var.project_name}/socialmedia:latest"
                ports {
                    container_port = 5000
                }
                env {
                    name="ENV"
                    value = "production"
                }
                env {
                    name="JWT_KEY"
                    value = var.jwt_key
                }
                env {
                    name="DB_URL"
                    value = "postgresql://${var.database_user}:${var.database_password}@/socialmedia?host=/cloudsql/${google_sql_database_instance.instance.connection_name}"
                }
            }
        }
    }
    metadata {
        annotations = {
            "run.googleapis.com/cloudsql-instances"=google_sql_database_instance.instance.connection_name
        }
    }
}
#?
resource "google_cloud_run_service_iam_member" "member" {
    service = google_cloud_run_service.run.name
    location = google_cloud_run_service.run.location
    role = "roles/run.invoker"
    member = "allUsers"
}

2 answers

  • answered 2021-06-17 12:07 al-dann

    Looking into the code - I guess that you would like to provide secret information using terraform into an environment variable of a Cloud Run...

    I would say, there may be at least 2 issues with such approach:

    1/ Those secret values are going to be stored in the terrafrom state file. In a decrypted format. I am not sure this is a good idea.

    2/ Those secret values become accesible to anybody (or anything) who has access to environment variables. From my point of view - a bit risky.

    I would suggest to store secret values in the Secret Manager... - see documentation here and provide only the secret names as environment variables...

    Thus, the Cloud Run code should get the environment variables and fetch the correspondent secret values from the Secret Manager.

    To make this happen, the secrets (as placeholders) can be created by terraform, so there names can be provided smoothly in the Cloud Run.

    The Cloud Run service account should get relevant IAM roles to access the secrets. That can be done using terraform as well.

    Finally, don't forget to populate (using UI console or gcloud CLI command) the secrets with actual values (or versions in GCP terminology), and modify Cloud Run code so it can hanlde exceptions - an absence of the secret value or incorrect values.

  • answered 2021-06-17 17:10 gerem28468

    I solved it. I was supposed to add metadata under template. This is the correct one:

    variable "database_password" {
    type = string
    }
    variable "database_user" {
        type = string
    }
    variable "project_name" {
        type=string
    }
    variable "jwt_key" {
        type = string
    }
    provider "google"{
        credentials=file("credentials.json")
        project=var.project_name
        region="us-west1"
    }
    resource "google_sql_database_instance" "instance" {
        name="socialmediadatabase"
        region="us-central1"
        database_version="POSTGRES_13"
        deletion_protection = false
        settings{
            tier="db-f1-micro"
        }
    }
    resource "google_sql_database" "database"{
        name="socialmedia"
        instance=google_sql_database_instance.instance.name
    }
    resource "google_sql_user" "database-user" {
        name = var.database_user
        instance = google_sql_database_instance.instance.name
        password = var.database_password
    }
    resource "google_cloud_run_service" "run" {
        name="socialmedia"
        location = "us-central1"
        template {
            spec {
                containers {
                    image = "gcr.io/${var.project_name}/socialmedia:latest"
                    ports {
                        container_port = 5000
                    }
                    env {
                        name="ENV"
                        value = "production"
                    }
                    env {
                        name="JWT_KEY"
                        value = var.jwt_key
                    }
                    env {
                        name="DB_URL"
                        value = "postgresql://${var.database_user}:${var.database_password}@/socialmedia?host=/cloudsql/${google_sql_database_instance.instance.connection_name}"
                    }
                }
            }
            metadata {
                annotations = {
                    "run.googleapis.com/cloudsql-instances"=google_sql_database_instance.instance.connection_name
                }
            }
        }
    }
    #?
    resource "google_cloud_run_service_iam_member" "member" {
        service = google_cloud_run_service.run.name
        location = google_cloud_run_service.run.location
        role = "roles/run.invoker"
        member = "allUsers"
    }