Featured image of post Automating Ubuntu-20.04 live-server template generation with packer vsphere-iso build.

Automating Ubuntu-20.04 live-server template generation with packer vsphere-iso build.

Ubuntu is discontinuing support for the Debian-installer (preseed). Ubuntu Server 20.04 comes with a new automated OS installation method called autoinstall with subiquity server installer. This post shows packer build with new installer.


This setup is only for Ubuntu-20.04 live-server not legacy.


Subiquity is the Ubuntu server’s new automated installer, which was introduced in 18.04. The setup for autoinstallation is given by the cloud-init configuration. If set, values will be taken from the config file, otherwise default values will be used. There are different ways of delivering cloud-init configuration. User configuration is usually contained in user-data and cloud configuration in meta-data files. For more detailed info you can look The AutoInstall documentation provided by Canonical.

Our installer is based on curtin, netplan and cloud-init.


  • user-data
  • packer file
  • variable file

Lets take a look one by one

 3    version: 1
 4    early-commands:
 5        # Stop ssh for packer
 6        - sudo systemctl stop ssh
 7    locale: en_US
 8    keyboard:
 9        layout: en
10        variant: us
11    identity:
12        hostname: ubuntu-server
13        username: ubuntu
14        password: '$6$rounds=4096$NYG7e8HxIMgz1$BqP28Ppt0FqXiBQuiE6PxiVBJJJAbm8tJrNz4HC7MEC.7Gv/eOyQIfaLqZ6W6fnMMtxP.BYfHmTBxUFQQs0u91'
15    ssh:
16        install-server: yes
17        allow-pw: yes
18    storage:
19        layout:
20            name: direct
21    apt:
22        primary:
23            - arches: [i386, amd64]
24              uri: ""
25    user-data:
26      disable_root: false
27    late-commands:
28      - sed -i -e 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /target/etc/ssh/sshd_config
29      - sed -i -e 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' /target/etc/ssh/sshd_config
30      - echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu
31      - curtin in-target --target=/target -- chmod 440 /etc/sudoers.d/ubuntu
32      - curtin in-target --target=/target -- apt-get update
33      - curtin in-target --target=/target -- apt-get upgrade --yes

As you see it is .yaml formatted config. We have specifeied all the details (what we need otherwise will use default) in this config. But we need to understand some tricky parts. Firstly on identity part as you see we have used hashed password. It is default requirement of autoinstaller for security purpose. Actually we need more for deeply secure system but at that moment it is okay. But how we generate this hashed password ? Actually I used ubuntu as the password for ssh connection and for making it hashed I used below command:

1mkpasswd -m SHA-512 --rounds=4096

Now lets move a little bit up and find out early-commands. What it means? Actually when we start packer build, packer try to set ssh connection from the beginning to our machine. So During initialization it could gives us error can break the build. So at the first stage we need to be sure that our ssh connection is stopped. For understanding all the parts you can look Ubuntu Autoinstall Reference. Fortunately documentation are very simple and useful provided by Canonical.

Now we need to create meta-data file. But we will keep it empty. At last we need to create http directory and put these files into it.

Now we need to define our packer file:

 2  "builders": [
 3    {
 4      "CPUs": 2,
 5      "RAM": 2048,
 6      "RAM_reserve_all": true,
 7      "firmware": "bios",
 8      "boot_command": [
 9         "<esc><esc><esc>",
10         "<enter><wait>",
11         "/casper/vmlinuz ",
12         "root=/dev/sr0 ",
13         "initrd=/casper/initrd ",
14         "autoinstall ",
15         "ds=nocloud-net;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/",
16         "<enter>"
17      ],
18      "boot_wait": "2s",
19      "convert_to_template": "true",
20      "create_snapshot": "false",
21      "datacenter": "{{user `vcenter_datacenter`}}",
22      "datastore": "{{user `vcenter_datastore`}}",
23      "disk_controller_type": "pvscsi",
24      "folder": "{{user `vcenter_folder`}}",
25      "guest_os_type": "ubuntu64Guest",
26      "host": "{{user `vcenter_host`}}",
27      "http_directory": "{{user `http_directory`}}",
28      "insecure_connection": "true",
29      "iso_checksum": "{{user `iso_checksum_type`}}:{{user `iso_checksum`}}",
30      "iso_paths": "{{user `iso_paths`}}",
31      "name": "Ubuntu-20.04",
32      "network_adapters": [
33        {
34          "network": "{{user `vcenter_network`}}",
35          "network_card": "vmxnet3"
36        }
37      ],
38      "notes": "Default SSH User: {{user `ssh_username`}}\nDefault SSH Pass: {{user `ssh_password`}}\nBuilt by Packer @ {{isotime \"2006-01-02 03:04\"}}.",
39      "password": "{{user `vcenter_password`}}",
40      "shutdown_command": "echo 'ubuntu'|sudo -S shutdown -P now",
41      "ssh_password": "{{user `ssh_password`}}",
42      "ssh_timeout": "20m",
43      "ssh_handshake_attempts": "100000",
44      "ssh_username": "{{user `ssh_username`}}",
45      "storage": [
46        {
47          "disk_size": 20480,
48          "disk_thin_provisioned": true
49        }
50      ],
51      "type": "vsphere-iso",
52      "username": "{{user `vcenter_username`}}",
53      "vcenter_server": "{{user `vcenter_server`}}",
54      "vm_name": "{{user `vm_name`}}"
55    }
56  ],
57  "provisioners": [
58    {
59       "type": "shell",
60       "execute_command": "echo 'ubuntu' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
61       "script": "scripts/"
62     }
63   ]

I need mention some tricky points now. So as you see I used user keyword on packer file. It is for defining variable. Another most important part is ds=nocloud-net;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/. So what it means ? As you remember I created http directory and put config files into it. So we need a web server to serve these files in order packer use them during build process. When the build is executed, Packer will launch a small HTTP server and replace the {{.HTTPIP}} and {.HTTPPort}} variables with the respective IP and port. You must also set the http_directory (I already defined in packer file) configuration option to define which directory hosts the files that you want to be served by the HTTP server on your filesystem. Bu as you wish you can also serve your files with different ways. For example I have uploaded config files to my custom static server and served through this server. I mean instead of http://{{ .HTTPIP }}:{{ .HTTPPort }}/ I used my own server for my enterprise configuration.

At least we need to define variable file. Here is mine:

 2  "vm_name": "Ubuntu-2004-Template",  
 3  "vcenter_server":"",
 4  "vcenter_username":"",
 5  "vcenter_password":"",
 6  "vcenter_datacenter": "",
 7  "vcenter_datastore": "",
 8  "vcenter_host": "",
 9  "vcenter_folder": "",
10  "vcenter_network": "",
11  "guest_os_type": "ubuntu64Guest",
12  "http_directory": "http",
13  "iso_urls": "",
14  "iso_checksum_type": "sha256",
15  "iso_checksum": "443511f6bf12402c12503733059269a2e10dec602916c0a75263e5d990f6bb93",
16  "iso_paths": "[Datastore VMS] /packer_cache/ubuntu-20.04.1-live-server-amd64.iso",
17  "ssh_username": "ubuntu",
18  "ssh_password": "ubuntu"

You can use your own custom details for empty vcenter variables. I have used iso_path variable on packer file. But Also I have added iso_url variable to variable file. So If you dont have ready Ubuntu-20.04 live-server then you can basically change iso_path variable with iso_url in packer file.

This builds a VM Template in vSphere based on Ubuntu 20.04, with a predefined ubuntu/ubuntu user. Although this image is still really helpful, I would like to add another phase to my image. I plan to use this image with Rancher’s vSphere Provisioner which means I need the datasource for vSphere cloud-init. It’s available here as a plugin. So I added a script in provisioners line in packer file:

1  "provisioners": [
2    {
3       "type": "shell",
4       "execute_command": "echo 'ubuntu' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
5       "script": "scripts/"
6     }
7   ]

This sets up the datasource and resets the machine id to reset the state of the machine. You can find both packer file and configs and also script from my github repo.

comments powered by Disqus