Experimenting Locally with Terraform

Terraform supports a bunch of providers, but the vast majority of them are public cloud based.

However, you could set up a local VMware vSphere cluster and use the vSphere provider to interact with that to get you going. There's also a provider for OpenStack if you want to set up an OpenStack cluster.

Alternatively you could try using something like HPE's Eucalyptus which provides API compatibility with AWS, but on-premises.

That said, unless you already have a datacenter running VMware, all of those options are pretty awful and will take a lot of effort to get setup so you may be best waiting for your firewall to be opened up instead.

There isn't unfortunately a nice frictionless first party implementation of a VirtualBox provider but you could try this third-party VirtualBox provider.


While we wait for these network issues to be resolved, is there any way that I can experiment with Terraform locally without needing to connect to Azure or AWS? Perhaps with Virtual Box?

I use KVM to have as many virtual machines as I need to play around with anything, and I am now learning Terraform to, but with the specific intention of automating the creation of this VMs.

So if you are looking for a localhost playground to just learn Terraform, then KVM is a nice and more lightweight approach then using other heavy virtualization technologies, like the one you mentioned, Virtualbox.

If you want to learn Terraform while at same time you play with the AWS and Azure providers, then this is not a good solution for you, otherwise just give it a try.

Install KVM

First you need to install KVM on your Linux machine.

sudo apt update &&  sudo apt upgrade

Check for if the system support hardware virtualization:

$ egrep -c ‘(svm|vmx)’ /proc/cpuinfo
4

The output needs to be greater then 0, otherwise we need to enter into the Bios and enable VT technology.

Check if support KVM acelaration:

Installing the tool:

sudo apt install cpu-checker

Checking:

$ sudo kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used

Installing KVM

sudo apt install qemu qemu-kvm libvirt-bin  bridge-utils  virt-manager

Check is running:

service libvirtd status

If not running:

sudo service libvirtd start && sudo update-rc.d libvirtd enable

Another check:

sudo virsh -c qemu:///system list

How to Create VMs in KVM with Terraform

We will create the VMs by using this KVM Libvirt Provider.

mkdir playground && cd playground

Terraform main file

Create the file main.tf:

################################################################################
# ENV VARS
################################################################################

# https://www.terraform.io/docs/commands/environment-variables.html

variable "VM_COUNT" {
  default = 3
  type = number
}

variable "VM_USER" {
  default = "developer"
  type = string
}

variable "VM_HOSTNAME" {
  default = "vm"
  type = string
}

variable "VM_IMG_URL" {
  default = "https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img"
  type = string
}

variable "VM_IMG_FORMAT" {
  default = "qcow2"
  type = string
}

variable "VM_CIDR_RANGE" {
  default = "10.10.10.10/24"
  type = string
}


################################################################################
# PROVIDERS
################################################################################

# instance the provider
provider "libvirt" {
  uri = "qemu:///system"
}


################################################################################
# DATA TEMPLATES
################################################################################

# https://www.terraform.io/docs/providers/template/d/file.html

# https://www.terraform.io/docs/providers/template/d/cloudinit_config.html
data "template_file" "user_data" {
  template = file("${path.module}/cloud_init.cfg")
  vars = {
    VM_USER = var.VM_USER
  }
}

data "template_file" "network_config" {
  template = file("${path.module}/network_config.cfg")
}


################################################################################
# RESOURCES
################################################################################

resource "libvirt_pool" "vm" {
  name = "${var.VM_HOSTNAME}_pool"
  type = "dir"
  path = "/tmp/terraform-provider-libvirt-pool-ubuntu"
}

# We fetch the latest ubuntu release image from their mirrors
resource "libvirt_volume" "vm" {
  count  = var.VM_COUNT
  name   = "${var.VM_HOSTNAME}-${count.index}_volume.${var.VM_IMG_FORMAT}"
  pool   = libvirt_pool.vm.name
  source = var.VM_IMG_URL
  format = var.VM_IMG_FORMAT
}

# Create a public network for the VMs
resource "libvirt_network" "vm_public_network" {
   name = "${var.VM_HOSTNAME}_network"
   mode = "nat"
   domain = "${var.VM_HOSTNAME}.local"
   addresses = ["${var.VM_CIDR_RANGE}"]
   dhcp {
    enabled = true
   }
   dns {
    enabled = true
   }
}

# for more info about paramater check this out
# https://github.com/dmacvicar/terraform-provider-libvirt/blob/master/website/docs/r/cloudinit.html.markdown
# Use CloudInit to add our ssh-key to the instance
# you can add also meta_data field
resource "libvirt_cloudinit_disk" "cloudinit" {
  name           = "${var.VM_HOSTNAME}_cloudinit.iso"
  user_data      = data.template_file.user_data.rendered
  network_config = data.template_file.network_config.rendered
  pool           = libvirt_pool.vm.name
}

# Create the machine
resource "libvirt_domain" "vm" {
  count  = var.VM_COUNT
  name   = "${var.VM_HOSTNAME}-${count.index}"
  memory = "1024"
  vcpu   = 1

  cloudinit = "${libvirt_cloudinit_disk.cloudinit.id}"

  # TODO: Automate the creation of public network
  network_interface {
    network_id = "${libvirt_network.vm_public_network.id}"
    #network_id = "6d8e2494-835d-4baf-a14f-3a5c705febcc"
    #network_name = "vm_docker_network"
    network_name = "${libvirt_network.vm_public_network.name}"
  }

  # IMPORTANT
  # Ubuntu can hang is a isa-serial is not present at boot time.
  # If you find your CPU 100% and never is available this is why.
  #
  # This is a known bug on cloud images, since they expect a console
  # we need to pass it:
  # https://bugs.launchpad.net/cloud-images/+bug/1573095
  console {
    type        = "pty"
    target_port = "0"
    target_type = "serial"
  }

  console {
    type        = "pty"
    target_type = "virtio"
    target_port = "1"
  }

  disk {
    volume_id = "${libvirt_volume.vm[count.index].id}"
  }

  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}


################################################################################
# TERRAFORM CONFIG
################################################################################

terraform {
  required_version = ">= 0.12"
}

Env Vars

You can set env vars to override any default value for the variables defined in the ENV VARS section, but each env var name must be prefixed with TF_VAR:

export TF_VAR_VM_COUNT=5

Cloud Init

Create the file cloud_init.cfg:

#cloud-config
users:
  - name: ${VM_USER}
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: users, admin
    home: /home/${VM_USER}
    shell: /bin/bash
    ssh-authorized-keys:
      - ssh-rsa your-public-key-here
ssh_pwauth: True
disable_root: false
chpasswd:
  list: |
     ${VM_USER}:linux
  expire: False
package_update: true
package_upgrade: true
packages:
    - qemu-guest-agent
    - apt-transport-https
    - ca-certificates
    - curl
    - gnupg-agent
    - software-properties-common
    - zsh
growpart:
  mode: auto
  devices: ['/']
runcmd:
  - [ sh, -c, 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -' ]
  - [ sh, -c, 'sudo apt-key fingerprint 0EBFCD88']
  - [ sh, -c, 'sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"' ]
  - [ sh, -c, 'sudo apt update' ]
  - [ sh, -c, 'sudo apt install -y docker-ce docker-ce-cli containerd.io' ]
  - [ sh, -c, 'printf "\nalias dk=\"sudo docker\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dkc=\"sudo docker container\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dki=\"sudo docker image\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dks=\"sudo docker service\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dkn=\"sudo docker node\"\n" >> /home/${VM_USER}/.bashrc' ]

NOTE: Add your public key into the file

Network Config

Create the file network_config.cfg:

version: 2
ethernets:
  ens3:
     dhcp4: true

Run Terraform

To init Terraform:

terraform init

Create the VMs:

terraform apply

VMSs IP Addresses

Get the ips for each VM:

virsh net-dhcp-leases vm_network

NOTE: The VMs we created will take some seconds until they get their ips, thus you may need to repeat this command several times until you get the ips.

Now just SSH into one of the VMs with the ip address you got from virsh:

ssh developer@vm-ip-address

Have fun and enjoy your new playground.

Tags:

Terraform