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) orGOOGLE_APPLICATION_CREDENTIALSset
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 ifkms_key_nameis set. - IAM roles: The deploying principal needs
roles/compute.admin,roles/storage.admin, and (whenservice_account_emailis omitted)roles/iam.serviceAccountAdmin. Addroles/cloudkms.cryptoKeyEncrypterDecrypteron the KMS key ifkms_key_nameis 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¶
-
Edit
main.tfwith your values (look for<-- Replacecomments) -
Initialize and deploy:
Configuration¶
This example creates:
- A 3-node Qumulo cluster in a single zone
n2-highmem-8storage-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 clustercluster_uuid: UUID of the Qumulo clusterdeployment_unique_name: Unique deployment identifierendpoint_ips: IP addresses for client connections (floating IPs if configured, otherwise primary IPs)primary_ips: Per-node primary IPsendpoints: 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:
- Set
deletion_protection = falseand runterraform apply. - 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¶
- 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>. - 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.
- Insufficient IAM permissions: Use the canonical roles listed in the prerequisites admonition above. The most common gap is
roles/iam.serviceAccountAdminon the deploying principal when noservice_account_emailis supplied. - Invalid instance type: The provider accepts a specific allow list of machine types. See Supported Instance Types.
- 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. - Two-zone deployment rejected: Use 1 zone (single-zone) or 3+ zones (multi-zone). 2 is never valid.
- Debug logs: Run
TF_LOG=DEBUG terraform applyfor 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"
}
}