TerraformPilot

Terraform

GCP Cloud Functions with Terraform

Deploy Google Cloud Functions with Terraform. HTTP and event-driven functions, Pub/Sub triggers, Cloud Storage triggers, VPC connectors, and IAM configuration.

LLuca Berton1 min read

Quick Answer

#
resource "google_cloudfunctions2_function" "api" {
  name     = "my-function"
  location = "us-central1"
 
  build_config {
    runtime     = "nodejs20"
    entry_point = "handler"
    source {
      storage_source {
        bucket = google_storage_bucket.source.name
        object = google_storage_bucket_object.source.name
      }
    }
  }
 
  service_config {
    max_instance_count = 10
    available_memory   = "256M"
    timeout_seconds    = 60
  }
}

HTTP Function (Gen 2)

#
# Upload source code
data "archive_file" "source" {
  type        = "zip"
  source_dir  = "${path.module}/function-source"
  output_path = "${path.module}/function-source.zip"
}
 
resource "google_storage_bucket" "source" {
  name     = "${var.project}-function-source"
  location = var.region
  uniform_bucket_level_access = true
}
 
resource "google_storage_bucket_object" "source" {
  name   = "function-source-${data.archive_file.source.output_md5}.zip"
  bucket = google_storage_bucket.source.name
  source = data.archive_file.source.output_path
}
 
resource "google_cloudfunctions2_function" "api" {
  name     = "${var.project}-api"
  location = var.region
 
  build_config {
    runtime     = "nodejs20"
    entry_point = "handler"
    source {
      storage_source {
        bucket = google_storage_bucket.source.name
        object = google_storage_bucket_object.source.name
      }
    }
  }
 
  service_config {
    max_instance_count               = 100
    min_instance_count               = 1    # Keep warm
    available_memory                 = "512M"
    timeout_seconds                  = 300
    max_instance_request_concurrency = 80
    available_cpu                    = "1"
    service_account_email            = google_service_account.function.email
 
    environment_variables = {
      ENVIRONMENT = var.environment
      PROJECT_ID  = var.gcp_project
    }
 
    secret_environment_variables {
      key        = "API_KEY"
      project_id = var.gcp_project
      secret     = google_secret_manager_secret.api_key.secret_id
      version    = "latest"
    }
  }
 
  labels = { environment = var.environment }
}
 
# Public access
resource "google_cloud_run_service_iam_member" "public" {
  location = google_cloudfunctions2_function.api.location
  service  = google_cloudfunctions2_function.api.name
  role     = "roles/run.invoker"
  member   = "allUsers"
}
 
output "function_url" {
  value = google_cloudfunctions2_function.api.service_config[0].uri
}

Pub/Sub Trigger

#
resource "google_pubsub_topic" "events" {
  name = "${var.project}-events"
}
 
resource "google_cloudfunctions2_function" "processor" {
  name     = "${var.project}-processor"
  location = var.region
 
  build_config {
    runtime     = "python312"
    entry_point = "process_event"
    source {
      storage_source {
        bucket = google_storage_bucket.source.name
        object = google_storage_bucket_object.source.name
      }
    }
  }
 
  service_config {
    max_instance_count    = 50
    available_memory      = "256M"
    timeout_seconds       = 120
    service_account_email = google_service_account.function.email
  }
 
  event_trigger {
    trigger_region = var.region
    event_type     = "google.cloud.pubsub.topic.v1.messagePublished"
    pubsub_topic   = google_pubsub_topic.events.id
    retry_policy   = "RETRY_POLICY_RETRY"
  }
}

Cloud Storage Trigger

#
resource "google_cloudfunctions2_function" "image_processor" {
  name     = "${var.project}-image-processor"
  location = var.region
 
  build_config {
    runtime     = "python312"
    entry_point = "process_image"
    source {
      storage_source {
        bucket = google_storage_bucket.source.name
        object = google_storage_bucket_object.source.name
      }
    }
  }
 
  service_config {
    max_instance_count    = 20
    available_memory      = "1Gi"
    timeout_seconds       = 300
    service_account_email = google_service_account.function.email
  }
 
  event_trigger {
    trigger_region        = var.region
    event_type            = "google.cloud.storage.object.v1.finalized"
    retry_policy          = "RETRY_POLICY_RETRY"
    service_account_email = google_service_account.function.email
 
    event_filters {
      attribute = "bucket"
      value     = google_storage_bucket.uploads.name
    }
  }
}

Service Account

#
resource "google_service_account" "function" {
  account_id   = "${var.project}-function"
  display_name = "Cloud Functions Service Account"
}
 
resource "google_project_iam_member" "function_secrets" {
  project = var.gcp_project
  role    = "roles/secretmanager.secretAccessor"
  member  = "serviceAccount:${google_service_account.function.email}"
}

Gen 1 vs Gen 2

#
FeatureGen 1Gen 2
Concurrency1 request/instanceUp to 1000
Timeout9 min (HTTP), 10 min (event)60 min
Instance size8 GB / 2 vCPU16 GB / 4 vCPU
Traffic splittingNoYes
Min instancesNoYes
Terraform resourcegoogle_cloudfunctions_functiongoogle_cloudfunctions2_function
#

Conclusion

#

Always use Gen 2 functions (google_cloudfunctions2_function) — they support concurrency, longer timeouts, traffic splitting, and are built on Cloud Run. Upload source via Cloud Storage with MD5 in the filename for automatic redeployment on code changes. Use dedicated service accounts with least-privilege IAM.

#Terraform#GCP#Cloud Functions#Serverless#Infrastructure as Code

Share this article