Skip to content

Production Azure Qumulo Cluster Example

This example demonstrates a production-ready configuration with high availability, enhanced security, and enterprise features.

Prerequisites

  • Azure subscription with Contributor permissions
  • Enabling deletion_protection additionally requires Microsoft.Authorization/locks/*, carried by Owner, User Access Administrator, or a custom role (Contributor alone cannot manage management locks)
  • Setting floating_ip_count additionally requires Microsoft.Network/virtualNetworks/CheckIPAddressAvailability/action on the virtual network, used to find free subnet addresses. Contributor and Network Contributor both carry it; a narrow cross-resource-group grant of only subnets/read plus subnets/join/action does not
  • Existing virtual network and subnet, sized for one address per node, one for the provisioner VM, one per floating IP, and the 5 Azure reserves in every subnet
  • SSH public key for node access
  • Terraform >= 1.0

Subnet Service Endpoints Required

Your Virtual Network subnet must have the Microsoft.KeyVault and Microsoft.Storage service endpoints enabled before deploying a cluster. To verify, go to the Azure Portal, navigate to Virtual networks > your VNet > Settings > Service endpoints, and confirm both endpoints are added for the target subnet. Deployment will fail without these service endpoints.

Usage

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

  2. Initialize and deploy:

    terraform init -upgrade
    terraform plan
    terraform apply
    

Features

This example includes:

  • Multi-zone deployment for high availability
  • Zone-redundant storage (ZRS)
  • SSH key authentication for node access
  • CIDR-based access restrictions
  • Production-appropriate tagging

Optional Features

Uncomment and configure in main.tf:

  • Private Endpoints: Secure access to Azure services via private network
  • Custom Managed Identities: Use pre-configured identities with RBAC
  • Custom VM Images: Use hardened or enterprise images
  • Floating IPs: Additional IPs for load balancing

Outputs

  • cluster_name: Name of the Qumulo cluster
  • cluster_uuid: UUID of the Qumulo cluster
  • deployment_unique_name: Unique deployment identifier
  • endpoint_ips: Client-facing IPs. Floating IPs if configured, otherwise primary IPs.
  • primary_ips: Per-node primary IPs. Use these directly when no floating IPs are configured, or for per-node access.
  • endpoints: Connection endpoints for various protocols (web UI, API, NFS, SMB)

VM Type Selection

Only L-series storage-optimized VMs are supported:

# L-Series v3 (Intel)
vm_type = "Standard_L8s_v3"   # 8 vCPUs, 64 GB RAM, 1.92 TB NVMe
vm_type = "Standard_L16s_v3"  # 16 vCPUs, 128 GB RAM, 3.84 TB NVMe
vm_type = "Standard_L32s_v3"  # 32 vCPUs, 256 GB RAM, 7.68 TB NVMe
vm_type = "Standard_L48s_v3"  # 48 vCPUs, 384 GB RAM, 11.52 TB NVMe
vm_type = "Standard_L64s_v3"  # 64 vCPUs, 512 GB RAM, 15.36 TB NVMe
vm_type = "Standard_L80s_v3"  # 80 vCPUs, 640 GB RAM, 19.20 TB NVMe

# L-Series v3 (AMD)
vm_type = "Standard_L8as_v3"  # 8 vCPUs, 64 GB RAM, 1.92 TB NVMe
vm_type = "Standard_L16as_v3" # 16 vCPUs, 128 GB RAM, 3.84 TB NVMe
vm_type = "Standard_L32as_v3" # 32 vCPUs, 256 GB RAM, 7.68 TB NVMe
vm_type = "Standard_L48as_v3" # 48 vCPUs, 384 GB RAM, 11.52 TB NVMe
vm_type = "Standard_L64as_v3" # 64 vCPUs, 512 GB RAM, 15.36 TB NVMe
vm_type = "Standard_L80as_v3" # 80 vCPUs, 640 GB RAM, 19.20 TB NVMe

# L-Series v4 (Intel)
vm_type = "Standard_L8s_v4"   # 8 vCPUs, 64 GB RAM, 1.92 TB NVMe
vm_type = "Standard_L16s_v4"  # 16 vCPUs, 128 GB RAM, 3.84 TB NVMe
vm_type = "Standard_L32s_v4"  # 32 vCPUs, 256 GB RAM, 7.68 TB NVMe
vm_type = "Standard_L48s_v4"  # 48 vCPUs, 384 GB RAM, 11.52 TB NVMe
vm_type = "Standard_L64s_v4"  # 64 vCPUs, 512 GB RAM, 15.36 TB NVMe
vm_type = "Standard_L80s_v4"  # 80 vCPUs, 640 GB RAM, 19.20 TB NVMe

# L-Series v4 (AMD)
vm_type = "Standard_L8as_v4"  # 8 vCPUs, 64 GB RAM, 1.92 TB NVMe
vm_type = "Standard_L16as_v4" # 16 vCPUs, 128 GB RAM, 3.84 TB NVMe
vm_type = "Standard_L32as_v4" # 32 vCPUs, 256 GB RAM, 7.68 TB NVMe
vm_type = "Standard_L48as_v4" # 48 vCPUs, 384 GB RAM, 11.52 TB NVMe
vm_type = "Standard_L64as_v4" # 64 vCPUs, 512 GB RAM, 15.36 TB NVMe
vm_type = "Standard_L80as_v4" # 80 vCPUs, 640 GB RAM, 19.20 TB NVMe

# L-Series v2 (Legacy)
vm_type = "Standard_L8s_v2"   # 8 vCPUs, 64 GB RAM, 1.92 TB NVMe
vm_type = "Standard_L16s_v2"  # 16 vCPUs, 128 GB RAM, 3.84 TB NVMe
vm_type = "Standard_L32s_v2"  # 32 vCPUs, 256 GB RAM, 7.68 TB NVMe
vm_type = "Standard_L48s_v2"  # 48 vCPUs, 384 GB RAM, 11.52 TB NVMe
vm_type = "Standard_L64s_v2"  # 64 vCPUs, 512 GB RAM, 15.36 TB NVMe
vm_type = "Standard_L80s_v2"  # 80 vCPUs, 640 GB RAM, 19.20 TB NVMe

Custom Managed Identity

Use pre-existing user-assigned managed identities for organizations with centralized RBAC management:

data "azurerm_user_assigned_identity" "cluster" {
  name                = "qumulo-cluster-identity"
  resource_group_name = "rg-identities"
}

data "azurerm_user_assigned_identity" "provisioner" {
  name                = "qumulo-provisioner-identity"
  resource_group_name = "rg-identities"
}

resource "qumulo_filesystem_azure" "custom_identity" {
  # ... required fields ...

  cluster_node_identity_id = data.azurerm_user_assigned_identity.cluster.id
  provisioner_identity_id  = data.azurerm_user_assigned_identity.provisioner.id
  deletion_protection      = true # recommended: guard the cluster's VMs and storage accounts
}

Required Roles for Custom Identities:

For cluster_node_identity_id:

  • Key Vault Secrets User on the Key Vault
  • Storage Blob Data Contributor on storage accounts
  • Reader on the resource group

For provisioner_identity_id:

  • Key Vault Secrets User on the Key Vault
  • App Configuration Data Owner on the App Configuration
  • Virtual Machine Contributor on the resource group

Cross-subscription identities are supported. The subscription is automatically extracted from the identity resource ID.

Customer-Managed Key Vault

key_vault_id points the deployment at a vault you own, and the provider never changes that vault's network configuration. The vault must permit network access from the cluster subnet. The nodes read their storage SAS tokens from it while forming the first quorum and for the life of the cluster, so this is a standing requirement, not a create-time one.

Terraform writing the SAS token definitions successfully is not evidence the nodes can reach the vault: those writes come from wherever Terraform runs, and the nodes egress from the cluster subnet.

If the vault's firewall is set to deny by default (networkAcls.defaultAction = "Deny"), grant the cluster subnet access one of two ways:

# Service endpoint plus a matching virtual network rule
az network vnet subnet update -g <network-rg> --vnet-name <vnet> -n <cluster-subnet> \
    --service-endpoints Microsoft.KeyVault
az keyvault network-rule add -g <vault-rg> -n <vault-name> --subnet <cluster-subnet-id>

Or place a Key Vault private endpoint in the cluster subnet, linked to a privatelink.vaultcore.azure.net private DNS zone. The provider's create_keyvault_private_endpoint attribute is rejected together with key_vault_id, because the provider only builds private endpoints for vaults it creates. Build the endpoint for your own vault outside Terraform.

Allowing an egress IP address works only when that address is pinned by a NAT gateway. Azure's default outbound SNAT address can change, which breaks the cluster's access to its storage later. "Allow trusted Microsoft services" does not cover this traffic: the caller is a VM managed identity, not a trusted service.

When the nodes cannot reach the vault, cluster creation fails with a storage URI validation error that names the vault and the blocked client address:

Error 400: object_storage_uri_validate_error: Timed out validating uri https://<account>.blob.core.windows.net
  (http_forbidden_error: /secrets/<account>-SasTokenDefinition ... "code":"ForbiddenByFirewall")

Private Endpoints

The provider can place a private endpoint in the cluster subnet for the deployment's App Configuration store, the Key Vault it creates, and every one of its storage accounts. Each service has its own switch:

create_appconfig_private_endpoint = true
create_keyvault_private_endpoint  = true
create_storage_private_endpoint   = true

There are two ways to run this, and which one you pick decides whether public network access can be disabled in the same apply.

With Azure Private DNS

Set the matching create_appconfig_private_endpoint, create_keyvault_private_endpoint, or create_storage_private_endpoint switch and the matching zone ID. The endpoint registers in the zone during create, so the provisioner can resolve it minutes later, and public network access may be disabled in the same apply:

create_appconfig_private_endpoint       = true
private_link_appconfig_dns_zone_id      = "/subscriptions/.../privateDnsZones/privatelink.azconfig.io"
disable_appconfig_public_network_access = true

create_keyvault_private_endpoint        = true
private_link_keyvault_dns_zone_id       = "/subscriptions/.../privateDnsZones/privatelink.vaultcore.azure.net"
disable_keyvault_public_network_access  = true

create_storage_private_endpoint         = true
private_link_storage_dns_zone_id        = "/subscriptions/.../privateDnsZones/privatelink.blob.core.windows.net"

Each zone must exist and be linked to the cluster VNet before the apply. The provider checks both and fails the plan with the zone and VNet named if either is missing.

With your own DNS

Set only the create_appconfig_private_endpoint, create_keyvault_private_endpoint, and create_storage_private_endpoint switches. The endpoint's private IP does not exist until the apply creates it, so no DNS record can exist while the provisioner boots, which means public network access must stay enabled during create. After the apply, read the endpoint details out of the computed outputs, create your records, and disable public access on the Azure resources out of band:

output "storage_endpoints" {
  value = qumulo_filesystem_azure.cluster.storage_private_endpoints
}

Check for an existing Azure Private DNS zone before choosing this path. This path only works when no Azure Private DNS zone for the service (privatelink.azconfig.io, privatelink.vaultcore.azure.net, or privatelink.blob.core.windows.net) is already linked to the cluster VNet. If one is, Azure DNS answers queries from inside the VNet using that zone, and the zone has no record for the endpoint you just created, so it returns NXDOMAIN for the resource as soon as the endpoint exists. The cluster cannot reach its Key Vault or storage during creation, and the apply fails. If a zone is already linked, use the "With Azure Private DNS" path above and pass the matching private_link_appconfig_dns_zone_id, private_link_keyvault_dns_zone_id, or private_link_storage_dns_zone_id instead.

To check, run az network private-dns link vnet list --zone-name <zone> --resource-group <rg> for each of the three zone names against every resource group you can see, since the zone can live in a subscription other than the VNet's. A broader check across subscriptions is an Azure Resource Graph query on type microsoft.network/privatednszones/virtualnetworklinks filtered to links whose virtual network ID matches the cluster VNet.

appconfig_private_endpoint and keyvault_private_endpoint are single objects, and storage_private_endpoints is a map keyed by storage account name. Every record carries five fields:

Field Purpose
fqdn The name to point your DNS record at.
ip_address The private IP the record should resolve to.
endpoint_name The Azure private endpoint resource's name.
resource_group The resource group the private endpoint was created in.
target_resource_id The ARM ID of the account, vault, or App Configuration store the endpoint fronts.

target_resource_id is the resource to PATCH once you have confirmed the private endpoint is serving traffic. Point a network-posture change (for example, disabling public network access) at that ID. The provider writes network posture only at creation and never reads it back, so disabling public access yourself causes no drift on later plans.

On a capacity increase, the whole storage_private_endpoints map is unknown at plan time. Terraform cannot know the keys of accounts that do not exist yet, so a downstream for_each over the map needs -target on the cluster first, or a second apply.

Remove your DNS records before turning a switch off. Setting create_appconfig_private_endpoint, create_keyvault_private_endpoint, or create_storage_private_endpoint back to false deletes that service's endpoints. A record still pointing at a deleted endpoint answers with a dead address. Removing a private_link_appconfig_dns_zone_id, private_link_keyvault_dns_zone_id, or private_link_storage_dns_zone_id value on its own only deletes that endpoint's DNS zone group, leaving the endpoint itself in place.

Storage traffic path

Today the nodes reach blob storage over the subnet's Microsoft.Storage service endpoint. Once your DNS points a storage account's FQDN at its private endpoint IP, that traffic moves to Private Link. Both paths work, and neither needs a configuration change, but if you watch service-endpoint metrics, expect them to go quiet at that point.

Cost

Each private endpoint carries a per-hour Azure charge, and create_storage_private_endpoint creates one endpoint per storage account. A cluster with 30 accounts gets 30 endpoints.

Day-2 Operations

The provider supports in-place cluster modifications with a single terraform apply:

Scaling Nodes

Change node_count to scale the cluster up or down. The provider handles everything automatically:

  • Scale-up: New nodes are added to the cluster
  • Scale-down: Two-phase removal (remove from quorum, then delete VMs)

Changing VM Types

Change vm_type to upgrade to a larger VM size. This triggers an automatic cluster replacement:

  1. Creates new nodes with the new VM type
  2. Adds new nodes to cluster membership
  3. Removes old nodes from membership
  4. Deletes old VMs

Converting to Multi-AZ

Add or change availability_zones to convert between single-AZ and multi-AZ deployments. This also triggers an automatic cluster replacement.

Full Configuration

# Example: Production Azure Qumulo Cluster
# This example shows a production-ready configuration with HA, monitoring, and security.
# The resource group is automatically created if it doesn't exist.
# 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" {
  azure {
    subscription_id = "your-subscription-id" # <-- Replace with your Azure subscription ID
    environment     = "public"               # "public" or "usgovernment"
  }
}

variable "ssh_public_key_path" {
  description = "Path to SSH public key file for cluster node access"
  type        = string
  default     = "~/.ssh/id_rsa.pub"
}

# Production Qumulo cluster with full features
# Note: The resource group is automatically created if it doesn't exist.
# If it exists, the provider will use it (location must match).
resource "qumulo_filesystem_azure" "cluster" {
  provider = qumulo

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

  # Admin password for cluster access.
  # 8-128 characters, must include 3+ of: uppercase, lowercase, digit, special character.
  # Immutable after creation - use Qumulo UI or CLI to change.
  admin_password = "YourSecurePassword123!"

  # CIDR blocks allowed to access the cluster.
  # Empty list means no IP-based restrictions (use with caution).
  # Example: ["10.0.0.0/8", "172.16.0.0/12"]
  allow_cidrs = ["10.0.0.0/8", "172.16.0.0/12"]

  # Availability zones for node distribution.
  # Controls both node placement and VM disk storage type:
  # - With zones: Uses PremiumV2_LRS disks (better price/performance).
  # - Without zones: Uses Premium_LRS disks (required for zoneless regions).
  # Omit for single-zone or zoneless regions (e.g., northcentralus).
  # Example: ["1", "2", "3"] for multi-AZ high availability.
  availability_zones = ["1", "2", "3"]

  # # User-assigned managed identity for cluster nodes.
  # # When provided, RBAC roles must be pre-configured (not auto-created).
  # # Format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{name}
  # cluster_node_identity_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ManagedIdentity/userAssignedIdentities/xxx"

  # Cluster storage product type.
  # HOT: Optimized for frequently accessed data (default).
  # COLD: Optimized for archival/infrequently accessed data.
  # Immutable after creation.
  cluster_product_type = "HOT"

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

  # # Custom managed image ID for cluster nodes.
  # # Use for hardened or pre-configured VMs.
  # # WARNING: Changes trigger cluster replacement!
  # # Format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Compute/images/{name}
  # custom_image_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/xxx"

  # # Disable App Configuration public network access.
  # # When true, requires private_link_appconfig_dns_zone_id.
  # disable_appconfig_public_network_access = false

  # # Disable Key Vault public network access.
  # # When true, requires private_link_keyvault_dns_zone_id.
  # disable_keyvault_public_network_access = false

  # # Floating IP addresses for client access.
  # # Use for DNS round-robin or custom load balancing.
  # # When set, endpoint_ips output returns these instead of node IPs.
  # # Number of floating IPs for client access. The provider picks the
  # # addresses from the cluster subnet and reports them in floating_ips.
  # floating_ip_count = 3

  # # Customer-managed Key Vault resource ID.
  # # When provided, the provider uses this vault instead of creating one.
  # # Conflicts with disable_keyvault_public_network_access and private_link_keyvault_dns_zone_id.
  # # Immutable after creation. You own vault lifecycle, network, and access policies.
  # # The vault must permit access from the cluster subnet - the nodes read their
  # # storage SAS tokens from it. See "Customer-Managed Key Vault" in azure-production.md.
  # # Format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.KeyVault/vaults/{name}
  # key_vault_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.KeyVault/vaults/xxx"

  # Azure region for deployment.
  location = "eastus2" # <-- Azure region

  # # Azure Marketplace image for cluster nodes.
  # # Alternative to custom_image_id for standard enterprise images.
  # # WARNING: Changes trigger cluster replacement!
  # marketplace_image = [{
  #   publisher = "Canonical"
  #   offer     = "0001-com-ubuntu-server-jammy"
  #   sku       = "22_04-lts-gen2"
  #   version   = "latest"
  # }]

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

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

  # # Custom naming templates for Azure resources.
  # # vm_name must contain exactly one {node_id} placeholder.
  # # storage_account must contain exactly one {index} or {index:N} placeholder.
  # # {index} is the unpadded index. {index:N} zero-pads the index to width N, so
  # # {index:2} yields "01", "02", ... ({index:02} is equivalent -- the leading 0 is redundant).
  # # Changing either forces replacement of the affected resource.
  # naming = [{
  #   vm_name         = "myapp-{node_id}"
  #   storage_account = "myappstor{index:02}"
  # }]

  # # Network management mode.
  # # "host_managed" (default) for clusters created by this provider.
  # # "qumulo_managed" only for clusters originally created by azure-terraform-cnq.
  # # Immutable after creation. Mixing modes across nodes is prohibited.
  # networking_mode = "host_managed"

  # # Qumulo Nexus registration key for remote support.
  # # Obtain from https://nexus.qumulo.com/user/registration-key
  # # Only applied during initial cluster creation.
  # nexus_registration_key = "your-nexus-key"

  # 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 = 5

  # # Enable ICMP ingress (ping) in network security group.
  # # Useful for network diagnostics.
  # nsg_allow_ingress_icmp = false

  # # Resource group containing persistent storage accounts and KeyVault.
  # # Required when migrating from the legacy azure-terraform-cnq module, where persistent
  # # storage lived in a separate resource group. Defaults to resource_group_name when omitted.
  # persistent_storage_resource_group = "rg-qumulo-persistent"

  # # Private DNS zone ID for App Configuration private endpoint.
  # # Required when disable_appconfig_public_network_access is true.
  # # Format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.azconfig.io
  # private_link_appconfig_dns_zone_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Network/privateDnsZones/privatelink.azconfig.io"

  # # Private DNS zone ID for Key Vault private endpoint.
  # # Required when disable_keyvault_public_network_access is true.
  # # Format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net
  # private_link_keyvault_dns_zone_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net"

  # # Custom managed image ID for provisioner VM.
  # # Does NOT trigger cluster replacement when changed.
  # provisioner_custom_image_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/xxx"

  # # User-assigned managed identity for provisioner VM.
  # # When provided, RBAC roles must be pre-configured.
  # provisioner_identity_id = "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ManagedIdentity/userAssignedIdentities/xxx"

  # # Azure Marketplace image for provisioner VM.
  # # Does NOT trigger cluster replacement when changed.
  # provisioner_marketplace_image = [{
  #   publisher = "Canonical"
  #   offer     = "0001-com-ubuntu-server-jammy"
  #   sku       = "22_04-lts-gen2"
  #   version   = "latest"
  # }]

  # # VM size for the provisioner instance.
  # # Used during deployment operations only.
  # provisioner_vm_type = "Standard_B2s"

  # Azure resource group name.
  # The resource group is automatically created if it doesn't exist.
  # If it exists, the provider will use it (location must match).
  resource_group_name = "rg-qumulo-prod" # <-- Name your resource group

  # Soft capacity limit in TB (50-10000).
  # Can be increased to add storage, but cannot be decreased.
  soft_capacity_limit_tb = 1000

  # SSH public key for cluster node access.
  # Point to your public key file (e.g., ~/.ssh/id_rsa.pub).
  ssh_public_key = file(var.ssh_public_key_path)

  # Azure storage replication type for storage accounts only (does not affect VM disks).
  # LRS: Locally redundant (3 copies in single datacenter).
  # ZRS: Zone redundant (3 copies across availability zones) - RECOMMENDED for multi-AZ clusters.
  # Note: VM disk redundancy is controlled by availability_zones (see above).
  # Immutable after creation.
  storage_replication_type = "ZRS"

  # Full Azure resource ID of the subnet for cluster deployment.
  # The subnet must already exist before deploying the cluster.
  # Format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/{vnet}/subnets/{subnet}
  subnet_id = "/subscriptions/your-subscription-id/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-main/subnets/subnet-qumulo"

  # Tags to apply to all Azure resources (including the resource group if created).
  tags = {
    Environment        = "Production"
    Department         = "IT"
    CostCenter         = "Storage"
    DataClassification = "Confidential"
    ManagedBy          = "Terraform"
    BackupPolicy       = "Daily"
    SLA                = "99.99"
  }

  # Azure VM size for cluster nodes.
  # Only L-series storage-optimized VMs are supported.
  # Examples: Standard_L8s_v3, Standard_L16s_v3, Standard_L32s_v3
  vm_type = "Standard_L16s_v3"

  deletion_protection = true # recommended: guard the cluster's VMs and storage accounts

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

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

output "cluster_uuid" {
  description = "UUID of the Qumulo cluster"
  value       = qumulo_filesystem_azure.cluster.cluster_uuid
}

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

output "endpoint_ips" {
  description = "Client-facing IPs. Floating IPs if configured, otherwise primary IPs."
  value       = qumulo_filesystem_azure.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_azure.cluster.primary_ips
}

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