bcdedit in Windows: read the BCD store and edit boot entries safely

bcdedit is the only editor Windows ships for the Boot Configuration Data store: the binary database that replaced boot.ini and decides which loader runs, what the boot menu shows, how long it waits, and which diagnostic switches the kernel starts with. The store is binary. Unlike boot.ini, it cannot be opened and corrected in a text editor.

That is exactly what makes it uncomfortable on a production server. There is no confirmation prompt, no dry run and no undo. A /set aimed at {current} takes effect on the entry you are booted from, and you find out whether it was a good idea on the next restart, when the machine is either at a logon screen or at a recovery prompt.

The switch list is already on Microsoft Learn. What follows is the working loop that makes bcdedit safe to run on a box you care about: export the store, do every experiment on a copy of the boot entry, boot that copy exactly once with a sequence that reverts itself, then delete it. Three parsing traps are included at the end, all measured rather than remembered.

Applies to: Windows 10 / 11, Windows Server 2016 / 2019 / 2022 / 2025


Quick answer

Read the store, back it up, and know the one command that puts it back. The export file is the only undo bcdedit has, and it takes about a second to produce. Everything else in this article assumes it exists.

rem all three need an elevated prompt - bcdedit refuses to read or write the store otherwise
bcdedit /enum

rem the whole system store, written to one file, before any change
md C:\bcd-backup 2>nul
bcdedit /export C:\bcd-backup\bcd-2026-09-14.bcd

rem put it back if a change goes wrong - this REPLACES the live store, it does not merge
bcdedit /import C:\bcd-backup\bcd-2026-09-14.bcd
Warning: /import deletes every existing entry in the system store before the import takes place. It is a restore, not a merge. If you added a second OS to the boot menu after the export was taken, that entry is gone once you import.

What bcdedit does

bcdedit.exe lives in %WINDIR%\System32 and is present on every supported Windows build, client and server. It reads and writes the system BCD store by default; the /store switch points it at a different store file instead, which is how you inspect a backup without touching the live one.

There is no commit step. Each command writes straight through. A normal shutdown and reboot is still needed to be sure modified settings are flushed to disk, so a hard reset immediately after a change is the one way to lose work you thought you had saved.

These thirteen cover every command this article uses. The full list, including the Emergency Management Services and kernel debugger groups, is on the Microsoft Learn page linked at the end.

CommandOperates onWhat it does
/enumthe storeLists entries. It is the default, so bare bcdedit equals bcdedit /enum active.
/voutputVerbose. Prints full GUIDs instead of the friendly names such as {current}.
/exportthe storeWrites the contents of the system store to a file. System store only.
/importthe storeRestores the system store from an /export file, deleting existing entries first.
/storethe storePoints any of the commands above at a different store file instead of the live system store.
/copyan entryDuplicates a boot entry inside the same store and returns a new GUID.
/setan entry optionCreates or changes one datatype on one entry.
/deletevaluean entry optionRemoves one datatype from an entry, leaving the entry in place.
/deletean entryRemoves a whole boot entry. Needs /f for well-known identifiers.
/displayorderboot managerSets the order the boot menu shows entries in.
/defaultboot managerSets the entry chosen when the timeout expires.
/bootsequenceboot managerA one-time display order, used for the next boot only.
/timeoutboot managerSeconds the boot manager waits before selecting the default entry.

Reading the store: what /enum actually prints

Before changing anything, learn to read the output, because every later command is addressed by an identifier taken from it. The sample below is a plain single-boot UEFI Windows 11 machine, with a second boot entry added so the multi-value case is visible. It comes from a published bcdedit /enum capture; the GUIDs are the only thing altered.

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=\Device\HarddiskVolume1
path                    \EFI\Microsoft\Boot\bootmgfw.efi
description             Windows Boot Manager
locale                  en-US
inherit                 {globalsettings}
default                 {current}
resumeobject            {36596990-877d-11ee-b62c-c8f75008677e}
displayorder            {current}
                        {b4f9a2c1-1d3e-11f0-9c2a-0050569b1a77}
toolsdisplayorder       {memdiag}
timeout                 30

Windows Boot Loader
-------------------
identifier              {current}
device                  partition=C:
path                    \WINDOWS\system32\winload.efi
description             Windows 11
locale                  en-US
inherit                 {bootloadersettings}
recoverysequence        {36596993-877d-11ee-b62c-c8f75008677e}
displaymessageoverride  Recovery
recoveryenabled         Yes
isolatedcontext         Yes
allowedinmemorysettings 0x15000075
osdevice                partition=C:
systemroot              \WINDOWS
resumeobject            {36596990-877d-11ee-b62c-c8f75008677e}
nx                      OptIn
bootmenupolicy          Standard
hypervisorlaunchtype    Auto

Two blocks, two jobs. Windows Boot Manager is the menu itself: what it lists, what it picks, how long it waits. Windows Boot Loader is one bootable Windows installation and the switches the kernel starts with. A machine with three operating systems has one boot manager block and three boot loader blocks.

These are the eleven fields in the boot manager block above, which is the block you edit when you are changing menu behaviour rather than kernel behaviour.

FieldMeaning
identifierThe GUID or well-known name this entry is addressed by.
deviceThe partition the boot manager itself was loaded from, normally the EFI system partition.
pathThe boot manager binary on that partition.
descriptionThe text shown on the boot menu. Changed with /set ID description.
localeLanguage of the pre-boot user interface.
inheritThe settings collection this entry inherits from.
defaultThe entry selected when the timeout expires. Changed with /default.
resumeobjectThe resume-from-hibernation entry paired with this one.
displayorderEvery entry the menu lists, in order. Multi-value.
toolsdisplayorderThe Tools submenu, normally just {memdiag}.
timeoutSeconds before the default entry is chosen. Changed with /timeout.

The layout matters more than it looks. Every label is padded so that values begin at column 25, and the single longest label in the sample, allowedinmemorysettings at 23 characters, still leaves one space before its value. A multi-value field such as displayorder continues on the next line with no label at all, aligned to the same column 25. That continuation line is where most scripted parsing of bcdedit quietly goes wrong, and it is measured in the Hidden gems section below.

Identifiers come in two forms. Most entries are addressed by a GUID in braces. Fourteen entries have a well-known short name that bcdedit prints instead, unless you add /v.

IdentifierWhat it refers to
{bootmgr}The Windows boot manager entry.
{fwbootmgr}The firmware boot manager, on systems that implement EFI.
{current}Virtual: the boot entry for the operating system running right now.
{default}Virtual: whichever entry the boot manager currently defaults to.
{memdiag}The memory diagnostic application entry.
{ntldr}The loader for operating systems earlier than Windows Vista.
{ramdiskoptions}Extra options the boot manager needs for RAM disk devices.
{dbgsettings}Global debugger settings, inheritable by any boot application.
{emssettings}Global Emergency Management Services settings.
{badmemory}The global RAM defect list.
{globalsettings}Settings inherited by all boot application entries.
{bootloadersettings}Settings inherited by all Windows boot loader entries.
{resumeloadersettings}Settings inherited by all resume-from-hibernation entries.
{hypervisorsettings}Hypervisor settings, inheritable by any OS loader entry.
Note: {current} and {default} are virtual. They resolve to whatever is true at the moment the command runs, so the GUID behind {current} can differ between two boots of a multi-boot machine. When you want a stable reference in a script, run bcdedit /v once and use the real GUID.

Before the first example

Five things need to be true before any command below is worth running. Each is one line. The example host throughout is SRV-APP-04.

First, confirm the prompt is actually elevated. bcdedit needs administrative privileges even to read the store, and an unelevated prompt gives an access denied message rather than a permissions hint. The SID below is the mandatory high integrity label, present only in an elevated token.

rem S-1-16-12288 is the High Mandatory Level SID - it appears only in an elevated token
whoami /groups | findstr /c:"S-1-16-12288"

Second, confirm which bcdedit you are about to run. It should resolve to the copy in System32 and nowhere else.

where bcdedit
C:\Windows\System32\bcdedit.exe

Third, check BitLocker on the system drive. Microsoft’s guidance is that BitLocker and Secure Boot may need to be suspended before boot options are changed, because a change to the boot configuration alters the measurements the TPM seals the key against. On a protected volume that means a recovery key prompt at the next start.

rem run this before any /set - a protected volume can demand the recovery key after a boot change
manage-bde -status C:
Common mistake: changing boot settings on a BitLocker-protected server during an unattended maintenance window, then discovering at 3am that the machine is sitting at a recovery key prompt on a remote console with nobody to type it. Suspend protection first, or have the recovery key open in another window before you press Enter.

Fourth, create the backup directory and export the store. Later examples assume this file exists, and the cleanup example restores nothing without it.

rem 2>nul hides the "already exists" error so the line is safe to re-run
md C:\bcd-backup 2>nul
bcdedit /export C:\bcd-backup\bcd-2026-09-14.bcd

Fifth, two conventions that apply to every example below rather than to any single command.

  • In PowerShell, every identifier must be quoted, so "{current}" and not {current}. Unquoted braces are not a quoting nicety there, they change what the command receives entirely, and the first hidden gem measures what actually arrives.
  • Every for /f loop below is written for a batch file, so the loop variable is doubled as %%A. Typing the same loop straight into an interactive cmd prompt needs a single %A instead, or it fails on the syntax.

Practical examples

1. Inspect the store without risking it

The problem: you have inherited SRV-APP-04 and need to know what its boot menu is configured to do before a planned change, without writing anything at all.

The solution: every read-only question is answered by /enum with a type or an identifier, and the export from the prerequisites can be read back with /store so you can compare against it later.

rem just the boot manager block - menu order, default entry and timeout
bcdedit /enum {bootmgr}

rem every operating system loader, one block each - one per bootable Windows install
bcdedit /enum osloader

rem full GUIDs instead of {current}, so a script has something stable to address
bcdedit /enum active /v

rem read the backup file instead of the live store - nothing is written
bcdedit /store C:\bcd-backup\bcd-2026-09-14.bcd /enum all
Note: the type argument is one of active, firmware, bootapp, bootmgr, osloader, resume, inherit or all. active is the default and is what you get from bare bcdedit, which is why a freshly imaged machine can look like it only has one entry when all would list several more.

2. Make a practice entry instead of editing {current}

The problem: you need to add a boot-time diagnostic to SRV-APP-04, but if it turns out to be wrong you do not want the only bootable entry on the machine carrying it.

The solution: /copy duplicates the current boot entry under a new GUID and a new description. Edit the copy. The original stays exactly as it was, and it stays first in the display order.

rem /d sets the description, which is the text the boot menu will show
bcdedit /copy {current} /d "ZT-Test-Entry"

The new GUID is printed, and it is the handle for everything that follows. Microsoft’s own sample of this message reads:

The entry was successfully copied to {55556666-ffff-7777-aaaa-8888bbbb9999}.

Copy that GUID somewhere before you carry on. If you would rather capture it in a batch file, this is the form that works, and the reason for the trailing substring is the third hidden gem below.

@echo off
setlocal
rem token 7 is the GUID, but bcdedit ends the sentence with a full stop
for /f "tokens=7" %%A in ('bcdedit /copy {current} /d "ZT-Test-Entry"') do set "NEWID=%%A"
rem strip that trailing full stop - without this every later command rejects the id
set "NEWID=%NEWID:~0,-1%"
echo New entry: %NEWID%
endlocal
Warning: a copied boot loader entry is added to the boot menu automatically as the last item, but an entry made with /create is not. If you ever use /create instead, you must add it yourself with /displayorder or it will never appear.

3. Change one setting on the copy and read it back

The problem: a driver on SRV-APP-04 is suspected of failing during startup, and you want the boot-time driver load log without leaving it enabled on the production entry forever.

The solution: /set writes one datatype onto one entry. Point it at the copy’s GUID and never at {current}. The bootlog datatype enables the system initialization log in Ntbtlog.txt.

rem enables the system initialization log in Ntbtlog.txt for this entry only
bcdedit /set {55556666-ffff-7777-aaaa-8888bbbb9999} bootlog yes

rem read the entry back - the new datatype appears as its own line
bcdedit /enum {55556666-ffff-7777-aaaa-8888bbbb9999}

Read the block that comes back against the {current} block at the top of this article. Three things should differ and nothing else: the identifier is the new GUID, the description is ZT-Test-Entry, and there is now a bootlog line that the original does not have. Everything else, osdevice and systemroot included, came across with the copy, which is why the copy boots the same installation rather than a different one.

Common mistake: typing bcdedit /set bootlog yes with no identifier. When the identifier is omitted, /set modifies the current operating system boot entry. That is documented behaviour, not a bug, and it is the single easiest way to change the entry you were trying to protect.

4. Show the menu and boot the copy exactly once

The problem: the test entry exists but the machine still boots straight into the production entry, and you do not want to make the test entry the default in case it does not come back.

The solution: give yourself a visible menu with /timeout, then use /bootsequence, which is a one-time display order. It applies to the next start only, and afterwards the computer reverts to the original display order on its own.

rem put the test entry last on the menu - /addlast avoids retyping the whole order
bcdedit /displayorder {55556666-ffff-7777-aaaa-8888bbbb9999} /addlast

rem 15 seconds is long enough to choose on a remote console that redraws slowly
bcdedit /timeout 15

rem next boot only - no permanent change to the default or the display order
bcdedit /bootsequence {55556666-ffff-7777-aaaa-8888bbbb9999}
Result: the machine boots the test entry once. If it fails to start, the next reboot falls back to the normal display order and the unchanged default entry without you touching anything, because /bootsequence applied to that one start and nothing else. That self-reverting property is the reason to prefer it over /default while testing.

/default is the permanent version of the same idea, and it is the right command once a configuration is proven rather than while it is being tested.

rem permanent: this entry is chosen every time the timeout expires
bcdedit /default {55556666-ffff-7777-aaaa-8888bbbb9999}

5. Remove the practice entry and restore the settings

The problem: the diagnostic is finished and SRV-APP-04 should look exactly as it did before, with no stray menu entry for the next person to wonder about.

The solution: /deletevalue removes a single datatype and leaves the entry, and /delete removes the entry itself. Because the test entry has a plain GUID rather than a well-known identifier, no /f is needed, and /cleanup is assumed, so it is taken out of the display order in the same step.

rem drop just the datatype, keeping the entry - useful when you want to retest later
bcdedit /deletevalue {55556666-ffff-7777-aaaa-8888bbbb9999} bootlog

rem remove the entry itself - /cleanup is the default, so displayorder is tidied too
bcdedit /delete {55556666-ffff-7777-aaaa-8888bbbb9999}

rem put the menu timeout back where it started
bcdedit /timeout 30

rem confirm: the menu should list what it listed before the test entry existed
bcdedit /enum {bootmgr}
Warning: /f exists precisely so that well-known identifiers are hard to delete by accident. bcdedit /delete {bootmgr} /f is a valid command and it removes the boot manager entry. Nothing asks you to confirm. This is the moment the export file from the prerequisites earns its keep.

If anything has gone wrong at any point, the whole store goes back in one command, and it does not matter how many entries were changed in between.

bcdedit /import C:\bcd-backup\bcd-2026-09-14.bcd

Hidden gems

In PowerShell, {current} is not text at all

Microsoft’s documentation says to put quotes around identifiers in PowerShell. It does not say why, and the why is worse than a quoting convention. Braces are PowerShell’s script block syntax, so an unquoted {current} is parsed as a ScriptBlock object before bcdedit is ever launched. Measured in PowerShell 7.4.6:

unquoted {current}   -> System.Management.Automation.ScriptBlock
quoted   "{current}" -> System.String

What the native executable then receives is not a mangled version of the identifier. It is not the identifier at all. Passing that script block to an external command produces three unrelated arguments:

unquoted: [-encodedCommand YwB1AHIAcgBlAG4AdAA= -inputFormat xml -outputFormat xml]
quoted  : [{current}]

That base64 string decodes as UTF-16LE to the single word current, with the braces gone. PowerShell has turned the identifier into an instruction to run a nested PowerShell, and bcdedit is handed the pieces. The same happens to a full GUID in braces. The fix is one pair of quotes, but the reason to remember it is that the failure looks nothing like a quoting problem when you read the error.

Note: this bites hardest in a script that was written and tested in cmd and later pasted into a PowerShell remoting session or a scheduled task that runs powershell.exe. The text is identical; the meaning is not.

for /f loses half of displayorder and never says so

Parsing bcdedit /enum with for /f "tokens=1,2" looks obviously right: label in the first token, value in the second. Running CMD’s tokenizer against the sample output at the top of this article shows two places where it is not.

tok1                      tok2
------------------------  --------------------------------------
identifier                {bootmgr}
description               Windows                 <-- value truncated at the first space
displayorder              {current}
{b4f9a2c1-1d3e-11f0-...}  (empty)                 <-- continuation line, GUID landed in tok1
timeout                   30

Two separate failures. description Windows Boot Manager is a multi-word value, so tokens=2 returns Windows and silently drops the rest. And the second displayorder GUID sits on a continuation line with no label, so the tokenizer puts the GUID itself in token 1 and leaves token 2 empty. A loop that collects display order entries by reading token 2 after a displayorder label therefore returns 1 of the 2 entries actually present, raises no error and sets no error level.

Two habits avoid both. Use tokens=2,* so a multi-word value keeps its tail, and filter to the single line you want with findstr before tokenizing, so continuation lines never reach the loop.

rem ^| escapes the pipe so for /f passes it to the command, instead of cmd eating it here
rem /c: treats the argument as one literal string, which matters the moment a pattern has a space
rem tokens=2,* keeps the tail of a multi-word value in %%B instead of discarding it
for /f "tokens=2,*" %%A in ('bcdedit /enum {bootmgr} ^| findstr /c:"timeout"') do echo Timeout: %%A

If you do need every display order entry rather than one field, do not use for /f for it. PowerShell can match the continuation lines by the column they align to, and that version is in the next section.

The GUID bcdedit hands back ends with a full stop

/copy reports its result as an English sentence, and sentences end with punctuation. Tokenizing the documented message shows what a batch capture actually gets:

The entry was successfully copied to {55556666-ffff-7777-aaaa-8888bbbb9999}.

tokens=7      -> {55556666-ffff-7777-aaaa-8888bbbb9999}.   39 chars, valid id: no
%~A of that   -> {55556666-ffff-7777-aaaa-8888bbbb9999}.   unchanged
%NEWID:~0,-1% -> {55556666-ffff-7777-aaaa-8888bbbb9999}    38 chars, valid id: yes

The instinct is to reach for %%~A, but that modifier strips one surrounding pair of double quotes and nothing else, so it leaves the string exactly as it found it. The substring form %NEWID:~0,-1% drops the last character and produces a 38-character identifier that bcdedit accepts. That is the one line separating a working provisioning script from one that creates the entry and then fails on every command afterwards.


bcdedit from PowerShell

Windows ships no PowerShell module for the BCD store. PowerShell runs bcdedit.exe the same way cmd does, with identifiers quoted, and the value of doing so is the parsing rather than the calling. This reads the menu timeout out of the padded layout:

# note the quotes - unquoted {bootmgr} would be parsed as a script block
$enum = bcdedit /enum "{bootmgr}"

# ^timeout anchors to the start of the line so toolsdisplayorder cannot match
$timeout = ($enum | Select-String '^timeout\s+(\d+)$').Matches.Groups[1].Value
"Menu timeout: $timeout seconds"
Note: the captured value is a String, not an Int32. Comparing it with -gt 10 works because PowerShell coerces the left side, but sorting a collection of these gives you a text sort. Cast with [int] if the number is going anywhere numeric.

The continuation-line problem from the second gem is solvable here, because a regular expression can match the exact column the values align to. This collects every display order entry, including the ones for /f drops:

$enum = bcdedit /enum "{bootmgr}"
$ids = @()
$inDisplayOrder = $false
foreach ($line in $enum) {
    if ($line -match '^displayorder\s+(\S+)') { $ids += $Matches[1]; $inDisplayOrder = $true; continue }
    # a continuation line is 24 spaces then a value, with no label of its own
    if ($inDisplayOrder) {
        if ($line -match '^\s{24}(\S+)$') { $ids += $Matches[1] } else { $inDisplayOrder = $false }
    }
}
"Entries on the boot menu: $($ids.Count)"
$ids

Run against the two-entry sample from the top of this article, that returns both GUIDs and a count of 2, where the for /f version returned one.

For anything beyond reading, Microsoft documents a Boot Configuration Data WMI provider as the supported route for programmatic changes. It is considerably more work than shelling out to bcdedit, and it is the right answer only when you need transactional behaviour or are building a management tool rather than a maintenance script.


Where this matters

  • A server that will not boot after a driver rollout. A copied entry carrying bootlog gives you Ntbtlog.txt from a failing start without making the production entry log every boot forever.
  • A physical-to-virtual migration. The converted machine can carry boot loader entries pointing at hardware that no longer exists, and bcdedit /enum all is what shows you which ones to remove.
  • A boot menu that appeared out of nowhere. An in-place upgrade or a failed image restore leaves a second entry in displayorder, and users see a menu and a timeout wait on every start.
  • Change control on a hardened server. An /export before and after gives you two files that can be diffed by a reviewer, and a rollback that does not depend on remembering what you typed.
  • Provisioning scripts that create boot entries. The GUID capture is where these break, and they break quietly, leaving a half-configured entry on every machine the script touched.

Tips and limitations

  • Administrative privileges are required even to read the store, so bcdedit is not a tool you can hand to a helpdesk account for diagnostics.
  • There is no dry run and no confirmation prompt. The export file is the entire safety net, and it is worth taking even for a change you are certain about.
  • A normal shutdown and reboot is needed to be sure modified settings are flushed to disk. Do not validate a change by pulling power.
  • Microsoft’s own recommendation, repeated in the caution block on its BCDEdit documentation pages, is to use the System Configuration utility msconfig.exe for routine boot settings. On a workstation that advice is sound; bcdedit earns its place when the change has to be scripted or applied remotely.
  • bcdedit is limited to the standard data types and is designed for single common changes. Recovering a partition or setting up a new system partition is bcdboot territory, not this tool.
  • Setting /timeout 0 makes the boot menu invisible rather than fast. On a multi-boot machine that means no way to reach the second entry without /bootsequence from inside the first.
  • The examples above create one file, C:\bcd-backup\bcd-2026-09-14.bcd. Booting the test entry afterwards also produces Ntbtlog.txt, which is the point of enabling bootlog. Nothing else on disk is touched.

Official documentation


Related tools

  • Windows Event Log Analyzer: paste the Kernel-Boot and Kernel-General entries from a failed start to see what the boot manager actually reported before you change anything.

Related guides