Deploy Infrastructure VMs for Application
In this section we will deploy four VMs. Deploy a production-like three-tier application stack on Nutanix AHV using both Visual Studio Code (VSCode) and OpenTofu:
- 1 x MySQL DB VM (v4 API cURL) - Persistent data storage
- 2 x Frontend Wordpress App VM (Prism UI)
- 1 x HAProxy VM - Load balancer frontend
Create Infrastructure VMs
-
Open
VSCode, Go to File -> New Window , Click on Open Folder and create new workspace folder (i.e.,/home/ubuntu). -
In
VSCodeExplorer pane, Click on New Folder and name it:`tofu -
In the
tofufolder, click on New File with the following name -
Paste the following contents inside the file:
Warning
Make sure to paste the contents of the public key created inside the file in the highlighted line
#cloud-config hostname: ${hostname} fqdn: ${hostname}.local manage_etc_hosts: true preserve_hostname: false package_update: true package_upgrade: true package_reboot_if_required: true packages: - open-iscsi - nfs-common runcmd: - systemctl stop ufw && systemctl disable ufw users: - default - name: ubuntu groups: sudo shell: /bin/bash sudo: - 'ALL=(ALL) NOPASSWD:ALL' ssh-authorized-keys: - ssh-rsa AAAAB3Nxxxxxx # (1) runcmd: - hostnamectl set-hostname ${hostname}-
Copy and paste the contents of your
~/.ssh/id_rsa.pubfile or any public key file that you wish to use.
If you are using a Mac, the command
pbcopycan be used to copy the contents of a file to clipboard.Cmd+v will paste the contents of clipboard to the console.
Warning
If needed, make sure to update the target
hostnameand copy / paste the value of the RSA public key in thecloudinit.yamlfile. -
-
In
VSCodeExplorer, within thejumphost-vmfolder, click on New File and create a config file with the following name:Warning
Update Nutanix environment access details along with any infrastructure VM configurations. See example file for details
nutanix: endpoint: "_pc_endpoint_ip_fqdn" user: "admin" password: "XXXXX_" infrastructure: cluster_name: "_pe_cluster_name" subnet_name: "_pe_subnet_name" vm_defaults: image_name: "_ubuntu_image_name" # (1) roles: fe: count: 2 name_prefix: "fe_your_username" hostname_prefix: "fe_your_username"" num_vcpus_per_socket: 1 num_sockets: 2 memory_size_mib: 4096 disk_size_mib: 102400 db: count: 1 name_prefix: "db_your_username" hostname_prefix: "db_your_username" num_vcpus_per_socket: 1 num_sockets: 4 memory_size_mib: 4096 disk_size_mib: 102400 haproxy: count: 1 name_prefix: "haproxy_your_username" hostname_prefix: "haproxy_your_username" num_vcpus_per_socket: 1 num_sockets: 2 memory_size_mib: 4096 disk_size_mib: 102400- Check Images in PC for ubuntu image name that was previously uploaded
nutanix: endpoint: "pc.example.com" user: "admin" password: "XXXXXX_" infrastructure: cluster_name: "pe" subnet_name: "primary" vm_defaults: image_name: "ubuntu-24.04" # (1) roles: fe: count: 2 name_prefix: "fe-user01" hostname_prefix: "fe-user01" num_vcpus_per_socket: 1 num_sockets: 2 memory_size_mib: 4096 disk_size_mib: 102400 db: count: 1 name_prefix: "db-user01" hostname_prefix: "db-user01" num_vcpus_per_socket: 1 num_sockets: 4 memory_size_mib: 4096 disk_size_mib: 102400 haproxy: count: 1 name_prefix: "haproxy-user01" hostname_prefix: "haproxy-user01" num_vcpus_per_socket: 1 num_sockets: 2 memory_size_mib: 4096 disk_size_mib: 102400- Check Images in PC for ubuntu image name that was previously uploaded
-
In
VSCodeExplorer pane, navigate to thejumphost-vmfolder, click on New File and create a opentofu manifest file with the following name:with the following content:
############################ # Locals ############################ locals { config = yamldecode(file("${path.module}/config.yaml")) # Expand roles into individual VM definitions vms = flatten([ for role_name, role in local.config.roles : [ for i in range(role.count) : { role = role_name index = i + 1 name = format("%s-%02d", role.name_prefix, i + 1) hostname = format("%s-%02d", role.hostname_prefix, i + 1) num_vcpus_per_socket = role.num_vcpus_per_socket num_sockets = role.num_sockets memory_size_mib = role.memory_size_mib disk_size_mib = role.disk_size_mib } ] ]) vm_map = { for vm in local.vms : "${vm.role}-${vm.index}" => vm } } ############################ # Provider ############################ provider "nutanix" { endpoint = local.config.nutanix.endpoint username = local.config.nutanix.user password = local.config.nutanix.password insecure = true } data "nutanix_cluster" "cluster" { name = local.config.infrastructure.cluster_name } data "nutanix_subnet" "subnet" { subnet_name = local.config.infrastructure.subnet_name } ############################ # Image lookup ############################ data "nutanix_image" "ubuntu" { image_name = local.config.vm_defaults.image_name } ############################ # VMs ############################ resource "nutanix_virtual_machine" "vm" { for_each = local.vm_map name = each.value.name cluster_uuid = data.nutanix_cluster.cluster.id num_vcpus_per_socket = each.value.num_vcpus_per_socket num_sockets = each.value.num_sockets memory_size_mib = each.value.memory_size_mib disk_list { data_source_reference = { kind = "image" uuid = data.nutanix_image.ubuntu.id } disk_size_mib = each.value.disk_size_mib } nic_list { subnet_uuid = data.nutanix_subnet.subnet.id } guest_customization_cloud_init_user_data = base64encode( templatefile("${path.module}/cloud-init.yaml.tpl", { hostname = each.value.hostname }) ) depends_on = [data.nutanix_image.ubuntu] } ############################ # Outputs ############################ output "vm_ips_by_role" { description = "IP addresses grouped by role" value = { for role in distinct([for v in local.vms : v.role]) : role => [ for k, vm in nutanix_virtual_machine.vm : { name = vm.name ips = flatten([ for nic in vm.nic_list : nic.ip_endpoint_list[*].ip ]) } if local.vm_map[k].role == role ] } } -
Open a terminal within
VSCode, Terminal > New Terminal -
Make sure to change to
tofudirectory -
Initialize and Validate your tofu code
-
Plan your tofu code to create Jump Host VM
-
Apply your tofu code to create Jump Host VM
Type
yesto confirm -
Obtain the IP address of the
Jump HostVM from the Tofu output -
Run the Terraform state list command to verify what resources have been created
# Sample output for the above command data.nutanix_cluster.cluster # < This is your existing Prism Element cluster data.nutanix_subnet.subnet # < This is your existing primary subnet nutanix_image.machine-image # < This is the ubuntu image file nutanix_virtual_machine.vm["db-1"] # < This is the db vm nutanix_virtual_machine.vm["fe-1"] # < This is the fe-1 vm nutanix_virtual_machine.vm["fe-2"] # < This is the fe-2 vm nutanix_virtual_machine.vm["haproxy-1"] # < This is the haproxy vm -
Validate that all the infrastructure VMs are accessible using VSCode > Terminal
Now the infrastructure VMs are ready with all the tools to deploy application on this site.