EN 
09.03.2026 Františka WELCOME IN MY WORLD

This website is originally written in the Czech language. Most content is machine (AI) translated into English. The translation may not be exact and may contain errors.

Tento článek si můžete zobrazit v originální české verzi. You can view this article in the original Czech version.
Terraform a VMware Cloud Director - Guest Customization Script

Terraform and VMware Cloud Director - Guest Customization Script

| Petr Bouška - Samuraj |
Following the introductory overview of Terraform and the VMware Cloud Director provider, we will now look at the Guest OS Customization feature. It enables automated OS configuration of a newly deployed virtual machine. We will focus on the ability to run a custom script as part of the customization. The script can contain variables defined in Terraform, which are dynamically substituted during VM creation. The result is an automated and parameterized VM deployment from a template.
displayed: 536x (269 CZ, 267 EN) | Comments [0]

Note: The description in this article is based on Terraform version 1.14.4, VMware Cloud Director Provider version 3.14, and VMware Cloud Director 10.6.

VM Guest OS Customization

Guest OS Customization is a feature of VMware Cloud Director that allows you to customize (configure) certain settings of the guest operating system. It is used for VMs created from a template. Configuration takes place at first boot of the VM, or when the VM is started with the Power on, Force Recustomization option. VMware Tools are used for this purpose. Cloud Director creates a package that it copies to, runs on, and removes from the VM during customization.

You can configure the hostname, network parameters, change the SID and join a domain (for Windows), set the administrator password, or run a custom script. This article focuses specifically on the ability to run a custom script inside the virtual machine's OS. The script is limited to a maximum length of 1500 characters.

Guest Customization must be enabled for it to run. When the VM is powered on, the computer name and network parameters should be configured. This setting cannot be disabled if you want customization to be enabled. On first power-on (or when forced), the remaining settings are applied, including running the script. It is recommended to disable customization after it has been applied for the first time.

Note: For customization, you can also use Guest Properties on a VM. These two options should not be combined.

Terraform VMware Cloud Director Provider

We will build on the example described in the previous article about creating a new VM from a template using Terraform and the VMware Cloud Director Provider. Some values that were previously entered directly into the configuration will now be replaced with variables.

We will add new variable definitions to the variables.tf file.

# VM parameters
variable "vm_name" {
  type = string
}
variable "vm_ip" {
  type = string
}
variable "vm_ip_mask" {
  type = string
}
variable "vm_gw" {
  type = string
}
variable "vm_dns" {
  type = string
}

In the terraform.tfvars file, we will set their values.

# VM parameters
vm_name    = "Demo-VM"
vm_ip      = "172.30.21.100"
vm_ip_mask = "22"
vm_gw      = "172.30.20.1"
vm_dns     = "172.30.20.1"

Creating a VM and configuring customization (the customization block)

In the vcd_vapp_vm resource, we can use the customization block to configure the Guest OS Customization parameters.

# create VM from template wtih manual IP assignment
resource "vcd_vapp_vm" "DemoVM" {
  vapp_name        = vcd_vapp.Demo.name
  name             = var.vm_name
  computer_name    = var.vm_name
  vapp_template_id = data.vcd_catalog_vapp_template.my-vapp-template.id
  memory           = 2048 
  cpus             = 2
  cpu_cores        = 1
  depends_on       = [ vcd_vapp_org_network.vappOrgNet ]

  network {
    type               = "org"
    name               = vcd_vapp_org_network.vappOrgNet.org_network_name
    ip_allocation_mode = "MANUAL"
    ip                 = var.vm_ip
    is_primary         = true
  }

  customization {
    enabled   = true
  } 
}

We can also use the force argument, which causes a VM restart on every terraform apply operation along with forced customization. It can be used when guest reconfiguration is needed and then disabled again afterwards.

Entering the script directly into the initscript attribute

The customization block contains an initscript argument, into which you can directly insert the contents of the customization script. Here is a simple example that sets the hostname and network parameters (even though most of these settings are handled automatically by Guest Customization) for our VM running AlmaLinux 10.1.

  customization {
    enabled    = true
    initscript = <<-EOF
      #!/bin/bash
      hostnamectl hostname "${var.vm_name}"
      nmcli connection modify ens192 ipv4.addresses "${var.vm_ip}/${var.vm_ip_mask}" ipv4.gateway ${var.vm_gw} ipv4.method manual ipv4.dns ${var.vm_dns} ipv6.method disable
      nmcli device reapply ens192
    EOF
  } 

Using the templatefile function to load the script

It may be cleaner to store the script in a separate file and use the templatefile function, which loads it and substitutes the specified variables.

We will create a file called initscript.tftpl and place our script content inside it.

#!/bin/bash
hostnamectl hostname "${vm_name}"
nmcli connection modify ens192 ipv4.addresses ${vm_ip_mask} ipv4.gateway ${vm_gw} ipv4.method manual ipv4.dns ${vm_dns} ipv6.method disable
nmcli device reapply ens192

We then update the customization block as follows.

  customization {
    enabled    = true
    initscript = templatefile("initscript.tftpl", { vm_name = var.vm_name, vm_ip_mask = "${var.vm_ip}/${var.vm_ip_mask}", vm_gw = var.vm_gw, vm_dns = var.vm_dns })    
  } 
Visual Studio Code - Terraform project - main.tf file

Running part of the script before or after customization

The VMware documentation states that the script is called with the precustomization parameter before customization and the postcustomization parameter after customization. Conditions can be used to control which commands should run at which point.

Note: In practice, I have experienced (seemingly only sometimes) that the network connection gets renamed from ens192 to VMware customization ens192. I therefore added a command to the script that renames it back.

#!/bin/bash
if [ x$1 == x"precustomization" ]; then
echo "Do Precustomization tasks"
elif [ x$1 == x"postcustomization" ]; then
echo "Do Postcustomization tasks"
hostnamectl hostname "${vm_name}"
nmcli connection modify "VMware customization ens192" connection.id ens192
nmcli connection modify ens192 ipv4.addresses ${vm_ip_mask} ipv4.gateway ${vm_gw} ipv4.method manual ipv4.dns ${vm_dns} ipv6.method disable
nmcli device reapply ens192
fi

In some cases, it may be necessary to replace == with = in the comparison. VCloud Guest Customization Script : [: postcustomization: unexpected operator

Applying configuration changes

Once the configuration is ready, we run the update using the Terraform CLI command terraform apply. The VM is created and customization is configured, including our script, which will run after the VM starts.

Only part of the output is shown below.

PS D:\VCD-terraform> terraform apply  
data.vcd_catalog.my-catalog: Reading...
...
Terraform will perform the following actions:
...
  # vcd_vapp_vm.DemoVM will be created
  + resource "vcd_vapp_vm" "DemoVM" {
...
      + customization {
          + admin_password                      = (sensitive value)
          + allow_local_admin_password          = (known after apply)
          + auto_generate_password              = (known after apply)
          + change_sid                          = (known after apply)
          + enabled                             = true
          + initscript                          = <<-EOT
              #!/bin/bash
              if [ x$1 == x"precustomization" ]; then
              echo "Do Precustomization tasks"
              elif [ x$1 == x"postcustomization" ]; then
              echo "Do Postcustomization tasks"
              hostnamectl hostname "Demo-VM"
              nmcli connection modify "VMware customization ens192" connection.id ens192
              nmcli connection modify ens192 ipv4.addresses 172.30.21.100/22 ipv4.gateway 172.30.20.1 ipv4.method manual ipv4.dns 172.30.20.1 ipv6.method disable
              nmcli device reapply ens192
              fi
            EOT
          + join_domain                         = (known after apply)
          + join_domain_account_ou              = (known after apply)
          + join_domain_name                    = (known after apply)
          + join_domain_password                = (sensitive value)
          + join_domain_user                    = (known after apply)
          + join_org_domain                     = (known after apply)
          + must_change_password_on_first_login = (known after apply)
          + number_of_auto_logons               = (known after apply)
        }
...

Disabling customization on the VM

After the VM's first boot, during which customization ran, it is recommended to disable Guest OS Customization. This means updating the customization block configuration by setting the argument enabled = false, and then running terraform apply again.

  customization {
    enabled    = false
    initscript = templatefile("setup.tftpl", { vm_name = var.vm_name, vm_ip_mask = var.vm_ip_mask, vm_gw = var.vm_gw, vm_dns = var.vm_dns })    
  } 

Note: I tried to find a way to trigger these two steps at once, but did not find a solution. Based on various discussions, this is a known limitation of the declarative approach.

Guest OS Customization logs

VMware Cloud Director saves our provided script (on Linux) to the file /root/.customization/customize.sh in the guest OS. Guest OS Customization logs are stored in the /var/log/vmware-imc/ directory. The primary log file is toolsDeployPkg.log, and optionally also customization.log.

Cloud-init

I came across mentions of other ways to customize the OS configuration of new VMs. One such approach is cloud-init. I have not tested it hands-on, but a description can be found in the article Using cloud-init for Customization with VCD and Terraform.

Author:

Related articles:

Infrastructure as Code - Terraform

Infrastructure as Code (IaC) tools allow you to define, deploy, and manage infrastructure in a declarative (or imperative) way using configuration files. We describe the resources (servers, networks, storage, etc.) in a text file that defines the desired state. The tool ensures that the real environment matches the definition. For now, we will focus on the Terraform tool.

Virtualization

Articles from popular topics about virtualization of servers and workstations.

If you want write something about this article use comments.

Comments

There are no comments yet.

Add comment

Insert tag: strong em link

Help:
  • maximum length of comment is 2000 characters
  • HTML tags are not allowed (they will be removed), you can use only the special tags listed above the input field
  • new line (ENTER) ends paragraph and start new one
  • when you respond to a comment, put the original comment number in squar brackets at the beginning of the paragraph (line)