Using Cloudbase-Init in VMware vRealize Automation Cloud blueprints

August 4, 2020

Tweet This:
Share on LinkedIn:

By Dana Gertsch, Kovarus Senior Consultant

The following article demonstrates how to use Cloudbase-Init in a VMware vRealize Automation Cloud (vRA Cloud) blueprint.

The customer use case called for the following without using any external orchestration tools (vRO / ABX):

  1. Add a new user to the local admin group.
  2. Enable RDP.
  3. Set a static IP address, subnet, gateway, and DNS settings.
  4. Automatically format all of the attached disks.
  5. Set the OS hostname.
  6. Set the OS time zone.
  7. Disable IPv6 on the primary interface.

I’ll admit that I tinkered with Cloudbase-Init a few times in the past, generally without success. However, something clicked with this attempt.

First I built a basic Windows 2019 server, installed VMware Tools then followed the directions on this blog article to install and configure cloudbase-init. I used version 1.1.1 for this example.

Then I reconfigured the machine to allow for CD-ROM passthrough, converted it to a template and let vRA Cloud discover it. Then added a new Image Mapping and updated the Flavor Mappings.

I wanted to use PowerShell to meet the customers’ requirements. Looking at userdata in the documentation indicated I could just add #ps1 or #ps1_sysnative to my CloudConfig. Of course, it wasn’t that easy.

Adding #ps1_sysnative under CloudConfig resulted in failures when deploying the machine. Here is a very basic broken example. (Don’t do this!)

cloudConfig: |
  #cloud-config
  write_files:
    content: cloudbase-init test
    path: c:\test.txt
  #ps1_sysnative
  New-NetIpAddress -InterfaceAlias Ethernet0 -IPAddress ${input.ipaddr} -PrefixLength ${input.subnetMask} -DefaultGateway ${input.gateway}

Looking at C:\Program Files\Cloudbase Solutions\Cloudbase-init\log\cloudbase-init.log I found C:\Program Files\Cloudbase Solutions\Cloudbase-init\Python\lib\site-package\cloudbaseinit\plugins\common\userdata.py was throwing errors when parsing userdata.

When in doubt, reverse engineer. So, looking at this script, I found it was looking for either #cloud-config or Content-Type: multipart in the header.

After some digging, I found an old Cloud-Init multipart example. Copying a portion of the sample into my blueprint worked. Now to flesh it out.

After some tinkering and testing, I finally came up with a Cloudbase-Init configuration that met the customer’s requirements.

cloudConfig: |
  Content-Type: multipart/mixed; boundary="===123456789"
  MIME-Version: 1.0   

  --===123456789
  Content-Type: text/cloud-config; charset="us-ascii"
  MIME-Version: 1.0
  Content-Transfer-Encoding: 7bit
  Content-Disposition: attachment; filename="cloud-config"

  set_timezone: US/Pacific
  set_hostname: ${input.hostname}

  --===123456789
  Content-Type: text/x-shellscript; charset="us-ascii"
  MIME-Version: 1.0
  Content-Transfer-Encoding: 7bit
  Content-Disposition: attachment; filename="doPsStuff.ps1"

  #ps1_sysnative
  New-NetIpAddress -InterfaceAlias Ethernet0 -IPAddress ${input.ipaddr} -PrefixLength ${input.subnetMask} -DefaultGateway ${input.gateway}
  Set-DnsClientServerAddress -InterfaceAlias Ethernet0 -ServerAddresses("${input.dnsServer}")
  Set-DnsClient -InterfaceAlias Ethernet0 -ConnectionSpecificSuffix "${input.dnsSuffix}"
  Set-DnsClientGlobalSetting -SuffixSearchList @("${input.dnsSuffix}")
  Get-Disk | Where-Object PartitionStyle -Eq "RAW" | Initialize-Disk -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume
  Disable-NetAdapterBinding -InterfaceAlias "Ethernet0" -ComponentID ms_tcpip6
  Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
  Set-Service -Name TermService -StartType Automatic -Status Running
  Set-Service -Name UmRdpService -Status Running
  Set-Service -Name SessionEnv -Status Running
  Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server'-name "fDenyTSConnections" -Value 0
  $password = ConvertTo-SecureString ${input.newUserPassword} -AsPlainText -Force
  $newUser = New-LocalUser -Name "${input.newUser}" -Password $password -FullName "CAS Added Admin User" -Description "CAS Added Admin User"
  Add-LocalGroupMember -Group "Administrators" -Member "${input.newUser}"

Now to prove it worked. After deploying the machine and logging in as the new admin user (casadminuser) via RDP, you can see the machine name (dg-test1206), assigned IP information (10.44.14.206), and a formatted and mounted data disk (E:).

And that IPv6 has been disabled.

Some notes about this solution.

  1. The boundary set in the header needs to be prepended with “‐‐” and match the value in the header. Here is used ===123456789. Each part starts with ‐‐===123456789.
  2. The initial article published by VMware has you set first_logonbehavior to always. This forces you to change the administrator’s password on first boot. The new admin user will need to update the password to protect the console.
  3. Using a static IP in Cloudbase bypasses the pool assigned to the network in vRA Cloud.
  4. This is also true if you are using an external IPAM solution.

The working blueprint containing the multipart Cloudbase configuration is available on this GitHub repository.

I hope this provides another building block to help you use vRA Cloud.


Looking to learn more about modernizing and automating IT? We created the Kovarus Proven Solutions Center (KPSC) to let you see what’s possible and learn how we can help you succeed. To learn more about the KPSC go to the KPSC page.

Also, follow Kovarus on LinkedIn for technology updates from our experts along with updates on Kovarus news and events.