3

I am trying to do something like this: How can I reinstall GRUB to the EFI partition?

However, I don't see how I can run chroot because it's an automated process (with Ansible, but that shouldn't matter; if this can be scripted in Bash, that'd work for me), so I am having trouble with grub-install.

My setup

Running a Ubuntu 18.04 system with a second hard drive (/dev/sdb) with two partitions: root partition (/dev/sdb2) mounted in /mnt/root, EFI partition (/dev/sdb1) mounted in /mnt/root/boot/efi. I copied everything from the running system to those two partitions as appropriate.

I've then tried to run this to install grub and make the second hard drive bootable:

grub-install /dev/sdb1 --efi-directory=/mnt/root/boot/efi --boot-directory=/mnt/root/boot --target=x86_64-efi

I've also tried (in addition) to regenerate the grub.cfg:

grub-mkconfig -o /mnt/root/boot/grub/grub.cfg

I know I probably need to mess with the UUIDs and tell GRUB which HD to boot from. The goal is to take out the second hard drive and boot it by itself in another machine (so GRUB may know it first as (hd1), but chances are it will be (hd0) in the new box.

Any idea on this?

EDIT:

I think GRUB is actually installed successfully. I am thrown into a grub> prompt, and can manually boot from the second hard drive. I guess that means I just need a working grub.cfg and possible /etc/fstab to make this work.

godfool
  • 51

1 Answers1

1

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
godfool
  • 51