Merge cloud-init

Hi!

I’m currently learning how to use Terraform with my vmware home-lab.
I have written a module that uses OVFs and that passing a cloud-init with a vApp and I have a few questions :slight_smile:

This is how the module directory structure looks like:

.
β”œβ”€β”€ modules
β”‚   └── virtual_machine
β”‚       β”œβ”€β”€ cloud-init
β”‚       β”‚   └── userdata.yaml
β”‚       β”œβ”€β”€ variables.tf
β”‚       β”œβ”€β”€ vm.tf
β”‚       └── vsphere.tf
└── vms
    β”œβ”€β”€ consul
    β”‚   β”œβ”€β”€ main.tf

This is the cloud-init part in vm.tf

  vapp {
    properties ={
      "user-data" = base64encode(file("${path.module}/cloud-init/userdata.yaml"))
      "hostname"  = "${var.name}"
    }
  }

If I want to deploy the vm called consul, i go to that directory and run terraform apply

Question 1: As I might want to pass some custom cloud-init depending on what server I’m deploying, for example I might want to write a configuration file. Is it possible to merge the userdata.yaml file with a file that I might have in vms/consul? Let’s say vms/consul/custom-init.yaml. I could of course use ansible at deploy time instead but as this is very simple setup, ansible is a bit overkill.

Question 2: And this is pretty simple I suppose. But I can’t figure out how it works to get output from something. During deploy I’d like to get the IP of the server I’ve just deployed.
I thought this is something it should look like, but it doesn’t work

output "ips" {
  value = vsphere_virtual_machine.virtual_machine.*.default_ip_address
}

Below is the files (please don’t judge :slight_smile: ):

$ cat modules/virtual_machine/vsphere.tf 
# Basic configuration without variables

# Define authentification configuration
provider "vsphere" {
  # If you use a domain set your login like this "Domain\\User"
  user           = ""
  password       = ""
  vsphere_server = ""

  # If you have a self-signed cert
  allow_unverified_ssl = true
}

data "vsphere_datacenter" "dc" {
  name = "dc1"
}

data "vsphere_host" "host" {
  name          = ""
  datacenter_id = data.vsphere_datacenter.dc.id
}

# If you don't have any resource pools, put "Resources" after cluster name
data "vsphere_resource_pool" "pool" {
  name          = "${var.env}"
  datacenter_id = data.vsphere_datacenter.dc.id
}

# Retrieve datastore information on vsphere
data "vsphere_datastore" "datastore" {
  name          = "datastore2"
  datacenter_id = data.vsphere_datacenter.dc.id
}

# Retrieve network information on vsphere
data "vsphere_network" "network" {
  name          = "LAN"
  datacenter_id = data.vsphere_datacenter.dc.id
}

$ cat modules/virtual_machine/vm.tf 
# Set vm parameters
resource "vsphere_virtual_machine" "vm" {
  name             = "${var.name}"
  num_cpus         = "${var.vcpu}"
  memory           = "${var.memory}"
  host_system_id   = data.vsphere_host.host.id
  datastore_id     = data.vsphere_datastore.datastore.id
  resource_pool_id = data.vsphere_resource_pool.pool.id
  datacenter_id    = data.vsphere_datacenter.dc.id
  cdrom {
    client_device = true
  }

  # Set network parameters
  network_interface {
    network_id = data.vsphere_network.network.id
  }

  disk {
    label = "disk0"
    size = "${var.disk_size}"
  }

  vapp {
    properties ={
      "user-data" = base64encode(file("${path.module}/cloud-init/userdata.yaml"))
      "hostname"  = "${var.name}"
    }
  }
  
  extra_config = {
    "disk.enableUUID" = true
  }
  
  lifecycle {
    ignore_changes = [
      tags,
    ]
  }
  
  ovf_deploy {
    remote_ovf_url = "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.ova"
    disk_provisioning    = "thin"
    ip_protocol          = "IPV4"
  }
}

Thank you for your feedback :pray: