I've finally got it to work, as follows (full Ansible playbook at the end).
I copy the original root partition to the new root partition, excluding some special mounts and (more significantly) the /boot
directory contents, so I start from a clean slate:
rsync -ahPHAXx --delete --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/boot/*,/mnt/*,/media/*,/lost+found/*} / /mnt/root
Then install grub:
grub-install --efi-directory=/mnt/esp --boot-directory=/mnt/root/boot --removable --target=x86_64-efi
Generate grub config:
grub-mkconfig -o /mnt/root/boot/grub/grub.cfg
This doesn't include the vmlinuz
or initrd
images for later (assuming an empty boot partition), so copy those over:
rsync -ahPHAXx --exclude="*/" --delete /boot/* /mnt/root/boot
Since the UUIDs are different, I need to update /mnt/root/boot/grub/grub.cfg
and /mnt/root/etc/fstab
, and replace every instance of the old UUID with the new one.
I also need to edit the ESP grub.cfg (in /mnt/esp/EFI/BOOT/grub.cfg
) to look on hd0
instead of hd1
.
I also removed reference to these /dev/sdb
mounts from fstab
, so all that is left is a line to mount the ESP, and one to mount /
.
The whole Ansible playbook that does this for me is here:
---
- hosts:
- deploy
become: yes
become_method: sudo
vars:
device: /dev/sdb
tasks:
- name: Unmount
mount:
path: "/mnt/{{ item }}"
state: unmounted
with_items:
- esp
- root
- name: Delete partitions
community.general.parted:
device: "{{ device }}"
number: "{{ item }}"
state: absent
with_items:
- 1
- 2
- name: Create efi partition
community.general.parted:
device: "{{ device }}"
number: 1
label: gpt
fs_type: fat32
name: EFI System Partition
flags: [ esp, boot ]
part_start: "1MiB"
part_end: "513MiB"
state: present
register: part
- debug:
var: part
- name: Create root partition
community.general.parted:
device: "{{ device }}"
number: 2
name: root
label: gpt # need to repeat the label here, otherwise the first partition gets overwritten
part_start: "513MiB"
part_end: "100%"
state: present
- name: Make root partition ext4
filesystem:
dev: "{{ device }}2"
fstype: ext4
- name: Make efi partition fat32
shell: "mkfs.fat -F32 {{ device }}1"
- name: "Get UUID for existing root"
command: blkid -s UUID -o value /dev/sda2
register: root_uuid
changed_when: False
- name: "Get UUID for existing efi"
command: blkid -s UUID -o value /dev/sda1
register: efi_uuid
changed_when: False
- name: "Get UUID for new root"
command: blkid -s UUID -o value {{ device }}2
register: new_root_uuid
changed_when: False
- name: "Get UUID for new efi"
command: blkid -s UUID -o value {{ device }}1
register: new_efi_uuid
changed_when: False
- debug:
var: efi_uuid.stdout
- debug:
var: new_efi_uuid.stdout
- debug:
var: root_uuid.stdout
- debug:
var: new_root_uuid.stdout
- name: Mount root from the other device
mount:
path: /mnt/root
src: "{{ device }}2"
fstype: ext4
state: mounted
- name: Mount efi from the other device
mount:
path: /mnt/esp
src: "{{ device }}1"
fstype: vfat
state: mounted
- name: copy root partition over
shell: rsync -ahPHAXx --delete --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/boot/*,/mnt/*,/media/*,/lost+found/*} / /mnt/root
args:
executable: /bin/bash
- name: copy boot images over
shell: rsync -ahPHAXx --exclude="*/" --delete /boot/* /mnt/root/boot
- name: install GRUB to make it bootable
shell: "grub-install --efi-directory=/mnt/esp --boot-directory=/mnt/root/boot --removable --target=x86_64-efi"
- name: Edit grub.cfg to point to hd0
replace:
path: /mnt/esp/EFI/BOOT/grub.cfg
regexp: hd1
replace: hd0
- name: Generate grub.cfg
shell: "grub-mkconfig -o /mnt/root/boot/grub/grub.cfg"
- name: Edit UUID in boot/grub/grub.cfg
replace:
path: /mnt/root/boot/grub/grub.cfg
regexp: "{{ root_uuid.stdout }}"
replace: "{{ new_root_uuid.stdout }}"
- name: Edit root UUID in etc/fstab
replace:
path: /mnt/root/etc/fstab
regexp: "{{ root_uuid.stdout }}"
replace: "{{ new_root_uuid.stdout }}"
- name: Edit ESP UUID in etc/fstab
replace:
path: /mnt/root/etc/fstab
regexp: "{{ efi_uuid.stdout }}"
replace: "{{ new_efi_uuid.stdout }}"
- name: Remove /dev/sdb references from fstab
lineinfile:
path: /mnt/root/etc/fstab
state: absent
regexp: '^/dev/sdb'
With these requirements:
---
collections:
- community.general
dd
and co won't work well to clone a running system. – godfool Oct 20 '20 at 18:12