Skip to content

Basic GCP Qumulo Cluster Example

This example demonstrates the minimal configuration required to deploy a Qumulo cluster on GCP in a single zone.

Prerequisites

  • GCP project with billing enabled
  • IAM permissions to create compute instances, GCS buckets, service accounts, and firewall rules
  • Existing VPC network and subnetwork in the target region
  • Terraform >= 1.0
  • Application Default Credentials (gcloud auth application-default login) or GOOGLE_APPLICATION_CREDENTIALS set

Project Prerequisites

Your GCP project must meet these requirements before deploying a cluster:

  • Enabled APIs: Compute Engine (compute.googleapis.com), Cloud Storage (storage.googleapis.com), and IAM (iam.googleapis.com) must be enabled. Cloud KMS (cloudkms.googleapis.com) is also required if kms_key_name is set.
  • IAM roles: The deploying principal needs roles/compute.admin, roles/storage.admin, and (when service_account_email is omitted) roles/iam.serviceAccountAdmin. Add roles/cloudkms.cryptoKeyEncrypterDecrypter on the KMS key if kms_key_name is set.
  • Network: An existing VPC and subnetwork in the target region. The subnet must have Private Google Access enabled so cluster nodes without external IPs can reach Cloud Storage.
  • Billing: Billing must be enabled on the project.

See the GCS state backend example for a working IAM and project setup.

Usage

  1. Edit main.tf with your values (look for <-- Replace comments)

  2. Initialize and deploy:

    terraform init -upgrade
    terraform plan
    terraform apply
    

Configuration

This example creates:

  • A 3-node Qumulo cluster in a single zone
  • n2-highmem-8 storage-optimized instances
  • Default Ubuntu 24.04 LTS image (ubuntu-os-cloud / ubuntu-2404-lts-amd64) for both cluster nodes and the provisioner VM
  • Provider-managed service account with the minimum required roles
  • A VPC firewall rule covering the CIDR blocks listed in allow_cidrs

Node Count

Valid node counts are:

  • 1 (single node for testing)
  • 3-24 (cluster mode)

2-node clusters are not supported because they cannot form a majority quorum. 4-node clusters are supported only in single-zone deployments.

Single-Zone vs Multi-Zone

This example uses a single zone (defaults to <region>-b when availability_zones is omitted). For multi-zone, set availability_zones to a list of 3 or more zones in the same region. A 2-zone deployment is not supported because it cannot form a majority quorum. node_count = 4 is single-zone only.

Outputs

After deployment, you'll get:

  • cluster_name: The name of your cluster
  • cluster_uuid: UUID of the Qumulo cluster
  • deployment_unique_name: Unique deployment identifier
  • endpoint_ips: IP addresses for client connections (floating IPs if configured, otherwise primary IPs)
  • primary_ips: Per-node primary IPs
  • endpoints: Pre-formatted connection strings for web UI, API, NFS, and SMB

Supported Instance Types

The provider accepts a curated allow list of GCE machine types. Set instance_type to any of the values below:

Family Supported sizes
n2-highmem 8, 16, 32, 48
n2d-highmem 8, 16, 32, 48
z3-highmem 14-standardlssd, 16-highlssd, 22-standardlssd, 22-highlssd, 32-highlssd, 44-standardlssd, 44-highlssd, 88-standardlssd, 88-highlssd

Any type outside this list fails plan validation.

Custom Images

By default the provider deploys Ubuntu 24.04 LTS (ubuntu-os-cloud / ubuntu-2404-lts-amd64) for both nodes and the provisioner. To use a custom image, set the (project, family) pair for either the cluster nodes, the provisioner, or both. Each pair is all-or-nothing: setting node_image_project without node_image_family (or vice versa) is rejected at plan time. You can also pin an exact build with node_image / provisioner_image, which takes precedence over the family pair.

See GCP Custom Images for the full set of options, how to switch modes on an existing cluster (set a field to "" to clear it), and the cluster-replacement rules that apply to node image changes.

Deletion Protection

Set deletion_protection = true to protect the cluster's node instances from deletion, whether via terraform destroy, the GCP console, or gcloud. Each node carries the native GCE deletionProtection flag. The provider also refuses terraform destroy while the attribute is true in state.

Deleting a protected cluster is a deliberate two-step:

  1. Set deletion_protection = false and run terraform apply.
  2. Run terraform destroy.

Provider-managed operations (node-count scaling, instance_type replacement) succeed transparently while protection is on. The provider lifts the flag on exactly the instances it removes, immediately before removing them, then re-asserts protection across the whole cluster at the end of the update.

GCS buckets are not protected out-of-band

Unlike AWS (bucket-policy deny) and Azure (management locks), GCP offers no bucket deletion-protection mechanism. deletion_protection does not block deleting the cluster's GCS buckets from the console or gcloud; only the node instances carry a cloud-side flag. The buckets are covered by the terraform destroy gate alone. Non-empty buckets still refuse deletion (BucketNotEmpty), which is meaningful incidental protection for a live cluster's backing store. An empty or force-emptied bucket can still be deleted out-of-band.

No additional IAM roles are required for this feature: compute.instances.setDeletionProtection is included in roles/compute.admin, which the deploying principal already needs (see the prerequisites admonition above).

Troubleshooting

  1. API not enabled: Cluster creation fails early with a "service not enabled" error if Compute Engine, Cloud Storage, or IAM APIs are off. Enable them with gcloud services enable compute.googleapis.com storage.googleapis.com iam.googleapis.com --project <PROJECT_ID>.
  2. Private Google Access missing: If the subnet does not have Private Google Access enabled, nodes without external IPs cannot reach Cloud Storage and the provisioner hangs. Enable it on the subnet in the GCP console under VPC network > Subnets > Edit > Private Google access: On.
  3. Insufficient IAM permissions: Use the canonical roles listed in the prerequisites admonition above. The most common gap is roles/iam.serviceAccountAdmin on the deploying principal when no service_account_email is supplied.
  4. Invalid instance type: The provider accepts a specific allow list of machine types. See Supported Instance Types.
  5. Image pair mismatch: Setting one of (node_image_project, node_image_family) without the other (or the same for the provisioner pair) fails plan validation. Either set both fields of a pair or omit both to take the Ubuntu 24.04 default.
  6. Two-zone deployment rejected: Use 1 zone (single-zone) or 3+ zones (multi-zone). 2 is never valid.
  7. Debug logs: Run TF_LOG=DEBUG terraform apply for detailed output.

Full Configuration

# Example: Basic GCP Qumulo Cluster (single-zone)
# Edit the values below directly, then run: terraform init -upgrade && terraform apply

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

provider "qumulo" {
  gcp {
    # project_id and region can also come from the GOOGLE_PROJECT and
    # GOOGLE_REGION environment variables. Application Default Credentials
    # (gcloud auth application-default login) provide authentication.
    project_id = "your-gcp-project-id" # <-- Replace
    region     = "us-central1"         # <-- Replace
  }
}

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).
  # Sensitive and write-only -- not stored in Terraform state.
  admin_password = "YourSecurePassword123!" # <-- Replace

  # 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. Use 1 zone (single-zone) or 3+
  # # zones (multi-zone) within the deployment region. 2 zones is not valid.
  # availability_zones = ["us-central1-a", "us-central1-b", "us-central1-c"]

  # # Qumulo software version. Defaults to latest. Immutable after creation.
  # 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

  # GCE machine type for cluster nodes.
  # Supported families: n2-highmem, n2d-highmem, z3-highmem.
  # See the "Supported Instance Types" section of this page for the full list.
  instance_type = "n2-highmem-8"

  # # Cloud KMS key for CMEK encryption of cluster disks.
  # # Format: projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY
  # # Immutable after creation. Requires roles/cloudkms.cryptoKeyEncrypterDecrypter on the key.
  # 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    = "qumulo"

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

  # # 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 (single node), or 3-24 (4 nodes: single-zone only).
  # Note: 2 is not a valid node count.
  node_count = 3

  # # Image (project, family) for cluster nodes. All-or-nothing pair.
  # # Defaults to the Ubuntu 24.04 LTS image when omitted.
  # node_image_project = "ubuntu-os-cloud"
  # node_image_family  = "ubuntu-2404-lts-amd64"

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

  # # Image (project, family) for the provisioner VM. All-or-nothing pair.
  # # Defaults to the Ubuntu 24.04 LTS image when omitted.
  # provisioner_image_project = "ubuntu-os-cloud"
  # provisioner_image_family  = "ubuntu-2404-lts-amd64"

  # # 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, the
  # # provider creates a service account with the minimum required roles
  # # (and the deploying principal needs roles/iam.serviceAccountAdmin).
  # service_account_email = "qumulo-nodes@your-project.iam.gserviceaccount.com"

  # Usable storage capacity in TB. Determines the number of GCS buckets created.
  # Can be increased to add storage, but cannot be decreased.
  soft_capacity_limit_tb = 100

  # Subnetwork name or self-link for cluster nodes.
  # Must have Private Google Access enabled.
  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.
  # # Keys must not start with the reserved "goog-" prefix.
  # tags = {
  #   environment = "development"
  #   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 "cluster_uuid" {
  description = "UUID of the Qumulo cluster"
  value       = qumulo_filesystem_gcp.cluster.cluster_uuid
}

output "deployment_unique_name" {
  description = "Unique deployment identifier"
  value       = qumulo_filesystem_gcp.cluster.deployment_unique_name
}

output "endpoint_ips" {
  description = "Client-facing IPs. Floating IPs if configured, otherwise primary IPs."
  value       = qumulo_filesystem_gcp.cluster.endpoint_ips
}

output "primary_ips" {
  description = "Per-node primary IPs. Use these directly when no floating IPs are configured, or for per-node access."
  value       = qumulo_filesystem_gcp.cluster.primary_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"
  }
}