avatarManoj Saini

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

8498

Abstract

hljs-meta"># }</span></pre></div><ul><li><b>backend.tf: </b>To configure the backend for storing and retrieving the Terraform state.</li></ul><div id="6e90"><pre>terraform { backend <span class="hljs-string">"gcs"</span> { bucket = <span class="hljs-string">"gcp-cloud-function-terraform-bucket-"</span> <span class="hljs-comment"># GCS bucket name to store terraform tfstate</span> prefix = <span class="hljs-string">"function"</span> <span class="hljs-comment"># Prefix name should be unique for each Terraform project having same remote state bucket.</span> } <span class="hljs-comment"># backend "local" {}</span> }</pre></div><blockquote id="9c2c"><p><i>Note: Terraform stores the state file locally in the same directory as the Terraform configuration files. choose ‘local’ to stored in the local directory.</i></p></blockquote><ul><li><b>variables.tf:</b> Declare the variables used in the Terraform files.</li></ul><div id="44f2"><pre>variable <span class="hljs-string">"project_id"</span> { <span class="hljs-keyword">type</span> = <span class="hljs-type">string</span> <span class="hljs-keyword">default</span> = <span class="hljs-string">"<YOUR-PROJECT-ID>"</span> }

variable <span class="hljs-string">"region"</span> { <span class="hljs-keyword">type</span> = <span class="hljs-type">string</span> <span class="hljs-keyword">default</span> = <span class="hljs-string">"europe-west2"</span> }

variable <span class="hljs-string">"service_account_email"</span> { <span class="hljs-keyword">type</span> = <span class="hljs-type">string</span> <span class="hljs-keyword">default</span> = <span class="hljs-string">"your-service-account-email@your-project-id.iam.gserviceaccount.com"</span>

}</pre></div><blockquote id="10db"><p>NOTE: make sure service-account have all the relevent premsissions.</p></blockquote><ul><li><b>storage-bucket.tf:</b> Cloud Storage buckets to store the code of the Cloud Function and to upload files.</li></ul><div id="9597"><pre>resource <span class="hljs-string">"google_storage_bucket"</span> <span class="hljs-string">"Cloud_function_bucket"</span> { name = <span class="hljs-string">"cloud-function-${var.project_id}"</span> location = <span class="hljs-keyword">var</span>.<span class="hljs-type">region</span> <span class="hljs-variable">project</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.<span class="hljs-type">project_id</span> <span class="hljs-variable">force_destroy</span> <span class="hljs-operator">=</span> <span class="hljs-type">true</span> <span class="hljs-variable">uniform_bucket_level_access</span> <span class="hljs-operator">=</span> <span class="hljs-literal">true</span> }

resource <span class="hljs-string">"google_storage_bucket"</span> <span class="hljs-string">"input_bucket"</span> { name = <span class="hljs-string">"input-{var.project_id}"</span> location = <span class="hljs-keyword">var</span>.<span class="hljs-type">region</span> <span class="hljs-variable">project</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.<span class="hljs-type">project_id</span> <span class="hljs-variable">force_destroy</span> <span class="hljs-operator">=</span> <span class="hljs-type">true</span> <span class="hljs-variable">uniform_bucket_level_access</span> <span class="hljs-operator">=</span> <span class="hljs-literal">true</span> }</pre></div><ul><li><b>cloudfunctiongen2.tf:</b> Declare the Cloud Function</li></ul><div id="578f"><pre><span class="hljs-comment"># Generates an archive of the source code compressed as a .zip file.</span> data <span class="hljs-string">"archive_file"</span> <span class="hljs-string">"source"</span> { <span class="hljs-built_in">type</span> = <span class="hljs-string">"zip"</span> source_dir = <span class="hljs-string">"<span class="hljs-variable">{path.module}</span>/src"</span> output_path = <span class="hljs-string">"<span class="hljs-variable">${path.module}</span>/tmp/function.zip"</span> }

<span class="hljs-comment"># Add source code zip to the Cloud Function's bucket (Cloud_function_bucket) </span> resource <span class="hljs-string">"google_storage_bucket_object"</span> <span class="hljs-string">"zip"</span> { <span class="hljs-built_in">source</span> = data.archive_file.source.output_path content_type = <span class="hljs-string">"application/zip"</span> name = <span class="hljs-string">"src-<span class="hljs-variable">${data.archive_file.source.output_md5}</span>.zip"</span> bucket = google_storage_bucket.Cloud_function_bucket.name depends_on = [ google_storage_bucket.Cloud_function_bucket, data.archive_file.source ] }

resource <span class="hljs-string">"google_cloudfunctions2_function"</span> <span class="hljs-string">"function"</span> { name = <span class="hljs-string">"Cloud-function-trigger-using-terraform-gen-2"</span> location = var.region description = <span class="hljs-string">"Cloud function gen2 trigger using terraform "</span>

build_config { runtime = <span class="hljs-string">"python39"</span> entry_point = <span class="hljs-string">"helloGET"</span> environment_variables = { BUILD_CONFIG_TEST = <span class="hljs-string">"build_test"</span> } <span class="hljs-built_in">source</span> { storage_source { bucket = google_storage_bucket.Cloud_function_bucket.name object = google_storage_bucket_object.zip.name } } }

service_config { max_instance_count = 3 min_instance_count = 1 available_memory = <span class="hljs-string">"256M"</span> timeout_seconds = 60 environment_variables = { SERVICE_CONFIG_TEST = <span class="hljs-string">"config_test"</span> } ingress_settings = <span class="hljs-string">"ALLOW_INTERNAL_ONLY"</span> all_traffic_on_latest_revision = <span class="hljs-literal">true</span> service_account_email = var.service_account_email }

event_trigger { trigger_region = var.region event_type = <span class="hljs-string">"google.cloud.storage.object.v1.finalized"</span> retry_policy = <span class="hljs-string">"RETRY_POLICY_RETRY"</span> service_account_email = var.service_account_email event_filters { attribute = <span class="hljs-string">"bucket"</span> value = google_storage_bucket.input_bucket.name } } depends_on = [ google_storage_bucket.Cloud_function_bucket, google_storage_bucket_object.zip ] }</pre></div><h1 id="bbb2">Deploy cloud function:</h1><ul><li>Start with initializing the Terraform workspace. A<code>terraform init</code>downloads all the required providers and plugins. Run a <code>Terraform plan</code> creates an execution plan. The execution plan looks good, so let’s move ahead and apply this plan.</li></ul><div id="201e"><pre><span class="hljs-meta prompt_"> </span><span class="language-bash">terraform init</span> <span class="hljs-meta prompt_"> </span><span class="language-bash">terraform <span class="hljs-built_in">fmt</span></span> <span class="hljs-meta prompt_"> </span><span class="language-bash">terraform validate</span> <span class="hljs-meta prompt_"> </span><span class="language-bash">terraform apply -auto-approve</span></pre></div><p id="580f">Cloud-Function- Gen2:</p><figure id="73a7"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*9wTN15-txhrjytt3rvhkQw.png"><figcaption></figcaption></figure><p id="b28b">Storage buckets:</p><figure id="aeef"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*jsrH5Rl9qeGaC9ZP3DcJEA.png"><figcaption></figcaption></figure><h1 id="94a9">To test if everything is working correctly, follow these steps:</h1><ol><li>Open the Google Cloud Console and log in to your project.</li><li>Navigate to the Google Cloud Storage browser.</li><li>Click on the bucket named <code>input-<YOUR-PROJECT-ID></code>.</li><li>Upload any file into the bucket to trigger the Cloud Function.</li><li>To verify that the Cloud Function was triggered, go to the Cloud Functions list in the Google Cloud Console.</li></ol><figure id="5097"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*DFpWIBuGg1ypSGzrcXAGAQ.png"><figcaption></figcaption></figure><p id="0bc0">This test ensures that the Cloud Function — Gen2 is successfully triggered, whenever a file is uploaded to the bucket <code>in

Options

put-<YOUR-PROJECT-ID></code>, and you can confirm its execution by checking the logs.</p><ul><li><b>Destroy: </b>To destroy Terraform-provisioned infrastructure.</li></ul><div id="7064"><pre> $ terraform destroy --<span class="hljs-keyword">auto</span>-approve</pre></div><p id="e21f"><b>Reference: <a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function#example-usage---cloudfunctions2-full"></a></b><a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function#example-usage---cloudfunctions2-full">https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function#example-usage---cloudfunctions2-full</a></p><p id="90e9">I trust that you have found this user-friendly.</p><p id="3f23">Please share your thoughts and experiences after following the steps outlined. Your feedback is valuable and helps us improve the quality.</p><h1 id="ceb0">Topics:</h1><ul><li><a href="https://readmedium.com/deploy-cloud-functions-on-gcp-with-terraform-c6026be9d645">Deploy Cloud Functions on GCP with Terraform- (Environment -1st Gen)</a></li><li><a href="https://readmedium.com/provision-gke-cluster-with-terraform-ee787457f3e">Provision GKE Cluster with Terraform Using Module</a></li><li><a href="https://readmedium.com/provision-amazon-eks-cluster-using-terraform-modules-16680794fbae">Provision EKS Cluster with Terraform Using Modules</a></li><li><a href="https://readmedium.com/terraform-tool-447568c12425">Terraform Tools That You Need</a></li></ul><p id="1969">Do not forget the 👏<b>✌️❤️</b> if you like this content! Also, I will be glad if you hit the follow button so you get notified of my new posts. You can also follow me on <a href="https://www.linkedin.com/in/manoj-kumar-saini/"><b>LinkedIn</b></a><b>!</b> Thank you!</p><h2 id="d67d">Further Reading:</h2><div id="75e9" class="link-block"> <a href="https://gethelios.dev/blog/opentelemetry-tracing/?utm_source=medium&amp;utm_medium=referral&amp;utm_campaign=cloud+native+daily"> <div> <div> <h2>OpenTelemetry Tracing: Everything you need to know</h2> <div><h3>OpenTelemetry tracing is filling the gaps of traditional observability methods in microservices apps. Here's how it's…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*OOiNa8ruT1VjxUw5)"></div> </div> </div> </a> </div><div id="b170" class="link-block"> <a href="https://gethelios.dev/blog/opentelemetry-python/?utm_source=medium&amp;utm_medium=referral&amp;utm_campaign=cloud+native+daily"> <div> <div> <h2>OpenTelemetry Python - Walkthrough and monitoring Examples</h2> <div><h3>Python app monitoring and debugging can be challenging. Using distributed tracing and OpenTelemetry visualization…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*lCeaLUyt9Pw2fFEo)"></div> </div> </div> </a> </div><div id="0dda" class="link-block"> <a href="https://gethelios.dev/blog/api-observability-leveraging-otel-to-improve-developer-experience/?utm_source=medium&amp;utm_medium=referral&amp;utm_campaign=cloud+native+daily"> <div> <div> <h2>API observability: Leveraging OTel to improve developer experience</h2> <div><h3>A Helios developer shares her experience applying API observability to improve API discovery, enforcement and…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*ztYqoLGE8_FLDio4)"></div> </div> </div> </a> </div><div id="f166" class="link-block"> <a href="https://gethelios.dev/blog/serverless-observability/?utm_source=medium&amp;utm_medium=cloud+native+daily"> <div> <div> <h2>Serverless observability, monitoring, and debugging explained</h2> <div><h3>Serverless troubleshooting requires E2E observability, through collecting trace data on top of logs and metrics- Here's…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*kXqB4bcmHGZ1gh2f)"></div> </div> </div> </a> </div><div id="c71f" class="link-block"> <a href="https://gethelios.dev/opentelemetry-a-full-guide/?utm_source=medium&amp;utm_medium=cloud+native+daily"> <div> <div> <h2>OpenTelemetry: A full guide</h2> <div><h3>Learn all about OpenTelemetry OpenSource and how it transforms microservices observability and troubleshooting</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*IcMWCDYabvwLtH4C)"></div> </div> </div> </a> </div><div id="7248" class="link-block"> <a href="https://gethelios.dev/blog/api-monitoring-vs-observability-in-microservices-troubleshooting-guide/?utm_source=medium&amp;utm_medium=cloud+native+daily"> <div> <div> <h2>API monitoring vs. observability in microservices</h2> <div><h3>This article looks at the pillars of API observability vs. API monitoring, its important role in troubleshooting…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*l0h3Aix-dzd1aDOa)"></div> </div> </div> </a> </div><div id="8a8a" class="link-block"> <a href="https://gethelios.dev/blog/kubernetes-monitoring-opentelemetry/?utm_source=medium&amp;utm_medium=cloud+native+daily"> <div> <div> <h2>Kubernetes Monitoring with OpenTelemetry</h2> <div><h3>Learn how to monitor Kubernetes using OpenTelemetry with real-time visibility and granular error data - Reduce MTTR by…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*Jjb9G9NlFTmm_M5c)"></div> </div> </div> </a> </div><div id="27f2" class="link-block"> <a href="https://aws.plainenglish.io/top-tools-for-aws-lambda-monitoring-8ea51b1f095c"> <div> <div> <h2>Top Tools for AWS Lambda Monitoring</h2> <div><h3>Helios, AWS CloudFront, AWS X-Ray, OpenTelemetry and more</h3></div> <div><p>aws.plainenglish.io</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*Q161Jukh2rnyhAZ_.png)"></div> </div> </div> </a> </div><div id="d197" class="link-block"> <a href="https://gethelios.dev/blog/combinining-opentelemetry-traces-with-prometheus-metrics-to-build-a-powerful-alerting-mechanism/?utm_source=medium&amp;utm_medium=referral&amp;utm_campaign=cloud+native+daily"> <div> <div> <h2>Combining OTel and Prometheus metrics for alerting machine</h2> <div><h3>Using both OpenTelemetry and Prometheus, we delivered a trace-based alerting mechanism quickly and efficiently - Here's…</h3></div> <div><p>gethelios.dev</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*HECW_XClLJ8_b2xQ)"></div> </div> </div> </a> </div></article></body>

Deploy Cloud Functions on GCP with Terraform (2nd Gen Environment)

Set up a Cloud Function — Gen2 in Google Cloud Platform (GCP) that is triggered whenever a file is uploaded to a specific Google Cloud Storage (GCS) bucket.

Cloud Functions offers two product versions: Cloud Functions (1st gen), the original version, and Cloud Functions (2nd gen), a new version built on Cloud Run and Eventarc to provide an enhanced feature set.

Cloud Function — Generation 2: Uses Cloud Run in the background!

The 2nd generation of Cloud Functions is a significant advancement in the serverless computing space. It builds upon the success of the first generation and introduces several key enhancements that empower developers to build more robust and scalable applications. Let’s go deeper into the 2nd generation of Cloud Function.

  1. 1st Generation: First version
  2. 2nd Generation: New version built on Cloud Run and Eventarc

1st Generation vs 2nd Generation:

Differences as below:

https://cloud.google.com/functions/docs/concepts/version-comparison

Key Enhancements in 2nd Generation:

  • Longer request processing times: Cloud Functions 2nd Generation can handle HTTP requests that take up to 60 minutes to execute.
  • Larger instance sizes: Cloud Functions 2nd Generation can use instances with up to 16 GiB of RAM and 4 virtual CPUs.
  • Improved concurrency: Cloud Functions 2nd Generation can handle up to 1000 concurrent requests per function instance.
  • Traffic management: Cloud Functions 2nd Generation supports multiple function revisions and traffic splitting
  • Eventarc integration: Cloud Functions 2nd Generation has native support for Eventarc triggers, bringing all 90+ event sources supported by Eventarc to Cloud Functions.
  • Broader CloudEvents support: Cloud Functions 2nd Generation supports industry-standard CloudEvents in all language runtimes, providing a consistent developer experience.

The console of cloud function as shown below:

NOTE: Will focus on creating the function using terraform.

In this tutorial, you will learn how to set up a Cloud Function — Gen2 in Google Cloud Platform (GCP) that is triggered whenever a file is uploaded to a specific Google Cloud Storage (GCS) bucket.

Prerequisites

If API's are not enable the use the below commands. 
# gcloud services enable cloudfunctions.googleapis.com
# gcloud services enable eventarc.googleapis.com
# gcloud services enable cloudresourcemanager.googleapis.com
# gcloud services enable cloudrun.googleapis.com
# gcloud services enable pubsub.googleapis.com
# gcloud services enable cloudstorage.googleapis.com
# gcloud services enable cloudlogging.googleapis.com
# gcloud services enable cloudmonitoring.googleapis.com
# gcloud services enable cloudbuild.googleapis.com
# gcloud services enable cloudcode.googleapis.com

We will focus on deploying the following resources using Terraform:

  • Bucket for file uploads: Cloud Storage bucket that will be used to upload files to. It provides a scalable and durable storage solution for storing files.
  • Bucket for Cloud Function source code: Bucket that will store the source code for the Cloud Function. The Cloud Function will be triggered by file uploads to the first bucket.
  • Cloud Function: Serverless function that runs in response to events.

Folder Structure:

  • src folder contains the Python source code of the cloud function
  • terraform folder contains the configuration files

Create Python function:

src/main.py: The fileUpload function is the entry point of the Cloud Function. It takes two parameters: event and context. The event parameter contains information about the file upload event, such as the bucket name, file name, and file size. You can extract and use this information in your function.

def fileUpload(event, context):
    file_data = event

    # Extract relevant information from the event
    bucket_name = file_data['bucket']
    file_name = file_data['name']
    file_size = file_data['size']

    # Perform desired operations on the uploaded file
    # For example, you can process the file, store metadata, or trigger other actions

    print(f"File uploaded: {file_name} in bucket: {bucket_name}")
    print(f"File size: {file_size} bytes")

    # Add your custom logic here

    # Return a response (optional)
    return "File processing completed"

Create Terraform Infrastructure:

  • provider.tf: To declare the connection to the Google provider in Terraform, you need to specify the provider block in your Terraform configuration file.
terraform {
  required_version = ">= 1.0"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 4.69.1"
    }
  }
}

# provider "google" {
#   credentials = file("gcp-project.json")
#   project     = var.project_id
#   region      = var.region
# }
  • backend.tf: To configure the backend for storing and retrieving the Terraform state.
terraform {
  backend "gcs" {
    bucket = "gcp-cloud-function-terraform-bucket-" # GCS bucket name to store terraform tfstate
    prefix = "function"               # Prefix name should be unique for each Terraform project having same remote state bucket.
  }
  # backend "local" {}
}

Note: Terraform stores the state file locally in the same directory as the Terraform configuration files. choose ‘local’ to stored in the local directory.

  • variables.tf: Declare the variables used in the Terraform files.
variable "project_id" {
  type = string
  default = "<YOUR-PROJECT-ID>"
}

variable "region" {
  type = string
  default = "europe-west2"
}

variable "service_account_email" {
  type = string
  default = "your-service-account-email@your-project-id.iam.gserviceaccount.com"
  
}

NOTE: make sure service-account have all the relevent premsissions.

  • storage-bucket.tf: Cloud Storage buckets to store the code of the Cloud Function and to upload files.
resource "google_storage_bucket" "Cloud_function_bucket" {
  name          = "cloud-function-${var.project_id}"
  location      = var.region
  project       = var.project_id
  force_destroy = true
  uniform_bucket_level_access = true
}

resource "google_storage_bucket" "input_bucket" {
  name          = "input-${var.project_id}"
  location      = var.region
  project       = var.project_id
  force_destroy = true
  uniform_bucket_level_access = true
}
  • cloudfunctiongen2.tf: Declare the Cloud Function
# Generates an archive of the source code compressed as a .zip file.
data "archive_file" "source" {
  type        = "zip"
  source_dir  = "${path.module}/src"
  output_path = "${path.module}/tmp/function.zip"
}

# Add source code zip to the Cloud Function's bucket (Cloud_function_bucket) 
resource "google_storage_bucket_object" "zip" {
  source       = data.archive_file.source.output_path
  content_type = "application/zip"
  name         = "src-${data.archive_file.source.output_md5}.zip"
  bucket       = google_storage_bucket.Cloud_function_bucket.name
  depends_on = [
    google_storage_bucket.Cloud_function_bucket,
    data.archive_file.source
  ]
}

resource "google_cloudfunctions2_function" "function" {
  name = "Cloud-function-trigger-using-terraform-gen-2"
  location = var.region
  description = "Cloud function gen2 trigger using terraform "

  build_config {
    runtime     = "python39"
    entry_point = "helloGET" 
    environment_variables = {
      BUILD_CONFIG_TEST = "build_test"
    }
    source {
      storage_source {
        bucket = google_storage_bucket.Cloud_function_bucket.name
        object = google_storage_bucket_object.zip.name
      }
    }
  }

  service_config {
    max_instance_count  = 3
    min_instance_count = 1
    available_memory    = "256M"
    timeout_seconds     = 60
    environment_variables = {
        SERVICE_CONFIG_TEST = "config_test"
    }
    ingress_settings = "ALLOW_INTERNAL_ONLY"
    all_traffic_on_latest_revision = true
    service_account_email = var.service_account_email
  }

  event_trigger {
    trigger_region = var.region
    event_type = "google.cloud.storage.object.v1.finalized"
    retry_policy = "RETRY_POLICY_RETRY"
    service_account_email = var.service_account_email
    event_filters {
      attribute = "bucket"
      value = google_storage_bucket.input_bucket.name
    }
  }
  depends_on = [
    google_storage_bucket.Cloud_function_bucket,
    google_storage_bucket_object.zip
  ]
}

Deploy cloud function:

  • Start with initializing the Terraform workspace. Aterraform initdownloads all the required providers and plugins. Run a Terraform plan creates an execution plan. The execution plan looks good, so let’s move ahead and apply this plan.

$ terraform init
$ terraform fmt
$ terraform validate
$ terraform apply -auto-approve

Cloud-Function- Gen2:

Storage buckets:

To test if everything is working correctly, follow these steps:

  1. Open the Google Cloud Console and log in to your project.
  2. Navigate to the Google Cloud Storage browser.
  3. Click on the bucket named input-<YOUR-PROJECT-ID>.
  4. Upload any file into the bucket to trigger the Cloud Function.
  5. To verify that the Cloud Function was triggered, go to the Cloud Functions list in the Google Cloud Console.

This test ensures that the Cloud Function — Gen2 is successfully triggered, whenever a file is uploaded to the bucket input-<YOUR-PROJECT-ID>, and you can confirm its execution by checking the logs.

  • Destroy: To destroy Terraform-provisioned infrastructure.
 $ terraform destroy --auto-approve

Reference: https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function#example-usage---cloudfunctions2-full

I trust that you have found this user-friendly.

Please share your thoughts and experiences after following the steps outlined. Your feedback is valuable and helps us improve the quality.

Topics:

Do not forget the 👏✌️❤️ if you like this content! Also, I will be glad if you hit the follow button so you get notified of my new posts. You can also follow me on LinkedIn! Thank you!

Further Reading:

Cloud Functions
Gcp
Terraform
DevOps
Serverless
Recommended from ReadMedium