The Excloud Terraform provider lets you describe your infrastructure in HCL and apply it through Terraform’s normal plan/apply flow. It uses the same APIs the exc CLI does and reads the same credentials, so you can mix-and-match — bootstrap with the CLI, manage steady state with Terraform.

Source addressexcloud-dev/excloud
Registryregistry.terraform.io/excloud-dev/excloud
Public repogithub.com/excloud-dev/terraform-provider-excloud

In this section

PageCovers
QuickstartInstall, authenticate, apply a VM with a public IP, destroy

Resources

ResourcePurpose
excloud_ssh_keyRegister a public key for use with VMs
excloud_compute_instanceVirtual machine
excloud_volumeBlock volume
excloud_volume_attachmentAttach a volume to a VM
excloud_snapshotPoint-in-time copy of a volume
excloud_public_ipv4Reserved public IPv4
excloud_security_groupSecurity group
excloud_security_group_ruleIngress/egress rule
excloud_security_group_bindingBind a security group to a VM interface
excloud_bucketS3-compatible object storage bucket
excloud_bucket_access_keyS3 access key (secret returned once)
excloud_dns_zoneDNS zone
excloud_dns_recordDNS record (any supported type)

Data sources

Data sourcePurpose
excloud_compute_imagesLook up image IDs by name (avoid hard-coding)
excloud_instance_typesLook up instance types by name
excloud_subnetsFind the DEFAULT subnet in a zone
excloud_snapshotsList existing snapshots
excloud_bucketsList existing buckets
excloud_bucket_usagePer-bucket usage figures
excloud_dns_zonesList DNS zones

Provider configuration

terraform {
  required_providers {
    excloud = {
      source  = "excloud-dev/excloud"
      version = "~> 0.2"
    }
  }
}

provider "excloud" {
  # Optional. Everything below resolves from environment variables or
  # ~/.exc/config from `exc login` if not set explicitly.
  # api_key    = var.excloud_api_key
  # org_id     = var.excloud_org_id
  # account_id = var.excloud_account_id
}

Authentication resolution order

The provider tries, in this order:

  1. Explicit provider attributes (api_key, id_token, account_id, org_id).
  2. Environment variables — EXCLOUD_API_KEY / API_KEY / ACCESS_TOKEN, EXCLOUD_ORG_ID, EXCLOUD_ACCOUNT_ID, etc.
  3. ~/.exc/config, as written by exc login. The provider reads default_acc, default_org, that account’s id_token, and that org’s access_token.

For CI, set env vars (EXCLOUD_API_KEY, EXCLOUD_ORG_ID) from a service-account API key. For local development, exc login once and the provider just works.

Per-service base URLs

The provider points at production by default. Override per service if you need to:

provider "excloud" {
  compute_base_url = "https://compute.excloud.in"
  buckets_base_url = "https://buckets.excloud.in"
  dns_base_url     = "https://dns.excloud.in"
}

Or via env: EXCLOUD_COMPUTE_BASE_URL, EXCLOUD_BUCKETS_BASE_URL, EXCLOUD_DNS_BASE_URL.

A minimal real-world stack

data "excloud_compute_images" "all" {}
data "excloud_instance_types" "all" {}
data "excloud_subnets" "zone_1" { zone_id = 1 }

locals {
  ubuntu = one([for i in data.excloud_compute_images.all.images : i if i.name == "ubuntu-24.04-latest"])
  size   = one([for t in data.excloud_instance_types.all.instance_types : t if t.name == "m1a.large"])
  subnet = one([for s in data.excloud_subnets.zone_1.subnets : s if s.name == "DEFAULT"])
}

resource "excloud_ssh_key" "main" {
  name       = "main"
  public_key = file("~/.ssh/id_ed25519.pub")
}

resource "excloud_security_group" "web" {
  name        = "web"
  description = "HTTP/HTTPS in, all out"
}

resource "excloud_security_group_rule" "http" {
  security_group_id = excloud_security_group.web.id
  direction         = "ingress"
  protocol          = "TCPv4"
  cidr              = "0.0.0.0/0"
  port_range        = "80,443"
}

resource "excloud_public_ipv4" "web" {
  name = "web-prod"
}

resource "excloud_compute_instance" "web" {
  name                       = "web-1"
  zone_id                    = local.subnet.zone_id
  subnet_id                  = local.subnet.id
  image_id                   = local.ubuntu.id
  instance_type              = local.size.name
  ssh_pubkey                 = excloud_ssh_key.main.public_key
  security_group_ids         = [tonumber(excloud_security_group.web.id)]
  public_ipv4_reservation_id = tonumber(excloud_public_ipv4.web.id)

  root_volume = {
    name     = "web-1-root"
    size_gib = 40
  }
}

output "web_ip" {
  value = excloud_public_ipv4.web.address
}

terraform apply creates everything; terraform destroy tears it down. The plan is idempotent — re-running it without changes is a no-op.

Versioning

The provider follows Terraform’s normal semver convention. Pin to a major in your required_providers block (~> 0.2) so minor releases pick up bug fixes without surprises.

Provider source vs. CLI source

The provider talks to the same APIs as exc. If you can do something with exc compute create, the matching excloud_compute_instance exists. If a flag is missing from the provider but present in the CLI, open an issue — most of the provider is auto-generated from the API specs, so additions are quick.