UTM icon indicating copy to clipboard operation
UTM copied to clipboard

Allow creating the VM with `utmctl`

Open onnimonni opened this issue 1 year ago • 2 comments

Hey!

There currently exists a workaround to create the VM following these steps: https://blog.vkhitrin.com/creating-a-utm-virtual-machine-from-cli/ but it's very very cumbersome.

Would it be possible to extend the utmctl and add subcommand create which would take the ISO file and few flags?

I'm most interested in Linux virtualization with the Enable Rosetta (x86_64 Emulation) option at this case because it's the best way to cross-compile x86_64-linux from Apple silicon machines at the moment.

onnimonni avatar Sep 23 '24 06:09 onnimonni

+1 for this feature request.

Given the amount of configuration needed for VM. It would be better to split this into utmctl vm create to create VM instance and then utmctl vm edit to edit config details.

Side note: May I know why are you referring to such a hard , convoluted and complex route to create VM using cli, when a simple recommended way exists.

The approach in the blog you shared, uses

  • uuidgen
  • PlistBuddy
  • swift
  • hdiutil

You can do the same using Apple Script and single osa command. Check out my Packer plugin scripts where I create and configure VM with simple script

naveenrajm7 avatar Sep 25 '24 20:09 naveenrajm7

Side note: May I know why are you referring to such a hard , convoluted and complex route to create VM using cli, when a simple recommended way exists.

This was the only way I was aware. The applescript you linked was much more cleaner indeed 👍

onnimonni avatar Sep 26 '24 06:09 onnimonni

I am trying to use utmctl and apple script to automate the deployment of home assistant. Struggling a little with my script. I keep getting errors on the apple script portion. Is this still possible to do with apple script and utmctl?

` #!/bin/bash

==============================================================================

Home Assistant UTM Installation Script for macOS (Fully Automated)

This script automates the entire process of installing Home Assistant OS

in a UTM virtual machine. It uses a combination of shell commands for

setup and native AppleScript to create and configure the VM.

==============================================================================

--- Helper Functions ---

Function to check if a command exists

command_exists() { command -v "$1" &> /dev/null }

--- Prerequisite Checks ---

echo "🔎 Checking for required tools..."

1. Check for Homebrew

if ! command_exists brew; then echo "🍺 Homebrew is not installed. It is required to install other tools." read -p "Do you want to install Homebrew now? (y/n) " response if [[ "$response" =~ ^[Yy]$ ]]; then echo "Installing Homebrew..." /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # Add brew to the PATH for the current script execution if [ -x "/opt/homebrew/bin/brew" ]; then eval "$(/opt/homebrew/bin/brew shellenv)" fi else echo "❌ Homebrew is required. Installation cancelled by user." exit 1 fi fi

2. Check for xz (used for decompression)

if ! command_exists xz; then echo "📦 The 'xz' utility is not installed. It is required to decompress the Home Assistant image." read -p "Do you want to install xz now using Homebrew? (y/n) " response if [[ "$response" =~ ^[Yy]$ ]]; then echo "Installing xz..." brew install xz else echo "❌ xz is required. Installation cancelled by user." exit 1 fi fi

3. Check for UTM (utmctl command-line tool)

if ! command_exists utmctl; then echo "🖥️ UTM or its command-line tools are not installed. It is required to create and manage the VM." read -p "Do you want to install UTM now using Homebrew? (y/n) " response if [[ "$response" =~ ^[Yy]$ ]]; then echo "Installing UTM..." brew install --cask utm else echo "❌ UTM is required. Installation cancelled by user." exit 1 fi fi

echo "✅ All required tools are installed." echo ""

--- First-time UTM Launch ---

echo "🚀 Launching UTM application..." echo "If this is your first time running UTM, please complete the initial setup and accept any agreements." open -a "UTM" read -p "Once UTM is open and ready, press [Enter] to continue the script..." echo ""

--- VM Configuration ---

VM_NAME="Home Assistant" VM_MEMORY=2048 # in MB VM_CPU_CORES=2

--- Main Installation Script ---

1. Check for latest HAOS version and download if needed

echo "🔎 Checking for the latest Home Assistant OS version..." LATEST_RELEASE_INFO=$(curl -s "https://api.github.com/repos/home-assistant/operating-system/releases/latest") LATEST_VERSION_NUM=$(echo "$LATEST_RELEASE_INFO" | grep '"tag_name":' | sed -E 's/."([^"]+)"./\1/')

if [ -z "$LATEST_VERSION_NUM" ]; then echo "❌ Could not fetch the latest Home Assistant OS version from GitHub. Please check your internet connection." exit 1 fi echo "👍 Latest version is: $LATEST_VERSION_NUM"

Define version-specific file paths

HAOS_IMAGE_URL="https://github.com/home-assistant/operating-system/releases/download/${LATEST_VERSION_NUM}/haos_ova-${LATEST_VERSION_NUM}.qcow2.xz" DOWNLOAD_PATH="$HOME/Downloads/haos_ova-${LATEST_VERSION_NUM}.qcow2.xz" IMAGE_PATH="$HOME/Downloads/haos_ova-${LATEST_VERSION_NUM}.qcow2" VERSION_FILE="$HOME/Downloads/haos.version"

Check local version and existence of the image

LOCAL_VERSION="" if [ -f "$VERSION_FILE" ]; then LOCAL_VERSION=$(cat "$VERSION_FILE") fi

if [ "$LOCAL_VERSION" == "$LATEST_VERSION_NUM" ] && [ -f "$IMAGE_PATH" ]; then echo "✅ You already have the latest version ($LATEST_VERSION_NUM) of Home Assistant OS." else if [ "$LOCAL_VERSION" != "$LATEST_VERSION_NUM" ] && [ ! -z "$LOCAL_VERSION" ]; then echo "🆙 A new version ($LATEST_VERSION_NUM) is available. Your local version is ($LOCAL_VERSION)." else echo "📂 Image file not found. Will download the latest version." fi

read -p "Do you want to download the latest version now? (y/n) " response
if [[ "$response" =~ ^[Yy]$ ]]; then
    echo "🔽 Downloading Home Assistant OS version $LATEST_VERSION_NUM..."
    curl -L -f "$HAOS_IMAGE_URL" -o "$DOWNLOAD_PATH"
    if [ $? -ne 0 ]; then
        echo "❌ Download failed. Please check the URL or your internet connection."
        rm -f "$DOWNLOAD_PATH" # Clean up partial download
        exit 1
    fi

    echo "📦 Decompressing image..."
    unxz -T0 "$DOWNLOAD_PATH"
    if [ $? -eq 0 ]; then
         echo "$LATEST_VERSION_NUM" > "$VERSION_FILE"
         echo "👍 Decompression successful."
    else
         echo "❌ Decompression failed. The downloaded file might be corrupt."
         exit 1
    fi
else
    echo "❌ Download cancelled. Cannot proceed without the OS image."
    exit 1
fi

fi echo ""

2. Dynamically find the active network interface

echo "🔎 Finding active network interface..." ACTIVE_INTERFACE=$(route -n get default | grep 'interface:' | awk '{print $2}')

if [ -z "$ACTIVE_INTERFACE" ]; then echo "❌ Could not determine active network interface. Exiting." exit 1 fi echo "👍 Found active interface: $ACTIVE_INTERFACE" echo ""

3. Generate a random, locally administered MAC address

echo "🎲 Generating random MAC address..." RANDOM_MAC=$(openssl rand -hex 6 | sed 's/(..)/\1:/g; s/.$//') echo "👍 Generated MAC: $RANDOM_MAC"

4. Stop and remove any existing VM with the same name to avoid conflicts

if utmctl list | grep -q "$VM_NAME"; then echo "⚠️ A VM named '$VM_NAME' already exists." read -p "Do you want to stop and remove it to create a new one? This action is irreversible. (y/n) " response if [[ "$response" =~ ^[Yy]$ ]]; then echo "Stopping and removing existing VM..." utmctl stop "$VM_NAME" --kill &> /dev/null utmctl delete "$VM_NAME" else echo "❌ Installation cancelled. The existing VM will not be modified." exit 1 fi fi

5. Permissions Check and Guidance

echo "🔐 Checking for Automation Permissions..." echo "This script uses AppleScript to create the VM. For this to work, your terminal application needs permission to control UTM." echo "The script will now attempt to create the VM. If it fails, please grant the necessary permissions in System Settings." read -p "Press [Enter] to continue..." echo ""

6. Automate VM Creation with Native AppleScript

echo "🤖 Automating VM creation in UTM via native AppleScript..."

Convert image path to POSIX path for AppleScript

POSIX_IMAGE_PATH=$(osascript -e "get POSIX path of "${IMAGE_PATH}"")

osascript <<EOD tell application "UTM" -- Step 1: Create a minimal VM with only the essentials, based on the working example set theVM to make new virtual machine with properties {¬ backend:qemu, ¬ configuration:{¬ name:"${VM_NAME}", ¬ architecture:"aarch64" ¬ } ¬ }

-- Step 2: Get the configuration object of the new VM
set theConfig to configuration of theVM

-- Step 3: Set the remaining properties on the configuration object
set memory size of theConfig to ${VM_MEMORY}
set cpu count of theConfig to ${VM_CPU_CORES}

-- Step 4: Clear default drives and networks
set drives of theConfig to {}
set networks of theConfig to {}

-- Step 5: Add the correct drive and network
make new drive at end of drives of theConfig with properties {image path:"${POSIX_IMAGE_PATH}", image type:disk, interface:"virtio"}
make new network at end of networks of theConfig with properties {mode:bridged, bridged interface:"${ACTIVE_INTERFACE}", mac address:"${RANDOM_MAC}"}

-- Step 6: Apply the fully modified configuration back to the VM
update theVM with configuration theConfig

end tell EOD

Check the exit code of the AppleScript

if [ $? -ne 0 ]; then echo "" echo "❌ AppleScript automation failed." echo "This can be due to a permissions issue or an incompatibility with your UTM version." echo "" echo "Please ensure you have granted Automation permissions in:" echo "System Settings -> Privacy & Security -> Automation -> [Your Terminal] -> UTM" echo "" echo "You can open the correct settings pane directly by running this command:" echo "open "x-apple-systemsettings:com.apple.preference.security?Privacy_Automation"" exit 1 fi

echo "✅ VM created and configured successfully."

7. Start the VM

echo "🚀 Starting Home Assistant!" utmctl start "$VM_NAME"

if [ $? -ne 0 ]; then echo "❌ Failed to start the VM. Make sure you named it '$VM_NAME' exactly." exit 1 fi

echo "✅ Installation complete. Home Assistant will be available at http://homeassistant.local:8123 shortly." `

tymorton avatar Jul 08 '25 20:07 tymorton