How to Use Hashicorp Terraform with OpenStack

Terraform is a tool from HashiCorp that can be used to deploy and manage cloud infrastructure easily by defining configuration files. It is similar to OpenStack Heat. However, unlike Heat which is specific to OpenStack, Terraform is provider agnostic and can work with multiple cloud platforms such as OpenStack, AWS and VMware. This blog will give an overview of how to use Terraform with OpenStack.

Users define configuration in files that Terraform processes to create and manage infrastructure resources across multiple cloud providers — from physical and virtual servers, network switches, to containers and DNS providers. Terraform generates an execution plan describing what it will do to reach the desired state, and then executes it to build the described infrastructure. As the configuration changes, Terraform is able to determine what changed and create incremental execution plans which can be applied. Built-in dependency resolution means things happen in the right order.

Terraform allows you to effortlessly combine multiple system providers with your own or with each other. Terraform is agnostic to the underlying platforms by supporting providers. Providers generally are an IaaS such as OpenStack, PaaS such as Heroku, or SaaS services such as CloudFlare. For example, the OpenStack provider is used to interact with the many resources supported by OpenStack. The provider needs to be configured with the proper credentials before it can be used. Terraform can also be extended to support custom providers and resources to manage infrastructure that Terraform doesn’t support out of the box.

Terraform supports OpenStack Compute resources such as Instance, Floating i/p, Key Pair, Security Group and Server Group. It supports OpenStack Network resources such as Network, Subnet, Router and Router interface, Floating i/p.  For Block Storage it supports Volume and for Object Storage it supports Containers. The complete list of supported OpenStack resources and attributes is available in the Terraform documentation.

Below we describe an overview of the steps to deploy OpenStack resources using Terraform.

Setup

  • Download and extract the Terraform zip file from this location
  • Update PATH variables to include the Terraform dir

Terraform concepts

  • Terraform configuration files are saved as *.tf files
  • Resources representinfrastructure components  e.g. server, virtual machine, container, network port.
  • Providers are components that let you manage a resource on a particular platform e.g. OpenStack, AWS, GCE
  • Provisioners provide the ability to execute commands e.g. copy a file into the VM

Using Terraform with Platform9

  • Get the OpenStack environment variables from the Platform9 controller.
    • Available in ‘Access and Security’ → ‘API Access
    • Set these variables in your environment
  • Describe your infrastructure as a Terraform configuration file
  • Execute it using Terraform commands
    • terraform apply – Applies infrastructure configuration as per loaded/discovered terraform files.
    • terraform destroy – Destroys infrastructure as per the configuration file
    • terraform graph – Creates a visual graph of the resources
    • terraform plan – Generates and shows an execution plan

Configuration file details

Based on the OpenStack example hosted  at github, the files of interest are variable.tf

variable "image" {
default = "Ubuntu 14.04"
}

variable "flavor" {
default = "m1.small"
}

variable "ssh_key_file" {
default = "~/.ssh/id_rsa.terraform"
}

variable "ssh_user_name" {
default = "ubuntu"
}

variable "external_gateway" {
}

variable "pool" {
default = "public"
}

The variable.tf file specifies all the variables a user can provide while applying Terraform resources on their infrastructure. They can have default values and can be provided at the CLI during command execution. Note that these are referred to in the main execution Terraform script using the representation “${var.}”. For example “${var.external_gateway}”
main.tf

esource "openstack_compute_keypair_v2" "terraform" {
name = "terraform"
public_key = "${file("${var.ssh_key_file}.pub")}"
}

resource "openstack_networking_network_v2" "terraform" {
name = "terraform"
admin_state_up = "true"
}

resource "openstack_networking_subnet_v2" "terraform" {
name = "terraform"
network_id = "${openstack_networking_network_v2.terraform.id}"
cidr = "10.0.0.0/24"
ip_version = 4
dns_nameservers = ["8.8.8.8","8.8.4.4"]
}

resource "openstack_networking_router_v2" "terraform" {
name = "terraform"
admin_state_up = "true"
external_gateway = "${var.external_gateway}"
}

resource "openstack_networking_router_interface_v2" "terraform" {
router_id = "${openstack_networking_router_v2.terraform.id}"
subnet_id = "${openstack_networking_subnet_v2.terraform.id}"
}

resource "openstack_compute_secgroup_v2" "terraform" {
name = "terraform"
description = "Security group for the Terraform example instances"
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}

rule {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}

rule {
from_port = -1
to_port = -1
ip_protocol = "icmp"
cidr = "0.0.0.0/0"
}
}

resource "openstack_compute_floatingip_v2" "terraform" {
pool = "${var.pool}"
depends_on = ["openstack_networking_router_interface_v2.terraform"]
}

resource "openstack_compute_instance_v2" "terraform" {
name = "terraform"
image_name = "${var.image}"
flavor_name = "${var.flavor}"
key_pair = "${openstack_compute_keypair_v2.terraform.name}"
security_groups = [ "${openstack_compute_secgroup_v2.terraform.name}" ]
floating_ip = "${openstack_compute_floatingip_v2.terraform.address}"
network {
uuid = "${openstack_networking_network_v2.terraform.id}"
}

provisioner "remote-exec" {
connection {
user = "${var.ssh_user_name}"
key_file = "${var.ssh_key_file}"
}

inline = [
"sudo apt-get -y update",
"sudo apt-get -y install nginx",
"sudo service nginx start"
]
}
}

output "address" {
value = "${openstack_compute_floatingip_v2.terraform.address}"
}

The main.tf file provides the steps of what your infrastructure should look like. The line resource “openstack_compute_instance_v2” “terraform” creates a compute instance resource named “terraform”using the OpenStack provider. The properties specified in the following block are passed in as arguments to the instance creation step.
Each resource exposes a set of variables that can be referred to in other resource configurations. In the above file, notice that the value of ${openstack_compute_secgroup_v2.terraform.name} is determined by the completion of  the step resource “${openstack_compute_secgroup_v2.terraform.name}”

After the complete execution, attributes of interest to the end user can be output on the CLI using the ‘output‘ block. In the above case, the attribute ‘address‘ is output which will be the value of floating IP address of the instance Terraform created.

To make development easier, the builtin/providers/openstack/devstack/deploy.sh script will assist in installing and configuring a standardized DevStack environment along with Golang, Terraform, and all development dependencies. It will also set the required environment variables in the devstack/openrc file.

Summary

Terraform allows representing infrastructure as code and eases the process of infrastructure automation. It works across multiple cloud platforms by providing higher abstraction to managing infrastructure. This is achieved by wrapping the APIs into Terraform providers and provisioners.

While Terraform supports OpenStack, only a limited set of resources are supported out-of the box. These are sufficient to perform usual tasks such as spinning up a VM and attaching it to a network and storage. For advanced tasks, custom handlers will need to be created to support other OpenStack resources.

Paavan Shanbhag

You may also enjoy

Tackling Kubernetes Underutilization: Cutting EKS Costs by 50%

By Kamesh Pemmaraju

How Platform9 Uses OpenStack Ironic to Manage Bare Metal as a Service

By Platform9

The browser you are using is outdated. For the best experience please download or update your browser to one of the following: