Using Terraform to provision VMs

Terraform

Terraform is one of the most famous IaaC (Infrastructure as a code) tools that is widely used to provision resources on top of well known cloud providers.

The idea of this example is to create the following:

  • A router connected to the public gateway
  • A network to have the VMs in
  • A subnet in the created network
  • A keypair to allow SSH
  • 2 VMs (frontend and backend)
  • 2 floating IPs
  • Multiple security groups

Steps:

  1. Installation: Follow this Guide to install Terraform on the target OS

  2. Create a directory to contain the terraform (.tf) files mkdir bluvalt-terraform

  3. Create a terraform file for configs touch configs.tf

  4. Add the following snippet to configs.tf:

    terraform {
      required_providers {
        openstack = {
          source = "terraform-provider-openstack/openstack"
          version = "~> 1.48.0"
        }
      }
    }
    
    provider "openstack" {
      user_name           = "<Your Username>"
      tenant_name         = "<Your Tenant Name>"
      password            = "<Your Password>"
      auth_url            = "https://api-<Cloud Domain>-vdc.bluvalt.com/identity/v3"
      user_domain_name    = "<Cloud Domain>"
      project_domain_name = "<Cloud Domain>"
    }
    

    To access STC Jeddah Cloud JED1, replace withjed1To access STC Riyadh2 Cloud RUH2, replace withruh2`

  5. Create a terraform file for resources touch resources.tf

  6. Add the following snippet to resources.tf:

    data "openstack_images_image_v2" "ubuntu" {
        name        = "Ubuntu-18.04-LTS"
        most_recent = true
    }
    
    data "openstack_networking_network_v2" "public" {
        name = "Public_Network"
    }
    
    resource "openstack_compute_keypair_v2" "terraform-keypair" {
        name = "terraform-keypair"
    }
    
    data "openstack_compute_flavor_v2" "r1-generic-8" {
        name = "R1-Generic-8"
    }
    
    resource "openstack_networking_network_v2" "terraform-network" {
        name           = "terraform-network"
        admin_state_up = "true"
    }
    
    resource "openstack_networking_subnet_v2" "subnet-1" {
        network_id = "${openstack_networking_network_v2.terraform-network.id}"
        cidr       = "10.1.0.0/16"
        ip_version = 4
    }
    
    resource "openstack_networking_router_v2" "terraform-router" {
        name                = "terraform-router"
        external_network_id = "${data.openstack_networking_network_v2.public.id}"
    }
    
    resource "openstack_networking_router_interface_v2" "router-subnet-interface" {
        router_id = "${openstack_networking_router_v2.terraform-router.id}"
        subnet_id = "${openstack_networking_subnet_v2.subnet-1.id}"
    }
    
    resource "openstack_compute_floatingip_v2" "backend" {
        pool = "Public_Network"
    }
    
    resource "openstack_compute_floatingip_v2" "frontend" {
        pool = "Public_Network"
    }
    
    resource "openstack_networking_secgroup_v2" "allow-http-https" {
        name        = "allow-http-https"
        description = "HTTP HTTPS security group"
    }
    resource "openstack_networking_secgroup_rule_v2" "allow-http" {
        direction         = "ingress"
        ethertype         = "IPv4"
        protocol          = "tcp"
        port_range_min    = 80
        port_range_max    = 80
        remote_ip_prefix  = "0.0.0.0/0"
        security_group_id = "${openstack_networking_secgroup_v2.allow-http-https.id}"
    }
    
    resource "openstack_networking_secgroup_rule_v2" "allow-https" {
        direction         = "ingress"
        ethertype         = "IPv4"
        protocol          = "tcp"
        port_range_min    = 443
        port_range_max    = 443
        remote_ip_prefix  = "0.0.0.0/0"
        security_group_id = "${openstack_networking_secgroup_v2.allow-http-https.id}"
    }
    
    resource "openstack_networking_secgroup_v2" "allow-ssh" {
        name        = "allow-ssh"
        description = "SSH security group"
    }
    
    resource "openstack_networking_secgroup_rule_v2" "allow-ssh" {
        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.allow-ssh.id}"
    }
    
    resource "openstack_compute_instance_v2" "backend" {
        name            = "backend"
        flavor_id       = "${data.openstack_compute_flavor_v2.r1-generic-8.id}"
        key_pair        = "terraform-keypair"
        security_groups = ["allow-ssh", "allow-http-https"]
        block_device {
            uuid                  = "${data.openstack_images_image_v2.ubuntu.id}"
            source_type           = "image"
            destination_type      = "volume"
            boot_index            = 0
            volume_size           = 20
            delete_on_termination = true
        }
        network {
            name = "${openstack_networking_network_v2.terraform-network.name}"
        }
    }
    
    resource "openstack_compute_floatingip_associate_v2" "backend" {
        floating_ip = "${openstack_compute_floatingip_v2.backend.address}"
        instance_id = "${openstack_compute_instance_v2.backend.id}"
        fixed_ip    = "${openstack_compute_instance_v2.backend.network.0.fixed_ip_v4}"
    }
    
    resource "openstack_compute_instance_v2" "frontend" {
        name            = "frontend"
        flavor_id       = "${data.openstack_compute_flavor_v2.r1-generic-8.id}"
        key_pair        = "terraform-keypair"
        security_groups = ["allow-ssh", "allow-http-https"]
        block_device {
            uuid                  = "${data.openstack_images_image_v2.ubuntu.id}"
            source_type           = "image"
            destination_type      = "volume"
            boot_index            = 0
            volume_size           = 20
            delete_on_termination = true
        }
        network {
            name = "${openstack_networking_network_v2.terraform-network.name}"
        }
    }
    
    resource "openstack_compute_floatingip_associate_v2" "frontend" {
        floating_ip = "${openstack_compute_floatingip_v2.frontend.address}"
        instance_id = "${openstack_compute_instance_v2.frontend.id}"
        fixed_ip    = "${openstack_compute_instance_v2.frontend.network.0.fixed_ip_v4}"
    }
    
    

    `

  7. Run terraform init to prepare for provisioning.

  8. Run terraform plan to see the execution plan and what resources are going to be created.

  9. Run terraform apply to start the provisioning and you will prompt for confirmation.

For more details about other resources in terraform, please refer to this Document