├── .gitignore ├── LICENSE ├── README.md ├── linux_migration.sh └── windows_migration.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.ova 2 | test.sh 3 | test2.sh 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Tanner Cude 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VM Migration from ESXi to Proxmox 2 | 3 | For detailed instructions, including how to prepare your Proxmox and ESXi environments for migration, how to address common issues during the migration process, and post-migration steps, please refer to my comprehensive blog post: 4 | 5 | https://tcude.net/migrate-linux-vms-from-esxi-to-proxmox-guide/ 6 | 7 | ## Overview 8 | 9 | This collection of scripts facilitates the migration of virtual machines from VMware ESXi to Proxmox VE. Designed with simplicity and efficiency in mind, the scripts aim to streamline the migration process, making it accessible to administrators of varying expertise levels. While primarily focused on Linux VMs, particularly Ubuntu Server VMs, the methodology may be adaptable for other distributions with minor adjustments. 10 | 11 | This repo contains a collection of scripts I used to migrate my virtual machines off of VMWare ESXi and onto Proxmox VE. While my linux migration script is particularly focused on Ubuntu Server VMs, the methodology may be adaptable for other distributions with minor adjustments. 12 | 13 | ## Getting Started 14 | 15 | ### Prerequisites 16 | 17 | - Ensure you have temporary storage available at `/mnt/vm-migration` on your Proxmox host. 18 | - The Proxmox host should have `ovftool`, `jq`, and `libguestfs-tools` installed. 19 | - Familiarize yourself with the essential variables within the script, such as ESXi server details and VM specifics. 20 | 21 | ### Usage 22 | 23 | 1. Download the `linux_migration.sh` script from the repository. 24 | 2. Modify the script with your ESXi server details (`ESXI_SERVER`, `ESXI_USERNAME`, `ESXI_PASSWORD`). 25 | 3. Make the script executable: `chmod +x linux_migration.sh`. 26 | 4. Execute the script: `./linux_migration.sh`. Ensure the target VM in ESXi is powered off before proceeding. 27 | 28 | ## Support and Contributions 29 | 30 | Your feedback and contributions are welcome! If you encounter issues, have suggestions, or would like to contribute improvements to the script, please open an issue or pull request in this repository. 31 | 32 | ## License 33 | 34 | This project is released under the [MIT License](LICENSE). 35 | 36 | -------------------------------------------------------------------------------- /linux_migration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### PREREQUISITES ### 4 | # - Install ovftool on the Proxmox host - https://developer.vmware.com/web/tool/ovf/ 5 | # - Hardcode the variables for your ESXi IP, user, etc. 6 | 7 | # Function to get user input with a default value 8 | get_input() { 9 | read -p "$1 [$2]: " input 10 | echo ${input:-$2} 11 | } 12 | 13 | # Check if ovftool is installed 14 | if ! ovftool --version &> /dev/null; then 15 | echo "Error: ovftool is not installed or not found in PATH. Please install ovftool and try again." 16 | exit 1 17 | fi 18 | 19 | # Check if jq is installed 20 | if ! jq --version &> /dev/null; then 21 | echo "Error: jq is not installed or not found in PATH. Please install jq and try again." 22 | exit 1 23 | fi 24 | 25 | # Check if libguestfs-tools is installed 26 | if ! virt-customize --version &> /dev/null; then 27 | echo "Error: virt-customize is not installed or not found in PATH. Please install libguestfs-tools and try again." 28 | exit 1 29 | fi 30 | 31 | ### Set the following variables to their respective values 32 | echo "Using hardcoded details for VM migration" 33 | ESXI_SERVER="default_esxi_server" # Set your ESXi server hostname/IP 34 | ESXI_USERNAME="root" # Set your ESXi server username 35 | ESXI_PASSWORD="your_esxi_password" # Set your ESXi server password 36 | 37 | VM_NAME=$(get_input "Enter the name of the VM to migrate") 38 | VLAN_TAG=$(get_input "Enter the VLAN tag" "80") 39 | VM_ID=$(get_input "Enter the VM ID you would like to use in Proxmox") 40 | STORAGE_TYPE=$(get_input "Enter the storage type (local-lvm or local-zfs)" "local-lvm") 41 | FIRMWARE_TYPE=$(get_input "Does the VM use UEFI firmware? (yes/no)" "no") 42 | 43 | # Convert user input for firmware type into a format used by the script 44 | if [ "$FIRMWARE_TYPE" == "yes" ]; then 45 | FIRMWARE_TYPE="ovmf" # Correct setting for UEFI firmware in Proxmox 46 | else 47 | FIRMWARE_TYPE="seabios" # Default BIOS setting 48 | fi 49 | 50 | # Check if a VM with the given ID already exists before proceeding 51 | if qm status $VM_ID &> /dev/null; then 52 | echo "Error: VM with ID '$VM_ID' already exists. Please enter a different ID." 53 | exit 1 54 | fi 55 | 56 | if ! [[ $VM_ID =~ ^[0-9]+$ ]] || [[ $VM_ID -le 99 ]]; then 57 | echo "Error: Invalid VM ID '$VM_ID'. Please enter a numeric value greater than 99." 58 | exit 1 59 | fi 60 | 61 | # Export VM from VMware 62 | function export_vmware_vm() { 63 | #local ova_file="/var/vm-migration/$VM_NAME.ova" 64 | local ova_file="/mnt/vm-migration/$VM_NAME.ova" 65 | if [ -f "$ova_file" ]; then 66 | read -p "File $ova_file already exists. Overwrite? (y/n) [y]: " choice 67 | choice=${choice:-y} 68 | if [ "$choice" != "y" ]; then 69 | echo "Export cancelled." 70 | exit 1 71 | fi 72 | rm -f "$ova_file" 73 | fi 74 | echo "Exporting VM from VMware directly to Proxmox..." 75 | echo $ESXI_PASSWORD | ovftool --sourceType=VI --acceptAllEulas --noSSLVerify --skipManifestCheck --diskMode=thin --name=$VM_NAME vi://$ESXI_USERNAME@$ESXI_SERVER/$VM_NAME $ova_file 76 | } 77 | 78 | function create_proxmox_vm() { 79 | 80 | # Extract OVF from OVA 81 | echo "Extracting OVF from OVA..." 82 | tar -xvf /mnt/vm-migration/$VM_NAME.ova -C /mnt/vm-migration/ 83 | 84 | # Find the OVF file 85 | local ovf_file=$(find /mnt/vm-migration -name '*.ovf') 86 | echo "Found OVF file: $ovf_file" 87 | 88 | # Find the VMDK file 89 | echo "Finding .vmdk file..." 90 | local vmdk_file=$(find /mnt/vm-migration -name "$VM_NAME-disk*.vmdk") 91 | echo "Found .vmdk file: $vmdk_file" 92 | 93 | # Ensure that only one .vmdk file is found 94 | if [[ $(echo "$vmdk_file" | wc -l) -ne 1 ]]; then 95 | echo "Error: Multiple or no .vmdk files found." 96 | exit 1 97 | fi 98 | 99 | # Convert the VMDK file to raw format 100 | local raw_file="$VM_NAME.raw" 101 | local raw_path="/mnt/vm-migration/$raw_file" 102 | echo "Converting .vmdk file to raw format..." 103 | qemu-img convert -f vmdk -O raw "$vmdk_file" "$raw_path" 104 | 105 | # Install qemu-guest-agent using virt-customize 106 | echo "Installing qemu-guest-agent using virt-customize..." 107 | virt-customize -a "$raw_path" --install qemu-guest-agent || { 108 | echo "Failed to install qemu-guest-agent." 109 | exit 1 110 | } 111 | 112 | # Create the VM and set various options such as BIOS type 113 | echo "Creating VM in Proxmox with $FIRMWARE_TYPE firmware, VLAN tag, and SCSI hardware..." 114 | qm create $VM_ID --name $VM_NAME --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0,tag=$VLAN_TAG --bios $FIRMWARE_TYPE --scsihw virtio-scsi-pci 115 | 116 | echo "Enabling QEMU Guest Agent..." 117 | qm set $VM_ID --agent 1 118 | 119 | # Import the disk to the selected storage 120 | echo "Importing disk to $STORAGE_TYPE storage..." 121 | qm importdisk $VM_ID $raw_path $STORAGE_TYPE 122 | 123 | # Attach the disk to the VM and set it as the first boot device 124 | local disk_name="vm-$VM_ID-disk-0" 125 | echo "Attaching disk to VM and setting it as the first boot device..." 126 | qm set $VM_ID --scsi0 $STORAGE_TYPE:$disk_name --boot c --bootdisk scsi0 127 | 128 | # Enable discard functionality for the disk 129 | echo "Enabling discard functionality" 130 | qm set $VM_ID --scsi0 $STORAGE_TYPE:$disk_name,discard=on 131 | } 132 | 133 | # Clear out temp files from /var/vm-migrations 134 | function cleanup_migration_directory() { 135 | echo "Cleaning up /mnt/vm-migration directory..." 136 | rm -rf /mnt/vm-migration/* 137 | } 138 | 139 | # Retrieve the actual LVM volume group name 140 | vg_name=$(vgdisplay | awk '/VG Name/ {print $3}') 141 | 142 | # Add an EFI disk to the VM after all other operations have concluded 143 | function add_efi_disk_to_vm() { 144 | echo "Adding EFI disk to the VM..." 145 | local vg_name="nvme" # Adjusted to the correct volume group name if necessary 146 | local efi_disk_size="4M" 147 | local efi_disk="vm-$VM_ID-disk-1" 148 | 149 | # Ensure correct volume group name is used 150 | echo "Creating EFI disk as a logical volume in volume group $vg_name..." 151 | lvcreate -L $efi_disk_size -n $efi_disk $vg_name || { 152 | echo "Failed to create EFI disk logical volume." 153 | exit 1 154 | } 155 | 156 | # Attach EFI disk 157 | echo "Attaching EFI disk to VM..." 158 | qm set $VM_ID --efidisk0 $vg_name:$efi_disk,size=$efi_disk_size,efitype=4m,pre-enrolled-keys=1 || { 159 | echo "Failed to add EFI disk to VM." 160 | exit 1 161 | } 162 | } 163 | 164 | # Main process 165 | export_vmware_vm 166 | create_proxmox_vm 167 | cleanup_migration_directory 168 | 169 | # Add EFI disk based on the user's input 170 | if [ "$FIRMWARE_TYPE" == "ovmf" ]; then # Correct check for UEFI firmware 171 | add_efi_disk_to_vm 172 | else 173 | echo "Skipping EFI disk creation for non-UEFI firmware type." 174 | fi 175 | -------------------------------------------------------------------------------- /windows_migration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### PREREQUISITES ### 4 | # - Install ovftool on the Proxmox host - https://developer.vmware.com/web/tool/ovf/ 5 | # - Hardcode the variables for your ESXi IP, user, etc. 6 | 7 | # Function to get user input with a default value 8 | get_input() { 9 | read -p "$1 [$2]: " input 10 | echo ${input:-$2} 11 | } 12 | 13 | # Function to check the firmware/BIOS type 14 | check_firmware_type() { 15 | local vmx_path="/vmfs/volumes/datastore/${VM_NAME}/${VM_NAME}.vmx" 16 | local firmware_type=$(sshpass -p "${ESXI_PASSWORD}" ssh -o StrictHostKeyChecking=no ${ESXI_USERNAME}@${ESXI_SERVER} "grep 'firmware =' ${vmx_path}") 17 | 18 | if [[ $firmware_type == *"efi"* ]]; then 19 | echo "uefi" 20 | else 21 | echo "seabios" 22 | fi 23 | } 24 | 25 | # Check if ovftool is installed 26 | if ! ovftool --version &> /dev/null; then 27 | echo "Error: ovftool is not installed or not found in PATH. Please install ovftool and try again." 28 | exit 1 29 | fi 30 | 31 | # Check if jq is installed 32 | if ! jq --version &> /dev/null; then 33 | echo "Error: jq is not installed or not found in PATH. Please install jq and try again." 34 | exit 1 35 | fi 36 | 37 | # Check if libguestfs-tools is installed 38 | if ! virt-customize --version &> /dev/null; then 39 | echo "Error: virt-customize is not installed or not found in PATH. Please install libguestfs-tools and try again." 40 | exit 1 41 | fi 42 | 43 | ### Set the following variables to their respective values 44 | echo "Using hardcoded details for VM migration" 45 | ESXI_SERVER="default_esxi_server" # Set your ESXi server hostname/IP 46 | ESXI_USERNAME="root" # Set your ESXi server username 47 | ESXI_PASSWORD="your_esxi_password" # Set your ESXi server password 48 | 49 | VM_NAME=$(get_input "Enter the name of the VM to migrate") 50 | VLAN_TAG=$(get_input "Enter the VLAN tag" "80") 51 | VM_ID=$(get_input "Enter the VM ID you would like to use in Proxmox") 52 | STORAGE_TYPE=$(get_input "Enter the storage type (local-lvm or local-zfs)" "local-lvm") 53 | 54 | # Check if a VM with the given ID already exists before proceeding 55 | if qm status $VM_ID &> /dev/null; then 56 | echo "Error: VM with ID '$VM_ID' already exists. Please enter a different ID." 57 | exit 1 58 | fi 59 | 60 | if ! [[ $VM_ID =~ ^[0-9]+$ ]] || [[ $VM_ID -le 99 ]]; then 61 | echo "Error: Invalid VM ID '$VM_ID'. Please enter a numeric value greater than 99." 62 | exit 1 63 | fi 64 | 65 | # Export VM from VMware 66 | function export_vmware_vm() { 67 | local ova_file="/mnt/vm-migration/$VM_NAME.ova" 68 | if [ -f "$ova_file" ]; then 69 | read -p "File $ova_file already exists. Overwrite? (y/n) [y]: " choice 70 | choice=${choice:-y} 71 | if [ "$choice" != "y" ]; then 72 | echo "Export cancelled." 73 | exit 1 74 | fi 75 | rm -f "$ova_file" 76 | fi 77 | echo "Exporting VM from VMware directly to Proxmox..." 78 | echo $ESXI_PASSWORD | ovftool --sourceType=VI --acceptAllEulas --noSSLVerify --skipManifestCheck --diskMode=thin --name=$VM_NAME vi://$ESXI_USERNAME@$ESXI_SERVER/$VM_NAME $ova_file 79 | } 80 | 81 | function create_proxmox_vm() { 82 | 83 | # Extract OVF from OVA 84 | echo "Extracting OVF from OVA..." 85 | tar -xvf /mnt/vm-migration/$VM_NAME.ova -C /mnt/vm-migration/ 86 | 87 | # Find the OVF file 88 | local ovf_file=$(find /mnt/vm-migration -name '*.ovf') 89 | echo "Found OVF file: $ovf_file" 90 | 91 | # Find the VMDK file 92 | echo "Finding .vmdk file..." 93 | local vmdk_file=$(find /mnt/vm-migration -name "$VM_NAME-disk*.vmdk") 94 | echo "Found .vmdk file: $vmdk_file" 95 | 96 | # Ensure that only one .vmdk file is found 97 | if [[ $(echo "$vmdk_file" | wc -l) -ne 1 ]]; then 98 | echo "Error: Multiple or no .vmdk files found." 99 | exit 1 100 | fi 101 | 102 | # Convert the VMDK file to raw format 103 | local raw_file="$VM_NAME.raw" 104 | local raw_path="/mnt/vm-migration/$raw_file" 105 | echo "Converting .vmdk file to raw format..." 106 | qemu-img convert -f vmdk -O raw "$vmdk_file" "$raw_path" 107 | 108 | # Check the firmware type 109 | FIRMWARE_TYPE=$(check_firmware_type) 110 | 111 | # Create the VM and set various options such as BIOS type 112 | echo "Creating VM in Proxmox with $FIRMWARE_TYPE firmware, VLAN tag, and SCSI hardware..." 113 | qm create $VM_ID --name $VM_NAME --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0,tag=$VLAN_TAG --bios $FIRMWARE_TYPE --scsihw virtio-scsi-pci 114 | 115 | echo "Enabling QEMU Guest Agent..." 116 | qm set $VM_ID --agent 1 117 | 118 | # Import the disk to the selected storage 119 | echo "Importing disk to $STORAGE_TYPE storage..." 120 | qm importdisk $VM_ID $raw_path $STORAGE_TYPE 121 | 122 | # Attach the disk to the VM and set it as the first boot device 123 | local disk_name="vm-$VM_ID-disk-0" 124 | echo "Attaching disk to VM and setting it as the first boot device..." 125 | qm set $VM_ID --scsi0 $STORAGE_TYPE:$disk_name --boot c --bootdisk scsi0 126 | 127 | # Enable discard functionality for the disk 128 | echo "Enabling discard functionality" 129 | qm set $VM_ID --scsi0 $STORAGE_TYPE:$disk_name,discard=on 130 | } 131 | 132 | # Clear out temp files from /var/vm-migrations 133 | function cleanup_migration_directory() { 134 | echo "Cleaning up /mnt/vm-migration directory..." 135 | rm -rf /mnt/vm-migration/* 136 | } 137 | 138 | # Add an EFI disk to the VM after all other operations have concluded 139 | function add_efi_disk_to_vm() { 140 | echo "Adding EFI disk to the VM..." 141 | local vg_name="pve" # The actual LVM volume group name 142 | local efi_disk_size="4M" 143 | local efi_disk="vm-$VM_ID-disk-1" 144 | 145 | # Create the EFI disk as a logical volume 146 | echo "Creating EFI disk as a logical volume..." 147 | lvcreate -L $efi_disk_size -n $efi_disk $vg_name || { 148 | echo "Failed to create EFI disk logical volume." 149 | exit 1 150 | } 151 | 152 | # Attach the EFI disk to the VM 153 | echo "Attaching EFI disk to VM..." 154 | qm set $VM_ID --efidisk0 $STORAGE_TYPE:$efi_disk,size=$efi_disk_size,efitype=4m,pre-enrolled-keys=1 || { 155 | echo "Failed to add EFI disk to VM." 156 | exit 1 157 | } 158 | } 159 | 160 | # Main process 161 | export_vmware_vm 162 | create_proxmox_vm 163 | cleanup_migration_directory 164 | 165 | # Check the firmware type and conditionally add EFI disk 166 | FIRMWARE_TYPE=$(check_firmware_type) 167 | if [ "$FIRMWARE_TYPE" == "uefi" ]; then 168 | add_efi_disk_to_vm 169 | else 170 | echo "Skipping EFI disk creation for non-UEFI firmware type." 171 | fi --------------------------------------------------------------------------------