Synchronize your Google Cloud Function code with Terraform
Have you developed Google Cloud Functions and are you thinking about deploying them? By reading this article, you will know how to do it with Terraform. You will be able to update your cloud functions after a code change.
This will be done through a small project to demonstrate it. We will develop a small simple cloud function for this purpose. Then we will create the Terraform code to deploy and update it.
The Terraform Project Structure
-
The
src
directory contains the source code of the cloud functionindex.js
is the file where the cloud function code is definedpackage-lock.json
is automatically generated for any operations where npm modifies either thenode_modules
tree, or package.json. It describes the exact tree that was generated, such that subsequent installs are able to generate identical trees, regardless of the intermediate dependency updates- package.json contains human-readable metadata about the project (like the project name and description) as well as functional metadata like the package version number and a list of dependencies required by the application
-
The terraform directory contains the code to deploy the cloud function
backend.tf
defines the terraform backend to store the state and version the infrastructuremain.tf
defines the Terraform resource to handle the cloud function deployment and updateoutputs.tf
returns the attributes relative to the cloud function deploymentvariables.tf
defines the inputs of the Terraform code
Create The Cloud Function With Node.JS
Write the cloud function code
The cloud function used here takes an environment variable called VERSION
and returns an HTML response with that value:
/**
* Responds to any HTTP request.
*
* @param {!express:Request} req HTTP request context.
* @param {!express:Response} res HTTP response context.
*/
const functions = require('@google-cloud/functions-framework');
functions.http('main', (req, res) => {
if (!process.env.VERSION) {
throw new Error('VERSION is not defined in the environment variable');
}
res.status(200).send('<h1>Cloud Function version ' + process.env.VERSION + '</h1>');
});
To define the function, we use a package called @google-cloud/functions-framework
. This dependency also allows testing the cloud function locally before the final deployment. We install it in the package.json
file
{
"name": "cloud-function-terraform-example",
"version": "1.0.0",
"description": "Google Cloud Function terraform example",
"scripts": {
"start": "functions-framework --target=main --port 8080"
},
"author": "Guillaume Vincent",
"dependencies": {
"@google-cloud/functions-framework": "^3.1.2"
}
}
To facilitate the test, a start
command is added to the package.json to launch it locally
Test the cloud function in local
Before tackling the deployment with Terraform, we will test the cloud function locally. The first thing to do is to install the dependencies via npm
:
After the installation, the package-lock.json
is generated. Now we can test the function locally with this command:
Executing curl on the function URL returns the expected output:
Enable Needed Google Services
In the next example, you will see how to deploy the cloud function 2nd generation with Terraform. This new generation is based on the CloudRun Google service. It needs to enable a few service APIs on your Google project. These operations are done with gcloud
command because it is not the role of the Terraform code exposed here to manage your project.
First of all, select your project:
$ gcloud config set project <YOUR_PROJECT_ID>
Next, enable the following services needed by 2nd gen functions:
$ gcloud services enable cloudfunctions.googleapis.com
$ gcloud services enable run.googleapis.com
$ gcloud services enable artifactregistry.googleapis.com
$ gcloud services enable cloudbuild.googleapis.com
Implement The Terraform Code
data.archive_file.this
is automatically launched at terraform apply to create an archive of the cloud function source code. It exposes an SHA checksum attribute to google_storage_bucket_object.this
. When the cloud function code changes, the checksum also changes. This triggers the update of google_storage_bucket_object and thus the cloud function resource google_cloudfunctions2_function.this
.
The data.archive_file.this
uses the exclude
argument helps to specify files to ignore when creating the archive. This helps to remove non-needed files in the cloud function and reduces its size. Particularly useful, because if the total size of files is too large you cannot use the live editor. In addition, there is a max deployment size per function.
The cloud function release update is smooth and managed by the Google Function Service itself. That means there is no downtime when updating a cloud function. When the new version is released, the traffic is automatically routed to the latest if all_traffic_on_latest_revision attribute is set to true
.
getbetterdevops-terraform-states
by your own. This is used to store the terraform states and versions remotely
Deploy The Google Cloud Function With Terraform
Initial cloud function deployment
Replace the following command with your own project ID and desired bucket name. This bucket will be used to store the function archive artifact:
$ terraform apply -var=environment_variables='{"VERSION": 1}' -var=project=<YOUR_PROJECT_ID> -var=bucket_name=<YOUR_BUCKET_NAME>
Once applied, the function URI is returned through the outputs:
$ FUNCTION_URI=$(terraform output --json | jq -r '.uri.value')
The cloud function is exposed publicly with authentication. With the following curl
command and gcloud
, you can call the cloud function URI:
$ curl -m 70 -X POST $FUNCTION_URI -H "Authorization: bearer $(gcloud auth print-identity-token)"
<h1>Cloud Function version 1</h1>
Update the cloud function
Change the VERSION
environment variable and re-apply the terraform code:
$ terraform apply -var=environment_variables='{"VERSION": 2}' -var=project=getbetterdevops-lab -var=bucket_name=getbetterdevops-cloud-functions
When recalling the cloud function URI, the output has changed. The function has been successfully applied!
$ curl -m 70 -X POST $FUNCTION_URI -H "Authorization: bearer $(gcloud auth print-identity-token)" -H "Content-Type: application/json"
<h1>Cloud Function version 2</h1>
Conclusion
To deploy a Google Cloud Function, there are 3 possible solutions:
- Deploy your functions with the
gcloud command
- Use Google Cloud Source Repositories which is a git repository managed in Google Cloud Platform. Either you push your code directly into Cloud Source or you can mirror a GitHub repository with it
- Use a Google Storage Bucket with the code of the function compressed in an archive. This is what was done here by automating the release with Terraform
The 3rd solution has the merit of being very quick to implement. Terraform is a must in the DevOps ecosystem. Once the Terraform code is modularized, you can deploy new cloud functions very quickly.
Find the code presented in this article here π