Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bcdedit, bcdstore and powershell

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?

like image 404
dwarf Avatar asked Mar 24 '23 17:03

dwarf


2 Answers

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.

like image 192
Ansgar Wiechers Avatar answered Apr 05 '23 16:04

Ansgar Wiechers


Team!

I`am wrote the BCDEdit parser. I think it will be usefull.

$Configs   = @() #Array contains the parsed objects
$NameArray = @()

$Pattern = '^(?<name>[a-z]*)?\s*(?<value>.*)?$'
$enum    = bcdedit /enum

foreach ($item in $enum ){
    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 ){
                    $PSO = [PSCustomObject]@{
                        Name  = $Name
                        Value = $Value
                    }
                    $NameArray += $PSO
                }
                Else {
                    if ( $NameArray.count ){
                        ( $NameArray | Select-Object -last 1 ).Value += "; $Value"
                    }
                }
            }            
        }
    }
    Else {
        if ( $NameArray ){
            $Configs  += ,$NameArray
            $NameArray = @()
        }
    }
}

#Show results
foreach ( $item in $Configs){
    $item | Format-Table
}

like image 23
Alexey I. Kuzhel Avatar answered Apr 05 '23 16:04

Alexey I. Kuzhel