Deploying Cloud Functions using Service Accounts

Service accounts can be used to allow limited access control and can be used without the need for the usual web authentication journey that is typically used when authenticating the gcloud SDK. These are ideal for use in a CI setup.

Creating the service account

The steps required to create a service account are outline below, for more information check out the documentation

gcloud iam service-accounts create [SA-NAME] \
    --display-name "[SA-DISPLAY-NAME]"

Where SA-NAME is the name for your service account and SA-DISPLAY-NAME is a friendly name for the account. e.g.

gcloud iam service-accounts create jenkins-deployer \
    --display-name "Jenkins Deployer Service Account"

List current service accounts

To get a list of current service accounts for the current project:

gcloud iam service-accounts list

We can use this with some additional parameters to to extract the email into an ENV var so that it can be used for later commands. The is used when adding roles to the account

export SA_EMAIL=$(gcloud iam service-accounts list \
    --filter="displayName:jenkins-deployer" --format='value(email)')

This sets the value of SA_EMAIL to be something like [email protected].

Get the current project

To set the current project as an ENV var so that it can be used, use the following:

export PROJECT=$(gcloud info —-format=‘value(config.project)’)

Adding roles to the Service Account

Now that the service account has been created, we can assign it roles. The roles you wish to assign depend on what you want to use the service account for. For the purpose of this post we are going to be looking at using the service account to deploy Cloud Functions as well as Firestore security rules.

First add the serviceAccountActor role.

gcloud projects add-iam-policy-binding $PROJECT —-role roles/iam.serviceAccountActor \
    —-member serviceAccount:$SA_EMAIL

There is a beta role that sets the required permissions to deploy cloud functions and firestore database rules, this is roles/firebase.developAdmin:

gcloud projects add-iam-policy-binding $PROJECT —-role roles/firebase.developAdmin \
    —-member serviceAccount:$SA_EMAIL

Generating an access key

To authenticate as the service account we need to generate an access key:

gcloud iam service-accounts keys create jenkins-sa.json —iam-account $SA_EMAIL

This will create a key for the account and download it into jenkins-sa.json. This file can then be deployed onto your CI server in order to authenticate the Service Account. It is important to secure access to this file as this can be used to authenticate as that account.

Authenticate using the key

In order to authenticate the Service Account to use the gcloud CLI you need to do the following, where GCP_KEY is the path to the key created above:

gcloud auth activate-service-account --project=$PROJECT --key-file=$GCP_KEY

Deploying cloud functions

In order to deploy the functions execute the following from the folder that contains the index.js where the functions are exported.

gcloud beta functions deploy [FUNCTION_NAME] --project $PROJECT --region [REGION]

The region flag is only required if you are deploying to a region other than the default one.

Scripting the deployment

The following bash script will extract the exported functions and deploy each one:

for FUNCTION_NAME in $(awk '/exports/ {print $1}' index.js | awk -F '.' '{print $2}')
  echo "About to deploy $FUNCTION_NAME"
  gcloud beta functions deploy $FUNCTION_NAME --project $PROJECT --region $REGION
  echo "Done deploying $FUNCTION_NAME"

It assumes that the PROJECT, REGION ENV variables are set.

Firebase CLI

The currently recommend way to authenticate in a CI environment when using the firebase CLI is to use a token. However, this token has to be for a full user account and can’t be for a service account.

If you wish to use the Firebase CLI to use the service account then all that is required is to set the GOOGLE_APPLICATION_CREDENTIALS ENV var to the path to the service account access key.


firebase deploy
firebase deploy -P $GCP_PROJECT --only firestore:rules --token "$FIREBASE_TOKEN"