Skip to content

Instantly share code, notes, and snippets.

@craig-m-unsw
Last active February 21, 2024 10:08
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save craig-m-unsw/f457a9a189cd939a73b7d215c22b96e2 to your computer and use it in GitHub Desktop.
Save craig-m-unsw/f457a9a189cd939a73b7d215c22b96e2 to your computer and use it in GitHub Desktop.
Parallels (MacOS M1 host) Packer + Vagrant of Ubuntu 20.04 arm64. Installed with cloud-init and configured with Ansible.

parallels Packer (arm64)

A simple Packer + Vagrant install of Ubuntu 20.04 (Focal Fossa) LTS server for arm64, to run from my M1 Mac on Parallels Pro (17.1). Currently on MacOS Monterey.

Packer will run the ansible playbook.yml before the machine is shutdown and exported.

The installation is automated by cloud-init (which reads user-data). The file meta-data just needs to be an empty text file (you need to create this - no blank files or folders allowed in gists).

ubuntu2004/
        ├──http/
        │    ├── meta-data
        │    └── user-data
        ├── .gitignore
        ├── README.md
        ├── playbook.yml
        ├── Ubuntu.pkr.hcl
        └── Vagrantfile

using

git clone https://gist.github.com/craig-m-unsw/f457a9a189cd939a73b7d215c22b96e2 ubuntu2004
cd ubuntu2004
mkdir http
touch http/meta-data
mv user-data http/user-data
cat <<'EOF' >> .gitignore
.vagrant
.DS_Store
output-ubuntu-*
*.log
*.box
EOF

Building:

This will produce ubuntu2004.box, a virtual machine image.

ubnfunc_build () {
    export PACKER_LOG=2
    export PACKER_LOG_PATH=$(pwd)/packer.log
    packer validate Ubuntu.pkr.hcl && packer build Ubuntu.pkr.hcl
};

ubnfunc_build

Running:

The Vagrantfile supports multiple VM, each with own static IP. Edit as needed.

vagrant validate Vagrantfile
vagrant up && vagrant ssh admin
exit

This will start the virtual machine/s and log you into them.

Cleaning:

Rinse and repeat.

ubnfunc_clean () {
    vagrant destroy -f
    vagrant box remove file://ubuntu.box -f
    rm -fv -- packer.log ubuntu2004.box
};

ubnfunc_clean
ubnfunc_build

docs

---
# playbook for local run by Packer
- name: "ansible playbook for Ubuntu"
hosts: all
gather_facts: yes
vars:
package_install: [
{ add_item: 'lsof' },
{ add_item: 'dos2unix' },
{ add_item: 'vim' },
{ add_item: 'make' },
{ add_item: 'jq' },
{ add_item: 'tcpdump' },
{ add_item: 'socat' },
{ add_item: 'expect' },
{ add_item: 'sshfs' },
{ add_item: 'gnupg2' },
{ add_item: 'inotify-tools' },
{ add_item: 'apparmor-utils' },
{ add_item: 'monitoring-plugins-common' },
{ add_item: 'monitoring-plugins-basic' },
{ add_item: 'ca-certificates' },
{ add_item: 'lsb-release' },
{ add_item: 'acl' }
]
package_remove: [
{ del_item: 'telnet' }
]
create_folders: [
{ dir: '/opt/data/', mode: '0775', owner: 'root', group: 'root' },
{ dir: '/home/ubuntu/downloads/', mode: '0700', owner: 'ubuntu', group: 'ubuntu' },
{ dir: '/home/ubuntu/upload/', mode: '0700', owner: 'ubuntu', group: 'ubuntu' },
{ dir: '/root/tmp/', mode: '0700', owner: 'root', group: 'root' },
{ dir: '/mnt/sshfs/', mode: '0775', owner: 'root', group: 'root' }
]
newuser_name: "cluster"
newuser_home: "/opt/cluster"
tasks:
- name: "checks and debug"
block:
- name: "OS check"
assert:
that:
- ansible_os_family == "Debian"
- ansible_architecture in ['aarch64']
- name: "Display hostname"
debug:
msg: "inventory_hostname {{ inventory_hostname }}"
- name: "cloud init files"
block:
- name: "wait for cloud-init vmhost.fact"
wait_for:
path: /root/welcome.txt
search_regex: Welcome
become: true
- name: "wait for cust-blacklist.conf"
wait_for:
path: /etc/modprobe.d/cust-blacklist.conf
search_regex: blacklist
- name: "wait for cloud-init vmhost.fact"
wait_for:
path: /etc/ansible/facts.d/vmhost.fact
- name: "cloud init mod"
block:
- name: "Gather all facts of cloud init"
cloud_init_data_facts:
filter: status
register: res
until: "res.cloud_init_data_facts.status.v1.stage is defined and not res.cloud_init_data_facts.status.v1.stage"
retries: 50
delay: 5
- name: "system config"
block:
- name: "create new user"
ansible.builtin.user:
name: "{{ newuser_name }}"
comment: "clust user"
home: "{{ newuser_home }}"
shell: /usr/bin/bash
create_home: false
- name: "homedir for {{ newuser_name }}"
ansible.builtin.file:
path: "{{ newuser_home }}"
state: directory
mode: "0775"
owner: "{{ newuser_name}}"
group: "{{ newuser_name }}"
- name: "files and folders"
block:
- name: "create common folders"
ansible.builtin.file:
path: "{{ item.dir }}"
state: directory
mode: "{{ item.mode }}"
owner: "{{ item.owner }}"
group: "{{ item.group }}"
with_items: "{{ create_folders }}"
become: true
- name: "software"
block:
- name: "install OS packages"
ansible.builtin.package:
name: "{{ item.add_item }}"
state: present
retries: 3
with_items: "{{ package_install }}"
become: true
tags:
- software
- name: "remove OS packages"
ansible.builtin.package:
name: "{{ item.del_item }}"
state: absent
with_items: "{{ package_remove }}"
become: true
tags:
- software
- name: "patch"
ansible.builtin.apt:
name: "*"
state: latest
become: true
tags:
- software
- name: "Finish up"
block:
- name: "create readme"
ansible.builtin.copy:
dest: /home/ubuntu/welcome.txt
mode: 0644
content: |
# --- packer image details ---
# distro: {{ ansible_distribution }} {{ ansible_distribution_version }} {{ ansible_architecture }}
# ansible: {{ ansible_version }}
# python: {{ ansible_playbook_python }}
- name: "debug note"
debug:
msg: "-- playbook.yml has finished --"
# Ubuntu Packer HCL
packer {
required_version = ">= 1.8.0"
}
variable "iso_checksum" {
type = string
default = "d6fea1f11b4d23b481a48198f51d9b08258a36f6024cb5cec447fe78379959ce"
}
variable "iso_url" {
type = string
default = "https://cdimage.ubuntu.com/releases/20.04/release/ubuntu-20.04.3-live-server-arm64.iso"
}
# boot_command issues solved by:
# https://github.com/hashicorp/packer/issues/9115
source "parallels-iso" "ubuntu-pa" {
boot_command = [
"<tab><tab><tab><tab><tab><c><wait><bs><bs>",
"linux /casper/vmlinuz --- autoinstall ds=\"nocloud-net;seedfrom=http://{{.HTTPIP}}:{{.HTTPPort}}/\"<enter><wait>",
"initrd /casper/initrd<enter><wait>",
"boot<enter>"
]
boot_wait = "5s"
communicator = "ssh"
cpus = "4"
disk_size = "32768"
guest_os_type = "ubuntu"
iso_checksum = "${var.iso_checksum}"
iso_url = "${var.iso_url}"
memory = "4096"
shutdown_command = "sudo -S shutdown now"
ssh_username = "ubuntu"
ssh_password = "ubuntu"
ssh_port = "22"
ssh_timeout = "2h"
parallels_tools_mode = "disable"
parallels_tools_flavor = "lin"
prlctl_version_file = ".prlctl_version"
vm_name = "ubuntu-pa"
http_directory = "http"
}
build {
sources = ["source.parallels-iso.ubuntu-pa"]
provisioner "shell" {
inline = ["sudo -S cloud-init status --wait"]
timeout = "15m0s"
}
provisioner "ansible-local" {
playbook_file = "playbook.yml"
pause_before = "10s"
timeout = "10m0s"
}
provisioner "shell" {
expect_disconnect = true
inline = ["sudo -S reboot now"]
pause_before = "10s"
timeout = "10m0s"
}
post-processor "vagrant" {
keep_input_artifact = false
compression_level = 0
output = "ubuntu2004.box"
}
}
#cloud-config
autoinstall:
version: 1
early-commands:
- systemctl stop ssh
locale: en_US.UTF-8
apt:
geoip: true
keyboard:
layout: en
variant: us
storage:
layout:
name: lvm
identity:
hostname: ubuntu
username: ubuntu
# pass is ubuntu
password: $6$rounds=4096$8dkK1P/oE$2DGKKt0wLlTVJ7USY.0jN9du8FetmEr51yjPyeiR.zKE3DGFcitNL/nF1l62BLJNR87lQZixObuXYny.Mf17K1
ssh:
install-server: yes
allow-pw: true
packages:
- basez
- dialog
- vim
- tmux
- git
- uuid
- software-properties-common
- apt-transport-https
- python3-pip
- python3-venv
- python3-dev
user-data:
disable_root: false
package_update: true
package_upgrade: true
write_files:
- path: /etc/sudoers.d/ubuntu
owner: 'root:root'
permissions: '0644'
defer: true
content:
ubuntu ALL=(ALL) NOPASSWD:ALL
- path: /root/welcome.txt
owner: 'root:root'
permissions: '0644'
defer: true
content:
Welcome to Ubuntu root.
- encoding: b64
path: /etc/modprobe.d/cust-blacklist.conf
owner: 'root:root'
permissions: '0644'
defer: true
content: YmxhY2tsaXN0IGpveWRldgpibGFja2xpc3Qgc25kX2hkYV9pbnRlbApibGFja2xpc3Qgc25kX2hkYV9jb2RlYwpibGFja2xpc3Qgc25kX2hkYV9jb2RlY19nZW5lcmljCmJsYWNrbGlzdCBzbmRfaGRhX2NvcmUKYmxhY2tsaXN0IHNuZF9wY20KYmxhY2tsaXN0IHNuZF9od2RlcApibGFja2xpc3Qgc25kX3RpbWVyCmJsYWNrbGlzdCBzbmQKYmxhY2tsaXN0IHNvdW5kY29yZQ==
- encoding: b64
path: /etc/ansible/facts.d/vmhost.fact
owner: 'root:root'
permissions: '0755'
defer: true
content: IyEvdXNyL2Jpbi9lbnYgc2gKZWNobyAie1widm0tY2xvdWQtaW5pdFwiIDogXCJ0cnVlXCJ9Ig==
late-commands:
- curtin in-target --target=/target -- add-apt-repository --yes --update ppa:ansible/ansible
- curtin in-target --target=/target -- apt install ansible -y -q
- mkdir /target/etc/ansible/facts.d
- 'sed -i "s/dhcp4: true/&\n dhcp-identifier: mac/" /target/etc/netplan/00-installer-config.yaml'
- uptime
- echo finished
# Ubuntu Vagrantfile
Vagrant.require_version ">= 2.2.18"
servers=[
{ :hostname => "admin", :ip => "192.168.50.10", :ram => 1024, :cpu => 2 },
{ :hostname => "node1", :ip => "192.168.50.11", :ram => 512, :cpu => 1 },
{ :hostname => "node2", :ip => "192.168.50.12", :ram => 512, :cpu => 1 },
{ :hostname => "node3", :ip => "192.168.50.13", :ram => 512, :cpu => 1 }
]
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu2004.box"
config.vm.box_check_update = false
config.ssh.username = "ubuntu"
config.ssh.password = "ubuntu"
config.ssh.insert_key = true
config.ssh.keep_alive = true
config.ssh.compression = false
config.ssh.forward_agent = false
config.vm.synced_folder ".", "/home/ubuntu/shares",
type: "rsync",
disabled: true
servers.each do |machine|
config.vm.define machine[:hostname] do |node|
node.vm.hostname = machine[:hostname]
node.vm.network "private_network", ip: machine[:ip]
node.vm.provider "parallels" do |prl|
prl.memory = machine[:ram]
prl.cpus = machine[:cpu]
prl.check_guest_tools = false
end
end
end
end
@rcousens
Copy link

rcousens commented Apr 6, 2022

Dude, I f*cking love you! This gist just rescued me from 16 hours of QEMU and VMware Fusion Tech Preview hell trying to get a basic packer Ubuntu box built on an M1 Mac so I can friggin' iterate on my ansible code locally using Vagrant without firing up cloud machines all over the place.

CAN I BUY YOU A BEER?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment