Over the last week or so I've posted some functions for testing whether a given registry item exists or not, or even validating its value. To round this out, today I have an advanced function that makes it easier to export parts of the registry on the local computer.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
The name of the function is Export-Registry, although I apologize if the name is a little misleading. Its purpose is to export a registry key and values to either a CSV or XML file. Here's the function.
[cc lang="PowerShell"]
Function Export-Registry
[cmdletBinding()]
Param(
[Parameter(Position=0,Mandatory=$True,
HelpMessage="Enter a registry path using the PSDrive format.",
ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[ValidateScript({(Test-Path $_) -AND ((Get-Item $_).PSProvider.Name -match "Registry")})]
[Alias("PSPath")]
[string[]]$Path,
[Parameter()]
[ValidateSet("csv","xml")]
[string]$ExportType,
[Parameter()]
[string]$ExportPath,
[switch]$NoBinary
)
Begin {
Write-Verbose -Message "$(Get-Date) Starting $($myinvocation.mycommand)"
#initialize an array to hold the results
$data=@()
} #close Begin
Process {
#go through each pipelined path
Foreach ($item in $path) {
Write-Verbose "Exporting non binary properties from $item"
#get property names
$properties= Get-ItemProperty -path $item |
#exclude the PS properties
Select * -Exclude PS*Path,PSChildName,PSDrive,PSProvider |
Get-Member -MemberType NoteProperty,Property -erroraction "SilentlyContinue"
if ($NoBinary)
{
#filter out binary items
Write-Verbose "Filtering out binary properties"
$properties=$properties | Where {$_.definition -notmatch "byte"}
}
Write-Verbose "Retrieved $(($properties | measure-object).count) properties"
#enumrate each property getting itsname,value and type
foreach ($property in $properties) {
Write-Verbose "Exporting $property"
$value=(get-itemproperty -path $item -name $property.name).$($property.name)
if (-not ($properties))
{
#no item properties were found so create a default entry
$value=$Null
$PropertyItem="(Default)"
$RegType="System.String"
}
else
{
#get the registry value type
$regType=$property.Definition.Split()[0]
$PropertyItem=$property.name
}
#create a custom object for each entry and add it the temporary array
$data+=New-Object -TypeName PSObject -Property @{
"Path"=$item
"Name"=$propertyItem
"Value"=$value
"Type"=$regType
"Computername"=$env:computername
}
} #foreach $property
}#close Foreach
} #close process
End {
#make sure we got something back
if ($data)
{
#export to a file both a type and path were specified
if ($ExportType -AND $ExportPath)
{
Write-Verbose "Exporting $ExportType data to $ExportPath"
Switch ($exportType) {
"csv" { $data | Export-CSV -Path $ExportPath -noTypeInformation }
"xml" { $data | Export-CLIXML -Path $ExportPath }
} #switch
} #if $exportType
elseif ( ($ExportType -AND (-not $ExportPath)) -OR ($ExportPath -AND (-not $ExportType)) )
{
Write-Warning "You forgot to specify both an export type and file."
}
else
{
#write data to the pipeline
$data
}
} #if $#data
else
{
Write-Verbose "No data found"
Write "No data found"
}
#exit the function
Write-Verbose -Message "$(Get-Date) Ending $($myinvocation.mycommand)"
} #close End
} #end Function
[/cc]
I've omitted the comment based help, but it is there.
The function requires a registry path, using the PSDriver format, like hklm:\system\currentcontrolset\services\browser\parameters. Assuming the path exists, the function uses Get-ItemProperty to retrieve a list property values for the registry key
[cc lang="PowerShell"]
$properties= Get-ItemProperty -path $item |
#exclude the PS properties
Select * -Exclude PS*Path,PSChildName,PSDrive,PSProvider |
Get-Member -MemberType NoteProperty,Property -erroraction "SilentlyContinue"
[/cc]
However, the function excludes the properties like PSDrive and PSProvider since they aren't technically part of the registry entry these values are then piped to Get-Member. The end result is that $properties is a collection of registry property names. This collection is used in a ForEach construct to get the value for each time.
[cc lang="PowerShell"]
foreach ($property in $properties) {
Write-Verbose "Exporting $property"
$value=(get-itemproperty -path $item -name $property.name).$($property.name)
[/cc]
Assuming a value was found, I define some variables to hold the property type, such as System.String
[cc lang="PowerShell"]
#get the registry value type
$regType=$property.Definition.Split()[0]
$PropertyItem=$property.name
[/cc]
For each registry property, the function creates a custom object which is then added to a temporary array.
[cc lang="PowerShell"]
#create a custom object for each entry and add it the temporary array
$data+=New-Object -TypeName PSObject -Property @{
"Path"=$item
"Name"=$propertyItem
"Value"=$value
"Type"=$regType
"Computername"=$env:computername
[/cc]
All of this happens in a Process Script block. In the End scriptblock $data is written to the pipeline. The end result is something like this:
[cc lang="DOS"]
PS C:\> export-registry hklm:\system\currentcontrolset\services\browser\parameters
Value : False
Name : IsDomainMaster
Path : hklm:\system\currentcontrolset\services\browser\parameters
Type : System.String
Computername : SERENITY
Value : False
Name : MaintainServerList
Path : hklm:\system\currentcontrolset\services\browser\parameters
Type : System.String
Computername : SERENITY
Value : C:\Windows\System32\browser.dll
Name : ServiceDll
Path : hklm:\system\currentcontrolset\services\browser\parameters
Type : System.String
Computername : SERENITY
Value : 1
Name : ServiceDllUnloadOnStop
Path : hklm:\system\currentcontrolset\services\browser\parameters
Type : System.Int32
Computername : SERENITY
[/cc]
By the way, if the registry key has no values, then I create a (Default) entry, just like you'd see in Regedit.exe.
[cc lang="PowerShell"]
#no item properties were found so create a default entry
$value=$Null
$PropertyItem="(Default)"
$RegType="System.String"
[/cc]
But where's the export to a file? I like my functions to be as flexible and re-usable as possible so by default the function writes the custom objects to the pipeline, where presumably you could perform additional sorting, grouping or filtering. Oh....because exporting binary data generally doesn't serve much purpose, the function has a -NoBinary switch which will filter out the properties that are of the type System.Byte[].
[cc lang="PowerShell"]
if ($NoBinary)
{
#filter out binary items
Write-Verbose "Filtering out binary properties"
$properties=$properties | Where {$_.definition -notmatch "byte"}
}
[/cc]
I could have stopped at this and simply let you pipe Export-Registry to Export-CSV or Export-Clixml. But to stick somewhat to the function name, you can specify whether to export to a CSV or XML format. The latter is better for more complex entries. In either case, you also need to specify a filename and path.
[cc lang="PowerShell"]
if ($ExportType -AND $ExportPath)
{
Write-Verbose "Exporting $ExportType data to $ExportPath"
Switch ($exportType) {
"csv" { $data | Export-CSV -Path $ExportPath -noTypeInformation }
"xml" { $data | Export-CLIXML -Path $ExportPath }
} #switch
} #if $exportType
[/cc]
Because the function accepts pipelined input, you can export more than one registry key.
[cc lang="PowerShell"]
PS C:\> dir hklm:\software\microsoft\windows\currentversion\uninstall |
>> export-registry -ExportType Csv -ExportPath "C:\work\uninstall.csv" -NoBinary
PS C:\> dir hklm:\system\currentcontrolset\services\tcpip -recurse |
>> export-registry -nobinary -ExportType XML -ExportPath c:\work\tcpip.xml
[/cc]
The first example will create a CSV export file for all registry entries under UnInstall, excluding any binary data. The second recursively gets all TCPIP registry items and exports to a PowerShell xml file.
Finally, because the function includes the computer name, when you run the function remotely, the computer name helps identify what values belong to which computer. To use the function with something like Invoke-Command you could write a script that used the function and exported the information. Another way would be to create a number of PSSessions and use them.
After creating your sessions, the first step is to source the file with the function on the remote computers.
[cc lang="PowerShell"]
PS C:\> Invoke-Command -Session $s1,$s2 -FilePath "c:\scripts\export-registry.ps1"
[/cc]
Now I can run any export-registry command I'd like.
[cc lang="PowerShell"]
PS C:\> Invoke-Command -Session $s1,$s2 {Export-Registry "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -nobinary}
[/cc]
If I used the export to file parameters, I'll get file on each computer. Here's where the flexibility comes in. Because I'd like a single file for both sessions, I'll take the Invoke-Command results and pipe them to an appopriate cmdlet on my computer.
[cc lang="PowerShell"]
PS C:\> Invoke-Command -Session $s1,$s2 {Export-Registry "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -nobinary }-hidecomputername | Select * -ExcludeProperty RunspaceID | export-csv c:\work\WinLogon.csv
[/cc]
Because my exported object already has the computername, I don't need it or the runspaceID from Invoke-Command. Now I have some useful data I can re-import and analyze all I want.
Download Export-Registry and let me know what you think.
1 thought on “Export Registry”
Comments are closed.