Skip to main content
IMHCloud Logo
Back to support home

How to Create Cloud Infrastructure with Terraform

Learn how Terraform automates cloud infrastructure creation on InMotion Cloud. This guide walks through a real example that provisions a network, security group, and virtual machine with a single command.

7 min read

Creating a server on InMotion Cloud through Horizon takes a few minutes of pointing and clicking. Creating ten servers the exact same way takes ten times the effort, and each one introduces the chance of a missed setting or a misconfigured firewall rule. Terraform eliminates that problem by letting you define your infrastructure in a text file and create all of it with a single command.

This guide walks through a working example that provisions a complete environment on InMotion Cloud: a private network, a security group, and a virtual machine with a public IP address. Everything is created from code, and the same code can rebuild the entire setup from scratch whenever you need it.

This article is part of our series on How Modern Applications Run on Cloud Infrastructure. Terraform is the first stage of the modern cloud workflow. Read the overview for the full picture of how Terraform, Ansible, Docker, and Kubernetes work together.

What Terraform Does

Terraform is an infrastructure-as-code tool. Instead of logging into a dashboard and clicking through menus to create servers, networks, and firewall rules, you write a configuration file that describes exactly what you want. Terraform reads that file and makes API calls to your cloud provider to create everything automatically.

Think of it as a blueprint for your infrastructure. An architect does not build a house by describing each step in real time. They draw a blueprint, and the builders follow it. Terraform works the same way: you describe the end result, and Terraform figures out what needs to happen to get there.

In practical terms: Terraform creates servers and networks automatically instead of doing it manually.

What You Need Before Starting

Before running Terraform against InMotion Cloud, gather the following:

  • Terraform installed on your local machine (version 1.0 or later)
  • OpenStack API credentials from your InMotion Cloud project (see the authentication options below)
  • An SSH key pair already created in your InMotion Cloud project (see Managing SSH Key Pairs)
  • Your project's external network name (typically External)

Installing Terraform

Download Terraform from the official HashiCorp website and add it to your system path. Verify the installation:

1terraform --version
1Terraform v1.9.8
2on linux_amd64

Setting Up OpenStack Credentials

Terraform needs credentials to authenticate with the InMotion Cloud API. There are three ways to provide them. Choose whichever fits your workflow.

Option 1: Download an OpenRC File from Horizon

Horizon can generate a shell script that sets the environment variables Terraform needs. This is the fastest way to get started because there is nothing to write manually.

  1. Log into Horizon and navigate to Identity > Application Credentials (or Project > API Access depending on your Horizon version).
  2. Click Download OpenRC File and select OpenStack RC File.
  3. Save the file (typically named app-openrc.sh or project-openrc.sh).
  4. Source the file in your terminal before running Terraform:
1source app-openrc.sh

The script will prompt for your password (or set the application credential secret, depending on the file type). Once sourced, the OS_AUTH_URL, OS_PROJECT_NAME, OS_USERNAME, and related variables are set in your shell session. Terraform's OpenStack provider reads these automatically, so no clouds.yaml file is needed.

Tip: If you downloaded an application credential OpenRC file, it sets OS_APPLICATION_CREDENTIAL_ID and OS_APPLICATION_CREDENTIAL_SECRET instead of username/password. Application credentials are preferred because they can be scoped to a single project and revoked without changing your account password.

Option 2: clouds.yaml with Application Credentials

If you prefer a file over environment variables, create a clouds.yaml in your working directory. Application credentials are the recommended authentication method because they are scoped to a single project and can be revoked independently of your account password.

To create an application credential, go to Identity > Application Credentials in Horizon and click Create Application Credential. Note the ID and secret.

1clouds:
2 inmotioncloud:
3 auth:
4 auth_url: https://iad1.inmotioncloud.com:5000
5 application_credential_id: "your-credential-id"
6 application_credential_secret: "your-credential-secret"
7 region_name: "IAD1"
8 interface: "public"
9 identity_api_version: 3
10 auth_type: "v3applicationcredential"

Option 3: clouds.yaml with Username and Password

You can also authenticate with your OpenStack username and password directly. This is simpler to set up but uses your full account credentials rather than a scoped application credential.

1clouds:
2 inmotioncloud:
3 auth:
4 auth_url: https://iad1.inmotioncloud.com:5000
5 username: "your-username"
6 password: "your-password"
7 project_name: "your-project-name"
8 user_domain_name: "Default"
9 project_domain_name: "Default"
10 region_name: "IAD1"
11 interface: "public"
12 identity_api_version: 3

Telling Terraform Which Cloud to Use

If you chose Option 2 or Option 3 with a clouds.yaml file, the provider block in your Terraform configuration references it by name:

1provider "openstack" {
2 cloud = "inmotioncloud"
3}

If you chose Option 1 (OpenRC environment variables), you can omit the cloud argument entirely. The provider will read credentials from the environment:

1provider "openstack" {}
Security note: Never commit clouds.yaml or OpenRC files to a public repository. Add both to your .gitignore file.

Writing the Terraform Configuration

Create a file called main.tf. This single file defines everything Terraform will create: a network, a subnet, a router, a security group, and a virtual machine with a floating IP.

Provider Configuration

The first block tells Terraform to use the OpenStack provider and connect to your InMotion Cloud project:

1terraform {
2 required_providers {
3 openstack = {
4 source = "terraform-provider-openstack/openstack"
5 version = "~> 3.0"
6 }
7 }
8}
9
10provider "openstack" {
11 cloud = "inmotioncloud"
12}

Network and Subnet

Next, define a private network for the instance. This is equivalent to creating a network and subnet manually through Project > Network > Networks in Horizon:

1resource "openstack_networking_network_v2" "demo_network" {
2 name = "terraform-demo-network"
3 admin_state_up = true
4}
5
6resource "openstack_networking_subnet_v2" "demo_subnet" {
7 name = "terraform-demo-subnet"
8 network_id = openstack_networking_network_v2.demo_network.id
9 cidr = "192.168.100.0/24"
10 ip_version = 4
11 dns_nameservers = ["8.8.8.8", "8.8.4.4"]
12}

Router for Internet Access

Connect the private network to the external network through a router, so the instance can reach the internet:

1data "openstack_networking_network_v2" "external" {
2 name = "External"
3}
4
5resource "openstack_networking_router_v2" "demo_router" {
6 name = "terraform-demo-router"
7 external_network_id = data.openstack_networking_network_v2.external.id
8}
9
10resource "openstack_networking_router_interface_v2" "demo_router_interface" {
11 router_id = openstack_networking_router_v2.demo_router.id
12 subnet_id = openstack_networking_subnet_v2.demo_subnet.id
13}

Security Group

Define firewall rules that allow SSH (port 22) and HTTP (port 80) access. Without this, the instance would be unreachable:

1resource "openstack_networking_secgroup_v2" "demo_secgroup" {
2 name = "terraform-demo-secgroup"
3 description = "Allow SSH and HTTP access"
4}
5
6resource "openstack_networking_secgroup_rule_v2" "ssh" {
7 direction = "ingress"
8 ethertype = "IPv4"
9 protocol = "tcp"
10 port_range_min = 22
11 port_range_max = 22
12 remote_ip_prefix = "0.0.0.0/0"
13 security_group_id = openstack_networking_secgroup_v2.demo_secgroup.id
14}
15
16resource "openstack_networking_secgroup_rule_v2" "http" {
17 direction = "ingress"
18 ethertype = "IPv4"
19 protocol = "tcp"
20 port_range_min = 80
21 port_range_max = 80
22 remote_ip_prefix = "0.0.0.0/0"
23 security_group_id = openstack_networking_secgroup_v2.demo_secgroup.id
24}

The Virtual Machine

Finally, define the instance (virtual machine) itself, along with a floating IP that makes it reachable from the internet:

1resource "openstack_compute_instance_v2" "demo_instance" {
2 name = "terraform-demo-vm"
3 image_name = "Ubuntu 24.04 LTS"
4 flavor_name = "gen2.small"
5 key_pair = "my-keypair"
6 security_groups = [openstack_networking_secgroup_v2.demo_secgroup.name]
7
8 network {
9 uuid = openstack_networking_network_v2.demo_network.id
10 }
11
12 depends_on = [openstack_networking_router_interface_v2.demo_router_interface]
13}
14
15resource "openstack_networking_floatingip_v2" "demo_fip" {
16 pool = "External"
17}
18
19resource "openstack_compute_floatingip_associate_v2" "demo_fip_assoc" {
20 floating_ip = openstack_networking_floatingip_v2.demo_fip.address
21 instance_id = openstack_compute_instance_v2.demo_instance.id
22}
23
24output "instance_ip" {
25 value = openstack_networking_floatingip_v2.demo_fip.address
26 description = "The public IP address of the demo instance"
27}

That is the entire configuration. Six resources and roughly 80 lines of code define a complete, functional environment.

Running Terraform

With the configuration written, creating the infrastructure takes three commands.

Step 1: Initialize

terraform init downloads the OpenStack provider plugin and prepares the working directory:

1terraform init
1Initializing the backend...
2
3Initializing provider plugins...
4- Finding terraform-provider-openstack/openstack versions matching "~> 3.0"...
5- Installing terraform-provider-openstack/openstack v3.0.0...
6- Installed terraform-provider-openstack/openstack v3.0.0
7
8Terraform has been successfully initialized!

Step 2: Preview the Changes

terraform plan shows exactly what Terraform will create before it makes any changes. This is one of Terraform's most valuable features. You see the full list of resources that will be added, changed, or destroyed before anything happens:

1terraform plan
1Terraform will perform the following actions:
2
3 # openstack_compute_instance_v2.demo_instance will be created
4 + resource "openstack_compute_instance_v2" "demo_instance" {
5 + name = "terraform-demo-vm"
6 + image_name = "Ubuntu 24.04 LTS"
7 + flavor_name = "gen2.small"
8 + key_pair = "my-keypair"
9 + security_groups = ["terraform-demo-secgroup"]
10 ...
11 }
12
13 # openstack_networking_network_v2.demo_network will be created
14 + resource "openstack_networking_network_v2" "demo_network" {
15 + name = "terraform-demo-network"
16 ...
17 }
18
19 ...
20
21Plan: 9 to add, 0 to change, 0 to destroy.

Nine resources will be created: the network, subnet, router, router interface, security group, two security group rules, the instance, the floating IP, and the floating IP association.

Step 3: Apply

terraform apply executes the plan and creates all resources on InMotion Cloud:

1terraform apply
1openstack_networking_network_v2.demo_network: Creating...
2openstack_networking_secgroup_v2.demo_secgroup: Creating...
3openstack_networking_network_v2.demo_network: Creation complete after 3s
4openstack_networking_subnet_v2.demo_subnet: Creating...
5openstack_networking_secgroup_v2.demo_secgroup: Creation complete after 2s
6openstack_networking_secgroup_rule_v2.ssh: Creating...
7openstack_networking_secgroup_rule_v2.http: Creating...
8openstack_networking_subnet_v2.demo_subnet: Creation complete after 4s
9openstack_networking_router_v2.demo_router: Creating...
10openstack_networking_secgroup_rule_v2.ssh: Creation complete after 2s
11openstack_networking_secgroup_rule_v2.http: Creation complete after 2s
12openstack_networking_router_v2.demo_router: Creation complete after 5s
13openstack_networking_router_interface_v2.demo_router_interface: Creating...
14openstack_networking_router_interface_v2.demo_router_interface: Creation complete after 4s
15openstack_compute_instance_v2.demo_instance: Creating...
16openstack_compute_instance_v2.demo_instance: Still creating... [10s elapsed]
17openstack_compute_instance_v2.demo_instance: Creation complete after 18s
18openstack_networking_floatingip_v2.demo_fip: Creating...
19openstack_networking_floatingip_v2.demo_fip: Creation complete after 3s
20openstack_compute_floatingip_associate_v2.demo_fip_assoc: Creating...
21openstack_compute_floatingip_associate_v2.demo_fip_assoc: Creation complete after 2s
22
23Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
24
25Outputs:
26
27instance_ip = "173.231.195.160"

Terraform created the entire environment in under 45 seconds: a network, subnet, router, security group with rules, a virtual machine, and a public IP address.

Verifying the Result

After terraform apply completes, you can verify the resources exist in two ways.

From the command line, SSH into the new instance using the floating IP from the output:

1ssh ubuntu@173.231.195.160
1Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86_64)
2ubuntu@terraform-demo-vm:~$

From Horizon, navigate to Project > Compute > Instances. The terraform-demo-vm instance appears in the list with its assigned network and floating IP. Under Project > Network > Networks, the terraform-demo-network and terraform-demo-subnet are visible. Under Project > Network > Security Groups, the terraform-demo-secgroup shows the SSH and HTTP rules.

Every resource Terraform created is visible in Horizon, just as if you had created them manually. The difference is that the configuration file is the permanent record of what exists and why.

Why Repeatability Matters

The real power of Terraform is not creating infrastructure once. It is creating the same infrastructure reliably, every time.

Recreate After a Failure

If something goes wrong with your environment, you do not need to remember what you built or how you built it. The configuration file is the complete record. Destroy everything and rebuild:

1terraform destroy
2terraform apply

An identical environment is back in under a minute.

Duplicate for Another Team Member

A new developer joins the team and needs their own environment. They clone the repository, update one variable (their SSH key name), and run terraform apply. They get the same network topology, the same security rules, and the same instance configuration. No setup guide to follow. No steps to miss.

Promote Across Environments

Your development environment works. Now you need staging and production. Use Terraform variables to customize instance sizes and naming while keeping the same architecture:

1variable "environment" {
2 description = "Environment name (dev, staging, production)"
3 type = string
4 default = "dev"
5}
6
7variable "instance_flavor" {
8 description = "Instance size"
9 type = string
10 default = "gen2.small"
11}
12
13resource "openstack_compute_instance_v2" "app_server" {
14 name = "${var.environment}-app-server"
15 flavor_name = var.instance_flavor
16 # ... rest of configuration
17}

Run terraform apply -var="environment=staging" -var="instance_flavor=gen2.medium" and staging is live. The same configuration, scaled up, with a different name.

Track Every Change

Because the .tf files are plain text, they belong in version control alongside your application code. Every infrastructure change goes through a pull request. Team members review what will change before it happens. The git history shows who changed what, when, and why. This audit trail does not exist when changes are made by clicking through a dashboard.

Cleaning Up

When you are done with the demo environment, Terraform removes everything it created:

1terraform destroy
1openstack_compute_floatingip_associate_v2.demo_fip_assoc: Destroying...
2openstack_compute_floatingip_associate_v2.demo_fip_assoc: Destruction complete after 2s
3openstack_networking_floatingip_v2.demo_fip: Destroying...
4openstack_compute_instance_v2.demo_instance: Destroying...
5openstack_networking_floatingip_v2.demo_fip: Destruction complete after 3s
6openstack_compute_instance_v2.demo_instance: Still destroying... [10s elapsed]
7openstack_compute_instance_v2.demo_instance: Destruction complete after 12s
8openstack_networking_router_interface_v2.demo_router_interface: Destroying...
9openstack_networking_secgroup_v2.demo_secgroup: Destroying...
10openstack_networking_secgroup_v2.demo_secgroup: Destruction complete after 3s
11openstack_networking_router_interface_v2.demo_router_interface: Destruction complete after 5s
12openstack_networking_router_v2.demo_router: Destroying...
13openstack_networking_subnet_v2.demo_subnet: Destroying...
14openstack_networking_router_v2.demo_router: Destruction complete after 4s
15openstack_networking_subnet_v2.demo_subnet: Destruction complete after 3s
16openstack_networking_network_v2.demo_network: Destroying...
17openstack_networking_network_v2.demo_network: Destruction complete after 3s
18
19Destroy complete! Resources: 9 destroyed.

Every resource is gone. No orphaned networks, no forgotten security groups, no stale floating IPs consuming quota. Terraform tracks what it created and removes exactly those resources.

Where Terraform Fits in the Bigger Picture

Terraform handles the first stage of a modern cloud workflow: building the raw infrastructure. Once servers exist, other tools take over. Configuration management tools like Ansible install software and apply settings. Container tools like Docker package applications for consistent deployment. Orchestration platforms like Kubernetes manage those applications at scale.

Each tool solves one problem well. Terraform's job is to make sure the infrastructure under all of it is consistent, repeatable, and defined in code.

For a complete view of how these tools work together, see How Modern Applications Run on Cloud Infrastructure.

Related Resources

Related Articles

Read article

How to Configure Servers with Ansible

Automate server setup on InMotion Cloud with Ansible. This guide walks through installing Ansible, building an inventory from OpenStack instances, and writing reusable playbooks for repeatable, idempotent deployments.

Read article

How to Deploy a Kubernetes Cluster Using GitHub Actions on InMotion Cloud

Step-by-step guide to provisioning a Magnum Kubernetes cluster on InMotion Cloud using a pre-built GitHub Actions repository, with automatic HTTPS and WordPress or Drupal deployment.