Terraform with cloud-init
-
Hi all,
New to the IaC world, trying to get Terraform to create a couple of VMs through Xen Orchestra but having a little trouble with cloud-init functionality. Network config gets parsed fine and the expected IP is applied.
However using the cloud-init user config that I usually use on the XO Web UI does not work. Within this cloud-init config, I'm using the name variable as shown below:
However it appears when this is used via Terraform, the variable does not get parsed and the cloud config drive ends up being generated and given to the VM as
#cloud-config
hostname: {name}If anyone can help with this, would be much appreciated.
-
Hello!
Hmm likely a question for @ddelnano
-
@m4xm0rris Xen Orchestra's web application is doing this templating on the client side (reference). Since terraform already has a nice interface for templating (using its
template_file
data source), you can do the substitution yourself.Here is an example below. Note: this code is not tested but was pulled from an internal project I have.
# template_file.tpl #cloud-config hostname: ${name} # any other cloud-config you need [...] # vm.tf data "template_file" "cloud_config" { template = file("${path.module}/template_file.tpl") vars = { name = "your_hostname_value" } } resource "xenorchestra_cloud_config" "cloud_config" { name = "cloud_config" # This performs the templating template = data.template_file.cloud_config.rendered } resource "xenorchestra_vm" "vm" { [ ... ] cloud_config = xenorchestra_cloud_config.cloud_config.template }
Please share your terraform code if you would like more specific advice on how to recreate exactly what you are trying to do.
-
Thanks @ddelnano , that worked perfectly!
Appreciate the help!
-
Interesting indeed, we should probably document this If you have time @ddelnano obviously
-
Just stumbled upon this provider too which may offer a more streamlined approach, not that I am familiar enough with Terraform yet to say this for sure: https://github.com/hashicorp/terraform-provider-cloudinit
-
I created terraform-provider-xenorchestra #181 to track updating the docs.
Cool, I didn't realize that there was a cloudinit terraform provider :). I'll be giving that a try and may use that in the examples if its better than using
template_file
directly. -
@ddelnano Yeah, I only found it as template_file is deprecated apparently and as it hasn't seen any updates, I couldn't use the code you posted from my M1 Macbook
Anyways, thanks for the help!
-
@m4xm0rris it looks like there is a builtin function to do templating now (templatefile). Apologies, it's been a while since I've written any new templated terraform code.
-
In case anyone comes here looking for an example (as it took me days of banging head against the proverbial wall to get this working with templatefile function):
cloud_config.tftpl
#cloud-config hostname: ${hostname} user: ops-user ssh_authorized_keys: - OPS_USER_SSH_KEY manage_etc_hosts: true fqdn: ${hostname}.${domain} package_upgrade: true users: - default
cloud_network_config.tftpl
#cloud-config version: 1 config: - type: physical name: eth0 subnets: - type: static address: '${ip}' netmask: '255.255.255.0' gateway: '10.1.50.1'
resource "xenorchestra_vm" "unc-lab" { ...... cloud_config = templatefile("cloud_config.tftpl", { hostname = "unc-lab" domain = "morris.lan" }) cloud_network_config = templatefile("cloud_network_config.tftpl", { ip = "10.1.55.34" }) .......
Thanks @ddelnano for your help!
-
I was able to confirm that using the cloudinit data source does work with Xen Orchestra.
Here is the following terraform code that I used (essential pieces like SR and network are excluded for the example):
data "cloudinit_config" "cloud_config" { gzip = false base64_encode = false part { content_type = "text/cloud-config" content = <<EOF users: - name: ddelnano sudo: ALL=(ALL) NOPASSWD:ALL ssh_import_id: - gh:ddelnano packages: - make - build-essential runcmd: - echo 'this is a test' EOF } } resource "xenorchestra_vm" "vm" { memory_max = 2147467264 cpus = 1 name_label = "XO terraform tutorial" template = data.xenorchestra_template.vm_template.id cloud_config = data.cloudinit_config.cloud_config.rendered network { network_id = data.xenorchestra_network.network.id } disk { sr_id = data.xenorchestra_sr.sr.id name_label = "VM root volume" size = 4294967296 } }
This creates a VM and runs the cloudinit I specified, however, it doesn't allow you to do any templating. So you would still need to use the
templatfile
function built into Terraform.I'm going to update the docs to use the
templatefile
function since that is the most streamlined approach. -
@m4xm0rris thanks for posting your solution. Would you mind reviewing my changes to the terraform registry documentation (here)?
Since I've been working with terraform long before Xen Orchestra, having your opinion on the documentation would be valuable
-
Hi, I have question to this topic. I figured out to use templatefiles in terraform to provision my VMs. But when I try to provision network alongside with userdata configuration (2 templatefiles) it did not work. It just apply network configuration. My use case was to firstly configure static networking and than install some packages, hostname and so on. I was resolving this few months ago, but did not understand how execution sequence works or if it is even possible. Cloud-init official documentation is not very clear.
I would appreciate any help with this.
-
@ddelnano Sir, the only thing I could point out from that pull request is the block in your vm.md where you are templating the variables in a separate block.
"
# Template the cloudinit if needed template = templatefile("cloud_config.tftpl", { hostname = "your-hostname" domain = "your.domain.com" })
"
I have no where near enough experience with Terraform to tell if makes any practical difference, but I am able to redner the template inline with the rest of my VM resource definition. eg.cloud_network_config = templatefile("cloud_network_config.tftpl", { ip = "10.1.55.37" netmask = var.network["netmask"] gateway = var.network["gateway"] })
Just a little tidier IMO, but again have no idea if the way you have done this is better practice etc.
Thanks
-
@ppikna97 Hi! How are you calling the templates in your VM resources? As you can see above from my examples I'm simply pulling both user data and network inline with the rest of my config e.g.
cloud_config = templatefile("cloud_config.tftpl", { hostname = "bw-lab" domain = var.network["domain"] }) cloud_network_config = templatefile("cloud_network_config.tftpl", { ip = "10.1.55.37" netmask = var.network["netmask"] gateway = var.network["gateway"] })
-
@m4xm0rris thanks. I was using template_file data source with xenorchestra_cloud_config data source like this:
But I found answer to both of us... Some video linked me here templatefile vs template_file. Resulting that it is better to use templatefile function to match terraform version and for working with different data types. But in the essence templatefile and template_file do the same thing.
-
@ppikna97 Np, I was also using template_file originally but as its now deprecated there have been no builds released for M1 based Mac (leading/forcing me down the templatefile function route).
-
@m4xm0rris This was very helpful. Thank you!
-
@m4xm0rris thanks, it helped much.
-
Hello there!
If you think some extra doc would be important to avoid searching for the solution, please do so