Automating Ubuntu-20.04 live-server template generation with packer vsphere-iso build.
Published: 2021-01-10 16:15:13

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.

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

## subiquity

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](https://ubuntu.com/server/docs/install/autoinstall)**.

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

## Overview

* user-data
* packer file
* variable file

## Lets take a look one by one

```shell
#cloud-config
autoinstall:
    version: 1
    early-commands:
        # Stop ssh for packer
        - sudo systemctl stop ssh
    locale: en_US
    keyboard:
        layout: en
        variant: us
    identity:
        hostname: ubuntu-server
        username: ubuntu
        password: '$6$rounds=4096$NYG7e8HxIMgz1$BqP28Ppt0FqXiBQuiE6PxiVBJJJAbm8tJrNz4HC7MEC.7Gv/eOyQIfaLqZ6W6fnMMtxP.BYfHmTBxUFQQs0u91'
    ssh:
        install-server: yes
        allow-pw: yes
    storage:
        layout:
            name: direct
    apt:
        primary:
            - arches: [i386, amd64]
              uri: "http://ro.archive.ubuntu.com/ubuntu/"
    user-data:
      disable_root: false
    late-commands:
      - sed -i -e 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /target/etc/ssh/sshd_config
      - sed -i -e 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' /target/etc/ssh/sshd_config
      - echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu
      - curtin in-target --target=/target -- chmod 440 /etc/sudoers.d/ubuntu
      - curtin in-target --target=/target -- apt-get update
      - 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:

```shell
mkpasswd -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](https://ubuntu.com/server/docs/install/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:

```shell
{
  "builders": [
    {
      "CPUs": 2,
      "RAM": 2048,
      "RAM_reserve_all": true,
      "firmware": "bios",
      "boot_command": [
         "<esc><esc><esc>",
         "<enter><wait>",
         "/casper/vmlinuz ",
         "root=/dev/sr0 ",
         "initrd=/casper/initrd ",
         "autoinstall ",
         "ds=nocloud-net;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/",
         "<enter>"
      ],
      "boot_wait": "2s",
      "convert_to_template": "true",
      "create_snapshot": "false",
      "datacenter": "{{user `vcenter_datacenter`}}",
      "datastore": "{{user `vcenter_datastore`}}",
      "disk_controller_type": "pvscsi",
      "folder": "{{user `vcenter_folder`}}",
      "guest_os_type": "ubuntu64Guest",
      "host": "{{user `vcenter_host`}}",
      "http_directory": "{{user `http_directory`}}",
      "insecure_connection": "true",
      "iso_checksum": "{{user `iso_checksum_type`}}:{{user `iso_checksum`}}",
      "iso_paths": "{{user `iso_paths`}}",
      "name": "Ubuntu-20.04",
      "network_adapters": [
        {
          "network": "{{user `vcenter_network`}}",
          "network_card": "vmxnet3"
        }
      ],
      "notes": "Default SSH User: {{user `ssh_username`}}\nDefault SSH Pass: {{user `ssh_password`}}\nBuilt by Packer @ {{isotime \"2006-01-02 03:04\"}}.",
      "password": "{{user `vcenter_password`}}",
      "shutdown_command": "echo 'ubuntu'|sudo -S shutdown -P now",
      "ssh_password": "{{user `ssh_password`}}",
      "ssh_timeout": "20m",
      "ssh_handshake_attempts": "100000",
      "ssh_username": "{{user `ssh_username`}}",
      "storage": [
        {
          "disk_size": 20480,
          "disk_thin_provisioned": true
        }
      ],
      "type": "vsphere-iso",
      "username": "{{user `vcenter_username`}}",
      "vcenter_server": "{{user `vcenter_server`}}",
      "vm_name": "{{user `vm_name`}}"
    }
  ],
  "provisioners": [
    {
       "type": "shell",
       "execute_command": "echo 'ubuntu' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
       "script": "scripts/setup_ubuntu2004.sh"
     }
   ]
}
```
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 `https://ubuntu-cloud-init.vercel.app/` for my enterprise configuration.

At least we need to define variable file. Here is mine:
```shell
{
  "vm_name": "Ubuntu-2004-Template",  
  "vcenter_server":"",
  "vcenter_username":"",
  "vcenter_password":"",
  "vcenter_datacenter": "",
  "vcenter_datastore": "",
  "vcenter_host": "",
  "vcenter_folder": "",
  "vcenter_network": "",
  "guest_os_type": "ubuntu64Guest",
  "http_directory": "http",
  "iso_urls": "https://releases.ubuntu.com/focal/ubuntu-20.04.1-live-server-amd64.iso",
  "iso_checksum_type": "sha256",
  "iso_checksum": "443511f6bf12402c12503733059269a2e10dec602916c0a75263e5d990f6bb93",
  "iso_paths": "[Datastore VMS] /packer_cache/ubuntu-20.04.1-live-server-amd64.iso",
  "ssh_username": "ubuntu",
  "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](https://github.com/vmware/cloud-init-vmware-guestinfo)**.
So I added a script in `provisioners` line in packer file:
```shell
  "provisioners": [
    {
       "type": "shell",
       "execute_command": "echo 'ubuntu' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
       "script": "scripts/setup_ubuntu2004.sh"
     }
   ]
```
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](https://github.com/efe136/packer-ubuntu20.04)**.