Host2vm Migration Script

In recent weeks, my team has had to virtualize many physical machines. These were mainly Windows client computers. As this was a recurring task, I created a script that simply pushes the existing hard disk into the vm using dd.

to get the running vms from our HV-Cluster i use a simple qm list (ssh auth is done via pubkey auth)
Output Example:
RWill_2024-05-17%2012-49-52

how does it work?

most of the script is there to make it look nice. the work is carried out by the following commands:

line 71:

 dd if=/dev/$disk | ssh gandalfpve dd of=/tmp/transfer/$disk.raw status=progress

dd (disk dump) reads the data carrier bit by bit and through the pipe the output of dd is used as input for the following ssh. ssh establishes a connection to the hypervisor. On the HV, the bit stream sent to ssh is fed back into dd. The result is a file on the hypervisor which contains an image of the selected hard disk.
After the dump has been successfully stored on the Hypvervisor, all we have to do is import it

line 78 and 85

qmimportdisk="qm importdisk $vmid /tmp/transfer/$disk.raw gandalf-lvm"

ssh gandalfpve $importdisk

qm importdisk reads the file and saves it as a corresponding disk in the virtual machine (vmid). the last parameter is the location to use (gandalf-lvm). After this we have a Machine that runs in a Bluescreen. This is because Windows isnt good in moving to new Machines (VM). we must do the following:

  • boot into Recovery (Windows ISO is needed)
  • load a virtio iso into the VM CD-Drive
  • start Command Prompt
    • drvload D:\vioscsi\w10\amd64\vioscsi.inf
    • diskpart
    • list disk (should show a Disk)
    • list volume
      • note the Driveletter which contains the Windows Installation
    • dism /image:E:\ /add-driver /Driver:D:\ /recurse

This fixes the Bluescreen. if the machine does not boot then do also this:

  • mbr2gpt.exe /validate /disk:0
  • mbr2gpt.exe /convert /disk:0



here is the script:

# ******************************************************** #
#                                                          #
#                                                          #
#    mvm.sh                                                #
#                                                          #
#    By: ♞  Raffael Willems <Raffael.Willems@im-c.de>      #
#                                                          #
#    Created: 2024/05/17 11:36:19 by ♞  Raffael Willems    #
#    Updated: 2024/05/17 12:30:40 by ♞  Raffael Willems    #
#                                                          #
# ******************************************************** #
#!/bin/bash
# Global Vars

disk="no drive selected"
vmid=''

# Colors
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
PINK='\033[1;35m'
LIME_GREEN='\033[1;32m'
NC='\033[0m' # No Color

select_disk() {
  # Runs lsblk and stores the output to a field
  mapfile -t output_field < <(lsblk)
  for i in "${!output_field[@]}"; do
    echo "$i: ${output_field[i]}"
  done
  # Which Disk should be converted
  read -p "Which Disk should be converted?:" userinput
  # input validation
    if [[ $userinput =~ ^[0-9]+$ && $userinput -ge 0 && $userinput -lt ${#output_field[@]} ]]; then
    # Set a global var with the choosen output
    disk=$(echo "${output_field[$userinput]}" | awk '{print $1}')
    echo -e "${YELLOW}Chosen disk: $disk ${NC}"
  else
    echo -e "${YELLOW}Invalid choice. please enter a valid line number.${NC}"
  fi
}

select_vm() {
  # get VMList
  mapfile -t output_field < <(ssh gandalfpve qm list)
  for i in "${!output_field[@]}"; do
    #echo ${output_field[i]} | awk '{print $1}'
    echo "$i: ${output_field[i]}"
  done
  #Select Target VM
  read -p "Select the target VM: " userinput
  # input validation
  if [[ $userinput =~ ^[0-9]+$ && $userinput -ge 0 && $userinput -lt ${#output_field[@]} ]]; then
    # Set a global variable with the chosen output
    vmid=$(echo "${output_field[$userinput]}" | awk '{print $1}')
    echo -e "${YELLOW}Chosen VM: $vmid ${NC}"
  else
    echo -e "${YELLOW}Invalid choice. Please enter a valid line number.${NC}"
  fi
}

copy_drive_to_raw() {
  echo -e ${RED}"Are you shure you want to create a .RAW from this Device: $disk"${YELLOW}
  lsblk | grep $disk
  echo -e "\nPlease input 'yes' to continue..."
  choice=""
  read choice
  if [ "$choice" == "yes" ]; then
    echo -e "Saving $disk.raw on gandalfpve:/tmp/transfer/\n"
    sudo dd if=/dev/$disk | ssh gandalfpve dd of=/tmp/transfer/$disk.raw status=progress
  else
    echo "canceled!"
  fi
}

import_vm() {
  qmimportdisk="qm importdisk $vmid /tmp/transfer/$disk.raw gandalf-lvm"
  echo -e ${RED}"Are you shure you want to import the $disk into $vmid?\n"${YELLOW}
  echo $qmimportdisk
  echo "\nPlease input 'yes' to continue..."
  choice=""
  read choice
  if [ "$choice" == "yes" ]; then
    ssh gandalfpve $qmimportdisk
  else
    echo "canceled!"
  fi
}

menu() {
  clear
  # Status
  echo -e ${CYAN}"=== Migration Menu ==="
  echo -e "Selected Drive:${NC}${YELLOW} $disk \n"${CYAN}
  echo -e "Selected VID:${YELLOW} $vmid\n"
  # Selections
  echo -e ${PINK}"1. Select Drive to Migrate"
  echo -e ${LIME_GREEN}"2. Migrate .raw to gandalfpve:/tmp/transfer/"gg
  echo -e ${CYAN}"3. Select Virtual Machine"
  echo -e ${PINK}"4. Import .raw into VM (gandalfpve)"
  echo -e ${RED}"5. Exit\n"${CYAN}
  echo -n "Enter your choice (1-4): " 
  read choice
}

while true; do
    menu
    case $choice in
        1)
            select_disk
            ;;
        2)
            copy_drive_to_raw
            ;;
        3)
            select_vm
            ;;
        4)
              import_vm
            ;;
        5)
           echo "Exiting. Goodbye!"
            exit 0
            ;;
          *)
            echo "Invalid choice. Please enter a valid option."
            ;;
    esac
    echo -n "Press enter to continue..."
    read
done

Previous Post Next Post