Skip to content

How to deploy a product's infrastructure for the first time

This guide covers the one-off steps needed to deploy an infrastructure project generated from the Google Cloud deployment boilerplate into a new terraform workspace. Once these steps are done, changes are deployed by GitLab CI.

Prerequisites

  • The Google Cloud, GitLab and GitLab runner configuration for the product must be in place. See how to bootstrap a product for a new product, or how to check product configuration for an existing one.
  • The infrastructure project must have been generated from the boilerplate and pushed to GitLab.
  • The webapp project's CI pipeline must have pushed a container image to the product's Artifact Registry repository. The terraform configuration looks up the image named in the container_images local in locals.tf and fails if it does not exist.
  • logan and the gcloud CLI must be installed. See how we use terraform for background on logan and workspaces.
  • You must be able to impersonate the product's terraform deploy service account. Usually this means being in the "deploy" or "admin" group for the product's team.

Sign in to Google Cloud

Sign in with your gcloudadmin account so that terraform can use your credentials:

gcloud auth application-default login

Initialise the terraform backend

Initialise the backend in the default workspace. The --writable-workdir flag is needed on the very first run so that terraform can create the .terraform.lock.hcl file:

logan --workspace=default --writable-workdir terraform init

Commit the generated .terraform.lock.hcl file.

Create and deploy a workspace

Do this section once for each of the development, staging and production workspaces. The commands below read the workspace name from a WORKSPACE shell variable, so set it first:

WORKSPACE=development

Create the workspace:

logan --workspace=default terraform workspace new $WORKSPACE

Note

Terraform may complain about a read-only filesystem. The workspace will still have been created.

Initialise the new workspace and apply the configuration:

logan --workspace=$WORKSPACE terraform init
logan --workspace=$WORKSPACE terraform apply

Restrict the webapp's database user

This step only applies to products using PostgreSQL. Skip it for MySQL or for products without a database.

By default all Cloud SQL users are members of the cloudsqlsuperuser role, which is more access than the webapp needs. The boilerplate includes a db_roles.sql file which removes it. See SQL instances for why this is not done by terraform.

Do this once for each workspace after its first terraform apply. The commands below use the WORKSPACE shell variable from the previous section. Set it again in any new terminal.

  1. Install the Cloud SQL Auth Proxy and psql, for example:

    gcloud components install cloud-sql-proxy
    sudo apt-get install -y postgresql-client
    
  2. Sign in to the gcloud CLI with your gcloudadmin account:

    gcloud auth login
    

    Note

    This is not the same as the earlier gcloud auth application-default login. That command sets up the Application Default Credentials which terraform uses. The proxy's --gcloud-auth flag uses the gcloud CLI's own credentials instead, which this command sets up.

  3. Get the Cloud SQL instance's connection name:

    logan --workspace=$WORKSPACE --nopull --quiet --notty \
        terraform output -raw sql_instance_connection_name
    
  4. Start the proxy, replacing CONNECTION_NAME with the value from the previous step:

    cloud-sql-proxy --gcloud-auth --port 5432 CONNECTION_NAME
    

    Note

    If the instance was created with database_private_network_only set, it has no public IP address and the proxy cannot reach it from outside Google Cloud's network.

  5. In a separate terminal, set WORKSPACE again and get the password for the admin user:

    logan --workspace=$WORKSPACE --nopull --quiet --notty \
        terraform output -raw sql_instance_password
    
  6. Apply db_roles.sql, pasting the password when asked:

    psql -h localhost -U admin -d postgres -1 -f db_roles.sql
    

Next steps