Skip to content

GCS State Backend Example

This example demonstrates using Google Cloud Storage (GCS) for Terraform state management when deploying Qumulo clusters on GCP.

GCS provides native state locking via object generation numbers, so no separate locking infrastructure (e.g., DynamoDB) is required.

Point Terraform at the bucket with a backend "gcs" block:

terraform {
  backend "gcs" {
    bucket = "my-terraform-state-bucket" # <-- Your GCS bucket name
    prefix = "qumulo/gcp/prod"           # <-- Path within bucket
  }
}

Prerequisites

  • GCP project with billing enabled
  • IAM permissions for storage.buckets.create and storage.objects.* on the state bucket
  • Existing VPC network and subnetwork
  • Terraform >= 1.0

Creating the Backend Bucket

Before running terraform init -upgrade, create the GCS bucket:

gcloud storage buckets create gs://my-terraform-state-bucket \
  --project=my-tfstate-project \
  --location=us-central1 \
  --uniform-bucket-level-access

# Enable object versioning so prior state revisions are recoverable
gcloud storage buckets update gs://my-terraform-state-bucket --versioning

Usage

  1. Edit main.tf:
  2. Update the backend "gcs" block with your bucket details
  3. Update provider and resource values (look for <-- Replace comments)

  4. Authenticate to GCP (one of):

    gcloud auth application-default login
    # or set GOOGLE_APPLICATION_CREDENTIALS to a service account key file
    

  5. Initialize and deploy:

    terraform init -upgrade
    terraform plan
    terraform apply
    

Backend Configuration Options

Options for the backend "gcs" block:

  • prefix: Path within the bucket. Each workspace stores its state at <prefix>/<workspace>.tfstate.
  • encryption_key: Customer-supplied AES-256 key (base64) for client-side encryption of state.
  • kms_encryption_key: Cloud KMS key resource name for server-side encryption of state.
  • impersonate_service_account: Run state operations as another service account.
  • credentials: Path to a service account JSON key (defaults to Application Default Credentials).

Best Practices

  • Enable object versioning so previous state revisions can be recovered
  • Use a dedicated project or bucket for Terraform state, separate from workload resources
  • Restrict bucket access using IAM (roles/storage.objectAdmin for operators, nothing wider)
  • Add a lifecycle rule to age out non-current versions after a retention window
  • For sensitive environments, encrypt state with a Cloud KMS key (kms_encryption_key)

Outputs

The full configuration below defines these outputs:

  • cluster_name: The name of your cluster
  • endpoint_ips: IP addresses for client connections
  • endpoints: Pre-formatted connection strings for web UI, API, NFS, and SMB

Full Configuration

# Example: Using GCS as Terraform State Backend
# This example shows how to configure remote state with Google Cloud Storage
# while deploying a Qumulo cluster on GCP.
# Edit the values below directly, then run: terraform init -upgrade && terraform apply
#
# PREREQUISITES:
# The GCS bucket must exist before running terraform init -upgrade.
# Create it with:
#   gcloud storage buckets create gs://my-terraform-state-bucket \
#     --project=my-tfstate-project \
#     --location=us-central1 \
#     --uniform-bucket-level-access
#   gcloud storage buckets update gs://my-terraform-state-bucket --versioning
#
# State locking is native to GCS (no extra resources required).

terraform {
  required_version = ">= 1.0"

  # GCS backend configuration
  backend "gcs" {
    bucket = "my-terraform-state-bucket" # <-- Your GCS bucket name
    prefix = "qumulo/gcp/prod"           # <-- Path within bucket

    # Optional: Impersonate a service account for state operations
    # impersonate_service_account = "tfstate@my-tfstate-project.iam.gserviceaccount.com"

    # Optional: Server-side encryption with a Cloud KMS key
    # kms_encryption_key = "projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY"
  }

  required_providers {
    qumulo = {
      source  = "qumulo-terraform-registry.s3.us-east-1.amazonaws.com/qumulo/qumulo"
      version = "~> 1.0"
    }
  }
}

provider "qumulo" {
  gcp {
    project_id = "your-gcp-project-id" # <-- GCP project for the cluster
    region     = "us-central1"         # <-- GCP region
  }
}

# Qumulo cluster on GCP (state stored in GCS)
resource "qumulo_filesystem_gcp" "cluster" {
  provider = qumulo

  # =============================================================================
  # All attributes (alphabetical order)
  # Uncomment any attribute to use it. Required attributes are not commented.
  # =============================================================================

  # Administrator password for cluster access (8-128 characters).
  admin_password = "YourSecurePassword123!"

  # CIDR blocks allowed to access the cluster via firewall rules (required, at least one).
  # Example: ["10.0.0.0/8", "172.16.0.0/12"]
  allow_cidrs = ["0.0.0.0/0"]

  # # Availability zones for node placement.
  # # Defaults to [region + "-b"] if omitted.
  # # Example: ["us-central1-a", "us-central1-b", "us-central1-c"]
  # availability_zones = ["us-central1-a", "us-central1-b", "us-central1-c"]

  # # Qumulo software version (optional, defaults to latest available).
  # # Immutable after creation - use Qumulo UI or CLI to upgrade.
  # cluster_version = "7.8.0"

  # # Number of floating IPs for client access. The provider reserves the
  # # addresses and reports them in floating_ips; endpoint_ips returns them
  # # instead of node IPs.
  # floating_ip_count = 3

  # GCP machine type for cluster nodes (e.g., n2-standard-8).
  instance_type = "n2-standard-8"

  # # Cloud KMS key for CMEK encryption of cluster disks.
  # # Format: projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY
  # # Immutable after creation.
  # kms_key_name = "projects/your-project/locations/us-central1/keyRings/qumulo/cryptoKeys/cluster"

  # qfsd cluster name, shown in the Qumulo UI (2-15 chars, case preserved).
  cluster_name    = "qumulogcs"

  # Prefix for the GCP resources this cluster creates (2-15 lowercase chars).
  deployment_name = "qumulogcs"

  # # Boot disk size for cluster nodes in GB. Defaults to 50.
  # node_boot_disk_size_gb = 50

  # Number of nodes in the cluster.
  # Valid values: 1, or 3-24 (2 is not valid; 4 nodes: single-zone only).
  node_count = 3

  # GCP image family for cluster nodes (Qumulo-published).
  node_image_family = "qumulo-cluster-prod" # <-- Replace with your Qumulo image family

  # GCP project hosting the cluster node image family.
  node_image_project = "qumulo-prod" # <-- Replace with the Qumulo image project

  # # GCP project ID. Defaults to the provider gcp { project_id } value.
  # project_id = "your-gcp-project-id"

  # GCP image family for the provisioner VM.
  provisioner_image_family = "qumulo-provisioner-prod" # <-- Replace with your provisioner image family

  # GCP project hosting the provisioner image family.
  provisioner_image_project = "qumulo-prod" # <-- Replace with the provisioner image project

  # # Machine type for the provisioner VM. Defaults to e2-standard-4.
  # provisioner_machine_type = "e2-standard-4"

  # # GCP region. Defaults to the provider gcp { region } value.
  # region = "us-central1"

  # # Pre-existing service account email for cluster nodes.
  # # If omitted, a service account is created automatically.
  # service_account_email = "qumulo-nodes@your-project.iam.gserviceaccount.com"

  # Usable storage capacity in TB. Determines GCS bucket count.
  # Can be increased to add storage, but cannot be decreased.
  soft_capacity_limit_tb = 100

  # Subnetwork name or self-link for cluster nodes.
  subnetwork = "projects/your-project/regions/us-central1/subnetworks/qumulo-subnet" # <-- Replace

  # # Labels applied to GCP resources created for this cluster.
  # # Keys/values: lowercase letters, digits, hyphens, underscores. Max 64 labels.
  # tags = {
  #   environment = "production"
  #   managed-by  = "terraform"
  # }

  deletion_protection = true # recommended: guard the cluster's node instances

  timeouts {
    create = "90m"
    delete = "90m"
  }
}

output "cluster_name" {
  description = "Name of the Qumulo cluster"
  value       = qumulo_filesystem_gcp.cluster.cluster_name
}

output "endpoint_ips" {
  description = "Endpoint IP addresses for client connections"
  value       = qumulo_filesystem_gcp.cluster.endpoint_ips
}

output "endpoints" {
  description = "Connection endpoints for various protocols"
  value = {
    web_ui = "https://${try(qumulo_filesystem_gcp.cluster.endpoint_ips[0], "pending")}"
    api    = "https://${try(qumulo_filesystem_gcp.cluster.endpoint_ips[0], "pending")}:8000"
    nfs    = "${try(qumulo_filesystem_gcp.cluster.endpoint_ips[0], "pending")}:/"
    smb    = "\\\\${try(qumulo_filesystem_gcp.cluster.endpoint_ips[0], "pending")}\\share"
  }
}