Calling Cloud Functions from Cloud Run
A simple guide to GCP authentication in production and development environments
One of the best parts of Google's Cloud Platform is it's authentication strategy.
GCP's Application Default Credentials (ADC) simplifies the process of authenticating with GCP services both in development and production environments.
But sometimes the documentation is hard to navigate.
The following guide will details the steps to setting up ADC locally and it's usage in production.
Setting up ADC
Setting up ADC locally is quite simple and it will allow us to understand how it works behind the scenes.
We begin with installing the gcloud
CLI by downloading the installer from the Google Cloud website.
Once installed, we can run the following command to use ADC:
gcloud auth application-default login
This adds credentials to the application_default_credentials.json
file under ~/.config/gcloud/
.
Google Cloud's client libraries will automatically use these credentials when making requests to GCP services.
For example, here is a simple Cloud Function that saves a file to a Cloud Storage bucket:
import { Storage } from "@google-cloud/storage"function saveFile() {const storage = new Storage()const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")}
Notice how we don't have to pass any credentials to the Storage
class.
This works as long as the account you logged in with has permissions to access the bucket.
If it doesn't, you can set the credentials explicitly.
Setting Credentials
import { Storage } from "@google-cloud/storage"function saveFile() {const storage = new Storage({credentials: {client_email: process.env.GCP_CLIENT_EMAIL,private_key: process.env.GCP_PRIVATE_KEY},})const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")
GCP_CLIENT_EMAIL
and GCP_PRIVATE_KEY
are environment variables we set in our .env file.
We can find the correct GCP_CLIENT_EMAIL
to use by looking at which service accounts have access to the bucket.
This can be found under the permissions tab of the bucket:
As for the GCP_PRIVATE_KEY
, we need to create a private key for the service account we want to use.
This can be done under the Keys tab of the service account:
Since we plan to call a Cloud Function from a Cloud Run service, we will use the default service account for App Engine.
Formatting the Private Key
One thing to note is that we need to format the private key as a multiline string.
The private key we generated is a string which contains the characters \
and n
.
We want to replace them with the actual newline character \n
.
import { Storage } from "@google-cloud/storage"function saveFile() {const storage = new Storage({credentials: {client_email: process.env.GCP_CLIENT_EMAIL,private_key: process.env.GCP_PRIVATE_KEY!.split(String.raw`\n`).join('\n')},})const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")}
Setting the Project
The last step is to set the project in which the bucket resides.
If the bucket is in your production environment, use the name of the production project.
import { Storage } from "@google-cloud/storage"function saveFile() {const storage = new Storage({credentials: {client_email: process.env.GCP_CLIENT_EMAIL,private_key: process.env.GCP_PRIVATE_KEY!.split(String.raw`\n`).join('\n')},projectId: "my-production-project",})const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")}
Recap
import { Storage } from "@google-cloud/storage"function saveFile() {const storage = new Storage({credentials: {client_email: process.env.GCP_CLIENT_EMAIL,private_key: process.env.GCP_PRIVATE_KEY!.split(String.raw`\n`).join('\n')},projectId: "my-production-project",})const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")}
We are now able to save a file to a production bucket from our local machine.
It's important to note that we only needed to set the credentials and project
because the account we used when running gcloud auth application-default login
didn't have access to the production bucket.
However, when we deploy our Cloud Function or Cloud Run service to production we may not need to set the credentials and project at all.
Deploying to Production
As long as the service account of the Cloud Function or Cloud Run service has access to the bucket, we can deploy the service without setting the credentials and project.
import { Storage } from "@google-cloud/storage"function saveFile() {// Uncomment this when running on your local machine// const storage = new Storage({// credentials: {// client_email: process.env.GCP_CLIENT_EMAIL,// private_key: process.env.GCP_PRIVATE_KEY!.split(String.raw`\n`).join('\n')// },// projectId: "my-production-project",// })const storage = new Storage()const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")}
import { Storage } from "@google-cloud/storage"function saveFile() {const storage = new Storage()const bucket = storage.bucket("my-bucket")const file = bucket.file("my-file")await file.save("Hello, world!")}
Notice how we don't have to pass any credentials to the Storage
class.
This works as long as the account you logged in with has permissions to access the bucket.
If it doesn't, you can set the credentials explicitly.
Setting Credentials
GCP_CLIENT_EMAIL
and GCP_PRIVATE_KEY
are environment variables we set in our .env file.
We can find the correct GCP_CLIENT_EMAIL
to use by looking at which service accounts have access to the bucket.
This can be found under the permissions tab of the bucket:
As for the GCP_PRIVATE_KEY
, we need to create a private key for the service account we want to use.
This can be done under the Keys tab of the service account:
Since we plan to call a Cloud Function from a Cloud Run service, we will use the default service account for App Engine.
Formatting the Private Key
One thing to note is that we need to format the private key as a multiline string.
The private key we generated is a string which contains the characters \
and n
.
We want to replace them with the actual newline character \n
.
Setting the Project
The last step is to set the project in which the bucket resides.
If the bucket is in your production environment, use the name of the production project.
Recap
We are now able to save a file to a production bucket from our local machine.
It's important to note that we only needed to set the credentials and project
because the account we used when running gcloud auth application-default login
didn't have access to the production bucket.
However, when we deploy our Cloud Function or Cloud Run service to production we may not need to set the credentials and project at all.
Deploying to Production
As long as the service account of the Cloud Function or Cloud Run service has access to the bucket, we can deploy the service without setting the credentials and project.
We have now seen how to use ADC to authenticate with GCP services both in development and production.
The beauty of ADC is that it handles the authentication for us behind the scenes.
We only need to set the credentials manually when we need to access a service that our account doesn't have access to or that resides in a different project.