How to Manage OpenStack Private Clouds Episode 6 – Automating Cloud Deployments

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.

YouTube player

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"
}