Cloud-init is an interesting industry solution for the distribution of computational resources across the major cloud providers (private included). Starting from an empty template, it’s possible to attach and apply every OS configuration, without manual intervention. In this way, IP addresses, users, packages and etc, are applied without user interaction. IMHO this is a key element to automate the delivery of computing instances, and in this post, I describe how to implement Cloud-init in vSphere environment using the infrastructure-as-code paradigm with Terraform.
Requirements and warm-up
This implementation requires:
- A vSphere 6.5 environment with a user able import, modify and clone VMs (quite obvious)
- Ubuntu cloud image (available here: https://cloud-images.ubuntu.com/releases/18.04/release/ubuntu-18.04-server-cloudimg-amd64.ova )
- Powershell and PowerCLI
- Standard tools like a good editor and ssh client (super obvious)
Following Ubuntu specifications brought with the OVA cloud image, there’s only one way to inject cloud-init user-data configuration starting from the “empty template”: the property called “user-data”. But, the other cloud-init specifications like metadata and vendor data, need a new “datasource” able to seed the other configurations belong with the VM in the advanced parameters called GuestInfo (here the project in GitHub).
The automation workflow will be:
- Deliver OVA (using PowerCLI)
- Clone to VM including kick start configuration injected as OVF param called user-data(reboot required)
- Attach user-data, metadata, and vendor-data in VM “guestinfo” param
- VM is ready
User-data, Metadata, Vendor-data
Checking the cloud-init official documentation every instance is initialized using the cloud provided metadata (which the basic configurations like hostname and networking config), and optionally it’s possible to define user data and vendor data. When cloud-init service is enabled (default in cloud template), the changing of VM configurations are handled by the data injected using a supported source (here the complete list).
In the vSphere scenario, the only way to inject configuration is by using the user-data param defined in the OVF, and IP address and the other config params should be defined in a classic way. Surfing the VMware, in their open-source GitHub, has published a component that simply extends the ability in a VM to inject data using GuestInfo params.
Bringing all together, the automation idea is to make a kickoff configuration:
chpasswd: #Change your local password here
- default #Define a default user
- name: local
groups: sudo, users, admin
sudo: ['ALL=(ALL) NOPASSWD:ALL']
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
#disable_root: false #Enable root acce
ssh_pwauth: yes #Use pwd to access (otherwise follow official doc to use ssh-keys)
command: ["pollinate", "-r", "-s", "https://entropy.ubuntu.com"]
- python3-pip #Dependency package for cloud-init vsphere component
- curl -sSL https://raw.githubusercontent.com/vmware/cloud-init-vmware-guestinfo/master/install.sh | sh - #Install cloud-init
Then prepare metadata and user data with the VM specific configuration like IP address, package, etc.
dhcp4: false #true to use dhcp
- 192.168.200.70/24 #Set you ip here
gateway4: 192.168.200.254 # Set gw here
- 192.168.200.10 # Set DNS ip address here
Finally the “real” user-data file:
- mkdir -p /mnt/sharedfolder
Time to Terraform
I already discussed in my previous post, how is important today for dev-ops people, to deliver their application including the infrastructure elements which are available in the private and in the public cloud, using the infrastructure-as-code paradigm. HashiCorp Terraform is one of the best tools to deliver multi-cloud elements, and cloud-init complete the deployment adding the running system configurations.
For this post, I realized an example composed of the main file, variable file, and YAML definitions (kickoff, userdata, metadata). Check these on GitHub repo: https://github.com/linoproject/terraform/tree/master/vsphere-cloudinit. I provided also a PowerCLI script that downloads and imports the template in your vSphere Environment.
You could simply download it from the repo and prepare you terraform.tfvars file with the following data (obviously with your environment data):
datacenter_name = "Datacenter"
cluster_name = "Cluster"
datastore_name = "Datastore"
vm_network_name = "Network"
vsphere_user = "firstname.lastname@example.org"
vsphere_password = "MySuperPassword"
vsphere_server = "vcsa-01.linoproject.lab"
template_name = "ubuntu1804template"
hostname = "ubuntu-01.linoproject.lab"
If you want to modify userdata, metadata on a terraformed VM, don’t forget to delete the content under /var/lib/cloud/ on running VM before running the “apply” command again. This force cloud-init to re-run the new config even if the VM already booted.
Sources and inspirations
The examples shown in this post are realized customizing sources taken from other projects/posts. Here a list of some resources useful: