bcdedit, bcdstore and powershell

dwarf picture dwarf · Jun 3, 2013 · Viewed 15.7k times · Source

so i can write bcd commands in a powershell script as if i were in a cmd prompt, for example:

bcdedit /default '{current}'

however i need a script that does this:

bcdedit /default '{current}'
bcdedit /set '{otherboot}' description "my description"

if it didnt do that, it would be the other way around:

bcdedit /default '{otherboot}'
bcdedit /set '{current}' description "my description"

what i need to do is find the identifier of the other boot in powershell and im not sure how. all google searches say to do this:

$bcdStore=gwmi -name root\wmi -list bcdstore -enableall
$bcdStore|gm
$result=$bcdStore.OpenStore("") # can also use explicit file name.
$store=$result.Store

but i have no idea how to use the store once i have it and this seems a little too complicated. i mean there should be an easier way... no?

Answer

Ansgar Wiechers picture Ansgar Wiechers · Jun 3, 2013

I don't know of a way to do it with WMI, but you could use bcdedit in combination with Select-String:

$otherboot = bcdedit /enum |
  Select-String "path" -Context 2,0 |
  ForEach-Object { $_.Context.PreContext[0] -replace '^identifier +' } |
  Where-Object { $_ -ne "{current}" }

Explanation:

The output of bcdedit /enum looks roughly like this:

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=\Device\HarddiskVolume1
description             Windows Boot Manager
locale                  en-US
...

Windows Boot Loader
-------------------
identifier              {current}
device                  partition=C:
path                    \Windows\system32\winload.exe
description             Windows 7
locale                  en-US
...

Windows Boot Loader
-------------------
identifier              {e0610d98-e116-11e1-8aa3-e57ee342122d}
device                  partition=C:
path                    \Windows\system32\winload.exe
description             DebugEntry
locale                  en-US
...

The relevant sections of this output are the Windows Boot Loader sections, which – unlike the Windows Boot Manager section – have a path record. Thus we can use this record to select only the Windows Boot Loader sections:

Select-String "path"

Since the identifier records are 2 lines before the path records, we need 2 lines of PreContext (and no PostContext):

Select-String "path" -Context 2,0

Now we have selected the follwing two chunks from the output of bcdedit /enum:

identifier              {current}
device                  partition=C:
path                    \Windows\system32\winload.exe
identifier              {e0610d98-e116-11e1-8aa3-e57ee342122d}
device                  partition=C:
path                    \Windows\system32\winload.exe

Since we're only interested in the first line of the PreContext we select these 2 lines using a ForEach-Object loop:

ForEach-Object { $_.Context.PreContext[0] }

which reduces the two chunks to this:

identifier              {current}
identifier              {e0610d98-e116-11e1-8aa3-e57ee342122d}

from which we remove the category (identifier) via string replacement:

ForEach-Object { $_.Context.PreContext[0] -replace '^identifier +' }

The regular expression '^identifier +' matches a (sub)string starting with the word "identifier" followed by one or more spaces, which is replaced with the empty string. After this replacement the two chunks are reduced to this:

{current}
{e0610d98-e116-11e1-8aa3-e57ee342122d}

So now we only need to filter out the chunk containing {current} and what's left is the identifier of the other boot record:

Where-Object { $_ -ne "{current}" }

After this, the variable $otherboot contains the identifier of the not-current boot record.