PSWMIRegistry.psm1

# PSWMIRegistry module compiled on 2023-03-18 22:53:12
#
<#
.SYNOPSIS
Parses a registry path, normalizing the hive dsignation.
 
.DESCRIPTION
Parses a registry path, normalizing the hive designation. So,
    HKLM\FirstKey\SecondKey
    HKLM:\FirstKey\SecondKey
    HKEY_LOCAL_MACHINE\FirstKey\SecondKey
will all normalize to HKLM as a hive and FirstKey\SecondKey as the path below the hive.
 
.PARAMETER Path
[string] Path to be parsed.
 
.EXAMPLE
PS> Parse-RegistryPath -Path "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Something"
 
Hive HiveKey Path
---- ------- ----
HKCU 2147483649 SOFTWARE\Microsoft\Something
 
.INPUTS
[string] path to be parsed
 
.OUTPUTS
[PSCustomObject] conaining the normalized hive designation, the path below the hive and the integer HiveID for RegStdProv.
 
$null, if the path could not be parsed.
 
#>

function Parse-RegistryPath {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]$Path
    )
    $hiveMap = @{
        'HKCR' = 'HKCR'
        'HKCR:' = 'HKCR'
        'HKEY_CLASSES_ROOT' = 'HKCR'
        'HKCU' = 'HKCU'
        'HKCU:' = 'HKCU'
        'HKEY_CURRENT_USER' = 'HKCU'
        'HKLM' = 'HKLM'
        'HKLM:' = 'HKLM'
        'HKEY_LOCAL_MACHINE' = 'HKLM'
        'HKU' = 'HKU'
        'HKU:' = 'HKU'
        'HKEY_USERS' = 'HKU'
        'HKCC' = 'HKCC'
        'HKCC:' = 'HKCC'
        'HKEY_CURRENT_CONFIG' = 'HKCC'
    }
    $hiveValue = @{
        'HKCR' = 2147483648
        'HKCU' = 2147483649
        'HKLM' = 2147483650
        'HKU'  = 2147483651
        'HKCC' = 2147483653
    }
    $Path = $Path.Trim("\")
    $Hive = $Path.Split("\",2)[0]
    if ($hiveMap.ContainsKey($Hive)) {
        $res = [PSCustomObject]@{
            'Hive' = $hiveMap[$Hive]
            'HiveKey' = $hiveValue[$hiveMap[$Hive]]
            'Path' = $Path.Split("\",2)[1]
        }
    } else {
        Write-Warning ('Hive designation {0} is not valid. Valid values are {1}' -f $Hive, ($hiveMap.keys -join ', '))
        $res = $null
    }
    return $res
}
<#
.SYNOPSIS
Returns the value of a registry value in a specified path.
 
.DESCRIPTION
Returns the value of a registry value in a specified path.
 
.PARAMETER Hostname
[string] Name or IP address of the remote computer. Defaults to '.'.
 
.PARAMETER Credential
[PSCredential] Credential used to access the remote computer.
 
.PARAMETER Path
[string] Path to the key containing the value.
 
.PARAMETER Name
[string] Name of the registry value to read.
 
.EXAMPLE
PS> Get-WMIRegistryValue -Path "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Something" -Name "Version"
1.1.0.5
 
.INPUTS
None
 
.OUTPUTS
$null, if the path could not be parsed or the registry value does not exist.
Various types, depending on the type of the registry value.
 
#>

function Get-WMIRegistryValue {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Hostname = '.',
        [Parameter(Mandatory=$false)]
        [PSCredential]$Credential,
        [Parameter(Mandatory=$true)]
        [string]$Path,
        [Parameter(Mandatory=$true)]
        [string]$Name
    )
    $parsedPath = Parse-RegistryPath -Path $Path
    if ($null -eq $parsedPath) {
        return $null
    }
    $type = $null
    $msPath = ('\\{0}\ROOT\DEFAULT:StdRegProv' -f $Hostname)
    $mScope = New-Object System.Management.ManagementScope($msPath)
    if ($null -ne $Credential) {
        $mScope.Options.Username = $Credential.UserName
        $mScope.Options.SecurePassword = $Credential.Password
    }
    try {
        $mScope.Connect()
    } catch {
        Write-Warning ('Error connecting management scope: {0}' -f $_.Exception.Message)
        return $null
    }
    $regObject = New-Object System.Management.ManagementClass($mScope, $msPath, $null)
    try {
        $res = $regObject.EnumValues($parsedPath.HiveKey, $parsedPath.Path)
    } catch {
        Write-Warning ('Error accessing key: {0}' -f $_.Exception.Message)
        return $null
    }
    if ($res.ReturnValue -ne 0) {
        Write-Warning ('Key not found: {0}' -f $Path)
        return $null
    } else {
        $valIndex = -1
        for ($i = 0; $i -lt $res.sNames.Count; $i++) {
            if ($res.sNames[$i] -ieq $Name) {
                $valIndex = $i
                $type = $res.Types[$i]
                $valName = $res.sNames[$i]
                break
            }
        }
    }
    if ($valIndex -gt -1) {
        switch ($type) {
            1 { 
                #REG_SZ
                $res = $regObject.GetStringValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
                return $res.sValue
            }
            2 { 
                #REG_EXPAND_SZ
                $res = $regObject.GetExpandedStringValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
                return $res.sValue
            }
            3 { 
                #REG_BINARY
                $res = $regObject.GetBinaryValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
                return $res.uValue
            }
            4 { 
                #REG_DWORD
                $res = $regObject.GetDWORDValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
                return $res.uValue
            }
            7 { 
                #REG_MULTI_SZ
                $res = $regObject.GetMultiStringValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
                return $res.sValue
            }
            11 { 
                #REG_QWORD
                $res = $regObject.GetQWORDValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
                return $res.uValue
            }
            default {
                Write-Warning ('Type [{0}] is not implemented.' -f $type)
                return $null
            }
        }
    } else {
        Write-Warning ('Value not found: {0}' -f $Name)
        return $null
    }
}
<#
.SYNOPSIS
Creates a registry key.
 
.DESCRIPTION
Creates a registry key.
 
.PARAMETER Hostname
[string] Name or IP address of the remote computer. Defaults to '.'.
 
.PARAMETER Credential
[PSCredential] Credential used to access the remote computer.
 
.PARAMETER Path
[string] Path to the key containing the value.
 
.PARAMETER Name
[string] Name of the registry value to read.
 
.EXAMPLE
PS> New-WMIRegistryKey -Path "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Something" -Name "Version"
True
 
.INPUTS
None
 
.OUTPUTS
$null, if the path could not be parsed or the registry value does not exist.
$true or $false, depending on whether the key could be created.
 
#>

function New-WMIRegistryKey {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Hostname = '.',
        [Parameter(Mandatory=$false)]
        [PSCredential]$Credential,
        [Parameter(Mandatory=$true)]
        [string]$Path,
        [Parameter(Mandatory=$true)]
        [string]$Name
    )
    $parsedPath = Parse-RegistryPath -Path $Path
    if ($null -eq $parsedPath) {
        return $null
    }
    $msPath = ('\\{0}\ROOT\DEFAULT:StdRegProv' -f $Hostname)
    $mScope = New-Object System.Management.ManagementScope($msPath)
    if ($null -ne $Credential) {
        $mScope.Options.Username = $Credential.UserName
        $mScope.Options.SecurePassword = $Credential.Password
    }
    try {
        $mScope.Connect()
    } catch {
        Write-Warning ('Error connecting management scope: {0}' -f $_.Exception.Message)
        return $null
    }
    $regObject = New-Object System.Management.ManagementClass($mScope, $msPath, $null)
    try {
        $res = $regObject.EnumKey($parsedPath.HiveKey, $parsedPath.Path)
    } catch {
        Write-Warning ('Error accessing key: {0}' -f $_.Exception.Message)
        return $null
    }
    if ($res.ReturnValue -ne 0) {
        return $false
    } else {
        $valIndex = -1
        for ($i = 0; $i -lt $res.sNames.Count; $i++) {
            if ($res.sNames[$i] -ieq $Name) {
                $valIndex = $i
                break
            }
        }
        if ($valIndex -gt -1) {
            Write-Warning 'Registry key already exists in this path!'
            return $false
        } else {
            $newPath = ('{0}\{1}' -f $parsedPath.Path, $Name)
            $res = $regObject.CreateKey($parsedPath.HiveKey, $newPath)
            return ($res.ReturnValue -eq 0)
        }
    }
}
<#
.SYNOPSIS
Removes a registry value from a specified path.
 
.DESCRIPTION
Removes a registry value from a specified path.
 
.PARAMETER Hostname
[string] Name or IP address of the remote computer. Defaults to '.'.
 
.PARAMETER Credential
[PSCredential] Credential used to access the remote computer.
 
.PARAMETER Path
[string] Path to the key containing the value.
 
.PARAMETER Name
[string] Name of the registry value to remove.
 
.EXAMPLE
PS> Remove-WMIRegistryValue -Path "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Something" -Name "Version"
True
 
.INPUTS
None
 
.OUTPUTS
$null, if the path could not be parsed or the registry value does not exist.
$true, if the value could be removed.
$false, if the value could not be removed.
 
#>

function Remove-WMIRegistryValue {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Hostname = '.',
        [Parameter(Mandatory=$false)]
        [PSCredential]$Credential,
        [Parameter(Mandatory=$true)]
        [string]$Path,
        [Parameter(Mandatory=$true)]
        [string]$Name
    )
    $parsedPath = Parse-RegistryPath -Path $Path
    if ($null -eq $parsedPath) {
        return $null
    }
    $type = $null
    $msPath = ('\\{0}\ROOT\DEFAULT:StdRegProv' -f $Hostname)
    $mScope = New-Object System.Management.ManagementScope($msPath)
    if ($null -ne $Credential) {
        $mScope.Options.Username = $Credential.UserName
        $mScope.Options.SecurePassword = $Credential.Password
    }
    try {
        $mScope.Connect()
    } catch {
        Write-Warning ('Error connecting management scope: {0}' -f $_.Exception.Message)
        return $null
    }
    $regObject = New-Object System.Management.ManagementClass($mScope, $msPath, $null)
    try {
        $res = $regObject.EnumValues($parsedPath.HiveKey, $parsedPath.Path)
    } catch {
        Write-Warning ('Error accessing key: {0}' -f $_.Exception.Message)
        return $null
    }
    if ($res.ReturnValue -ne 0) {
        Write-Warning ('Key not found: {0}' -f $Path)
        return $null
    } else {
        $valIndex = -1
        for ($i = 0; $i -lt $res.sNames.Count; $i++) {
            if ($res.sNames[$i] -ieq $Name) {
                $valIndex = $i
                $type = $res.Types[$i]
                $valName = $res.sNames[$i]
                break
            }
        }
    }
    if ($valIndex -gt -1) {
        $shpPath = ('\\{0}\{1}\{2}\{3}' -f $Hostname, $parsedPath.Hive, $parsedPath.Path, $valName)
        if($PSCmdlet.ShouldProcess($shpPath, 'DeleteValue')){
            $res = $regObject.DeleteValue($parsedPath.HiveKey, $parsedPath.Path, $valName)
            return ($res.ReturnValue -eq 0)
        } else {
            return $true
        }
    } else {
        Write-Warning ('Value not found: {0}' -f $Name)
        return $null
    }
}
<#
.SYNOPSIS
Sets the value of a registry value in a specified path.
 
.DESCRIPTION
Sets the value of a registry value in a specified path. If the value does not exist, it is created.
 
.PARAMETER Hostname
[string] Name or IP address of the remote computer. Defaults to '.'.
 
.PARAMETER Credential
[PSCredential] Credential used to access the remote computer.
 
.PARAMETER Path
[string] Path to the key containing the value.
 
.PARAMETER Name
[string] Name of the registry value to set or add.
 
.PARAMETER Type
[string] Type of the registry value to set or add.
 
.PARAMETER Value
[string] Value of the registry value to set or add.
 
.EXAMPLE
PS> Set-WMIRegistryValue -Path "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Something" -Name "Version" -Type REG_SZ -Value "1.0.0.1"
True
 
.INPUTS
None
 
.OUTPUTS
$null, if the path could not be parsed or the registry value does not exist.
$true, if the value could be added.
$false, if the value could not be added.
 
#>

function Set-WMIRegistryValue {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Hostname = '.',
        [Parameter(Mandatory=$false)]
        [PSCredential]$Credential,
        [Parameter(Mandatory=$true)]
        [string]$Path,
        [Parameter(Mandatory=$true)]
        [string]$Name,
        [Parameter(Mandatory=$false)]
        [ValidateSet('REG_SZ','REG_EXPAND_SZ','REG_BINARY','REG_DWORD','REG_MULTI_SZ','REG_QWORD')]
        [string]$Type = 'REG_SZ',
        [Parameter(Mandatory=$true)]
        [object]$Value
    )
    switch ($Type) {
        'REG_SZ' { $zValue = $Value.ToString() }
        'REG_EXPAND_SZ' { $zValue = $Value.ToString() }
        'REG_MULTI_SZ' { $zValue = $Value.Foreach({$_.ToString()}) }
        'REG_DWORD' { $zValue = $Value -as [int] }
        'REG_QWORD' { $zValue = $Value -as [int64] }
        'REG_BINARY' {
            if ($value.GetType().Name -eq 'Byte[]') { $zValue = $Value } else { $zValue = $null }
        }
    }
    if ($null -eq $zValue) {
        Write-Warning ('Cannot convert the supplied value into {0}' -f $Type)
        return $false
    }
    $parsedPath = Parse-RegistryPath -Path $Path
    if ($null -eq $parsedPath) {
        return $null
    }
    $valType = $null
    $msPath = ('\\{0}\ROOT\DEFAULT:StdRegProv' -f $Hostname)
    $mScope = New-Object System.Management.ManagementScope($msPath)
    if ($null -ne $Credential) {
        $mScope.Options.Username = $Credential.UserName
        $mScope.Options.SecurePassword = $Credential.Password
    }
    try {
        $mScope.Connect()
    } catch {
        Write-Warning ('Error connecting management scope: {0}' -f $_.Exception.Message)
        return $null
    }
    $regObject = New-Object System.Management.ManagementClass($mScope, $msPath, $null)
    try {
        $res = $regObject.EnumValues($parsedPath.HiveKey, $parsedPath.Path)
    } catch {
        Write-Warning ('Error accessing key: {0}' -f $_.Exception.Message)
        return $null
    }
    if ($res.ReturnValue -ne 0) {
        Write-Warning ('Key not found: {0}' -f $Path)
        return $null
    } else {
        $valIndex = -1
        for ($i = 0; $i -lt $res.sNames.Count; $i++) {
            if ($res.sNames[$i] -ieq $Name) {
                $valIndex = $i
                $valType = $res.Types[$i]
                $valName = $res.sNames[$i]
                break
            }
        }
    }
    $canAdd = $true
    if ($valIndex -gt -1) {
        switch ($valType) {
            1 { 
                #REG_SZ
                $exType = 'REG_SZ'
                if ($Type -ne 'REG_SZ') { $canAdd = $false }
            }
            2 { 
                #REG_EXPAND_SZ
                $exType = 'REG_EXPAND_SZ'
                if ($Type -ne 'REG_EXPAND_SZ') { $canAdd = $false }
            }
            3 { 
                #REG_BINARY
                $exType = 'REG_BINARY'
                if ($Type -ne 'REG_BINARY') { $canAdd = $false }
            }
            4 { 
                #REG_DWORD
                $exType = 'REG_DWORD'
                if ($Type -ne 'REG_DWORD') { $canAdd = $false }
            }
            7 { 
                #REG_MULTI_SZ
                $exType = 'REG_MULTI_SZ'
                if ($Type -ne 'REG_MULTI_SZ') { $canAdd = $false }
            }
            11 { 
                #REG_QWORD
                $exType = 'REG_QWORD'
                if ($Type -ne 'REG_QWORD') { $canAdd = $false }
            }
            default {
                Write-Warning ('Type [{0}] is not implemented.' -f $type)
                $exType = 'undefined'
                $canAdd = $false
            }
        }
    } else {
        $valName = $Name
    }
    if ($canAdd) {
        switch ($Type) {
            'REG_SZ' { $res = $regObject.SetStringValue($parsedPath.HiveKey, $parsedPath.Path, $valName, $zValue) }
            'REG_EXPAND_SZ' { $res = $regObject.SetExpandedStringValue($parsedPath.HiveKey, $parsedPath.Path, $valName, $zValue) }
            'REG_MULTI_SZ' { $res = $regObject.SetMultiStringValue($parsedPath.HiveKey, $parsedPath.Path, $valName, $zValue) }
            'REG_DWORD' { $res = $regObject.SetDWORDValue($parsedPath.HiveKey, $parsedPath.Path, $valName, $zValue) }
            'REG_QWORD' { $res = $regObject.SetQWORDValue($parsedPath.HiveKey, $parsedPath.Path, $valName, $zValue) }
            'REG_BINARY' { $res = $regObject.SetBinaryValue($parsedPath.HiveKey, $parsedPath.Path, $valName, $zValue) }
        }
        return ($res.ReturnValue -eq 0)
    } else {
        Write-Warning ('Value is already present and its type ({0}) does not match the specified type ({1})' -f $exType, $Type)
    }
}
<#
.SYNOPSIS
Tests for the existence of a registry path.
 
.DESCRIPTION
Tests for the existence of a registry path.
 
.PARAMETER Hostname
[string] Name or IP address of the remote computer. Defaults to '.'.
 
.PARAMETER Credential
[PSCredential] Credential used to access the remote computer.
 
.PARAMETER Path
[string] Path to be tested.
 
.EXAMPLE
PS> Test-WMIRegistryKey -Path "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Something"
True
 
.INPUTS
None
 
.OUTPUTS
$true, if the path exists
$false, if the path does not exist
$null, if the path could not be parsed.
 
#>

function Test-WMIRegistryKey {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Hostname = '.',
        [Parameter(Mandatory=$false)]
        [PSCredential]$Credential,
        [Parameter(Mandatory=$true)]
        [string]$Path
    )
    $parsedPath = Parse-RegistryPath -Path $Path
    if ($null -eq $parsedPath) {
        return $null
    }
    $msPath = ('\\{0}\ROOT\DEFAULT:StdRegProv' -f $Hostname)
    $mScope = New-Object System.Management.ManagementScope($msPath)
    if ($null -ne $Credential) {
        $mScope.Options.Username = $Credential.UserName
        $mScope.Options.SecurePassword = $Credential.Password
    }
    try {
        $mScope.Connect()
    } catch {
        Write-Warning ('Error connecting management scope: {0}' -f $_.Exception.Message)
        return $null
    }
    $regObject = New-Object System.Management.ManagementClass($mScope, $msPath, $null)
    try {
        $res = $regObject.EnumKey($parsedPath.HiveKey, $parsedPath.Path)
    } catch {
        Write-Warning ('Error accessing key: {0}' -f $_.Exception.Message)
        return $null
    }
    if ($res.ReturnValue -ne 0) {
        return $false
    } else {
        return $true
    }
}
Export-ModuleMember -Function @('Test-WMIRegistryKey','New-WMIRegistryKey','Get-WMIRegistryValue','Set-WMIRegistryValue','Remove-WMIRegistryValue')