Skip to content

Import Guide

This guide covers importing existing resources into Terraform state so the provider can manage them going forward.

Every resource supports terraform import except the threat detection appliances (qumulo_threat_detection_aws, qumulo_threat_detection_azure, qumulo_threat_detection_gcp). Those cannot be imported and must be created by Terraform to be managed by it. The import ID format depends on the resource type.

How Importing Works

Every import is the same three moves:

  1. Declare the resource in your main.tf. Identity attributes must match the live resource; each section below shows the minimal block.
  2. Import it with the resource address and the resource's import ID:

    terraform import <resource_type>.<name> "<import-id>"
    
  3. Plan and reconcile: run terraform plan, copy any values it reports into your HCL, and repeat until the plan is clean.

Infrastructure Resources

Two notes apply to all three cloud filesystem resources:

Cluster name vs deployment name (Azure/AWS/GCP filesystem resources)

Each filesystem resource accepts either name (a shortcut that sets both the qfsd cluster name and the deployment infrastructure prefix) or the explicit cluster_name + deployment_name pair. The examples below use the pair; you may instead use name.

Import recovers neither value. The deployment_unique_name in the import ID only encodes the lowercased deployment prefix plus a random suffix (added by this provider on first deploy, or by the legacy CNQ modules). It cannot reconstruct the qfsd cluster_name or its original case.

Declare name (or the cluster_name + deployment_name pair) in your config to match the running cluster, using the bare prefix without the random suffix. The first post-import plan sets these in place (a non-destructive in-place update); it never replaces the cluster. The suffixed deployment_unique_name is preserved in state and continues to name every existing and future resource (e.g. nodes added by a later scale).

terraform apply halts on an existing deployment; it never adopts

When a create discovers that a deployment already occupies the target name (you switched workspaces, lost state, or are migrating off the legacy Terraform modules), the provider stops with an error rather than modifying or deleting it. An existing cluster is never touched. To bring an existing cluster under management, terraform import it (below). To start over, destroy it first. This applies to all three filesystem clouds.

qumulo_filesystem_azure

Azure clusters use a slash-separated import ID (not commas):

subscription_id/resource_group_name/deployment_unique_name
terraform import qumulo_filesystem_azure.cluster \
  "12345678-1234-1234-1234-123456789012/my-rg/mycluster-a1b2c3"

The deployment_unique_name is the prefix of all VM names in the cluster. Look at your VM names in the Azure portal: they follow the pattern {deployment_unique_name}-node-{id}.

Minimal Configuration

Put the resource block in your main.tf (the reference tables below explain each attribute), then run the import:

resource "qumulo_filesystem_azure" "cluster" {
  cluster_name        = "mycluster"
  deployment_name     = "mycluster"
  resource_group_name = "my-rg"
  location            = "eastus"
  node_count          = 5
  vm_type             = "Standard_L8s_v3"
  admin_password      = var.admin_password
  allow_cidrs         = ["10.0.0.0/8"]
  subnet_id           = "/subscriptions/12345678-.../subnets/my-subnet"

  # Required if the cluster uses floating IPs; the addresses themselves are
  # discovered from the cluster and reported in floating_ips.
  floating_ip_count = 3 # match the cluster's actual floating IP count

  timeouts {
    create = "90m"
    delete = "30m"
  }
}
terraform import qumulo_filesystem_azure.cluster \
  "12345678-1234-1234-1234-123456789012/my-rg/mycluster-a1b2c3"

terraform plan  # review discovered values and update your HCL to match

Required in HCL

These attributes are Required by the schema, so terraform plan errors out if they're missing from your resource block. The import recovers live values into state for most of them; copy those from terraform state show into HCL. Rows marked otherwise cannot be recovered, so supply the values from your own records. Mismatches cause destructive plan diffs on the next apply.

Attribute Notes
cluster_name + deployment_name (or name) The cluster identity, 2-15 chars each. Not recovered on import; declare it to match the running cluster. name sets both; or set cluster_name (qfsd name, case preserved) and deployment_name (lowercase resource prefix, the bare value without the suffix) separately.
node_count Current node count.
vm_type Azure VM size running on the cluster nodes.
location Azure region.
subnet_id Full Azure subnet resource ID.
admin_password Write-only. The cluster's current admin password. Cannot be recovered; supply your own.
allow_cidrs Current NSG ingress CIDRs (recovered from the cluster NSG's inbound rule; copy to HCL to satisfy the Required field).

Customer-Declared Attributes

The provider does NOT auto-discover the following attributes. They must be present and accurate in the resource block before terraform import. Mismatches may cause destructive plan diffs on the next apply.

Attribute Notes
cluster_version Current Qumulo Core version. Set only if pinned in the original config.
networking_mode "host_managed" (default) or "qumulo_managed". Cannot be reliably detected from infrastructure; set explicitly if not the default. Immutable after creation.
floating_ip_count Customer-declared, never auto-discovered. Set it to the cluster's current floating IP count (0 if none); a mismatch surfaces as a plan diff. Applying an incorrectly resolved 0 removes the cluster's floating IPs and breaks client access via those addresses, so set it to match the cluster's actual float count on import. The live addresses appear in the read-only floating_ips attribute.
storage_class Declare it if the cluster runs a non-default class. If omitted, the first post-import apply records the version-aware default (HOT on Core 7.8.4+ = INTELLIGENT_TIERING, otherwise STANDARD; COLD = STANDARD), which may not match what the cluster actually runs. Check the storage account access tier in the Azure portal: Smart means INTELLIGENT_TIERING, Hot means STANDARD. Immutable once recorded.
soft_capacity_limit_tb Current soft cap. Cannot be recovered from infrastructure; supply your own. Mismatches cause drift on every apply.
cluster_uuid Optional. Set it to the cluster's UUID (from qq node_state_get or GET /v1/node/state) when automatic recovery cannot find it. Rejected on create; cannot be changed after adoption. If a qumulo_threat_detection_* resource references this cluster, adopting a different UUID than the one in state triggers a one-time replacement of that appliance.
persistent_storage_resource_group Required for legacy azure-terraform-cnq clusters where storage lives in a separate RG; see Legacy Clusters below.

Legacy Clusters (azure-terraform-cnq)

Clusters deployed with the older azure-terraform-cnq module use the same import command. Two extra attributes are required in your HCL:

  • networking_mode = "qumulo_managed": Legacy clusters use the Qumulo-managed networking mode. Without this, scaling operations will fail with a node_add_mixed_networking_mode_error. This is immutable after creation.
  • persistent_storage_resource_group: Legacy deployments put storage accounts and KeyVault in a separate resource group. If you don't set this, capacity scaling and KeyVault lookups will target the wrong RG.

For legacy clusters, the resource group name and deployment_unique_name are often the same value. Use the exact casing from Azure (VM discovery is case-sensitive).

resource "qumulo_filesystem_azure" "cluster" {
  # cluster_name / deployment_name are the bare prefix (the legacy module's
  # deployment_name), not the suffixed resource_group_name / deployment_unique_name.
  cluster_name        = "mycluster"
  deployment_name     = "mycluster"
  node_count          = 5
  location            = "eastus"
  resource_group_name = "mycluster-ABC1234"
  subnet_id           = "/subscriptions/.../subnets/your-subnet"
  vm_type             = "Standard_L16s_v3"
  admin_password      = var.admin_password
  allow_cidrs         = ["10.0.0.0/8"]

  networking_mode                  = "qumulo_managed"
  persistent_storage_resource_group = "mycluster-ABC1234-persistent-storage"

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

How to find your storage resource group: In the Azure Portal, look for a resource group containing multiple storage accounts and a KeyVault whose names share a common prefix.

qumulo_filesystem_aws

AWS clusters use a slash-separated import ID with two segments:

region/deployment_unique_name
TF_LOG=DEBUG terraform import qumulo_filesystem_aws.cluster \
  "us-east-2/mycluster-a1b2c3"

The random suffix belongs in the import ID, and only there: the import command requires the suffixed deployment_unique_name (the Terraform id), while the deployment_name / name in your resource block stays the bare prefix without the suffix. Where to find the suffixed name:

  • Clusters originally deployed by this provider: terraform output | grep deployment_unique_name (the example configs expose this as an output).
  • Clusters deployed by aws-terraform-cnq: the module's deployment_unique_name output.

In either case, look for EC2 instances named {deployment_unique_name}-node-* in the AWS console to confirm.

Minimal Configuration

Put the resource block in your main.tf (the reference tables below explain each attribute), then run the import:

resource "qumulo_filesystem_aws" "imported" {
  cluster_name           = "my-cluster"
  deployment_name        = "my-cluster" # bare prefix, no random suffix (the suffix goes only in the import ID)
  region                 = "us-east-2"
  node_count             = 4
  instance_type          = "m6idn.2xlarge"
  subnet_ids             = ["subnet-0123456789abcdef0"]
  cluster_product_type   = "HOT"
  storage_class       = "INTELLIGENT_TIERING" # what the cluster actually runs (see below)
  admin_password         = var.admin_password
  allow_cidrs            = ["10.0.0.0/8"]
  soft_capacity_limit_tb = 500
  floating_ip_count      = 0 # in host_managed networking mode, set to the cluster's actual floating IP count

  timeouts {
    create = "90m"
    delete = "30m"
  }
}
# The import ID uses the suffixed deployment_unique_name, unlike deployment_name above
TF_LOG=DEBUG terraform import qumulo_filesystem_aws.imported us-east-2/my-cluster-abc12345678
TF_LOG=DEBUG terraform plan

Review the plan output and update your HCL to match any discovered values before running terraform apply.

Required in HCL

These attributes are Required by the schema, so terraform plan errors out if they're missing from your resource block. The import recovers live values into state for most of them; copy those from terraform state show into HCL. Rows marked otherwise cannot be recovered, so supply the values from your own records. Mismatches cause destructive plan diffs on the next apply.

Attribute Notes
cluster_name + deployment_name (or name) The cluster identity, 2-15 chars each. Not recovered on import; declare it to match the running cluster. name sets both; or set cluster_name (qfsd name, case preserved) and deployment_name (lowercase resource prefix, the bare value without the random suffix that's in deployment_unique_name) separately.
region AWS region the cluster runs in.
node_count Current node count.
instance_type EC2 instance type running on the cluster nodes.
subnet_ids List of subnets the cluster was deployed into.
cluster_product_type "HOT" or "COLD". Immutable after creation. Cannot be recovered; supply your own.
storage_class Always declare it, before the first post-import apply. Cannot be recovered. The first apply permanently records whatever the plan shows: your declared value, or (if omitted) the product-type default (HOT = INTELLIGENT_TIERING, COLD = GLACIER_IR). The value is immutable once recorded, so a wrongly backfilled default cannot be corrected later without hand-editing state. Clusters that don't run the current defaults are common: COLD clusters created when STANDARD_IA was the default, and aws-terraform-cnq clusters deployed with a non-default q_persistent_storage_type. See below for reading the live value.
admin_password Write-only. The cluster's current admin password. Cannot be recovered; supply your own.
allow_cidrs Current security group ingress CIDRs (recovered from the cluster security group's ingress rule; copy to HCL). Required unless bring-your-own security groups are configured (cluster_security_group_id + provisioner_security_group_id), in which case omit it.

No cluster API reports the storage class, but the persistent-storage buckets do. Read it off any object in one of the cluster's ...-qps-N buckets (find them via the Qumulo-Cluster tag, or by the deployment_unique_name in their names). The returned value (STANDARD, INTELLIGENT_TIERING, STANDARD_IA, or GLACIER_IR) is exactly what to declare:

aws s3api list-objects-v2 --bucket <bucket> --max-items 1 --query 'Contents[0].StorageClass'

Customer-Declared Attributes

The provider does NOT auto-discover the following attributes. They must be present and accurate in the resource block before terraform import. Mismatches may cause destructive plan diffs on the next apply.

Attribute Notes
soft_capacity_limit_tb Current soft cap. Mismatches cause drift on every apply.
networking_mode "host_managed" (default) or "qumulo_managed". Immutable after creation; aws-terraform-cnq clusters require "qumulo_managed" (see Legacy Clusters below).
floating_ip_count Customer-declared, never auto-discovered. In the host_managed networking mode, set it to the cluster's current floating IP count (0 if none); a mismatch surfaces as a plan diff. In the qumulo_managed networking mode it must be 0: Qumulo Core owns the floating IPs and the provider never reconciles them, so the live addresses appear only in the read-only floating_ips attribute.
floating_ip_count_ipv6 Customer-declared, never auto-discovered — exactly like floating_ip_count. For a cluster whose floating IPs are IPv6 addresses, set floating_ip_count_ipv6 to that pool's size and leave floating_ip_count = 0; the live addresses show up in the read-only floating_ips attribute after the first refresh, so you can count them there. The two counts cannot both be non-zero, and declaring the wrong one asks the provider to migrate the pool across address families, which is refused at plan time.
provisioner_ami_id Set only if a non-default provisioner AMI was used.
cluster_iam_role_arn Bring-your-own cluster IAM role ARN. Copy from the original configuration; the provider does not discover it from AWS. Omit for provider-managed IAM.
provisioner_iam_role_arn Bring-your-own provisioner IAM role ARN. Same rules as cluster_iam_role_arn.
ec2_key_pair, kms_key_id, nexus_registration_key, permissions_boundary_arn Set only if applicable to this deployment.
cluster_uuid Optional. Set it to the cluster's UUID (from qq node_state_get or GET /v1/node/state) when automatic recovery cannot find it. Rejected on create; cannot be changed after adoption. If a qumulo_threat_detection_* resource references this cluster, adopting a different UUID than the one in state triggers a one-time replacement of that appliance.

Required IAM Permissions

In addition to the standard provider permissions (EC2, IAM, S3 object operations, SSM, KMS), the operator's Terraform credentials need the following for the discovery and adoption flow:

  • tag:GetResources (Resource Groups Tagging API): used to find cluster EC2 instances and S3 buckets by the Qumulo-Cluster tag
  • tag:TagResources: used to adopt and tag buckets that predate the Qumulo-Cluster tag (e.g., buckets created by aws-terraform-cnq before the cutover)
  • s3:ListAllMyBuckets: used to enumerate buckets during adoption

Without these permissions, import will fail when the provider attempts to discover S3 buckets associated with the cluster.

Legacy Clusters (aws-terraform-cnq)

The qumulo_managed networking mode is used exclusively by clusters originally deployed with the upstream aws-terraform-cnq module and then imported here; this provider never creates a cluster in that networking mode. Such clusters use the same import command, but three extra attributes are required in your HCL before the first apply:

  • networking_mode = "qumulo_managed": The upstream module installs Qumulo Core with QUMULO_NETWORK_MANAGED_BY_QUMULO=true (modules/qcluster/scripts/user-data-deb.sh), so the cluster runs in the Qumulo-managed networking mode. New nodes added via scale or replacement must use the same mode; otherwise the provisioner fails with node_add_mixed_networking_mode_error: Mixing nodes in host-managed and Qumulo-managed networking mode is prohibited. This is immutable after cluster creation.
  • floating_ip_count = 0: These clusters do have floating IPs, but Qumulo Core manages them (the upstream module assigns them via the Qumulo CLI as ENI secondary IPs). The provider never provisions or reconciles them, so floating_ip_count stays 0; a non-zero value fails validation with networking_mode = "qumulo_managed". The cluster's actual floating IPs are still surfaced in the read-only floating_ips attribute.
  • storage_class: Declare it (with the matching cluster_product_type) from the upstream workspace's q_persistent_storage_type variable, or read it from a bucket as shown above:
CNQ q_persistent_storage_type cluster_product_type storage_class
hot_s3_std HOT STANDARD
hot_s3_int (CNQ default) HOT INTELLIGENT_TIERING
cold_s3_ia COLD STANDARD_IA
cold_s3_gir COLD GLACIER_IR
resource "qumulo_filesystem_aws" "imported" {
  # The bare prefix (the upstream module's deployment_name), NOT the suffixed
  # deployment_unique_name from the import ID.
  cluster_name           = "my-cluster"
  deployment_name        = "my-cluster"
  region                 = "us-east-2"
  node_count             = 5
  instance_type          = "m6idn.2xlarge"
  subnet_ids             = ["subnet-0123456789abcdef0"]
  cluster_product_type   = "HOT"
  storage_class       = "INTELLIGENT_TIERING" # from the upstream q_persistent_storage_type (here: hot_s3_int)
  admin_password         = var.admin_password
  allow_cidrs            = ["10.0.0.0/8"]
  soft_capacity_limit_tb = 500

  networking_mode   = "qumulo_managed"
  floating_ip_count = 0

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

The deployment_unique_name for upstream clusters is the module's deployment_unique_name output (look for it in the aws-terraform-cnq workspace's terraform output or in the Qumulo-Cluster tag on the cluster's EC2 instances). The compute and persistent-storage modules each generate independent random suffixes; this provider's import expects the compute unique name and discovers the persistent-storage buckets automatically via the shared deployment-name prefix.

Residue After Destroying a Legacy Cluster

When destroying an imported aws-terraform-cnq cluster via this provider, the following resources are left behind (they are out of scope for the provider's destroy logic):

  • SSM parameters under /qumulo/<deployment_unique_name>/ beyond cluster-info, last-run-status, and uuid. Manual cleanup: aws ssm delete-parameters --names /qumulo/<deployment_unique_name>/param1 /qumulo/<deployment_unique_name>/param2 ...
  • The Secrets Manager secret at /qumulo/<deployment_unique_name>-cluster-secrets. Manual cleanup: aws secretsmanager delete-secret --secret-id /qumulo/<deployment_unique_name>-cluster-secrets --force-delete-without-recovery
  • Upstream-specific CloudWatch log groups (/qumulo/<deployment_unique_name>-audit-log), CloudWatch dashboards, NLB resources, and Route53 resolver resources.

Clusters from Older Provider Versions

If you have a cluster that was created by an older version of this provider (before the Qumulo-Cluster tag was introduced), it will appear missing after upgrade because the tag-based discovery won't find its EC2 instances or S3 buckets.

Migration steps:

  1. terraform state rm qumulo_filesystem_aws.<name>
  2. Tag the cluster's EC2 instances and S3 buckets with Qumulo-Cluster=<deployment_unique_name> (AWS console or CLI).
  3. TF_LOG=DEBUG terraform import qumulo_filesystem_aws.<name> <region>/<deployment_unique_name>

qumulo_filesystem_gcp

GCP clusters use a slash-separated import ID with three segments:

project_id/region/deployment_unique_name
terraform import qumulo_filesystem_gcp.cluster \
  "my-project/us-west1/mycluster-a1b2c3"

The deployment_unique_name is the prefix of all VM names in the cluster. Look at your VM names in the GCP console: they follow the pattern {deployment_unique_name}-node-{id}. The provisioner VM, if still present, is named {deployment_unique_name}-provisioner and is excluded from the cluster node set.

Minimal Configuration

Put the resource block in your main.tf (the reference tables below explain each attribute), then run the import:

resource "qumulo_filesystem_gcp" "cluster" {
  cluster_name           = "mycluster"
  deployment_name        = "mycluster"
  project_id             = "my-project"
  region                 = "us-west1"
  node_count             = 3
  instance_type          = "n2-highmem-8"
  subnetwork             = "my-subnet"
  admin_password         = var.admin_password
  allow_cidrs            = ["10.0.0.0/8"]
  soft_capacity_limit_tb = 200

  # Required if the cluster uses floating IPs; the addresses themselves are
  # discovered from the cluster and reported in floating_ips.
  floating_ip_count = 3 # match the cluster's actual floating IP count

  timeouts {
    create = "90m"
    delete = "90m"
  }
}
terraform import qumulo_filesystem_gcp.cluster \
  "my-project/us-west1/mycluster-a1b2c3"

terraform plan  # review discovered values and update your HCL to match

Required in HCL

These attributes are Required by the schema, so terraform plan errors out if they're missing from your resource block. The import recovers live values into state for most of them; copy those from terraform state show into HCL. Rows marked otherwise cannot be recovered, so supply the values from your own records. Mismatches cause destructive plan diffs on the next apply.

Attribute Notes
cluster_name + deployment_name (or name) The cluster identity, 2-15 chars each. Not recovered on import; declare it to match the running cluster. name sets both; or set cluster_name (qfsd name, case preserved) and deployment_name (lowercase resource prefix, the bare value without the random suffix that's in deployment_unique_name) separately.
node_count Current node count.
instance_type GCP machine type running on the cluster nodes.
subnetwork Subnetwork the cluster is attached to (short name).
admin_password Write-only. The cluster's current admin password. Cannot be recovered; supply your own.
allow_cidrs Current firewall ingress CIDRs (recovered from the external firewall rule's source ranges; copy to HCL to satisfy the Required field).
soft_capacity_limit_tb Current soft cap. Cannot be recovered from infrastructure; supply your own. Mismatches cause drift on every apply.

Customer-Declared Attributes

These attributes must be present and accurate in the resource block before terraform import. Most are not auto-discovered; rows note where import does recover a value into state. Mismatches may cause destructive plan diffs on the next apply.

Attribute Notes
cluster_version Current Qumulo Core version. Set only if pinned in the original config.
networking_mode "host_managed" (default) or "qumulo_managed". Cannot be reliably detected from infrastructure; set explicitly if not the default. Immutable after creation. Legacy qumulo-terraform-gcp clusters use the default "host_managed" (see Legacy Clusters below).
floating_ip_count Customer-declared, never auto-discovered. Set it to the cluster's current floating IP count (0 if none); a mismatch surfaces as a plan diff. Applying an incorrectly resolved 0 removes the cluster's floating IPs and breaks client access via those addresses, so set it to match the cluster's actual float count on import. The live addresses appear in the read-only floating_ips attribute.
node_image, provisioner_image Set only if a pinned image (rather than the family pair) was used originally. The family-pair recovery always wins on import; pinned-image users will see a config-vs-state diff until they reconcile.
cluster_uuid Optional. Set it to the cluster's UUID (from qq node_state_get or GET /v1/node/state) when automatic recovery cannot find it. Rejected on create; cannot be changed after adoption. Set this when the cluster's nodes are missing the recovery label and the provider would otherwise fall back to the deployment name. If a qumulo_threat_detection_* resource references this cluster, adopting a different UUID than the one in state triggers a one-time replacement of that appliance.

Legacy Clusters (qumulo-terraform-gcp)

Clusters originally deployed with the upstream qumulo-terraform-gcp module import with the same command. Their deployment_unique_name is the module's deployment_unique_name (<deployment_name>-<random>); find it in the legacy workspace's terraform output, or on the GCP instances/buckets. Use that full suffixed value as the third segment of the import ID, and declare the bare deployment_name (without the suffix) in HCL:

terraform import qumulo_filesystem_gcp.cluster \
  "my-project/us-west1/mycluster-a1b2c3d4"

Legacy GCP clusters use the host-managed networking mode: do not set networking_mode = "qumulo_managed"

Unlike the legacy AWS and Azure modules (whose clusters require the Qumulo-managed networking mode), every release of qumulo-terraform-gcp installs Qumulo Core with QUMULO_NETWORK_MANAGED_BY_HOST=true (compute/module/sub-modules/qcluster/scripts/user-data.py). Its clusters therefore run in the host-managed networking mode, this provider's default. Leave networking_mode unset or set it to "host_managed".

Declaring "qumulo_managed" on an imported legacy GCP cluster makes every later node add or replacement install new nodes in the wrong networking mode and fail with node_add_mixed_networking_mode_error. The attribute is immutable after import, so the mistake cannot be corrected without hand-editing state.

resource "qumulo_filesystem_gcp" "cluster" {
  # The bare prefix (the upstream module's deployment_name), NOT the suffixed
  # deployment_unique_name from the import ID.
  cluster_name           = "mycluster"
  deployment_name        = "mycluster"
  project_id             = "my-project"
  region                 = "us-west1"
  node_count             = 3
  instance_type          = "n2-highmem-8"
  subnetwork             = "my-subnet"
  admin_password         = var.admin_password
  allow_cidrs            = ["10.0.0.0/8"]
  soft_capacity_limit_tb = 200

  # networking_mode is deliberately omitted: legacy GCP clusters run in the
  # default host-managed networking mode (see the warning above).

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

The legacy module names persistent-storage buckets <random>-<deployment_unique_name>-qps-<N> (a per-bucket random prefix, no deployment label). The provider discovers and cleans these up by name on destroy, so no manual bucket removal is required after adoption.

Bare Metal Resources

Bare metal and edge clusters import with just the connection profile name:

terraform import qumulo_filesystem_baremetal.cluster baremetal
terraform import qumulo_filesystem_edge_baremetal.cluster edge

Perpetual Diff After Import

After import, several fields are not populated in state because the Qumulo REST API does not expose them:

  • cluster_name
  • admin_password
  • cluster_version
  • blocks_per_stripe, max_drive_failures, max_node_failures (baremetal only)

This causes terraform plan to show a diff on every run. Add lifecycle { ignore_changes } to suppress it:

resource "qumulo_filesystem_baremetal" "cluster" {
  connection_profile = "baremetal"
  cluster_name       = "prod"
  admin_password     = var.admin_password

  lifecycle {
    ignore_changes = [cluster_name, admin_password, cluster_version, blocks_per_stripe, max_drive_failures, max_node_failures]
  }
}

resource "qumulo_filesystem_edge_baremetal" "cluster" {
  connection_profile = "edge"
  cluster_name       = "edge01"
  admin_password     = var.admin_password

  lifecycle {
    ignore_changes = [cluster_name, admin_password, cluster_version]
  }
}

Threat Detection (Not Importable)

qumulo_threat_detection_aws, qumulo_threat_detection_azure, and qumulo_threat_detection_gcp do not support terraform import. An appliance deployed outside Terraform cannot be adopted; to manage one with Terraform, create it with Terraform.


Cluster API Resources

Cluster API resources (NFS exports, SMB shares, quotas, and the rest) use comma-separated import IDs that start with the connection profile name from your provider block. Each recipe below is the same two moves: put the block in your main.tf, then run the import command. Replace the example values with your cluster's, and finish with terraform plan.

Quick Reference

Resource Format Example
qumulo_nfs_export profile,export_path prod,/data
qumulo_smb_share profile,share_name prod,my-share
qumulo_directory_quota profile,directory_path prod,/data/users
qumulo_local_user profile,username prod,alice
qumulo_local_group profile,group_name prod,developers
qumulo_local_group_member profile,group_id,user_id prod,501,1001
qumulo_role profile,role_name prod,BackupOperators
qumulo_role_member profile,role_name,domain,auth_id prod,BackupOperators,LOCAL,1001
qumulo_s3_bucket profile,bucket_name prod,my-bucket
qumulo_s3_bucket_policy profile,bucket_name prod,my-bucket
qumulo_s3_access_key profile,access_key_id prod,AKIAIOSFODNN7EXAMPLE
qumulo_snapshot_policy profile,policy_id prod,123
qumulo_replication_source_relationship profile,id prod,rel-123
qumulo_replication_target_relationship profile,id prod,rel-456
qumulo_replication_object_relationship profile,id prod,obj-789

Singletons (settings resources) import with just the profile name, and portals have their own formats; both are covered below.

Storage Protocols and Quotas

resource "qumulo_nfs_export" "data" {
  connection_profile = "prod"
  export_path        = "/data"
  fs_path            = "/data"
}
terraform import qumulo_nfs_export.data prod,/data

export_path is the import identity and must match the live export. If the export has client restrictions, mirror them in restriction blocks; a config without them plans to strip the live restrictions.

resource "qumulo_smb_share" "data" {
  connection_profile = "prod"
  share_name         = "Data"
  fs_path            = "/data"
}
terraform import qumulo_smb_share.data prod,Data

share_name must match the live share. Mirror any live permission and network_permission blocks; an empty config plans to remove them.

resource "qumulo_directory_quota" "data" {
  connection_profile = "prod"
  directory_path     = "/data"
  limit              = "1099511627776" # bytes; must match the live quota
}
terraform import qumulo_directory_quota.data prod,/data

directory_path is the identity; a mismatch forces replacement.

Identity and Access

resource "qumulo_local_user" "alice" {
  connection_profile = "prod"
  name               = "alice"
  primary_group      = "513" # the live user's primary group ID
}
terraform import qumulo_local_user.alice prod,alice

The password attribute is write-only and never recovered; setting it after import pushes a new password.

resource "qumulo_local_group" "developers" {
  connection_profile = "prod"
  name               = "developers"
}
terraform import qumulo_local_group.developers prod,developers
resource "qumulo_local_group_member" "alice_developers" {
  connection_profile = "prod"
  group_id           = "501"  # or qumulo_local_group.developers.id
  user_id            = "1001" # or qumulo_local_user.alice.id
}
terraform import qumulo_local_group_member.alice_developers prod,501,1001

Both IDs are the identity and must match the import ID exactly.

resource "qumulo_role" "backup_operators" {
  connection_profile = "prod"
  name               = "BackupOperators"
  privileges         = ["PRIVILEGE_SNAPSHOT_READ"] # copy the live list
}
terraform import qumulo_role.backup_operators prod,BackupOperators

Warning

privileges defaults to an empty list. Omitting it does not mean "keep the live privileges"; it plans to strip every privilege from the role. Copy the live privilege list into your config before the first apply.

resource "qumulo_role_member" "alice_backup" {
  connection_profile = "prod"
  role_name          = "BackupOperators"
  domain             = "LOCAL"
  auth_id            = "1001"
}
terraform import qumulo_role_member.alice_backup prod,BackupOperators,LOCAL,1001

All three fields after the profile are the identity; any mismatch forces replacement.

S3 Object Storage

resource "qumulo_s3_bucket" "data" {
  connection_profile = "prod"
  name               = "data"
}
terraform import qumulo_s3_bucket.data prod,data
resource "qumulo_s3_bucket_policy" "data" {
  connection_profile = "prod"
  bucket_name        = "data"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Qumulo = ["local:alice"] }
      Action    = ["s3:GetObject"]
      Sid       = "ReadOnly"
    }]
  })
}
terraform import qumulo_s3_bucket_policy.data prod,data

The policy JSON must match the live policy to avoid a diff on the first plan.

resource "qumulo_s3_access_key" "alice" {
  connection_profile = "prod"

  user {
    domain = "LOCAL"
    name   = "alice" # must match the key's live owner
  }
}
terraform import qumulo_s3_access_key.alice prod,AKIAIOSFODNN7EXAMPLE

Warning

The secret_access_key can never be recovered; import sets it to an empty string with a warning. If you need the secret in Terraform, recreate the key instead of importing it.

Data Management

resource "qumulo_snapshot_policy" "data" {
  connection_profile = "prod"
  policy_name        = "daily-data"
  source_file_id     = "5" # the live file ID of the policy's directory

  schedule {
    timezone  = "UTC"
    frequency = "SCHEDULE_DAILY_OR_WEEKLY"
    hour      = 2
    minute    = 0
    on_days   = ["EVERY_DAY"]
  }
}
terraform import qumulo_snapshot_policy.data prod,123

Use source_file_id rather than source_path after an import: source_path is not recovered and configuring it on an imported policy forces replacement. policy_name and the schedule block must match the live policy.

Replication

resource "qumulo_replication_source_relationship" "data" {
  connection_profile = "prod"
  source_root_path   = "/data"
  target_address     = "10.100.0.10"
  target_root_path   = "/data-dr"
}
terraform import qumulo_replication_source_relationship.data prod,rel-123

The two paths are replace-on-change; they must match the live relationship exactly.

resource "qumulo_replication_target_relationship" "data" {
  connection_profile = "dr"      # profile for the TARGET cluster
  id                 = "rel-123" # same relationship ID as the source
}
terraform import qumulo_replication_target_relationship.data dr,rel-123
resource "qumulo_replication_object_relationship" "backup" {
  connection_profile   = "prod"
  direction            = "COPY_TO_OBJECT"
  local_directory_path = "/data"
  object_store_address = "s3.us-west-2.amazonaws.com"
  bucket               = "qumulo-backup"
  access_key_id        = var.object_store_access_key_id
  secret_access_key    = var.object_store_secret_access_key

  lifecycle {
    # Import sets both to "" and the API never returns them.
    ignore_changes = [secret_access_key, local_directory_path]
  }
}
terraform import qumulo_replication_object_relationship.backup prod,obj-789

secret_access_key and local_directory_path cannot be recovered, so keep the lifecycle block; without it any config value for local_directory_path forces replacement.

Portal Resources

Portal resources connect two clusters (a spoke and a hub), so some import IDs carry two profiles or encoded values. The portal_id used by qumulo_portal_root is the opaque qpv1.-prefixed id of a qumulo_portal resource.

resource "qumulo_portal" "main" {
  spoke_connection_profile = "spoke"
  hub_connection_profile   = "hub"
  type                     = "PORTAL_READ_WRITE"
  hub_hosts                = ["10.0.0.1", "10.0.0.2"]
  spoke_hosts              = ["10.1.0.1", "10.1.0.2"]
}
terraform import qumulo_portal.main spoke,hub,123,456

Import ID: spoke_profile,hub_profile,spoke_id,hub_id. Every attribute is replace-on-change; each value must match the live portal.

resource "qumulo_portal_spoke" "main" {
  connection_profile = "prod"
  type               = "PORTAL_READ_WRITE"
  hub_address        = "10.0.0.1"
}
terraform import qumulo_portal_spoke.main prod,123
resource "qumulo_portal_hub" "main" {
  connection_profile = "hub"
  spoke_cluster_uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  spoke_address      = "10.1.0.1"
}
terraform import qumulo_portal_hub.main hub,456
resource "qumulo_portal_root" "data" {
  portal_id       = qumulo_portal.main.id # opaque qpv1.<payload> ID
  spoke_root_path = "/data"
  hub_root_path   = "/shared"
}
terraform import qumulo_portal_root.data "qpv1.<payload>,891872330,/data,/shared"

Import ID: portal_id,local_root,spoke_root_path,hub_root_path. All attributes are replace-on-change and must match the import ID.

resource "qumulo_portal_spoke_root" "data" {
  connection_profile = "prod"
  spoke_id           = qumulo_portal_spoke.main.id
  spoke_root_path    = "/cache/data"
  hub_root_path      = "/data"
}
terraform import qumulo_portal_spoke_root.data prod,1,891872330,/cache/data,/data

Import ID: profile,spoke_id,local_root,spoke_root_path,hub_root_path.

resource "qumulo_portal_hub_root" "data" {
  connection_profile = "hub"
  hub_id             = qumulo_portal_hub.main.id
  root_id            = "12345" # hub root directory file ID
}
terraform import qumulo_portal_hub_root.data hub,21,12345

Singleton Resources

Settings resources exist exactly once per cluster, so the import ID is just the connection profile name and most need nothing but connection_profile in HCL:

resource "qumulo_nfs_settings" "this" {
  connection_profile = "prod"
}
terraform import qumulo_nfs_settings.this prod

The same pattern imports qumulo_smb_settings, qumulo_s3_settings, qumulo_file_system_settings, qumulo_ftp_settings, qumulo_time_configuration, qumulo_web_ui_settings, qumulo_saml_settings, and qumulo_cluster_ssl.

A few singletons require more in HCL; set these to the live values before the first plan:

Resource Also declare
qumulo_cluster_settings cluster_name (the live cluster name; a mismatch plans a rename)
qumulo_ldap_settings use_ldap, bind_uri, base_distinguished_names, ldap_schema. The bind password is write-only; supply your own.
qumulo_audit_syslog enabled, plus server_address when enabled
qumulo_audit_cloudwatch enabled, plus log_group_name and region when enabled
qumulo_ad domain, ad_username, ad_password

Note

qumulo_ad import fails if the cluster is not joined to an Active Directory domain, and the AD credentials are write-only: import leaves them empty, so your configured values show as a change on the first plan (expected). qumulo_cluster_ssl import cannot recover the certificate or private key; supply your own PEM material to manage them.