OpenStack is an open-source cloud computing platform that enables you to build your very own private cloud that is completely under your control. In this six-part guide, Jay will guide you through the finer points of OpenStack with hands-on examples. In the sixth and final episode, we’ll wrap up the series with a look at how to automate OpenStack with Terraform and Ansible.
Thanks to OpenMetal for sponsoring this series! Check out their awesome OpenStack service here.
Video Notes and Commands
Here, you’ll find some of the commands and code that was used in the video.
First Example
example.tf
# Select openstack as our provider
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "1.48.0"
}
}
}
# Configure the openstack provider
provider "openstack" {
user_name = "<openstack_username>"
tenant_name = "TerraformSandbox"
password = "<Password Here>"
auth_url = "http://<openstack_ip>:5000"
region = "iad3"
insecure = true
}
# Create a compute instance
resource "openstack_compute_instance_v2" "web-server" {
name = "web-server"
image_id = "31862507-0d3f-4f5f-98cf-f3ca3fb10c94"
flavor_id = "gp1.medium"
key_pair = "<key_pair_name>"
security_groups = ["default"]
network {
name = "External"
}
}
Second Example
With the second example, not much has actually changed. Functionally, it’s no different than the first example. But the benefit here is that the previous example was split into two files, in order to make the solution easier to maintain.
compute_instance.tf
resource "openstack_compute_instance_v2" "web-server" {
name = "web-server"
image_id = "<distribution_image_id>"
flavor_id = "gp1.medium"
key_pair = "terraform_testing"
security_groups = ["default"]
network {
name = "External"
}
}
provider.tf
# Select openstack as our provider
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "1.48.0"
}
}
}
# Configure the openstack provider
provider "openstack" {
user_name = "<user>
tenant_name = "TerraformSandbox"
password = "<user_password>"
auth_url = "http://<ip_address>:5000"
region = "iad3"
insecure = true
}
Third Example
The third example introduces variables, which helps us organize our Terraform files a little better.
compute_instance.tf
resource "openstack_compute_instance_v2" "web-server" {
name = "web-server"
image_id = "<distribution_image_id>"
flavor_id = "gp1.medium"
key_pair = "terraform_testing"
security_groups = ["default"]
network {
name = "External"
}
}
provider.tf
# Select openstack as our provider
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "1.48.0"
}
}
}
# Configure the openstack provider
provider "openstack" {
auth_url = var.openstack_auth_url
insecure = true
password = var.openstack_user_password
region = var.openstack_region
tenant_name = var.openstack_project
user_name = var.openstack_user
}
variables.tf
variable "openstack_auth_url" {
type = string
default = "http://<ip_address>:5000"
}
variable "openstack_project" {
type = string
default = "OpenShift"
}
variable "openstack_region" {
type = string
default = "iad3"
}
variable "openstack_user" {
type = string
default = "<username>"
}
variable "openstack_user_password" {
type = string
default = "<password>"
}
Fourth Example
This time around, we’re going to have Terraform set up a security group for us in addition to the compute instance.
compute_instance.tf
resource "openstack_compute_instance_v2" "web-server" {
name = var.web_server_name
image_id = var.web_server_image
flavor_id = var.web_server_flavor
key_pair = var.openstack_user_key
security_groups = ["${openstack_compute_secgroup_v2.allow_home_office.name}"]
network {
name = "External"
}
}
provider.tf
# Select openstack as our provider
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "1.48.0"
}
}
}
# Configure the openstack provider
provider "openstack" {
auth_url = var.openstack_auth_url
insecure = true
password = var.openstack_user_password
region = var.openstack_region
tenant_name = var.openstack_project
user_name = var.openstack_user
}
security_group.tf
resource "openstack_compute_secgroup_v2" "allow_home_office" {
name = "allow_home_office"
description = "Allow traffic from local office to OpenStack."
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = var.administrator_ip
}
rule {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr = var.administrator_ip
}
}
variables.tf
variable "administrator_ip" {
type = string
default = "<your_public_ip>/32"
}
variable "openstack_auth_url" {
type = string
default = "http://<ip_address>:5000"
}
variable "openstack_project" {
type = string
default = "TerraformSandbox"
}
variable "openstack_region" {
type = string
default = "iad3"
}
variable "openstack_user" {
type = string
default = "<user>"
}
variable "openstack_user_password" {
type = string
default = "<password>"
}
variable "openstack_user_key" {
type = string
default = "terraform_testing"
}
variable "web_server_name" {
type = string
default = "web-server-1"
}
variable "web_server_image" {
type = string
default = "<distribution_image_id>"
}
variable "web_server_flavor" {
type = string
default = "gp1.medium"
}
Fifth Example
With the fifth and final example, we add Ansible to the mix! After Terraform finishes creating the instance, Ansible will step in and provision the rest. As a proof of concept, we’ll have Ansible install Apache on the created instance.
ansible.cfg
[defaults]
host_key_checking = False
apache.yml
---
- name: Apache Playbook
hosts: all
become: yes
tasks:
- name: Install all available updates
apt:
upgrade: dist
update_cache: yes
- name: Install Apache
apt:
name: apache2
state: latest
compute_instance.tf
resource "openstack_compute_instance_v2" "web-server" {
name = var.web_server_name
image_id = var.web_server_image
flavor_id = var.web_server_flavor
key_pair = var.openstack_user_key
security_groups = ["${openstack_compute_secgroup_v2.allow_home_office.name}"]
network {
name = "External"
}
# Run an Ansible playbook against the new instance
provisioner "local-exec" {
command = "ansible-playbook -u root -i '${self.access_ip_v4},' -e 'ansible_python_interpreter=/usr/bin/python3' --private-key=~/.ssh/terraform.pem apache.yml"
}
}
provider.tf
# Select openstack as our provider
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "1.48.0"
}
}
}
# Configure the openstack provider
provider "openstack" {
auth_url = var.openstack_auth_url
insecure = true
password = var.openstack_user_password
region = var.openstack_region
tenant_name = var.openstack_project
user_name = var.openstack_user
}
security_group.tf
resource "openstack_compute_secgroup_v2" "allow_home_office" {
name = "allow_home_office"
description = "Allow traffic from local office to OpenStack."
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = var.administrator_ip
}
rule {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr = var.administrator_ip
}
}
variables.tf
variable "administrator_ip" {
type = string
default = "<public_ip_address>/32"
}
variable "openstack_auth_url" {
type = string
default = "http://<ip_address>:5000"
}
variable "openstack_project" {
type = string
default = "OpenShift"
}
variable "openstack_region" {
type = string
default = "iad3"
}
variable "openstack_user" {
type = string
default = "<username>"
}
variable "openstack_user_password" {
type = string
default = "<password>"
}
variable "openstack_user_key" {
type = string
default = "TerraformKey"
}
variable "web_server_name" {
type = string
default = "web-server-1"
}
variable "web_server_image" {
type = string
default = "<distribution_flavor_image>"
}
variable "web_server_flavor" {
type = string
default = "gp1.medium"
}