Import-RegFile function: imports Reg file to PSObject or directly to registry


#region Function Import-RegFile
Function Import-RegFile {
<#
.SYNOPSIS
	Reads a .REG file and exports to a PsObject with the following properties: Key, Name, Value, Type
	NEW: Applies the .REG file to the Registry 
.DESCRIPTION
	Reads a .REG file and exports to a PsObject with the following properties: Key, Name, Value, Type
	Handles huge .REG files that aren't easily rewritable in your deployment script.
	Generates a PS object collection of properties that can used by PSADT's Set-RegistryKey
.PARAMETER Path
	Path of the .REG file to import
.PARAMETER ApplyRegFile
	Applies the .REG file to the Registry
.EXAMPLE
	Returns hash table only. (Does not import anything)
	Import-RegFile -Path "C:\registry.reg"
.EXAMPLE
	Import masive REG file *And* feeds it to Set-RegistryKey
	$RegFileObject = Import-RegFile -Path "C:\Users\Username\Desktop\registry.reg"
	ForEach ($RegFileRow in $RegFileObject) {
		Set-RegistryKey -Key $RegFileRow.Key -Name $RegFileRow.Name -Type $RegFileRow.Type -Value $RegFileRow.Value
	}
.EXAMPLE
	Returns hash table *AND* imports at the same time (beta quality)
	Import-RegFile -Path "C:\registry.reg" -ApplyRegFile $true
.NOTES
	Authors:
	rivardma ( 9 may 2019 )
		-Modified to be more robust and PSADT-fied Script from: 
		https://social.technet.microsoft.com/Forums/scriptcenter/en-US/a623f698-4364-40bf-84a1-1fbae3852c1d/powershell-how-to-parse-a-registry-text-file?forum=ITCG
	Denis St-Pierre 21 Nov 2019 (Ottawa, Canada)
		-Improved Error handling and logging
		-Fixed handling of default @ values
		-added support for NONE type
		-Added comments section/header
	Denis St-Pierre 25 Mar 2020 (Ottawa, Canada)
		-Added -ApplyRegFile parameter
	Limitations: 
	-Only tested on Windows 10.
	-Cannot handle REG key deletion and REG value deletion via REG file. e.g  [-hklm\remove\key]
	-Cannot be used as a splat directly. Must treat as an array of HashTables and parse each line as per example.
#>
	[CmdLetBinding()]
	Param(
		 [Parameter(Mandatory=$true)]
		 [string]$Path,
		 [Parameter(Mandatory=$false)]
		 [Bool]$ApplyRegFile=$False
	)
	Begin {
		## Get the name of this function and write header
		[string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
		Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
		#Set the special character used in .REG file to define end of string
		$endOfString = [char]0x0000
		
		If (-not (Test-path -LiteralPath $Path)) {
			Write-Log -Message "REG file [$Path] does not exist" -Source ${CmdletName} -Severity 3
			Throw "REG file [$Path] does not exist" 
		}
	}
	Process {
		Try {
			$fi	= Get-Item $Path
			$file = $fi.OpenText()
			Write-Log -Message "REG file successfully loaded: [$Path]" -Source ${CmdletName}
			While (-not ($file.EndOfStream)){
				Remove-Variable hash -ErrorAction SilentlyContinue #Needed to make error messages accurate
				$line = $file.ReadLine()
				If ($line.Length -gt 0) {
					If ($line -match '^\[(.*)\]$' ) {
						If ($emptyFolder) {
							#$hash = @{Key=$key;Name="(Default)";Value=$null;Type="String"}
							If (-not $ApplyRegFile) {
								#Value cannot be $null when calling Set-Registry value, but seems ok when only building array
								$hash = @{Key=$key;Name="(Default)";Value=$null;Type="String"}
							}
							Write-Log -Message "Empty registry key found: Key=[$key]" -Source ${CmdletName}
							#New-Object  PSObject -Property $hash #Set-RegistryKey cannot handle Value=$null
							Write-Log -Message "Value is null, not including in output" -Source ${CmdletName}
						}
						$key = $matches[1]
						$emptyFolder = $true
					}
					If ($line -match '=') {
						$emptyFolder = $false
						while (($line.Trim().Substring($line.Trim().Length -1,1) -eq "\") -or ($line.Trim().Split("=",2)[1].Substring(0,1) -eq """" -and $line.Trim().Substring($line.Trim().Length -1,1) -ne """")){
							If ($line.Trim().Substring($line.Trim().Length -1,1) -eq "\"){
								#Not finished hex value
								$line = $line.Replace("\","").Trim() + $file.ReadLine().Trim()
							} Else {
								#String not finished, return carriage causes a new line in the file
								$line = $line.Trim() + "`r`n" + $file.ReadLine().Trim()
							}
						}
						$a = $line.Split("=",2)
						$name = $a[0].Replace('"','')
						If ($name -eq "@"){$name = "(Default)"}
						If ($a[1].Substring(0,1) -eq """"){
							$value = $a[1].Substring(1,$a[1].Length-2).Replace("\\","\").Replace("\""","""")
							$type = 'String'
						} Else {
							$b = $a[1].Split(':',2)
							If ($b[0] -eq "dword"){
								$type = "Dword"
								$value = [convert]::toint32($b[1],16)
							} ElseIf($b[0] -eq "hex(0)"){
								$type = "None"
								$value=[byte[]]($b[1].Split(',') | ForEach-Object { "0x$_"})
							} ElseIf($b[0] -eq "hex"){
								$type = "Binary"
								$value=[byte[]]($b[1].Split(',') | ForEach-Object { "0x$_"})
							} ElseIf($b[0] -eq "hex(b)"){
								$type = "QWord"
								$value = [BitConverter]::ToInt64([byte[]]($b[1].Split(',') | ForEach-Object { "0x$_"}),0)
							} ElseIf($b[0] -eq "hex(2)"){
								$type = "ExpandString"
								$value = [System.Text.Encoding]::Unicode.GetString([byte[]]($b[1].Split(',') | ForEach-Object { "0x$_"})).Trim($endOfString)
							} ElseIf($b[0] -eq "hex(7)"){
								$type = "MultiString"
								$value = [System.Text.Encoding]::Unicode.GetString([byte[]]($b[1].Split(',') | ForEach-Object { "0x$_"})).Trim($endOfString).Split($endOfString)
							} Else{
								$type = "Unknown" #CAVEAT: this will make Set-RegistryKey throw an error
							}
						}
						$hash = @{Key=$key;Name=$name;Value=$value;Type=$type}
						Write-Log -Message "Registry key found with the following properties: Key=$key, Name=$name, Value=$value, Type=$type" -Source ${CmdletName}
						New-Object  PSObject -Property $hash
					}
				}
				If ($ApplyRegFile) {
					ForEach ($RegFileRow in $hash) {
						Write-log $("Applying [Key=$($hash.key);Name=$($hash.name);Value=$($hash.value);Type=$($hash.type)]") -Source ${CmdletName} -Severity 2
						Set-RegistryKey -Key $RegFileRow.Key -Name $RegFileRow.Name -Type $RegFileRow.Type -Value $RegFileRow.Value
					}
				}
			}
		}
		catch {
			Write-Log -Message "Failed to parse the REG file [$Path]. `n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
			Write-Log -Message "`$line=[$line] `$type=[$type]" -Source ${CmdletName}
		}
	}
	End {
		Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
	}
}
#endregion Function Import-RegFile

2 Likes

Your function worked great for me! I ran into a single error, which was that I had to reduce the Severity level as Powershell didn’t recognize a severity of 5. I reduced it to 2 and all went well :slight_smile:

Error:

[Installation] :: Failed to parse the REG file [C:\IEModeScript\SupportFiles\HKLM_EdgeIEModePolicy.reg].
Error Record:
-------------At C:\IEModeScript\AppDeployToolkit\AppDeployToolkitMain.ps1:1082 char:119
+ … .name);Value=$($hash.value);Type=$($hash.type)]") -Severity 5 -Source …
+ ~
Error Inner Exception(s):


Sorry about the -Severity 5 thing.

My modified write-log is modified to add Grey(4) and Green(5) colors.

I’ll update the code to match yours.
Thank you!

2 Likes