Can Terraform be used to provision on-premises infrastructure?

Yes, Terraform can be used to provision on-premises servers, but there are some differences compared to how it manages cloud resources. While Terraform can directly interact with the APIs of many cloud providers to manage their resources, it does not have direct access to an API for your on-premises infrastructure.

To provision on-premises servers, you would likely need to use an abstraction layer that Terraform can interface with. Here are a few examples which you can see in the table of content:

Table of Content

  1. Virtualization Platforms(VMware, vSphere)
  2. Bare Metal Servers with a Management Layer
  3. Containers and Kubernetes
  4. Configuration Management Tools(Ansible, Chef, Puppet, or SaltStack)
  5. Conclusion

1. Virtualization Platforms(VMware, vSphere)

VMware vSphere is a popular virtualization platform used in many on-premises environments. Terraform can interact with vSphere through its vSphere provider, which allows you to create, read, update, and delete various vSphere resources such as -

  • Virtual machines
  • Datastores
  • Networks

Here's an example of how you might use Terraform to create a virtual machine in vSphere:

Step 1: Configure the vSphere Provider

This is where you tell Terraform how to connect to your vSphere vCenter server. Just pop in your user and password details like so:

 1# maint.tf
 2
 3provider "vsphere" {
 4  user           = "your-username"
 5  password       = "your-password"
 6  vsphere_server = "vcenter.your-domain.com"
 7
 8  # If your cert is self-signed, you might want to include this:
 9  allow_unverified_ssl = true
10}
11 

Step 2: Map Out Your Environment

You'll likely need to define some data sources so Terraform knows more about your vSphere environment. For instance, it might need to know the ID of a datacenter or a template to clone.

 1# datasource.tf
 2
 3data "vsphere_datacenter" "dc" {
 4  name = "dc1"
 5}
 6
 7data "vsphere_datastore" "datastore" {
 8  name          = "datastore1"
 9  datacenter_id = "${data.vsphere_datacenter.dc.id}"
10}
11
12data "vsphere_network" "network" {
13  name          = "public"
14  datacenter_id = "${data.vsphere_datacenter.dc.id}"
15}
16
17data "vsphere_virtual_machine" "template" {
18  name          = "centos-7-template"
19  datacenter_id = "${data.vsphere_datacenter.dc.id}"
20}

Step 3: Define Your Virtual Machine

Here's the fun part - setting up your virtual machine! You'll tell Terraform -

  1. How many CPUs you want
  2. How much memory
  3. Which network to connect to and other important details.
 1# vm_infra.tf
 2
 3resource "vsphere_virtual_machine" "vm" {
 4  name             = "terraform-test"
 5  resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
 6  datastore_id     = "${data.vsphere_datastore.datastore.id}"
 7
 8  num_cpus = 2
 9  memory   = 1024
10  guest_id = "${data.vsphere_virtual_machine.template.guest_id}"
11
12  network_interface {
13    network_id = "${data.vsphere_network.network.id}"
14  }
15
16  disk {
17    label            = "disk0"
18    size             = "${data.vsphere_virtual_machine.template.disks.0.size}"
19    thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}"
20  }
21
22  clone {
23    template_uuid = "${data.vsphere_virtual_machine.template.id}"
24
25    customize {
26      linux_options {
27        host_name = "terraform-test"
28        domain    = "yourdomain.com"
29      }
30
31      network_interface {
32        ipv4_address = "10.0.0.10"
33        ipv4_netmask = 24
34      }
35
36      ipv4_gateway = "10.0.0.1"
37    }
38  }
39}
40 

The above example will create a clone of an existing VM template, and set it up on a specific network.


Step 4: Provision Terraform infra

Once you've set everything up, fire up your terminal and use the following terraform commands -

  1. terraform init - To get Terraform ready to go and initialize the VMware vSphere provider
  2. terraform plan - To see exactly what changes Terraform will make
  3. terraform apply - If you're happy with the plan, use terraform apply to make it happen.

Just a friendly reminder to replace placeholders in the code (like "your-username", "your-password", "vcenter.your-domain.com", "dc1", etc.) with your actual vSphere environment details.

And always remember to handle your sensitive info (like passwords) with care. Using environment variables or input variables is a secure way to pass these values to your Terraform configuration.

Dealing with vSphere environments in Terraform can be a bit tricky, so be sure to spend some time with the vSphere provider documentation. It's a well of knowledge and will help you get the most out of Terraform and vSphere.


2. Bare Metal Servers with a Management Layer

Let's dive into how we can use Terraform with bare metal servers using an approach that involves a management layer, with MAAS (Metal as a Service) as an example.

MAAS is a fantastic tool that provides a way to manage bare metal servers as if they were virtual machines in the cloud. It gives you an API to interact with, and that's the sweet spot Terraform needs to work its magic.

First thing to know is, unfortunately, there is no official MAAS provider for Terraform. But don't worry, the open source community has our back. There's a third-party MAAS provider you can use.

You can use this Terraform provider for MAAS to define your infrastructure in a Terraform configuration file. You'll define resources just like you would for any other kind of infrastructure, and then use the Terraform commands to create, update, or destroy resources as needed.

Here's a sample configuration that might give you an idea:

Step 1: Configure the MAAS Provider

You'll start off by telling Terraform how to connect to your MAAS server.

1#main.tf
2# maas provider
3
4provider "maas" {
5  api_version = "2.0"
6  api_url     = "http://192.168.1.1:5240/MAAS"
7  api_key     = "your-api-key"
8}

Step 2: Define a Resource

Next, let's create a machine using Terraform. You'll be defining the machine parameters here.

 1#Create resource in maas
 2
 3resource "maas_instance" "server1" {
 4  hostname      = "server1"
 5  domain        = "local"
 6  architecture  = "amd64/generic"
 7  min_cpu_count = 2
 8  min_mem_mb    = 1024
 9
10  interface {
11    name   = "eth0"
12    subnet = "192.168.1.0/24"
13  }
14
15  storage {
16    id    = 0
17    size  = "20G"
18    mount = "/"
19  }
20}

This configuration tells MAAS to provision -

  1. New server with at least 2 CPUs and 1GB of memory
  2. It should also have an interface connected to the specified subnet and a storage device with at least 20GB of space mounted at "/"

Step 4: Provision Terraform infra

Once you have this configuration in place, you're ready to let Terraform do its thing. Run the following terraform command -

  1. terraform init to initialize your configuration, and
  2. terraform apply to create your resources.

Keep in mind, though, that working with bare metal infrastructure has its unique challenges compared to cloud-based infrastructure. For instance, 'destroying' a bare metal server is a physical task that can't be undone with a command.

Remember to replace the placeholders in the above example with your actual MAAS environment details. And as always, treat your sensitive information (like API keys) with the utmost care.

Also, be aware that while the Terraform provider for MAAS exists, it's not officially supported or maintained by HashiCorp or Canonical (the company behind MAAS). You might encounter bugs or limitations, and updates might not come as frequently as for official providers.

Don't forget to check out the provider's documentation and resources available on GitHub or other platforms for more detailed information and community support.


3. Containers and Kubernetes

Imagine you're running a Kubernetes cluster on your on-premises infrastructure. Terraform can be your best buddy here.

With its Kubernetes provider, Terraform can help manage resources within your cluster. Let's run through how that might look.

Step 1: Configure the Kubernetes Provider

Kick things off by setting up the Kubernetes provider in your Terraform configuration file. You're essentially telling Terraform how to chat with your Kubernetes cluster.

Here's how it could look:

 1# main.tf
 2
 3provider "kubernetes" {
 4  host     = "https://your-k8s-cluster-endpoint:port"
 5  username = "your-username"
 6  password = "your-password"
 7
 8  client_certificate     = "${file("~/.kube/client-cert.pem")}"
 9  client_key             = "${file("~/.kube/client-key.pem")}"
10  cluster_ca_certificate = "${file("~/.kube/cluster-ca-cert.pem")}"
11}

You're specifying the endpoint of your Kubernetes cluster and credentials to connect to it. Remember to replace the placeholders with your actual details.


Step 2: Define a Kubernetes Resource

Next, let's define a Kubernetes resource. How about a simple nginx deployment?

 1# nginx deployment inside 
 2
 3resource "kubernetes_deployment" "nginx" {
 4  metadata {
 5    name = "nginx-deployment"
 6    labels = {
 7      App = "nginx"
 8    }
 9  }
10
11  spec {
12    replicas = 3
13    selector {
14      match_labels = {
15        App = "nginx"
16      }
17    }
18    template {
19      metadata {
20        labels = {
21          App = "nginx"
22        }
23      }
24      spec {
25        container {
26          image = "nginx:1.7.8"
27          name  = "nginx"
28        }
29      }
30    }
31  }
32}
33 

In the above example, Terraform creates a Deployment on your Kubernetes cluster for nginx. It sets up 3 replicas of nginx (version 1.7.8).


Step 4: Provision Terraform infra

Once you have this configuration in place, you're ready to let Terraform do its thing. Run the following terraform command -

  1. terraform init to initialize your configuration, and
  2. terraform apply to create your resources.

Make sure you're replacing placeholders with your actual Kubernetes cluster details. And, like always, keep your sensitive information (like passwords) safe. Consider using environment variables or input variables for this.

Now, one thing to remember: Terraform is fantastic for creating and managing Kubernetes resources, but when it comes to deploying complex, multi-resource applications, Helm might be a better choice. Terraform does have a Helm provider, which you can use to deploy Helm charts. So you get the best of both worlds!

Finally, don't forget to check out the official Terraform documentation for the Kubernetes provider. It’s chock-full of useful info and will help you unlock all the cool stuff you can do with Terraform and Kubernetes.


4. Configuration Management Tools(Ansible, Chef, Puppet, or SaltStack)

First, let's clarify the roles that Terraform and a configuration management tool play. Terraform, as you know, is an infrastructure provisioning tool - it excels at creating, modifying, and tearing down infrastructure.

On the other hand, configuration management tools like Ansible, Chef, Puppet, or SaltStack handle the job of configuring this infrastructure.

In simpler words, Terraform lays the foundation and the configuration management tools build the house on top.

Now, let's see how Terraform and Ansible can work together. The most common way is using the 'null_resource' and 'local-exec' or 'remote-exec' provisioners in Terraform to trigger Ansible.

Step 1: Setting Up Terraform

Here's an example of how you could define a VM using Terraform:

 1#main.tf
 2
 3resource "aws_instance" "web" {
 4  ami           = "ami-0c94855ba95c574c8"
 5  instance_type = "t2.micro"
 6  key_name      = "my_key_pair"
 7
 8  provisioner "local-exec" {
 9    command = "echo ${self.private_ip} > inventory.ini"
10  }
11}

In the above example, Terraform is provisioning an EC2 instance and creating an inventory file with the instance's private IP address for Ansible to use later.

The local-exec provisioner is executing a local command after the instance is created.


Step 2: Triggering Ansible from Terraform

You can then add another 'null_resource' to trigger the Ansible playbook:

1resource "null_resource" "ansible_provisioning" {
2  depends_on = [aws_instance.web]
3
4  provisioner "local-exec" {
5    command = "ansible-playbook -i inventory.ini setup.yml"
6  }
7}

This null_resource uses another local-exec provisioner to execute the Ansible playbook called 'setup.yml' on the instance we just created.


Step 3: How the infra will be provisioned

  1. As you run terraform apply, Terraform will first create the EC2 instance,
  2. After that Ansible will pick up from there, using the inventory file generated by Terraform to configure the newly created instance.

Remember to replace the placeholders in these examples with your actual data.

One thing to note: while this approach works, it does somewhat go against the philosophy of Terraform being declarative, and it could lead to issues with idempotency.

Using Terraform and Ansible separately - Terraform for provisioning, Ansible for configuration - could give you a cleaner, more maintainable setup. This isn't a hard and fast rule, though, and how you choose to use these tools will depend on your specific use case and requirements.


Conclusion

I hope you've enjoyed our journey through Terraform for on-premises infrastructure. We've covered a lot of ground, right?

From managing VMware vSphere infrastructure to treating bare metal servers like cloud instances with MAAS, you've seen how versatile Terraform can be. And who could forget how smoothly it interacts with Kubernetes and configures things with Ansible?

This blog post is just the tip of the iceberg, there's so much more Terraform can do. And remember, it's okay to take your time, learning doesn't have to be a race. Always feel free to explore the official Terraform documentation whenever you need.

I truly hope you've found this exciting and enlightening. Always here if you need more guidance. Keep exploring, and as always, Happy Terraforming!

Posts in this Series