Automate your infrastructure with OpenTofu
Automate your infrastructure with OpenTofu
In this tutorial, you recreate the infrastructure from the Launch your first server tutorial, but entirely through code using OpenTofu. By the end, you will have a working OpenTofu project that provisions a boot-from-volume virtual machine, security group, and key pair on Quake AI, reachable over the public IP that the PublicEphemeral network assigns at boot, all from a single tofu apply command.
What you will learn:
- How OpenTofu/Terraform describes infrastructure as code in HCL files
- How to configure the OpenStack provider for Quake AI
- How to define compute, network, and security resources
- How to use
tofu planto preview changes before applying - How to destroy infrastructure cleanly when you are done
Prerequisites#
Before you start, you should have:
- Completed the Launch your first server tutorial (or equivalent understanding)
- A Quake AI account with application credentials
- OpenTofu installed locally (installation guide)
- An SSH key pair on your local machine
Step 1: Set up the project directory and create main.tf with the OpenStack provider#
Keep all configuration in a dedicated directory so state files, variables, and modules stay isolated from other work on your machine. A small, focused folder also matches how teams ship infrastructure changes in version control.
Create the project and open it in your editor:
mkdir -p YOUR_PROJECT_DIRECTORY
cd YOUR_PROJECT_DIRECTORYCreate main.tf with the OpenTofu block and the OpenStack provider. The required_providers stanza pins the OpenStack provider so everyone on your team resolves the same plugin version. Set auth_url to the Quake AI Keystone endpoint. Leave the region out of the provider block: the provider reads OS_REGION_NAME from the OpenRC file you source in Step 2, and that environment value is the source of truth for which region your credentials target.
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "~> 3.0"
}
}
}
provider "openstack" {
auth_url = "https://keystone.rumble.cloud/v3"
# region is read from OS_REGION_NAME, exported by `source openrc.sh` in Step 2.
}The provider still expects identity and project settings (user, password or application credential, project scope) from your environment. You load those in the next step.
Step 2: Configure authentication via environment variables#
OpenTofu reads standard OpenStack OS_* variables. Sourcing your downloaded OpenRC file puts them into the current shell session so you never paste secrets into .tf files.
Follow Generate app credentials to create and download your credentials file.
Source the file to load credentials into your shell session:
source ~/path/to/openrc.shStay in this shell for every tofu command for the rest of the tutorial. If you open a new terminal tab, source openrc.sh again before you run OpenTofu.
Step 3: Define an SSH key pair resource#
Quake AI injects the public key at boot so you can SSH as the default Linux user. Defining the key pair in OpenTofu keeps the key material under the same lifecycle as the rest of the stack: one apply creates it, and one destroy removes it when you are finished experimenting.
Create variables.tf:
variable "ssh_public_key_path" {
description = "Path to your SSH public key on the machine running OpenTofu"
type = string
default = "~/.ssh/id_ed25519.pub"
}Add the following to main.tf after the provider block. The openstack_compute_keypair_v2 resource registers your public key under a stable name that the instance will reference by name.
resource "openstack_compute_keypair_v2" "tutorial" {
name = "TUTORIAL_KEYPAIR_NAME"
public_key = file(var.ssh_public_key_path)
}Replace TUTORIAL_KEYPAIR_NAME with a name that is unique in your project, for example tutorial-opentofu-key.
Step 4: Define a security group with an SSH ingress rule#
Security groups are stateful firewalls attached to instance ports. Add a dedicated group with a single ingress rule for TCP port 22 so SSH reaches the VM while everything else stays closed by default. You still attach the platform default group on the instance in a later step so outbound and platform defaults stay consistent with what you used in the console tutorial.
Add to main.tf:
resource "openstack_networking_secgroup_v2" "tutorial_ssh" {
name = "tutorial-opentofu-ssh"
description = "SSH access for OpenTofu tutorial"
}
resource "openstack_networking_secgroup_rule_v2" "tutorial_ssh_ingress" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.tutorial_ssh.id
}The CIDR 0.0.0.0/0 matches the beginner console flow. For production servers, narrow remote_ip_prefix to your office or VPN range.
Step 5: Define a boot-from-volume compute instance and attach it to PublicEphemeral#
The instance needs an image, a flavor, a network, security groups, and the key pair name. On Quake AI the s1a.*, s2a.*, m1a.*, and m2a.* flavors all report disk = 0, so the Compute API accepts them only as boot-from-volume servers. Instead of attaching the image directly, you look it up with an openstack_images_image_v2 data source and pass its ID to a block_device block, which provisions a Cinder volume from the image and boots the instance off it. The console flow makes the same boot-from-volume choice in Launch your first server.
Attach the instance NIC to PublicEphemeral, which assigns a public IP at boot. The entry tier needs no floating IP, so this is the only network the stack references. You declare a data source for it and pass its ID to the instance.
Add to main.tf:
data "openstack_networking_network_v2" "instance_net" {
name = "PublicEphemeral"
}
data "openstack_images_image_v2" "ubuntu" {
name = "Ubuntu-22.04"
most_recent = true
}
resource "openstack_compute_instance_v2" "tutorial" {
name = "tutorial-opentofu-server"
flavor_name = "s1a.micro"
key_pair = openstack_compute_keypair_v2.tutorial.name
security_groups = ["default", openstack_networking_secgroup_v2.tutorial_ssh.name]
block_device {
uuid = data.openstack_images_image_v2.ubuntu.id
source_type = "image"
destination_type = "volume"
volume_size = 20
boot_index = 0
delete_on_termination = true
}
network {
uuid = data.openstack_networking_network_v2.instance_net.id
}
}
output "instance_ip" {
value = openstack_compute_instance_v2.tutorial.access_ip_v4
description = "Public IP assigned at boot on PublicEphemeral"
}The block_device block sets delete_on_termination = true, so tofu destroy removes the backing Cinder volume with the instance instead of leaving an orphaned volume behind. The instance_ip output prints the address PublicEphemeral assigns at boot, which Step 9 uses to connect.
Step 6: Run tofu init to download the provider#
Initialization downloads the OpenStack provider plugin and prepares the backend. Run it from the project directory:
tofu initYou should see the provider install complete without errors. If OpenTofu reports authentication issues, confirm you sourced openrc.sh in this shell and that your application credential password is correct.
Step 7: Run tofu plan to preview the infrastructure#
Planning performs a read-only dry run against the Quake AI APIs. Use it to confirm that OpenTofu creates the key pair, security group, rule, and instance, and nothing else.
tofu planReview the planned actions. You should see a set of + create operations. If anything unexpected appears, fix the configuration before moving on.
Step 8: Run tofu apply to create everything#
Applying commits the changes. OpenTofu shows the same plan again and waits for confirmation.
tofu applyType yes when prompted. When the run finishes, note the instance_ip output value.
Step 9: SSH into the instance to verify#
Connect with the default user for Ubuntu images on Quake AI and the private key that matches the public key you registered. Use the public IP from the instance_ip output:
ssh -i YOUR_PRIVATE_KEY_PATH ubuntu@$(tofu output -raw instance_ip)Replace YOUR_PRIVATE_KEY_PATH with the path to your private key (for example ~/.ssh/id_ed25519). Accept the host key prompt on first connect. A shell on the remote machine confirms the key pair, security group rule, and instance work together.
Step 10: Run tofu destroy to clean up#
When you no longer need the resources, destroy removes them in dependency order. This terminates the instance, deletes its boot volume (because delete_on_termination is set), and removes the security group and key pair from the project.
tofu destroyType yes to confirm. Verify in the console that the instance is gone.
What you learned#
You modeled the same logical stack as the console tutorial using declarative HCL: provider configuration, an SSH key pair resource, a security group with an ingress rule, and a boot-from-volume instance on PublicEphemeral that reaches the internet over the public IP assigned at boot. You used tofu plan to inspect intent before change and tofu apply / tofu destroy to drive the full lifecycle from your workstation.
Next steps#
- Read the Terraform on Quake AI concept page for how OpenTofu fits the platform.
- Compare tooling options in Infrastructure as code comparison.
- Follow Simple Terraform example for a concise walkthrough pattern.
- Explore Full-stack Terraform example for multi-tier layouts.
Clean up#
If you still have resources from this tutorial, run tofu destroy from the project directory so the instance and its boot volume do not keep billing or cause name conflicts for your next exercise.
Usage Guidelines
The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.
For the full policy, see Usage Guidelines.
Last validated: 26.05.2026