@lebrun78 Had another idea.
Since it does work with the system account in the psexec shell you try the scheduled task method again and have it use the system account. It would look something like below
Also, matching just ‘IPV4’ from the firmware boot options may not yield the most reliable results as not every device will name their network boot option like that. Another option that may work would be finding what your various models do use for pxe identification and script finding that one within this script, that would take some effort to maintain but would always get you the options you want
Another possibility is finding all possible choices and putting them all above the windows boot manager. For example, I have a custom built pc that has 3 options related to network boot (excluding ipv6) options, I could find each of those guids and put them all at the top of the list so each one will be tried at boot. I altered the script below to use that method matching the things I found in a couple places (‘IPV4’, ‘Network’, ‘pxe’) and I switched the matching up using a method I found here https://stackoverflow.com/questions/16903460/bcdedit-bcdstore-and-powershell to parse the enum of firmware into a powershell object, it’s not a perfect conversion to an object but it’s workable.
Another other option would be to get the tftpboot pxe files locally and add them to the efi partition and have it try a couple known ones, this method would work nicely as well especially on devices that don’t have built-in ethernet as they won’t have to support pxe boot to get to the bootfile that will get you into fog. But to make that work in a way that would support multiple pxe boot file options is a bit out of scope of this post.
#start the task 10 seconds after its defined
$trigger = New-ScheduledTaskTrigger -Once -at ((Get-date).AddSeconds(10))
$settings = New-ScheduledTaskSettingsSet -WakeToRun -Priority 0;
#make sure temp path exists, and use temp path to avoid issues with permissions on root of C
if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp'; }
#use system account
$principal = New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' -RunLevel Highest -LogonType ServiceAccount;
$sb = {
$Configs = New-Object -TypeName System.collections.generic.List['System.Object']
$NameArray = @()
$Pattern = '^(?<name>[a-z]*)?\s*(?<value>.*)?$'
$firmware = (bcdedit /enum firmware);
foreach ($item in $firmware ){
if ( $item.trim() ){
$res = [regex]::matches( $item, $pattern )
if ( $res ){
$Value = $res[0].Groups['value'].value
$Name = $res[0].Groups['name'].value
if ( $Value ){
if ( $Name ){
"$item creating pso" | out-host;
$PSO = [PSCustomObject]@{
Name = $Name
Value = $Value
}
$NameArray += $PSO
} else {
if ( $NameArray.count ){
( $NameArray | Select-Object -last 1 ).Value += "; $Value"
}
}
}
}
} else {
if ( $NameArray ){
$Configs.add(($NameArray))
$NameArray = @()
}
}
}
#the above loop from the example I found doesn't add the final item, so run this bit one more time
if ( $NameArray ){
$Configs.add(($NameArray))
$NameArray = @()
}
$netbootsipv4 = $configs | Where-Object { $_.Value[-1] -match "Network" -OR $_.Value[-1] -match "PXE" -OR $_.Value[-1] -match "IPV4" } | Where-Object { $_.Value[-1] -notmatch "IPV6" }
#loop through the found netboot options and put each one at the top so all of them are before windows boot
$result = New-Object -TypeName System.collections.generic.List['System.Object']
$netbootsipv4 | ForEach-Object {
$GUID = $_.Value[0];
$bootOption = "Adding Desc: $($_.Value[1]) GUID: $GUID to boot first";
$addBootToTop = (bcdedit /set "{fwbootmgr}" displayorder $GUID /addfirst);
$result.add($bootOption);
$result.add($addBootToTop);
}
#now that for sure all pxe boot options are first boots, try to find the active connection that matches and make it for sure the first boot option
$upNets = (get-netadapter | Where-Object status -eq up).InterfaceDescription
$upNets | ForEach-Object {
#set the current item to a variable
$adapterName = $_;
#see if the active adapter's description matches any of the found ipv4 boot options
$adapterBootEntry = $netbootsipv4 | Where-Object { $_.Value[-1] | Select-String -pattern $adapterName -SimpleMatch}
if ($adapterBootEntry) {
$guid = $adapterBootEntry.Value[0]
$bootOption = "This interface is active, put it towards the top - Adding Desc: $($_.Value[1]) GUID: $GUID to boot first";
$addBootToTop = (bcdedit /set "{fwbootmgr}" displayorder $GUID /addfirst);
$result.add($bootOption);
$result.add($addBootToTop);
}
}
$newBootOrder = (bcdedit /enum "{fwbootmgr}");
#make c:\temp if it doesn't exist
if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp'; }
#log everything in a new C:\temp\firmware.log file
$resultString = "Firmware: $($firmware | out-string)`n`nresult: $($result | out-string)`n`nNewBootOrder: $newBootOrder`n"
New-Item -path C:\temp\firmware.log -itemType File -value $resultString -force;
}
$netBootScript = "C:\temp\netboot.ps1";
New-Item $netBootScript -value $sb.tostring() -force;
$action = New-ScheduledTaskAction -Execute "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Argument "-File 'C:\temp\netboot.ps1'"
$task = New-ScheduledTask -Action $action -Description "update bcd" -Principal $principal -Trigger $trigger -Settings $settings;
$taskName = "boot-to-IPV4";
#register the task
Register-ScheduledTask -InputObject $task -TaskName $taskName;
# force the task to start
Start-ScheduledTask -TaskName $taskName;
# wait for the task to finish
while ((Get-ScheduledTask $taskName).State -eq 'Running') {
Start-Sleep -Seconds 1;
}
#delete the task
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -EA 0;
#delete the created script file
remove-Item $netBootScript -force -ea 0;
I saved the above as a .ps1 file and made it a snapin and it worked for me when I tested it on a vm. However, it did make it so when fog booted to the hard disk via refind it wasn’t able to boot into windows. This may be related to my own custom configs in refind, but something to test for on a few different platforms to be on the safe side.
Wait wait, I made a small change to the logging and updated the snapin and then it didn’t work the second time. That’s odd… I recreated and couldn’t get it to work a second time. There’s something weird going on for sure. I know I double checked that things were not already configured to boot to the network the time it did work.
Granted, since it’s happening via scheduled task, if you have an AD environment you could deploy this script in a scheduledTask via a gpo. Add the file (the one created from the $sb in the script) to computers with file preferences and create the scheduled task that runs as the system account using a scheduled task gpo.
Will have to dig more into this on why the script isn’t doing anything via snapin, maybe @Sebastian-Roth can help a bit on troubleshooting the client side. And maybe other @testers could test using this script as a snapin too to see what results are being had elsewhere.
Then just as an fyi to anyone reading this, you can revert back to the normal windows boot manager with (bcdedit /set "{fwbootmgr}" displayorder "{bootmgr}" /addfirst);