For the complete documentation index, see llms.txt. This page is also available as Markdown.

Terraform Provider

Manage Private Cloud Director infrastructure as code with the first-party PCD Terraform provider. Install, authenticate, and provision your first resources.

The Private Cloud Director (PCD) Terraform provider (platform9/pcd) is the first-party way to manage PCD as code. It is published on the public Terraform Registry and works with any standard Terraform (or OpenTofu) workflow.

Overview

The provider manages the services that PCD exposes: Identity Service, Compute Service, Networking Service (including QoS and quotas), Persistent Storage Service, Image Library Service, Load Balancing (Octavia/OVN), DNS (Designate), and Key Management (Barbican). It also manages PCD's own cluster blueprints and host configuration and roles, declared as code.

In this guide, you'll install the provider, authenticate against your PCD environment, and create your first resources with Terraform.

Requirements

Component
Version

PCD

PCD 2026 April release or later

Terraform

1.0 or later (provider protocol 6)

OpenTofu

Any release supporting protocol 6

Install the Provider

Declare the provider in your Terraform configuration and pin a compatible version:

terraform {
  required_providers {
    pcd = {
      source  = "platform9/pcd"
      version = "~> 0.1"
    }
  }
}

Run terraform init. Terraform downloads the provider from the registry and verifies its GPG signature automatically.

Authenticate

Password, token, and application-credential authentication are all supported, as is sourcing credentials from a clouds.yaml file (cloud / OS_CLOUD).

Every argument can instead be supplied through the standard OS_* environment variables (OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_PROJECT_NAME, OS_REGION_NAME, OS_INSECURE, and so on), so an existing RC file works unchanged.

Self-signed TLS

Against a Community Edition or lab endpoint, set insecure = true (or OS_INSECURE=true). For production, prefer pinning the CA with cacert_file instead of disabling verification.

Quickstart

This minimal example authenticates, then creates a private network and subnet:

Runnable, self-contained examples for every resource and data source, including import.sh files for importable resources, are in the provider repository under examples/.

Day 1 Configuration

Day 1 configuration stands up a new region: a cluster blueprint, host networking, a host cluster, and the roles that onboard a host into it. These resources configure PCD's own control plane. The order below matters: most steps either name a resource created earlier or otherwise depend on one being ready first. The complete, runnable version of these examples, applied and destroyed end to end on Community Edition, is the provider's Community Edition guide, with its source in examples/complete/community-edition/.

These examples assume the host is already prepared and authorized in PCD. See pcdctl if it isn't yet; the host_id variable below is that host's resmgr UUID. The host records that UUID in /etc/pf9/host_id.conf. The other ways to find it, and every other ID the provider asks for, are in Look Up IDs for Import below.

Create a Volume Type

A volume type is the name tenants choose when they create a volume. Its volume_backend_name extra spec routes those volumes to a storage backend that the cluster blueprint declares in storage_backends_json, and the blueprint's image_library_storage names a volume type as well. PCD validates that the type exists when the blueprint is saved, so create the type first. The nfs in volume_backend_name below is the backend name: it matches the top-level key of the backend declared in the blueprint that follows, and if you rename one, rename both.

Define the Cluster Blueprint

PCD keeps one cluster blueprint per region. storage_backends_json declares the region's Persistent Storage Service backends. The example below declares a single NFS backend.

storage_backends_json has two levels of keys. The top-level key is the backend name (nfs here): on the host it becomes volume_backend_name, which is what a volume type's volume_backend_name must equal. The key under it names one driver configuration for that backend (nfs-primary here; both names are yours to choose): it is what the persistent-storage role's backends list selects, and it becomes the backend section on the host. That configuration carries driver and config. Write boolean options as booleans, not quoted strings; a quoted "true" never validates and the host keeps converging. driver is one of the built-in driver identifiers (NFS, LVM, HitachiISCSI, and so on) or the full class path of a custom driver, and config holds that driver's own keys, the same ones the UI shows under Infrastructure > Cluster Blueprint > Persistent Storage Connectivity > Add Volume Backend Configuration. For other drivers, see Certified Block Storage Drivers & Configurations.

If the region already has a blueprint, don't create a new one: see the section below on managing an existing blueprint to import and manage it instead.

Configure and Assign Host Networking

pcd_host_config maps each traffic type on a host to a network interface, and network_labels maps a physical-network label to one of those same interfaces. A later provider network binds to the host through that label, so pick a name (physnet1 below) and reuse it consistently.

Create the Cluster and Onboard the Host

pcd_cluster is the host cluster a hypervisor joins; VM high availability and auto-rebalancing are declared on it, so they're part of the region's definition from the start rather than a setting you turn on later. pcd_host_cluster_role then onboards the host by assigning it a cluster role: PCD expands each cluster role into its granular roles and computes their settings from the blueprint and the host configuration above, instead of you setting each one by hand. That makes it a different, higher-level resource than pcd_host_role in the table below, which assigns one granular role directly and suits only roles that need no computed settings.

Setting wait_until_converged = true blocks each role's apply until the host reports it converged. Do this for every role here: the Day 2 resources that follow (images, instances, volumes) need a hypervisor, image library, and storage backend that are actually ready.

Each role explicitly depends on the host configuration assignment. Nothing above references it by attribute, so Terraform can't infer the order on its own, and assigning a cluster role before the host has its network configuration produces an inconsistent host. The persistent-storage role's backends list names a driver configuration from the blueprint's storage_backends_json: the second-level key (nfs-primary here), not the backend name (nfs) above it. Assigning the role is what turns that configuration into a running storage service on the host. Naming the top-level key instead leaves the service with no backend, and the host cannot converge.

Day 2 Configuration

With the region converged, Day 2 configuration adds the resources a workload runs on: a network, an image, a flavor, an instance, and a volume. These are the tenant-facing services described in the Overview above.

Configure Tenant Networking

This example creates a flat provider network on the physnet1 label from the host configuration above, so instances land directly on that network.

The network depends on the hypervisor role directly, because the physnet1 label only exists on a host once that role has converged. Nothing in the network's own attributes references the role, so Terraform needs the explicit dependency.

A security group with SSH and ICMP access completes the tenant-facing setup:

Add an Image and a Flavor

This example uses CirrOS, a minimal test image commonly used to validate an image library end to end. Point image_source_url (or local_file_path) at your own image for anything you plan to run for real.

The image depends on the image-library role directly: uploads need a working image-library host to receive them, and image_source_url isn't a reference Terraform can order on its own.

The upload goes to the image-library host itself, on port 9494, which is how the UI uploads images too. The machine running Terraform must reach the host on that port; if it cannot, set endpoint_overrides = { image = "https://<reachable-address>:9494/v2/" } in the provider block.

Boot an Instance with an Attached Volume

The instance boots from the image and flavor above onto the workload subnet, then gets a Persistent Storage Service volume attached to it:

The instance only references the network by ID, not the subnet, so it depends on the subnet directly: PCD needs an actual subnet, not just a bare network, to assign the instance an address. The volume depends on the persistent-storage role the same way: nothing in its attributes points at that role, but the backend needs a running service on the host before it can accept a new volume.

Manage PCD-Native Infrastructure

PCD keeps one cluster blueprint per region, so a region that already runs has a blueprint to import rather than create. The import ID is the blueprint's name.

With Terraform 1.5 or later, an import block and terraform plan -generate-config-out=generated.tf write the resource block for you, which is the quickest way to learn the shape of a blueprint you have only configured in the UI. The sensitive storage_backends_json comes out as null, which is what you want: the provider keeps the backends the region already has.

Destroying an imported blueprint deletes it from PCD. To stop managing it without deleting it, run terraform state rm pcd_cluster_blueprint.region.

Resource
Manages

pcd_cluster_blueprint

The region's cluster blueprint: virtual-network segmentation, DNS domain, image library and VM storage, and the Persistent Storage Service backends. One per region.

pcd_cluster

A cluster, the unit hypervisors join. VM high availability and auto-rebalancing are set here.

pcd_host_config

A host's mapping of each traffic type (management, VM console, tunneling, image library, live migration) to a network interface, plus physical-network labels.

pcd_host_config_assignment

Attaches a host configuration to a host.

pcd_host_cluster_role

Onboards a host by assigning it a cluster role (hypervisor, image-library, persistent-storage, dns); PCD computes the granular role settings from the blueprint and host configuration.

pcd_host_role

Assigns one granular role (for example pf9-ostackhost-neutron) directly. The low-level API under pcd_host_cluster_role; reserve it for roles that take no settings.

A read-only pcd_cluster_blueprint data source is also available.

Look Up IDs for Import

Import IDs are assigned by PCD. pcdctl prints them; source the pcdctl RC file from Settings > API Access first, and on Community Edition also set OS_INSECURE=true for its self-signed certificate. (pcdctl config set configures the host onboarding commands; it does not authenticate these.)

Resource
Import ID
Where to Find It

pcd_blockstorage_volume_type

Volume type UUID

pcdctl volume type list

pcd_blockstorage_volume

Volume UUID

pcdctl volume list

pcd_images_image

Image UUID

pcdctl image list

pcd_compute_flavor

Flavor UUID

pcdctl flavor list

pcd_compute_instance

Instance UUID

pcdctl server list

pcd_networking_network, pcd_networking_subnet

Network or subnet UUID

pcdctl network list, pcdctl subnet list

pcd_networking_secgroup

Security group UUID

pcdctl security group list

pcd_cluster_blueprint

Blueprint name

GET /resmgr/v2/blueprint (below)

pcd_cluster

Cluster name

pcdctl aggregate list

pcd_host_config

Host configuration UUID

GET /resmgr/v2/hostconfigs (below)

pcd_host_config_assignment, pcd_host_cluster_role, pcd_host_role

<host-uuid>/<config-or-role>

Host UUID, as below

The host UUID is not the ID that pcdctl hypervisor list shows. The host records it in /etc/pf9/host_id.conf; once the host has the hypervisor role, pcdctl hypervisor show <hypervisor-id> -c service_host and pcdctl compute service list --service nova-compute -c Host -c Zone print it too.

Host configurations, blueprints, and clusters are read from the resource manager API with a token from pcdctl:

The full per-resource table is in the provider's Importing guide.

Full Resource Reference

Per-resource and per-data-source documentation, including every argument, attribute, and import ID, is generated from the provider schema and published on the Terraform Registry:

Two guides there cover this page's workflows end to end:

  • Community Edition guide: a runnable zero-to-VM configuration for a Community Edition region.

  • Importing guide: the import ID and lookup command for every importable resource.

The Registry is the source of truth for the reference; this page covers concepts and getting started.

Support and Source

Last updated

Was this helpful?