Reference example for the Automating Private Cloud Director CI/CD blog series.
This repository contains a tested example of a GitOps pipeline that manages
network and security infrastructure inside a single PCD tenant, using
Terraform and GitHub Actions. It is intended to be read and copied, not
forked and run in place. The example pipeline workflow lives under
examples/github-actions/ rather than .github/workflows/ so that this
repository itself does not trigger pipeline runs.
modules/tenant-network-baseline/reusable Terraform module that creates a network, subnet, router, and security groups inside an existing tenantenvironments/dev/per-environment Terraform configuration that calls the moduleexamples/github-actions/terraform.yamlexample GitHub Actions workflow that runsplanon PRs andapplyon merges tomain
To adapt this for your own infrastructure repository:
- Create a tenant in PCD with the cloud admin tooling. Note the tenant ID.
- Identify the external network you want tenant routers to attach to.
Note its ID with
pcdctl network list --external. - Create a
clouds.yamlwith credentials scoped to your tenant. Application credentials are recommended over user passwords for pipeline use. - Copy
modules/tenant-network-baseline/into your repo'smodules/directory. - Copy
environments/dev/into your repo and updateterraform.tfvarswith your real tenant ID and external network ID. Useterraform.tfvars.exampleas the template. - Copy
examples/github-actions/terraform.yamlinto your repo at.github/workflows/terraform.yaml. - Add a
CLOUDS_YAMLsecret to your repository under Settings → Secrets and variables → Actions, containing the contents of yourclouds.yaml. - Open a PR with the change. The
planjob runs against the PR. After merge, theapply-devjob runs and creates the resources.
This example uses Terraform's default local state backend, which means the state file is created on the GitHub Actions runner during a pipeline run and discarded when the runner exits. The first apply works because Terraform creates resources from scratch. Subsequent applies will fail because the runner has no record of what was created last time, and Terraform will try to create resources that already exist.
Two ways to handle this:
- Recreate from scratch each time you iterate. Delete the resources
in PCD between pipeline runs (
pcdctl network delete,pcdctl router delete, and so on). Acceptable for lab use; not acceptable for anything you care about. - Configure a remote state backend. Production use of this pattern requires a remote state backend with locking and encryption. This example does not include one because the right backend depends on your environment. See the Terraform documentation on backends: https://developer.hashicorp.com/terraform/language/backend
This caveat applies whether you're starting from an empty tenant or adopting an existing one. The first commit may apply cleanly; the second one will not without remote state.
The example pipeline runs scoped to a single existing tenant. Tenant provisioning is a separate cloud-admin workflow with different authorization concerns; this repository does not address it.
- Blog series: Automating Private Cloud Director (link)
- Post 2: GitOps for tenant infrastructure with Terraform (link)