This is a example script to clone and deploy multiple Linux vCenter Virtual Machines on VMware vSphere vCenter server infrastructure using hashicorp terraform scripts. In my VMware infrastructure I already have configured virtual datacenter, cluster, network and datastore. There is Linux VM template exists in the infrastructure. This template will be used to clone new VMs.
Here I have 4 terraform code files written. First file contains 3 variables and it has vCenter server FQDN/IP, username and password mentioned which will be used to authenticate to vCenter server.
Download this this script here Terraform_vSphere_Clone_Linux_VM.zip or it is also available on github.com/janviudapi.
#Login information for vCenter Server:- credential.tf variable "vCenter_user" { description = "Username to connect to vCenter Server" type = string default = "[email protected]" } variable "vCenter_password" { description = "Password to connect to vCenter Server" type = string default = "Computer@123" } variable "vCenter_server" { description = "IP or DNS name to connect to vCenter server" type = string default = "192.168.34.20" }
Second file is for vSphere Provider which will download required Terraform vSphere plugins from hashicorp repository and login to vCenter server.
#vSphere Provider and login to vCenter server:- provider.tf terraform { required_providers { vsphere = { source = "hashicorp/vsphere" #version = ">= x.y.z" } } #required_version = ">= 0.13" } provider "vsphere" { user = var.vCenter_user password = var.vCenter_password vsphere_server = var.vCenter_server allow_unverified_ssl = true }
In the next file I have mentioned datacenter, cluster, virtual network, datastore variables names where cloned VM will be created using VM template name. For new virtual machines information I have mentioned hash table map. In the table I mentioned new names of the VM, CPU and memory.
#vCenter Server infrastructure and new VM entity information:- variable.tf variable "datacenter" { description = "Virtual Datacenter name where VM will be placed" type = string default = "Asgard" } variable "cluster" { description = "Password to connect to vCenter Server" type = string default = "Bifrost" } variable "network" { description = "IP or DNS name to connect to vCenter server" type = string default = "VM Network" } variable "datastore" { description = "IP or DNS name to connect to vCenter server" type = string default = "StarLord_Datastore01" } variable "template" { description = "IP or DNS name to connect to vCenter server" type = string default = "Wakanda_Ubuntu_01" } variable "vminfo" { type = map(object({ vm = string cpu = string memory = string })) default = { "dev" = { vm = "galaxy" cpu = "1" memory = "1024" } "prod" = { vm = "gardian" cpu = "2" memory = "2048" } } }
In the last main.tf file, I have mentioned actual code to clone and deploy virtual machines. I am not creating new datacenter, cluster, network, datastore and template, instead gathering information from existing VMware entities as terraform datasource, and will be using same information to create VMs.
#New Virtual Machine cloning deployment core code:- main.tf data "vsphere_datacenter" "datacenter" { name = var.datacenter } data "vsphere_compute_cluster" "cluster" { name = var.cluster datacenter_id = data.vsphere_datacenter.datacenter.id } data "vsphere_network" "network" { name = var.network datacenter_id = data.vsphere_datacenter.datacenter.id } data "vsphere_datastore" "datastore" { name = var.datastore datacenter_id = data.vsphere_datacenter.datacenter.id } data "vsphere_virtual_machine" "template" { name = var.template datacenter_id = data.vsphere_datacenter.datacenter.id } resource "vsphere_virtual_machine" "vm" { for_each = var.vminfo name = each.value["vm"] resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id datastore_id = data.vsphere_datastore.datastore.id num_cpus = each.value["cpu"] memory = each.value["memory"] guest_id = data.vsphere_virtual_machine.template.guest_id scsi_type = data.vsphere_virtual_machine.template.scsi_type network_interface { network_id = data.vsphere_network.network.id adapter_type = data.vsphere_virtual_machine.template.network_interface_types[0] } disk { label = "${each.value["vm"]}-disk0" size = data.vsphere_virtual_machine.template.disks.0.size thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned } clone { template_uuid = data.vsphere_virtual_machine.template.id customize { linux_options { host_name = each.value["vm"] domain = "example.com" } network_interface { # ipv4_address = "172.16.11.10" # ipv4_netmask = 24 } #ipv4_gateway = "172.16.11.1" } } }
Create all files under one folder and run the command terraform init to initialize the project and it will download required vSphere plugins for terraform. It will also do the basic validation of the .tf script files as there are no basic errors.
PS E:\Scripts\Terraform\ritesh\vm_creation> terraform init Initializing the backend... Initializing provider plugins... - Reusing previous version of hashicorp/vsphere from the dependency lock file - Using previously-installed hashicorp/vsphere v2.2.0 Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS E:\Scripts\Terraform\ritesh\vm_creation>
Run terraform plan to see if any changes are required in the VMware infrastructure, it will gather the information.
PS E:\Scripts\Terraform\ritesh\vm_creation> terraform plan
To actual create clone VMs in on vCenter server run command terraform apply --auto-approve, it takes some time to deploy.
Check to resolve the error: Terraform VMware vSphere Virtual Machine cloning Operating system not found
PS E:\Scripts\Terraform\ritesh\vm_creation> terraform apply --auto-approve data.vsphere_datacenter.datacenter: Reading... data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] data.vsphere_network.network: Reading... data.vsphere_datastore.datastore: Reading... data.vsphere_compute_cluster.cluster: Reading... data.vsphere_virtual_machine.template: Reading... data.vsphere_network.network: Read complete after 0s [id=network-12] data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11] data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c13001] data.vsphere_virtual_machine.template: Read complete after 0s [id=422a010e-6459-05cb-38fb-6360bbda149b] vsphere_virtual_machine.vm["prod"]: Refreshing state... [id=4202778d-02c9-339f-0d11-b3c7ca779ba3] vsphere_virtual_machine.vm["dev"]: Refreshing state... [id=42023dd7-f6a7-9e25-ebba-1489493fe2de] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # vsphere_virtual_machine.vm["dev"] will be created + resource "vsphere_virtual_machine" "vm" { + annotation = (known after apply) + boot_retry_delay = 10000 + change_version = (known after apply) + cpu_limit = -1 + cpu_share_count = (known after apply) + cpu_share_level = "normal" + datastore_id = "datastore-11" + default_ip_address = (known after apply) + ept_rvi_mode = "automatic" + firmware = "bios" + force_power_off = true + guest_id = "ubuntu64Guest" + guest_ip_addresses = (known after apply) + hardware_version = (known after apply) + host_system_id = (known after apply) + hv_mode = "hvAuto" + id = (known after apply) + ide_controller_count = 2 + imported = (known after apply) + latency_sensitivity = "normal" + memory = 1024 + memory_limit = -1 + memory_share_count = (known after apply) + memory_share_level = "normal" + migrate_wait_timeout = 30 + moid = (known after apply) + name = "galaxy" + num_cores_per_socket = 1 + num_cpus = 1 + power_state = (known after apply) + poweron_timeout = 300 + reboot_required = (known after apply) + resource_pool_id = "resgroup-13002" + run_tools_scripts_after_power_on = true + run_tools_scripts_after_resume = true + run_tools_scripts_before_guest_shutdown = true + run_tools_scripts_before_guest_standby = true + sata_controller_count = 0 + scsi_bus_sharing = "noSharing" + scsi_controller_count = 1 + scsi_type = "lsilogic" + shutdown_wait_timeout = 3 + storage_policy_id = (known after apply) + swap_placement_policy = "inherit" + tools_upgrade_policy = "manual" + uuid = (known after apply) + vapp_transport = (known after apply) + vmware_tools_status = (known after apply) + vmx_path = (known after apply) + wait_for_guest_ip_timeout = 0 + wait_for_guest_net_routable = true + wait_for_guest_net_timeout = 5 + clone { + template_uuid = "422a010e-6459-05cb-38fb-6360bbda149b" + timeout = 30 + customize { + timeout = 10 + linux_options { + domain = "example.com" + host_name = "galaxy" + hw_clock_utc = true } + network_interface {} } } + disk { + attach = false + controller_type = "scsi" + datastore_id = "" + device_address = (known after apply) + disk_mode = "persistent" + disk_sharing = "sharingNone" + eagerly_scrub = false + io_limit = -1 + io_reservation = 0 + io_share_count = 0 + io_share_level = "normal" + keep_on_remove = false + key = 0 + label = "galaxy-disk0" + path = (known after apply) + size = 100 + storage_policy_id = (known after apply) + thin_provisioned = true + unit_number = 0 + uuid = (known after apply) + write_through = false } + network_interface { + adapter_type = "e1000" + bandwidth_limit = -1 + bandwidth_reservation = 0 + bandwidth_share_count = (known after apply) + bandwidth_share_level = "normal" + device_address = (known after apply) + key = (known after apply) + mac_address = (known after apply) + network_id = "network-12" } } # vsphere_virtual_machine.vm["prod"] will be created + resource "vsphere_virtual_machine" "vm" { + annotation = (known after apply) + boot_retry_delay = 10000 + change_version = (known after apply) + cpu_limit = -1 + cpu_share_count = (known after apply) + cpu_share_level = "normal" + datastore_id = "datastore-11" + default_ip_address = (known after apply) + ept_rvi_mode = "automatic" + firmware = "bios" + force_power_off = true + guest_id = "ubuntu64Guest" + guest_ip_addresses = (known after apply) + hardware_version = (known after apply) + host_system_id = (known after apply) + hv_mode = "hvAuto" + id = (known after apply) + ide_controller_count = 2 + imported = (known after apply) + latency_sensitivity = "normal" + memory = 2048 + memory_limit = -1 + memory_share_count = (known after apply) + memory_share_level = "normal" + migrate_wait_timeout = 30 + moid = (known after apply) + name = "gardian" + num_cores_per_socket = 1 + num_cpus = 2 + power_state = (known after apply) + poweron_timeout = 300 + reboot_required = (known after apply) + resource_pool_id = "resgroup-13002" + run_tools_scripts_after_power_on = true + run_tools_scripts_after_resume = true + run_tools_scripts_before_guest_shutdown = true + run_tools_scripts_before_guest_standby = true + sata_controller_count = 0 + scsi_bus_sharing = "noSharing" + scsi_controller_count = 1 + scsi_type = "lsilogic" + shutdown_wait_timeout = 3 + storage_policy_id = (known after apply) + swap_placement_policy = "inherit" + tools_upgrade_policy = "manual" + uuid = (known after apply) + vapp_transport = (known after apply) + vmware_tools_status = (known after apply) + vmx_path = (known after apply) + wait_for_guest_ip_timeout = 0 + wait_for_guest_net_routable = true + wait_for_guest_net_timeout = 5 + clone { + template_uuid = "422a010e-6459-05cb-38fb-6360bbda149b" + timeout = 30 + customize { + timeout = 10 + linux_options { + domain = "example.com" + host_name = "gardian" + hw_clock_utc = true } + network_interface {} } } + disk { + attach = false + controller_type = "scsi" + datastore_id = "" + device_address = (known after apply) + disk_mode = "persistent" + disk_sharing = "sharingNone" + eagerly_scrub = false + io_limit = -1 + io_reservation = 0 + io_share_count = 0 + io_share_level = "normal" + keep_on_remove = false + key = 0 + label = "gardian-disk0" + path = (known after apply) + size = 100 + storage_policy_id = (known after apply) + thin_provisioned = true + unit_number = 0 + uuid = (known after apply) + write_through = false } + network_interface { + adapter_type = "e1000" + bandwidth_limit = -1 + bandwidth_reservation = 0 + bandwidth_share_count = (known after apply) + bandwidth_share_level = "normal" + device_address = (known after apply) + key = (known after apply) + mac_address = (known after apply) + network_id = "network-12" } } Plan: 2 to add, 0 to change, 0 to destroy. vsphere_virtual_machine.vm["prod"]: Creating... vsphere_virtual_machine.vm["dev"]: Creating... vsphere_virtual_machine.vm["prod"]: Still creating... [10s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [10s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [20s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [20s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [30s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [30s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [40s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [40s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [50s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [50s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [1m0s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [1m0s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [1m10s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [1m10s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [1m20s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [1m20s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [1m30s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [1m30s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [1m40s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [1m40s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [1m50s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [1m50s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [2m0s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [2m0s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [2m10s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [2m10s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [2m20s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [2m20s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [2m30s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [2m30s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [2m40s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [2m40s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [2m50s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [2m50s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [3m0s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [3m1s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [3m10s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [3m11s elapsed] vsphere_virtual_machine.vm["prod"]: Still creating... [3m20s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [3m21s elapsed] vsphere_virtual_machine.vm["prod"]: Creation complete after 3m25s [id=4202422c-016b-7f0f-1f54-9eb75f68793f] vsphere_virtual_machine.vm["dev"]: Still creating... [3m31s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [3m41s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [3m51s elapsed] vsphere_virtual_machine.vm["dev"]: Still creating... [4m1s elapsed] vsphere_virtual_machine.vm["dev"]: Creation complete after 4m4s [id=4202d570-026d-f9a8-f662-43d63da83e1d] Apply complete! Resources: 2 added, 0 changed, 0 destroyed. PS E:\Scripts\Terraform\ritesh\vm_creation>
While terraform is getting deploying VMs, you can verify in the vSphere Client (vCenter Portal), VMs are getting created in the recent tasks and check the VM hardware.
Useful Articles
Terraform module clone VMware vSphere Linux and Windows virtual machine
VMWARE VSPHERE UPDATE MANAGER (VUM) - IMPORTING ESXI ISO AND CREATE UPGRADE BASELINE
VMWARE VSPHERE UPDATE MANAGER (VUM) - UPGRADE ESXI OS
ESXi 6.0 update offline bundle via esxcli commandline: DependencyError VIB bootbank requires VSAN ImageProfile
ESXi 6.5 upgrade bundle via command line: No Space Left On Device Error
Registering HPE ILO amplifier pack (Hardware support manager) with vCenter 7 Lifecycle manager
VMware LifeCycle Manager import updates bundle and patch ESXi server
How to update a patch on an ESXi host via command line
Add a Trusted Root Certificate to the Certificate Store VMware Photon OS
Patching update VMware vCenter Server Appliance from a zipped update bundle Web server
Powershell GUI VMware ESXi custom patch bundle builder
Create a custom TCPIP stack on ESXi server - VMware PowerCLI GUI