Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bcdedit change on reboot commands that require booting to fog #124

Open
darksidemilk opened this issue Feb 28, 2020 · 6 comments
Open

Bcdedit change on reboot commands that require booting to fog #124

darksidemilk opened this issue Feb 28, 2020 · 6 comments

Comments

@darksidemilk
Copy link
Member

I don't have time to get too deep into it right now, but bcdedit in windows 10 has the ability to set onetime firmware level boot options. It doesn't always work perfect across all hardware sadly but would probably work for a lot of people.

The general idea is

if the fog client schedules a reboot on a windows computer to perform a task that requires booting to FOS. Then the fog client sets a onetime boot option with bcdedit (or some c+/.net code equivalent) to have the computer boot to the network. Or even download a copy of ipxe.efi and set it as the bootfile for a onetime boot option.

This would also help with the problem of refind compatibility as a onetime boot option may get around the fog pxe menu even needing to try to boot to hard drive in efi mode.

@Iri5s
Copy link
Contributor

Iri5s commented Jan 18, 2023

Late to the game here but I have a script for this in our windows 10 environment utilizing the following bat command
bcdedit /set {fwbootmgr} displayorder {bootmgr} /addfirst and was thinking about the possibility of implementing this with fog, although with the difference in hardware, along with Linux and mac I'm not sure how suitable this would be.

I'd be curious to hear @Sebastian-Roth, if any on possible implementation and another checkbox when doing a deploy task to attempt tp boot into PXE. It is very useful to avoid the constant PXE boot and only PXE boot when necessary.

image

@Sebastian-Roth
Copy link
Member

@Iri5s I am not sure I get why it would need another checkbox in the web UI on scheduling a deployment. Could run the bcdedit to do the onetime PXE boot in any case. Or do you see a possible chance that could mess up things and so we should choose the more conservative way by adding a checkbox that is not enabled by default?

I am pretty sure that checkbox selection could easily be sent to the fog-client and tested for within the TaskReboot module to then either run bcdedit or not.

@Iri5s
Copy link
Contributor

Iri5s commented Jan 27, 2023

After reviewing this, I agree. The original comment was mostly based around giving extra choice to the user and the fact that this would only work on a Windows environment (so stating this in the checkbox) but the checkbox there adds more confusion for other environments. So disregard, I do however think giving the user an option to turn this behavior off would be best, to avoid any unforeseen issues with the commands or if a user does not want the fog client to change their bios boot order. Maybe in the TaskReboot module settings? Under some title like 'Reboot direct to PXE', or some such thing, However you would probably know the best place for it.

Now in regards to actual implementation..

The script (Attached for reference) first runs the following command bcdedit.exe /enum {fwbootmgr} (which lists all the boot options for your firmware boot manager) it puts the results into a for loop and then it has to find the PXE entry. Now on my machines (Dell Vostros) this is Onboard NIC(IPV4) however this entry would be different if IPV6 is used Onboard NIC(IPV6). (I'm also not sure if the wording for IPV4 boot is different on different hardware, this isn't the end of the world but if so we may need to have a Dictionary in fog client with known entries for different hardware to counter this)

If it finds Onboard NIC(IPV4) then it runs bcdedit /set {fwbootmgr} displayorder {%%i} /addfirst ( {%%i} being the NIC(IPV4) ) to set that to the top of the boot order. Windows will then put itself back to the top after imaging and hey presto no need to constantly PXE boot or manually PXE boot.

TLDR: Implementation is possible if:

  • We know the name of the PXE boot entry in their firmware, (not sure how varied this is across manufacturers but maybe a simple check for the wording 'IPv4/6' is a way to get around this)
  • We know if they are using IPv4 or IPv6 (which should be able to be read via the fog client code)

Any thoughts @Sebastian-Roth, it should be quite possible.

pxeReboot.txt

@darksidemilk
Copy link
Member Author

So, especially with ipxe working on getting a signed shim, I think we shouldn't try to find the pxe boot entry for bcd. We should deploy the ipxe.efi (or whatever file the user has set as their default) to the machine's efi partition (or somewhere on the system drive would work to, but I feel the efi partition would be a better practice)
If we boot straight to the ipxe.efi file we'll have a more universal solution than trying to get the pxe boot option for the correct adapter when there are multiple.
It also gets around problems where windows will ignore your bcd changes on some hardware.

This is similar to what I currently do. I use grub2win and I put that efi binary as the boot option and it has an option to boot to windows and to fog (via an ipxe.efi file I deploy to the machine's efi partition). I have a function for deploying images where I change the grub2win grub.cfg default boot option on a machine. It would be much simpler to have the fog client change the boot order to booting to the ipxe.efi file on next boot or probably better to change it to be the first option. The biggest issue this will run into will be devices with no built-in ethernet, as changing the boot option on them may make them not boot if they don't get an adapter plugged in and if they don't have said adpater assigned to them.

@darksidemilk
Copy link
Member Author

darksidemilk commented Feb 22, 2023

I did a test of this with my existing code. In my environment I already have a symlink for ipxe.efi in my /var/www/root folder to allow the latest version to be downloaded internally, with the fog client I imagine we could send it similar to how we do snapins and not have to expose it in that way, not that it's a huge security issue as its already being served publicly over tftp. There could be a way to use the existing tftp protocol to download it i.e. microsoft's tftp client but that's not included in windows by default.

I would also note that I've found booting straight to a local version of ipxe.efi helps with hardware that doesn't have built-in pxe support or that has quirky support for only certain adapters that are specific to their vendor. When booting straight to it, as long as the usb or other type of attached ethernet adapter supports pxe, it will usually get to fog and do the needful.

Anyway here's the basics of what I did via powershell, more would be required for a full implementation, this is just a quick and dirty recreation and it assumes you've made a symlink on your fog server like this ln -s /tftpboot/ipxe.efi /var/www/html/ipxe-latest.efi

#use mountvol.exe to mount the efi system partition to A:\
$mountVol = "C:\Windows\System32\mountvol.exe";
$mountLtr='A:'
# (mountvol.exe A: /S)
Start-Process -FilePath $mountVol -Wait -NoNewWindow -ArgumentList @($mountLtr, '/S');

#download the ipxe boot file
Invoke-WebRequest -Uri "http://fog-server/ipxe-latest.efi" -OutFile "$mountLtr\EFI\ipxe-latest.efi";

#set the bootmgr path to use. Techcnially I also do some other things like renaming the built-in windows bootmgfw.efi to 
# winbootmgfw.efi before replacing it as it gets around some hardware ignoring this change when its sees the signed microsoft 
# bootmgfw.efi (especially microsoft branded hardware), I'm including that method in this comment block in case someone 
# tests this and finds its needed

<#
$EfiMnt = $mountLtr;
if (Test-Path "$EfiMnt\efi\Microsoft\Boot\bootmgfw.efi") {
            if (Test-Path "$EfiMnt\efi\Microsoft\Boot\winbootmgfw.efi") {
                Remove-Item "$EfiMnt\efi\Microsoft\Boot\winbootmgfw.efi" -force;
            }
            Rename-Item -path "$EfiMnt\efi\Microsoft\Boot\bootmgfw.efi" -newname "winbootmgfw.efi" -force
        } else {
            $msftBinaryExists = Test-path "$EfiMnt\efi\Microsoft\Boot\winbootmgfw.efi";
            if ($msftBinaryExists) {
                "The msft binary file has already been renamed and is ready to go" | Out-Host
            } else {
                Throw "microsoft boot binary missing!"
            }
        }
#>

$path = "\EFI\ipxe-latest.efi" #contextual path for efi partition
$bootOptionName = "{bootmgr}"
bcdedit /set $bootOptionName path $path

#make sure the edited bootmgr is the first boot option
bcdedit /set "{fwbootmgr}" displayorder $bootOptionName /addfirst

#remove any runonce boot options currently present, we could also try to use the inverse of this and set this to a runonce boot 
#entry but I've had mixed results with that. 
$fwboot = bcdedit /enum "{fwbootmgr}"
if ($fwboot -match "bootsequence") {
#remove any run once boot options which would be defined as bootsequence
    (bcdedit /deletevalue "{fwbootmgr}" bootsequence); 
}

#dismount efi partition (mountvol.exe A: /D)
Start-Process -FilePath $mountVol -Wait -NoNewWindow -ArgumentList @($mountLtr, '/D');

You could theoretically just create an alternate bootmgr that always exists but I've found renaming the microsoft boot manager and replacing the existing boot manager as the most reliable option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants