top of page

MEHUL'S SPACE

Writer's pictureMehul Ved

Creating Cloud Run Service with Secret mounted in the environment

Requirement

Make the latest version database password stored in the secret manager available to the Cloud Run service as an environment variable. This needs to be done using Terraform so it’s properly documented in the infrastructure code.

Problem

The given feature is newly introduced by Google Cloud and not yet in General Availability. The support for this feature was only added in terraform google module 3.67.0 and the documentation on this is scarce. Even the example mentioned in terraform docs can get confusing as the concepts haven’t been explained correctly.

Solution

After wasting hours trying to figure out the right thing, I am sharing a fully functional code followed by an explanation on what needs to be looked at, by isolating the required code snippets. Hopefully this will help others figure what they need to look at and save time.

The Code

version.tf

terraform {
  required_version = ">=0.13"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "3.69.0"
    }

    google-beta = {
      source  = "hashicorp/google-beta"
      version = "3.69.0"
    }

    random = {
      source  = "hashicorp/random"
      version = "3.1.0"
    }
  }

}

provider "google" {
  project = var.project
  region  = var.region
}

provider "google-beta" {
  project = var.project
  region  = var.region
} 

main.tf

resource "random_id" "db_name_suffix" {
  byte_length = 4
}

resource "random_password" "db_password" {
  length  = 20
  special = false
}

resource "google_sql_database_instance" "dbinstance" {
  name                = "db-instance-${random_id.db_name_suffix.hex}"
  database_version    = "POSTGRES_13"
  deletion_protection = true

  settings {
    tier = var.database_instance_type
  }
}

resource "google_sql_database" "db" {
  name     = "appdb"
  instance = google_sql_database_instance.dbinstance.name
}

resource "google_sql_user" "dbuser" {
  name     = "appuser"
  instance = google_sql_database_instance.dbinstance.name
  password = random_password.db_password.result
}

resource "google_secret_manager_secret" "db_password" {
  secret_id = "DB_PASSWORD"

  replication {
    user_managed {
      replicas {
        location = var.region
      }
    }
  }
}
resource "google_secret_manager_secret_version" "db_password_version" {
  secret = google_secret_manager_secret.db_password.id

  secret_data = google_sql_user.dbuser.password
}

resource "google_cloud_run_service" "run_service" {
  provider = google-beta
  name     = "myapp"
  location = var.region

  template {
    spec {
      containers {
        image = var.container_image
        ports {
          container_port = var.container_port
        }
        env {
          name = "PASSWORD"
          value_from {
            secret_key_ref {
              name = google_secret_manager_secret.db_password.id
              key  = split("/", "${google_secret_manager_secret_version.db_password_version.name}")[5]
            }
          }
        }
        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }
      }
    }
    metadata {
      annotations = {
        "autoscaling.knative.dev/maxScale"      = 2
        "run.googleapis.com/cloudsql-instances" = google_sql_database_instance.dbinstance.connection_name
        "run.googleapis.com/client-name"        = "terraform"
      }
    }
  }

  metadata {
    annotations = {
      "run.googleapis.com/launch-stage" = "BETA"
    }
  }

  traffic {
    percent         = 100
    latest_revision = true
  }

  lifecycle {
    ignore_changes = [
      metadata.0.annotations,
    ]
  }
} 

variables.tf

variable "project" {
  description = "Project ID where resources are to be created"
  type        = string
  default     = "nexsales-devops"
}

variable "region" {
  description = "Default region for the resources"
  type        = string
  default     = "us-central1"
}

variable "database_instance_type" {
  description = "Instance size for the database"
  type        = string
  default     = "db-f1-micro"
}

variable "environment" {
  description = "Environment for the app to operate in"
  type        = string
  default     = "production"
}

variable "container_image" {
  description = "URL of the image from where the container can be fetched"
  type        = string
  default     = "nginx:latest"
}

variable "container_port" {
  description = "Port number on which the container should listen"
  type        = number
  default     = 80
} 

Description

Environment

The difference between the env block structure. The normal env variable block just has name and value as keys, whereas the secret mounted one uses name and secret_key_ref. Secret key reference has name and key as keys. The name refers to the name of the secret in secret manager and the key refers to the version of the secret.

env {
  name = "PASSWORD"
  value_from {
    secret_key_ref {
      name = google_secret_manager_secret.db_password.id
      key = split("/","${google_secret_manager_secret_version.db_password_version.name}")[5]
    }
  }
} 
env {
  name = "ENVIRONMENT"
  value = var.environment
} 

Annotations

There are 2 annotation blocks, one inside the template, which defines autoscaling, sql connection among other things. The second is independent one which defines the launch stage of the service. Since this feature isn’t yet in General Availability, we need to use this block for cloud run services. Currently, the only supported value of the launch stage is ‘BETA’ but things can change from time to time, so keep an eye out for changes.

template {
  ...
  metadata {
    annotations = {
      "autoscaling.knative.dev/maxScale" = 2
      "run.googleapis.com/cloudsql-instances" = google_sql_database_instance.dbinstance.connection_name
      "run.googleapis.com/client-name" = "terraform"
    }
  }
} 

The above section uses revision template metadata as described at https://cloud.google.com/run/docs/reference/rest/v1/RevisionTemplate

metadata {
  annotations = {
    "run.googleapis.com/launch-stage" = "BETA"
  }
} 

Whereas this is object metadata as described at https://cloud.google.com/run/docs/reference/rest/v1/RevisionTemplate

7 views0 comments

Recent Posts

See All

How to use short url with sharethis

Using sharethis but disappointed that it doesn’t have shortlink support? If you are someone who is comfortable placing some code in the...

Mediawiki on dotcloud

Unfortunately, there’s no guide available for installing mediawiki on dotcloud and googling for it doesn’t turn up anything much useful....

WordPress Performance Tips

Do you find performance of your WordPress blog to be lousy? And have no clue on where to start with it? There are lots of articles on the...

Comments


bottom of page