I create a lot of virtual machines during the week to test deployments, or try to digg into problems of deployments. In the past I used Vmware Workstation, Oracle VirtualBox or MS HyperV on my desktops, but I also used Vmware ESX. I tried to use Vagrant and Packer to prepare images and distribute them, but wasn’t satisfied at all.
The biggest issues with this was the time when I tried to use WSL2 and HyperV on a Windows machine. There even creating a seperate virtual network was hard, unreliable, and so I stopped trying this.
What I want to achieve is automated and reliable distribution of virtual machines, easy management and performance.
I never had the need to do distribution and deployment in one step, so I split the process into the distribution of the virtual machines with Terraform and then the deployment with Ansible. I could combine this in one step, but as I said no need to do this. Additional environments use seperate repositories or folders, so I can create or destroy multiple environments in parallel without affecting each other.
The advantage of Terraform is the amount of providers , so it is easy to adjust the definition and deploy it on a Cloud provider, Vmware host or whatever is around.
On my local Linux machine I use libvirt to create virtual machines through Terraform.
I used this repository as a starting boilerplate for my Terraform projects with Centos Cloud images and libvirt virtualization. I uploaded one example to https://github.com/stoeps13/terraform-libvirt , so you can follow in the repository.
I added an unsafe parameter to my virtual discs, so they work as a charme on my notebook to test roles and installations, but are not reliable to store production data!
Follow the README.md to set up the environment.
Download and configure cloud images
Actually I use the latest available Centos GenericCloud image from: CentOS-7-x86_64-GenericCloud-2009.qcow2 , but just with exchanging the source in
volume.tf to CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2 I can build the same environment with a CentOS 8 image.
The cloud images are very handy, because you just need a file to configure the basic stuff like hostname, ip address, dns server, additional packages and your ssh keys for easy access later with Ansible.
Hostname and fqdn are set to variables, which we will use from Terraform.
Allow password authentication with SSH.
This part generates a user
sysadmin with the content of
cnx6.pub as authorized key (key-authentication with SSH) and
/bin/bash as shell, adds him to sudoers and the group
root user just gets an authorized key.
I add one more user which will be used to run Ansible. My users connect with SSH keys, but still have a password to manage and test things after deployment.
It’s easy to add more keys, users, or commands.
The comment describes the commands which can be used to generate the password hashes.
In my case I even copied the hashes from user to user, but normally even with the same password they have different salted hashes. But as I already stated, I use this only for demos and to test updates or development stuff.
Install additional packages, like your favorite editor and python3, so Ansible can connect and work.
The rest of the file is pretty straight forward, you can add more packages and reboot it.
Here can you find my complete file, which I use for my environments.
Configure hostname, ip address and disc configuration
terraform-libvirt.tfvars is the definition of our servers, the example file defines 3 virtual machines:
I added some comments of the first server with the description of definitions used.
So now we need to add a domain (fqdn = hostname + domain) and the first three octets of the network address.
These can be set in
So the domain is
stoeps.home and all ip addresses start with
Some of this things need to be repeated in
network.tf which holds the basic network configuration.
network.tf is only present in my very first set of virtual machines! I use this repository to build a dns server, nfs server and directory server (ldap), so here I define the network and the servers run all the time. Additional machines use this network, but don’t define it, so I can create and destroy machines without affecting the network.
Additionally, I added in the end of
Which adds the content of
volume.xsl to the disc definitions, because without this, the storage access on my notebook was just too slow to create databases on DB2.
This article on serverfault describes more details and one solution is to add
cache='unsafe' to the qemu driver. I found the solution here .
So I had to write a matching xsl to add this to all discs which are created by Terraform.
You can change
writeback which can save the filesystem on power-outages. But I use the virtualization only for automated deployed machines, so I can get them back within some hours from Ansible and Terraform.
Create the machines
The creator of the original repository created a Makefile as wrapper for the different terraform commands, for my test environments this speeds up the execution, so I stayed with it.
This command generates three running machines, which can be configured with Ansible. Test connections with
ssh firstname.lastname@example.org for example.
Apply complete! Resources: 11 added, 0 changed, 0 destroyed.
ssh email@example.com Warning: Permanently added '10.0.22.2' (ED25519) to the list of known hosts. [root@cnx-ns ~]#
No additional password prompt, we can start configuring with Ansible. In the next post I’ll show how to deploy a DNS server for the virtual environment.
To destroy the environment, just use following command: