# Authoring IaC templates for Quake AI

Source: https://docs.quake.ai/docs/automation/concepts/authoring-iac-templates
Markdown: https://docs.quake.ai/docs/automation/concepts/authoring-iac-templates.md

---

# Authoring IaC templates for Quake AI

You write OpenTofu or Terraform templates against the OpenStack provider the same way you would on any Antelope-era cloud. Quake AI adds a small, fixed catalog of flavors, networks, and volume types, a second AWS provider path for S3-compatible object storage, and Magnum behaviors that differ from generic OpenStack tutorials. This page collects those rules so a new template matches the [infrastructure template library](/resources/iac-templates) and passes CI validation.

If you are new to IaC on the platform, start with [Get started with IaC](/docs/automation/how-to/getting-started-iac) and [IaC on Quake AI](/docs/automation/concepts/iac-comparison). If you know OpenStack but not Quake AI branding, read [How Quake AI uses OpenStack](/resources/migration/openstack) first.

## What is different on Quake AI

Quake AI runs {OPENSTACK_RELEASE}. The OpenStack provider resource names (`openstack_compute_instance_v2`, `openstack_networking_port_v2`, and the rest) are standard. The differences show up in **catalog literals** (which flavor and network names resolve), **auth** (application credentials plus separate S3 keys), and **platform limits** (Magnum trust rules and no managed VPN).

The platform keeps the decision space small on purpose: 26 public flavors in four families, one boot volume type, two shared external networks, and project-scoped roles instead of IAM policies. Templates that hard-code those literals correctly are easier to validate and less likely to fail `tofu plan` in a new project.

## Authentication and providers

### OpenStack provider

Authenticate with **application credentials**, not username and password in HCL. Export the variables from the file you generate in [Generate app credentials](/docs/tools/generate-app-credentials):

```bash
export OS_AUTH_URL="https://keystone.rumble.cloud/v3"
export OS_AUTH_TYPE="v3applicationcredential"
export OS_APPLICATION_CREDENTIAL_ID="YOUR_APP_CREDENTIAL_ID"
export OS_APPLICATION_CREDENTIAL_SECRET="YOUR_APP_CREDENTIAL_SECRET"
export OS_REGION_NAME="us-east-1"
```

Declare an empty provider block and keep secrets out of `.tf` files:

```hcl
terraform {
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.0"
    }
  }
}

provider "openstack" {}
```

Regions: {REGIONS.join(", ")}. Parameterize the region in endpoint examples when your template README shows object storage URLs.

### AWS provider for object storage

Object storage buckets use the **AWS provider** pointed at Quake AI's S3-compatible endpoint. The OpenStack application credential does **not** satisfy the AWS provider. Mint EC2-compatible credentials with the OpenStack CLI and export `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` before `tofu plan`. The [S3 storage template](/resources/iac-templates/s3-storage-acl) README documents the bootstrap; mirror that pattern in any template that creates buckets.

```hcl
provider "aws" {
  region                      = var.s3_region
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    s3 = var.s3_endpoint
  }
}
```

## Catalog literals

These values come from the live platform catalog. Wrong defaults produce empty data sources on `tofu plan` even when `tofu validate` passes.

| Literal | Use in templates | Common mistake |
| --- | --- | --- |
| **Image** | `Ubuntu-24.04` (hyphenated Glance name) | `Ubuntu 24.04` with a space |
| **Flavors** | `m2a.*`, `c2a.*`, `r2a.*`, `s1a.*` only | Fabricated names or other clouds' size IDs |
| **External network** | `PublicEphemeral` or `PublicStatic` | `PublicNetwork`, `ext-net`, or `external` |
| **Volume type** | Boot from Cinder volume; platform storage is NVMe (`Flash_Premium`) | Picking a volume type that does not exist in the project |
| **Object endpoint** | `https://object.{region}.rumble.cloud` | Global `s3.*` hostnames that do not match the region |

Link flavor choice to [Compute flavors](/docs/compute/concepts/flavors) and capacity planning. Shared vCPU sizes (`s1a.*`) suit demos; dedicated families (`m2a`, `c2a`, `r2a`) suit production workloads.

### External network policy

Quake AI exposes two shared external networks. Pick the default in `variables.tf` based on what the template teaches:

| Class | Default | Use when |
| --- | --- | --- |
| Throwaway / quickstart-aligned | `PublicEphemeral` | Single-VM demos, labs, or templates that mirror the quickstart ephemeral path |
| Production / multi-tier | `PublicStatic` | Persisted floating IPs, edge reverse proxies, multi-tier stacks, and workloads that must retain an address across instance rebuilds |

Readers can override either value in `terraform.tfvars`. Document which class your template belongs to in the README.

## Recommended template skeleton

Most compute templates in the library follow the same Neutron shape: private network, router on the external network, security group rules, port with security groups attached by **ID**, boot-from-volume instance, optional floating IP.

```hcl
data "openstack_images_image_v2" "os" {
  name        = var.image_name
  most_recent = true
}

data "openstack_networking_network_v2" "external" {
  name = var.external_network
}

resource "openstack_networking_network_v2" "private" {
  name           = "${var.instance_name}-net"
  admin_state_up = true
}

resource "openstack_networking_subnet_v2" "private" {
  network_id = openstack_networking_network_v2.private.id
  cidr       = var.private_cidr
  ip_version = 4
}

resource "openstack_networking_router_v2" "main" {
  external_network_id = data.openstack_networking_network_v2.external.id
}

resource "openstack_networking_router_interface_v2" "private" {
  router_id = openstack_networking_router_v2.main.id
  subnet_id = openstack_networking_subnet_v2.private.id
}

resource "openstack_networking_secgroup_v2" "app" {
  name = "${var.instance_name}-sg"
}

resource "openstack_networking_port_v2" "app" {
  network_id         = openstack_networking_network_v2.private.id
  security_group_ids = [openstack_networking_secgroup_v2.app.id]

  depends_on = [openstack_networking_router_interface_v2.private]
}

resource "openstack_compute_instance_v2" "app" {
  flavor_name = var.flavor_name
  key_pair    = var.key_name

  block_device {
    uuid                  = data.openstack_images_image_v2.os.id
    source_type           = "image"
    destination_type      = "volume"
    volume_size           = 20
    boot_index            = 0
    delete_on_termination = true
  }

  network {
    port = openstack_networking_port_v2.app.id
  }
}

resource "openstack_networking_floatingip_v2" "app" {
  pool = var.external_network
}

resource "openstack_networking_floatingip_associate_v2" "app" {
  floating_ip = openstack_networking_floatingip_v2.app.address
  port_id     = openstack_networking_port_v2.app.id
}
```

Conventions embedded in that skeleton:

- Attach security groups to the **port** with `security_group_ids = [....id]`. Avoid `security_groups = [....name]` on the instance; Nova name lookup can match multiple groups after partial applies.
- Prefix security group and network resource names with `var.instance_name` or a dedicated prefix variable so repeated applies do not collide with orphaned groups.
- Use `openstack_networking_floatingip_associate_v2`, not the deprecated `openstack_compute_floatingip_associate_v2` resource.
- Wait for the router interface before creating ports that need routing (`depends_on` as shown).

The [simple-vm template](/resources/iac-templates/simple-vm) in `iac/templates/simple-vm/` is the canonical reference implementation.

## Variables, secrets, and cloud-init

### Standard variables

Expose a consistent surface so readers can reuse `terraform.tfvars` patterns across templates:

| Variable | Required | Typical default |
| --- | --- | --- |
| `key_name` | Yes | none (must exist in the project) |
| `image_name` | No | `Ubuntu-24.04` |
| `flavor_name` (or tier-specific flavors) | No | size appropriate to the workload |
| `external_network` | No | per network policy table above |
| `private_cidr` | No | unused `/24` in the project |
| `instance_name` | No | template slug |

Document every variable from `variables.tf` on the template reference page Parameters table.

### SSH keypairs

Require an existing project keypair via `var.key_name`. Do not create `openstack_compute_keypair_v2` in new templates unless the template explicitly owns key lifecycle and the README states that the private key lands in Terraform state. Prefer the import-key flow from [Add an SSH key](/docs/tools/add-ssh-key).

### Secrets

Never commit passwords, API keys, or credential defaults in HCL. Accept secrets through `terraform.tfvars` (gitignored), CI secret stores, or generated values on the instance via cloud-init.

### cloud-init and templatefile

Use `templatefile()` for user-data. Do not pass computed resource attributes (for example a floating IP address) into `templatefile()` variables when that creates a dependency cycle; render those values on the instance or split the bootstrap script.

## Platform quirks for template authors

| Area | What to encode in templates |
| --- | --- |
| **Public application entry points** | Attach a floating IP to a self-managed reverse proxy or API gateway instance. Use the [Edge Reverse Proxy](/resources/iac-templates/edge-reverse-proxy), [API Gateway](/resources/iac-templates/api-gateway), or [Edge WAF](/resources/iac-templates/edge-waf) template as a starting point. |
| **Kubernetes (Magnum)** | Magnum provisions cluster infrastructure; you operate the control plane. Application credentials cannot satisfy Magnum trust delegation; password-scoped sessions are required for `openstack coe cluster create`. See [Kubernetes FAQ](/docs/kubernetes/faq). Self-managed Kubernetes on Nova via OpenTofu is the path documented for new clusters in [IaC comparison](/docs/automation/concepts/iac-comparison). |
| **Heat** | CLI validation uses `openstack orchestration template validate`, not `openstack stack template validate`. |
| **VPN** | No managed VPNaaS. Use the [private network + VPN template](/resources/iac-templates/private-network-vpn) or your own WireGuard stack. |
| **Volume backups** | Cinder backup creation is not available; use snapshots or clones for data protection callouts in README prose. |
| **Quotas** | CLI quota output uses names like `public_ip`; older docs may say `floatingip`. Size templates against [Resource tiers](/docs/account/resource-tiers). |
| **Authorization** | `admin`, `member`, and `reader` at project scope only. Split environments by project, not by IAM-style resource policies. |

For the full OpenStack mapping and Console naming, see [How Quake AI uses OpenStack](/resources/migration/openstack).

## Validation expectations

Every template in the library passes [`tofu validate`](https://opentofu.org/docs/cli/commands/validate/) in CI before it ships. That check confirms the HCL parses and matches the provider schema. It does **not** mean the template was applied in your project or that your quotas allow the default flavors.

Treat [validated OpenTofu templates](/docs/platform/validation#how-infrastructure-templates-are-checked) as a correct starting point. Run `tofu plan` in your project, adjust flavors and counts for your tier, then apply. For the full validation program, see [How the platform validates its content](/docs/platform/validation).

## See also

- [IaC on Quake AI: OpenTofu and Terraform](/docs/automation/concepts/iac-comparison)
- [Terraform and OpenTofu on Quake AI](/docs/automation/concepts/terraform)
- [Get started with IaC](/docs/automation/how-to/getting-started-iac)
- [How to customize a template's image and flavor](/docs/automation/how-to/customize-template-image-flavor)
- [How to add a block volume to a template](/docs/automation/how-to/add-volume-to-template)
- [How to parameterize a template with tfvars](/docs/automation/how-to/parameterize-template-tfvars)
- [Infrastructure templates](/resources/iac-templates)
