Skip to main content

GCP Cloud Storage Event

This guide will help you set up GCP Cloud Storage event notifications using Terraform Infrastructure as Code.

Prerequisites

  • GCP Project with billing enabled
  • gcloud CLI installed
  • Terminal/Command Line access
  • Owner or Editor role in the GCP project

Step 1: Install Required Tools

1.1 Install gcloud CLI

Check if already installed:

gcloud --version

For macOS:

curl https://sdk.cloud.google.com | bash
exec -l $SHELL

For Linux:

curl https://sdk.cloud.google.com | bash
exec -l $SHELL

For Windows: Download from: https://cloud.google.com/sdk/docs/install


1.2 Install Terraform

For macOS:

brew tap hashicorp/tap
brew install hashicorp/tap/terraform

For Linux:

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

For Windows: Download from: https://www.terraform.io/downloads

Verify installation:

terraform --version

Step 2: Authenticate with GCP

2.1 Initialize gcloud

gcloud init

This will guide you through:

  • Logging in to your Google account
  • Selecting your GCP project
  • Setting default compute region/zone

2.2 Login to GCP

gcloud auth login

This will open your browser for authentication.


2.3 Set Application Default Credentials

gcloud auth application-default login

This allows Terraform to authenticate with GCP.


2.4 Set Your Project

List available projects:

gcloud projects list

Set the project you want to use:

gcloud config set project YOUR_PROJECT_ID

2.5 Verify Current Project

gcloud config get-value project

Step 3: Create Terraform Configuration Files

3.1 Create Project Directory

mkdir gcp-event-notification-setup
cd gcp-event-notification-setup

3.2 Create Main Terraform Configuration

Create a file named main.tf with the following content:

terraform {
required_version = ">= 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}

provider "google" {
project = var.project_id
region = var.region
}

# Variables for customization
variable "project_id" {
description = "GCP Project ID"
type = string
}

variable "region" {
description = "GCP Region for resources"
type = string
default = "us-central1"
}

variable "service_account_name" {
description = "Name for the service account"
type = string
default = "event-subscription-service"
}

variable "custom_role_id" {
description = "ID for the custom IAM role"
type = string
default = "gcs_event_notification_manager"
}

variable "custom_role_title" {
description = "Title for the custom IAM role"
type = string
default = "GCS Event Notification Manager"
}

# Enable required APIs
resource "google_project_service" "required_apis" {
for_each = toset([
"cloudresourcemanager.googleapis.com",
"pubsub.googleapis.com",
"storage.googleapis.com",
"secretmanager.googleapis.com",
"iam.googleapis.com",
"iamcredentials.googleapis.com"
])

project = var.project_id
service = each.value
disable_on_destroy = false
}

# Create Custom IAM Role
resource "google_project_iam_custom_role" "event_notification_manager" {
role_id = var.custom_role_id
title = var.custom_role_title
description = "Allows management of Cloud Storage event notifications and Pub/Sub resources"
project = var.project_id
stage = "GA"

permissions = [
# Storage Permissions
"storage.buckets.get",
"storage.buckets.update",

# Pub/Sub Topic Permissions
"pubsub.topics.create",
"pubsub.topics.delete",
"pubsub.topics.get",
"pubsub.topics.getIamPolicy",
"pubsub.topics.list",
"pubsub.topics.publish",
"pubsub.topics.setIamPolicy",
"pubsub.topics.attachSubscription",

# Pub/Sub Subscription Permissions
"pubsub.subscriptions.create",
"pubsub.subscriptions.delete",
"pubsub.subscriptions.get",
"pubsub.subscriptions.getIamPolicy",
"pubsub.subscriptions.list",
"pubsub.subscriptions.setIamPolicy",
"pubsub.subscriptions.update",
"pubsub.subscriptions.consume",
"pubsub.snapshots.seek",

# Resource Manager Permissions
"resourcemanager.projects.get",

# IAM Permission
"iam.serviceAccounts.actAs"
]

depends_on = [google_project_service.required_apis]
}

# Create Service Account
resource "google_service_account" "event_subscription_service" {
account_id = var.service_account_name
display_name = "Event Subscription Service Account"
description = "Service account for managing Cloud Storage event notifications"
project = var.project_id

depends_on = [google_project_service.required_apis]
}

# Bind Custom Role to Service Account
resource "google_project_iam_member" "service_account_role_binding" {
project = var.project_id
role = google_project_iam_custom_role.event_notification_manager.id
member = "serviceAccount:${google_service_account.event_subscription_service.email}"

depends_on = [
google_service_account.event_subscription_service,
google_project_iam_custom_role.event_notification_manager
]
}

# Create Service Account Key
resource "google_service_account_key" "event_subscription_key" {
service_account_id = google_service_account.event_subscription_service.name

depends_on = [
google_service_account.event_subscription_service,
google_project_iam_member.service_account_role_binding
]
}

# Store Service Account Key in Secret Manager
resource "google_secret_manager_secret" "service_account_credentials" {
secret_id = "${var.service_account_name}-credentials"
project = var.project_id

replication {
auto {}
}

labels = {
purpose = "gcs-event-subscription"
managed-by = "terraform"
}

depends_on = [google_project_service.required_apis]
}

# Store the actual key content in the secret
resource "google_secret_manager_secret_version" "service_account_credentials_version" {
secret = google_secret_manager_secret.service_account_credentials.id
secret_data = base64decode(google_service_account_key.event_subscription_key.private_key)

depends_on = [google_secret_manager_secret.service_account_credentials]
}

# Grant the service account access to read its own credentials from Secret Manager
resource "google_secret_manager_secret_iam_member" "service_account_secret_accessor" {
secret_id = google_secret_manager_secret.service_account_credentials.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.event_subscription_service.email}"

depends_on = [google_secret_manager_secret_version.service_account_credentials_version]
}

# Store credentials in local file (optional - for manual retrieval)
resource "local_file" "credentials" {
content = base64decode(google_service_account_key.event_subscription_key.private_key)
filename = "${path.module}/gcp-credentials.json"
file_permission = "0600"
}

# Outputs
output "project_id" {
description = "GCP Project ID"
value = var.project_id
}

output "region" {
description = "GCP Region"
value = var.region
}

output "service_account_email" {
description = "Email of the created service account"
value = google_service_account.event_subscription_service.email
}

output "service_account_id" {
description = "ID of the created service account"
value = google_service_account.event_subscription_service.id
}

output "custom_role_id" {
description = "ID of the custom IAM role"
value = google_project_iam_custom_role.event_notification_manager.id
}

output "custom_role_name" {
description = "Name of the custom IAM role"
value = google_project_iam_custom_role.event_notification_manager.name
}

output "secret_manager_secret_id" {
description = "Secret Manager secret ID containing service account credentials"
value = google_secret_manager_secret.service_account_credentials.secret_id
}

output "secret_manager_secret_name" {
description = "Full Secret Manager secret name"
value = google_secret_manager_secret.service_account_credentials.name
}

output "credentials_file" {
description = "Path to credentials file"
value = "Credentials saved to: ${abspath(local_file.credentials.filename)}"
}

output "retrieve_credentials_command" {
description = "Command to retrieve service account credentials from Secret Manager"
value = "gcloud secrets versions access latest --secret=${google_secret_manager_secret.service_account_credentials.secret_id} --project=${var.project_id}"
}

output "service_account_key_download_note" {
description = "Note about downloading service account key"
value = "Service account key is stored in Secret Manager. Use the retrieve_credentials_command to get the credentials."
}

3.3 Create Variables File

Create a file named terraform.tfvars with your values:

Option A: Default Configuration (Recommended)

project_id = "your-project-id"
region = "us-central1"
service_account_name = "event-subscription-service"

Option B: Custom Configuration

project_id = "your-project-id"
region = "us-east1"
service_account_name = "prod-event-service"
custom_role_id = "custom_gcs_event_manager"
custom_role_title = "Production Event Manager"

Replace your-project-id with your actual GCP project ID.


Step 4: Deploy Infrastructure

4.1 Initialize Terraform

terraform init

Expected output:

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/google versions matching "~> 5.0"...
Terraform has been successfully initialized!

4.2 Preview Changes

terraform plan

This shows what will be created. Review carefully.

You should see:

  • 6 APIs to be enabled
  • 1 custom IAM role to be created
  • 1 service account to be created
  • 1 service account key to be generated
  • 1 IAM role binding to be created
  • 2 Secret Manager resources to be created
  • 1 local file to be created

4.3 Apply Configuration

terraform apply

Type yes when prompted.

Deployment takes 2-3 minutes.


4.4 View Outputs

After successful deployment, you'll see outputs like:

Outputs:

credentials_file = "Credentials saved to: /path/to/gcp-event-notification-setup/gcp-credentials.json"
custom_role_id = "projects/your-project-id/roles/gcs_event_notification_manager"
custom_role_name = "projects/your-project-id/roles/gcs_event_notification_manager"
project_id = "your-project-id"
region = "us-central1"
retrieve_credentials_command = "gcloud secrets versions access latest --secret=event-subscription-service-credentials --project=your-project-id"
service_account_email = "event-subscription-service@your-project-id.iam.gserviceaccount.com"
service_account_id = "projects/your-project-id/serviceAccounts/event-subscription-service@your-project-id.iam.gserviceaccount.com"
service_account_key_download_note = "Service account key is stored in Secret Manager. Use the retrieve_credentials_command to get the credentials."
secret_manager_secret_id = "event-subscription-service-credentials"
secret_manager_secret_name = "projects/123456789/secrets/event-subscription-service-credentials"

Step 5: Retrieve and Use Credentials

5.1 Get Credentials from Local File

The credentials are automatically saved to a local file:

cat gcp-credentials.json

5.2 Get Credentials from Secret Manager

Retrieve from Secret Manager (recommended for production):

gcloud secrets versions access latest \
--secret=event-subscription-service-credentials \
--project=your-project-id > retrieved-credentials.json

5.3 Verify Service Account

Check the service account was created:

gcloud iam service-accounts list --project=your-project-id

You should see:

NAME EMAIL
Event Subscription Service Account event-subscription-service@your-project-id.iam.gserviceaccount.com

5.4 Verify Custom Role

Check the custom role:

gcloud iam roles describe gcs_event_notification_manager --project=your-project-id

5.5 Verify Role Binding

Check IAM policy:

gcloud projects get-iam-policy your-project-id \
--flatten="bindings[].members" \
--filter="bindings.members:serviceAccount:event-subscription-service@*"

Step 6: Verify Deployment

6.1 View All Resources Created

terraform state list

Expected output:

google_project_iam_custom_role.event_notification_manager
google_project_iam_member.service_account_role_binding
google_project_service.required_apis["cloudresourcemanager.googleapis.com"]
google_project_service.required_apis["iam.googleapis.com"]
google_project_service.required_apis["iamcredentials.googleapis.com"]
google_project_service.required_apis["pubsub.googleapis.com"]
google_project_service.required_apis["secretmanager.googleapis.com"]
google_project_service.required_apis["storage.googleapis.com"]
google_secret_manager_secret.service_account_credentials
google_secret_manager_secret_iam_member.service_account_secret_accessor
google_secret_manager_secret_version.service_account_credentials_version
google_service_account.event_subscription_service
google_service_account_key.event_subscription_key
local_file.credentials

6.2 Verify Service Account

Check the service account was created:

gcloud iam service-accounts list --project=your-project-id

You should see:

NAME EMAIL
Event Subscription Service Account event-subscription-service@your-project-id.iam.gserviceaccount.com

6.3 Verify Custom Role

Check the custom role:

gcloud iam roles describe gcs_event_notification_manager --project=your-project-id

6.4 Verify IAM Policy Binding

Check the role assignment:

gcloud projects get-iam-policy your-project-id \
--flatten="bindings[].members" \
--filter="bindings.members:serviceAccount:event-subscription-service@*"

6.5 Verify Enabled APIs

gcloud services list --enabled --project=your-project-id

Step 7: Retrieve Service Account Credentials

7.1 Option 1: From Local File

The credentials are automatically saved to a local file:

cat gcp-credentials.json

7.2 Option 2: From Secret Manager

Retrieve from Secret Manager (recommended for production):

gcloud secrets versions access latest \
--secret=event-subscription-service-credentials \
--project=your-project-id > retrieved-credentials.json

7.3 Option 3: From Terraform Output

View the Terraform output that shows where credentials are stored:

terraform output credentials_file
terraform output retrieve_credentials_command

Last Updated: November 2025
Version: 1.0
Author: GCP Event Notification Setup Team