Infrastructure as a Code using Terraform: AWS, vSphere

Terraform AWS vSphere

I got introduced to Terraform a few months back when trying to solve a problem in the AWS cloud. Since then I have had discussions with multiple other Automation and DevOps professionals in other companies, and the common problem everyone seems to have is Infrastructure Provisioning and Management. This task especially becomes exceedingly cumbersome to tackle with other Enterprise priorities. There are a number of popular solutions out there, but Terraform seems to be very popular and gaining adoption quite rapidly.

Though it's still very new, it is quite powerful. As a personal experience, using Terraform with AWS was very easy, but I cannot say the same thing about using its vSphere provider, which is maybe also because of the fact that AWS has many defaults configured, which vSphere does not. Plus, AWS also has its own AWS CloudFormation, which is awesome! vSphere, unfortunately, does not seem to have any of their own service or tool which allows users to manage their Infrastructure as a Code.

In this post, I will go over the basic configuration, and share my opinions of what I perceive of Terraform. If you would like to, you are welcome to download the code/configuration files from my GitHub repo. They should help you get started.

There is a variety of cool stuff you can do with Terraform: define inputs/outputs, specify resource dependencies, push redundant configurations to modules, etc. I will only go over the very basic stuff here.

What is Terraform?

Terraform is an infrastructure-as-code software developed by HashiCorp. It allows users to define a data center infrastructure in a very high-level configuration language, HCL in this case. With Terraform, you can define your entire infrastructure in configuration files and deploy it using a few commands. Bringing it down is also just as easy.
The cool thing is that you can also commit the file in your version control and track the changes. :-)

Terraform Install:

Installing terraform is extremely easy! All you have to do is download the package, unzip it, and add the folder location of the executable to your path. For details, you may refer to the product page.

Build Infrastructure:

Building infrastructure using Terraform is a three-step process:


There are mainly two types of files when working with Terraform:

  • terraform.tfvars: This file is used to store all the variables of the configuration. The values set in this file override the settings for variables with the same name in the .tf files. This file is usually used to store protected information like provider credentials, server information, etc., and not checked in the version control. Say, if you are using git, your .gitignore file should have ‘terraform.tfvars’
  • * .tf : This is the file that actually contains all the resources. You may have one or more files, which allows you to break down sections of infrastructure(e.g. containing all server VM resources, containing VM resources for client machines, Linux/Windows client configuration, etc.), and maintain each in a separate file. You can define/configure your provider, variables, data, resources, etc. in these files.

There are two main sets of configuration:

Configuring the provider to use:

This is usually done by specifying which Cloud you want to deploy on, and the credentials to authenticate for the same. Note in the example below, that the configuration is provider specific.
As an example, for AWS:

provider "aws" {
access_key = "${var.AWS_ACCESS_KEY}"
secret_key = "${var.AWS_SECRET_KEY}"
region = "${var.AWS_REGION}"

To authenticate with vSphere, it would be like:

provider "vsphere" {
vsphere_server = "${var.vsphere_vcenter}"
user = "${var.vsphere_user}"
password = "${var.vsphere_password}"
allow_unverified_ssl = "${var.vsphere_unverified_ssl}"

Configuring what resources you want to deploy:

I have seen a lot of people mention on multiple occasions that Terraform is ‘Cloud Agnostic’, but I don’t quite agree with that. I think this statement is false because you need an understanding of how the cloud provider works, its pre-requisites, etc when you are using Terraform. Note that Terraform only provides a common interface to deploy resources.

Let's consider a simple example of deploying a VM.

AWS: When you are deploying an EC2 instance from an AMI in your account, by default it goes in the default VPC, default subnet, uses the default NACL, default SG, gets its IP and Hostname assigned automatically. Since there are a lot of defaults, deploying a VM that can connect to the internet requires very little custom configuration. The resource for such an instance will look like this in Terraform HCL:

resource "amol_aws_instance" "test-ec2-instance" {
ami = "${data.ami_id}"
instance_type = "${data.instance_type}"
# Note that here the values for confuguration are getting pulled from data objects/blocks

vSphere: In vSphere environment, there are very few defaults, and you have to provide appropriate values to configure your VM. There is nothing like a default network, default host, etc. You may be able to build that for yourself by implementing a modular design, but I will not go through that here. The resource for a VM in vSphere will look like this in Terraform HCL:

resource "vsphere_virtual_machine" "amol-vm" {
# VM placement #
name = "${data.vm_name}"
resource_pool_id = "${}"
datastore_id = "${}"

# Guest OS #
guest_id = "${data.vsphere_virtual_machine.template.guest_id}"

# VM storage #
disk {
label = "${data.vm_name}.vmdk"
size = "${data.vsphere_virtual_machine.template.disks.0.size}"
thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}"
eagerly_scrub = "${data.vsphere_virtual_machine.template.disks.0.eagerly_scrub}"

# VM networking #
network_interface {
network_id = "${}"
adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}"

# Customization of the VM #
clone {
template_uuid = ${}"

# Customization is required to set hostname, IP.
customize {
linux_options {
host_name = "${data.vm_name}"
time_zone = "${var.vsphere_time_zone}"
domain = "${var.vsphere_domain}"

network_interface {
ipv4_address = "${var.vsphere_ipv4_address}"

# this will to allow to specify multiple values for dns servers
dns_server_list = "${split("-", var.vsphere_dns_servers)}"
dns_suffix_list = ["${var.vsphere_domain}"]

# Note that here the values for confuguration are getting pulled from data objects/blocks


Terraform uses a plugin-based architecture to support the numerous infrastructure and service providers available. As of Terraform version 0.10.0, each “Provider” is its own encapsulated binary distributed separately from Terraform itself.

The ‘init’ command will automatically download and install any provider binary for the providers in use within the configuration.

terraform init

If your provider configuration is for AWS (provider “aws”), then it will download the plugin and authenticate with AWS. It will do the same thing, if it's a different provider, say vSphere (provider “vsphere”).

Apply Changes:

This and the next part are what I like the most. After you have set up your configuration and build the infrastructure, Terraform is able to track your resources. If you were using scripting before, you know how much pain it was to keep track of and manage the resources, or even delete them. Terraform tracks the changes you have made, and is also able to bring down your entire infrastructure using a single command.

If you want to see what changes in the configuration will be applied to your infrastructure, use the command:

terraform plan

To apply the changes to your infrastructure, use the ‘apply’ command. This will create the infrastructure you have specified in your configuration files. If there are changes made to existing infrastructure in your configuration files, ‘apply’ will update your infrastructure accordingly.

terraform apply 

Once deployed, you can see your deployment using the ‘show’ command:

terraform show 

Once the deployment is done, a .tfstate file is created. This is a state file is extremely important; it keeps track of the IDs of created resources so that Terraform knows what it is managing.

Break Down:

To break down or destroy the infrastructure you have deployed, you can use the ‘destroy’ command. That’s it, no need to write a script to find VMs with specific names, in any folder locations, having any tags, etc.

terraform destroy

Learning Terraform has been a great experience! I can definitely see a lot of benefits to moving to Infrastructure as Code model. In this post, I have only talked about an example of using a pre-existing VM template, but Terraform can also be coupled with Packer (another tool developed by HashiCorp)to create VM images/templates that you can use to deploy from.

In case you wish to access the original source code files, please refer to my GitHub repo. I hope you enjoyed the post and were able to find the information you were looking for! :-)

Look forward to waking up every day to an interesting challenge!