Search code examples
powershellcommand-lineregistrybackup

REG EXPORT single value to .reg file


I want to export a single registry value to a file using PowerShell (eventually scripting to backup a bunch of registry values).

I can view the file with QUERY:

REG QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse
>>
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
    LimitBlankPasswordUse    REG_DWORD    0x1

However, I can't export the single value.

REQ EXPORT has no /v flag, so:

REG EXPORT "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa" \v "LimitBlankPasswordUse" LBPU.reg
ERROR: Invalid syntax.

Doesn't work.

If I try to export the value I get:

REG EXPORT "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse" LBPU.reg
ERROR: The system was unable to find the specified registry key or value.

And if I simply do the folder:

REG EXPORT "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\" Lsa.reg
The operation completed successfully.

I get a .reg file which includes the correct value:key pair "LimitBlankPasswordUse"=dword:00000001 - but also all the others. Which is no use. I want one pair, not hundreds.

How do I export a single value to a .reg file?


Solution

  • There is no direct way of achieving what you want, and given that you want a .reg file as output, using PowerShell's cmdlets (such as Get-Item, Get-ItemProperty, and Get-ItemPropertyValue) is not an option - unless you're prepared to emulate the .reg file format in all aspects, which is nontrivial both in terms of effort and complexity.[1]

    The best approach in your case is to first export the whole key, with reg.exe export, to a temporary file, and then extract and export only the header lines and the data-value pair of interest.

    Given that a single data-value pair can span multiple lines, this isn't straightforward, but the following should work robustly (you could wrap it in a function for convenience and reuse):

    # Determine the key path and the name of the value to export...
    $keyPath = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa'
    $valueName = 'LimitBlankPasswordUse'
    # ... and the output file path.
    $outFile = 'LBPU.reg'
    
    # Create a temporary file and export the whole key to it.
    $tempFile = New-TemporaryFile
    $null = reg.exe export $keyPath $tempFile /y
    
    # Extract the header lines and the key's *immediate* value children.
    # Note: reg.exe export invariably exports *recursively*, so that any 
    #       descendant keys are exported as well, which we want to exclude.
    $null = (Get-Content -Raw $tempFile) -match '(?s)^(.+?\])\r\n(.+?)\r\n(?:\r\n|\z)'
    Remove-Item $tempFile
    
    # Extract the header lines (format identifier and key path)...
    $headerLinesBlock = $Matches[1]
    # ... and the key's immediate children as value-data pairs
    $valueLinesBlock = $Matches[2]
    
    # Extract the specific value-data pair of interest.
    # Note: Such a pair can span *multiple* lines, if "\" is used as the
    #       line-continuation character at the end of a line, as is common
    #       with hex. data.
    #       Continuation lines always start with a *space*, so a data-value pair
    #       ends either with a new line whose first char. is a non-whitespace char. 
    #       or with the end of the input string.
    if ($valueLinesBlock -notmatch "(?sm)^`"$valueName`"=.+?(?=(\r\n\S|\z))") {
      throw "Value name not found: $valueName"
    }
    $valueDataPair = $Matches[0]
    
    # Save the header lines and the data-value pair line(s) to the result file.
    # Note that .reg files are "Unicode" (UTF-16LE) files.
    $headerLinesBlock, $valueDataPair | Set-Content -Encoding Unicode $outFile
    

    $outFile receives the following content:

    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa]
    "LimitBlankPasswordUse"=dword:00000001
    

    [1] To name a few challenges: REG_DWORD and REG_QWORD values must be represented as hex values, REG_EXPAND_SZ values, when queried with Get-ItemProperty are invariably expanded, so you wouldn't preserve the actual value as stored in the registry; furthermore, REG_EXPAND_SZ and REG_MULTI_SZ values must be represented as types hex(2) and hex(7) in the form of hex byte arrays, respectively.