In earlier posts in the Automating Private Cloud Director series, I covered the Private Cloud Director (PCD) API, Terraform, Ansible, and enterprise integration patterns. Those posts focused on the primitives: the interfaces and tools you use to drive PCD programmatically. In this post, I’d like to shift from primitives to patterns, and specifically to the patterns that come up when you wire PCD into a CI/CD pipeline.
This is the first post in a short sub-series on CI/CD patterns for PCD. The goal of this post is to establish the mental model. Later posts will work through specific patterns with real pipeline examples that I’ve validated in my lab.
Why CI/CD For The Private Cloud Itself
Most of the CI/CD content out there is written for application teams. You build an app, you test it, you deploy it, and the pipeline automates the boring parts. That’s useful, but it assumes the underlying infrastructure already exists and is stable.
Private cloud infrastructure is not stable in the same way. Tenants get created and decommissioned. Network designs evolve. Quotas change. Images need to be refreshed. New projects come online. All of that is change, and change that isn’t managed through a pipeline tends to end up as click-ops in the admin portal, and manual steps often introduce human error.
Different pieces of that change live at different layers, with different operators and different authorization. The examples later in this sub-series focus on the platform-team layer: managing infrastructure inside an existing tenant. Tenant provisioning itself is a cloud-admin concern that I’d like to cover separately.
The argument for CI/CD against PCD itself is the same argument that applies to any other cloud platform: if the infrastructure is defined in code, versioned in Git, and applied through a pipeline, you get review gates, audit trails, drift detection, and the ability to rebuild from source. Teams that have been running public cloud workloads for a while already take this for granted. Teams coming out of a VMware shop often haven’t had the tooling to do it, because vCenter was the interface and click-ops was the default.
PCD’s APIs are the enabler. Everything you can do in the admin portal is available through the API, which means everything is automatable, which means everything can be driven from a pipeline.
The Two Phases: Provisioning and Configuration
When you wire PCD into a pipeline, the work splits cleanly into two phases.
The first phase is provisioning. This is the “does this resource exist, with this shape, right now?” question. Tenants, projects, networks, routers, security groups, flavors, images, keypairs, volumes, and VMs are all provisioning concerns. Terraform is the right tool for this phase because it’s declarative, it has a plan-and-apply model that gives you a natural review gate, and it maintains a state file that represents the current shape of the world.
The second phase is configuration and orchestration. This is the “what happens inside or across these resources?” question. Configuring the guest OS, installing packages, wiring services together across multiple VMs, running smoke tests, triggering operational tasks. Ansible is the right tool for this phase because it’s procedural, it’s good at ordered multi-step workflows, and it can reach into guests over SSH.
You can use Ansible to provision PCD resources through the openstack.cloud collection, and I covered that in the Ansible post earlier in the series. It works, but in a pipeline context, Terraform is the better fit for provisioning because the plan phase gives you something concrete to review before anything changes. Ansible is the better fit for what happens after the resources exist.
The pipeline pattern, in its simplest form, looks like this:
git push
│
▼
terraform plan ──► PR review gate
│
▼ (on merge)
terraform apply ──► resources exist
│
▼
ansible-playbook ──► resources configured, app running
│
▼
smoke tests ──► validated
Each phase has a distinct job, and the handoff between them is where the interesting mechanics live.
The Handoff: How Terraform Talks to Ansible
This is the part people tend to get wrong, and it’s worth calling out now because it’ll get a full post of its own later in the sub-series.
Terraform creates the VMs. Ansible needs to configure them. For Ansible to know which VMs to configure, it needs an inventory. You could export Terraform outputs to a static inventory file, but that gets awkward fast once you have more than a handful of VMs or multiple environments. The cleaner approach is the OpenStack dynamic inventory plugin, which queries the PCD APIs directly and builds the inventory from live data.
The dynamic inventory plugin has a few requirements that aren’t obvious at first. The inventory file has to match a specific name pattern. The credentials need to be available to the plugin the same way they are to the Terraform provider. And you need a way to filter the inventory down to just the VMs you care about, usually through metadata tags that Terraform applies at create time.
I’ll cover all of this in detail in a later post. For now, the thing to internalize is that Terraform and Ansible don’t need a custom glue layer to work together against PCD. The dynamic inventory is the handoff, and once it’s set up, the pipeline just runs the two tools in sequence.
What the Sub-Series Will Cover
The plan for the remaining posts in this sub-series:
- GitOps for PCD infrastructure with Terraform. Repo layout, the plan-on-PR and apply-on-merge pattern, and a tested pipeline example that provisions networks, a router, and security groups inside an existing tenant.
- From provisioned to running: the Terraform-to-Ansible handoff. The dynamic inventory plugin, metadata-based filtering, passing context from Terraform to Ansible, and handling the SSH timing race that shows up when a VM is reachable on the network before cloud-init has finished.
- Ephemeral environments and pipeline patterns. Spinning up short-lived test environments for integration testing, blue/green and canary deployments using Octavia, and running VM and container workloads side by side in the same tenant from a single pipeline.
Each of those posts will include a working example that I’ve run in my lab. If you’ve read this series from the beginning, you’ll recognize the same validation bias from the Ansible post. Content about PCD should only cover things that can be validated in a real environment, and CI/CD content is no exception.
What’s Out of Scope
A few things I’m deliberately not covering in this sub-series, to keep the scope tight:
- Application-internal CI. The pipelines I’ll show treat the application as a black box. How you build and test the app itself is its own topic.
- Container image build pipelines. Related, but a different problem.
- Pulumi, Crossplane, and other infrastructure-as-code tools. PCD’s open APIs mean any of them are viable, and a Pulumi post is a good candidate for the future. Starting with Terraform and Ansible keeps continuity with the rest of the series.
- Policy-as-code gating with OPA or Conftest. Worth a dedicated post later, not a sidebar here.
Wrapping Up
The short version: PCD has open APIs, and that means the same CI/CD patterns that work against any other cloud platform work against PCD. Terraform handles provisioning, Ansible handles configuration, and the two compose cleanly through the OpenStack dynamic inventory.
In the next post, I’d like to cover the GitOps pattern for tenant infrastructure with Terraform, with a tested pipeline example.