Wednesday, February 25, 2015

PowerShell - Read, Write and Access Registry


The registry provider made it easy to work with the registry on a local machine or a remote server to load the registry as a PSDrive and work on it. By default, the Registry provider creates two registry drives HKCU and HKLM. If you want to load other drives, you need to use the New-PSDrive cmdlet to achieve this. This simplifies accessing data in the registry using PowerShell, you can now access the registry as if it were a file system and use the same cmdlets as working with files and directories like Get-Item, Set-Item etc.
We'll make use of this feature to add/ retrieve values from the registry. I've created some functions to check whether a value exists, get the value and set a value in a registry path, name combination.

Function Test-RegistryValue
{
    [CmdletBinding()]
    param
      (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,
       
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Name
    )
    if (Test-Path $Path)
    {
        $key = Get-Item -LiteralPath $Path
        if ($key.GetValue($name, $null) -ne $null)
        {
            $true
        }
        else
        {
            $false
        }
    }
    else
    {
        $false
    }
}

Function Get-RegistryValue
{
   [CmdletBinding()]
   param
      (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,
        
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Name
    )
     if (Test-RegistryValue -Path $Path -Name $Name)
    {
        $key = Get-Item -LiteralPath $Path
        $key.GetValue($name, $null)
    }
    else
    {
        $null
    }
}

Function Set-RegistryValue
{
    [CmdletBinding()]
    param
      (
       [Parameter(Mandatory = $true)]
       [ValidateNotNullOrEmpty()]
        [String]$Path,
        
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Name,
          
        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        $Value
    )
    if (Test-RegistryValue -Path $Path -Name $Name)
    {
        $key = Get-Item -LiteralPath $Path
        $key.SetValue($name, $Value)
    }
}

To test the scripts I’ve used the FluentShellUnit project and have my test cases as
[TestClass]
[DeploymentItem(@"Modules\Registry.psm1", "Modules")]
public class RegsitryTests
{
    [TestMethod]
    [TestCategory("SharePointDSCAdministration Module")]
    public void TestRegistryValue_returns_true_if_path_and_value_exists_in_registry()
    {
        const string path = @"REGISTRY::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\CredentialsDelegation";
        const string value = "AllowFreshCredentials";
        var current = false;
        var reg = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
            Microsoft.Win32.RegistryView.Default);
        var key = reg.OpenSubKey(@"Software\Policies\Microsoft\Windows\CredentialsDelegation");
        if (key != null)
        {
            var regValue = key.GetValue("AllowFreshCredentials", null);
            current = regValue != null;
        }
        var actual = PsFactory.Create(HostState.Core)
            .Load(@"Modules\Registry.psm1")
            .Execute("Test-RegistryValue", new Dictionary<string, string>
            {
                {"Path", path},
                {"Name", value}
            })
            .FirstResultItemAs<bool>();
        Assert.IsTrue(actual == current);
    }

    [TestMethod]
    [TestCategory("SharePointDSCAdministration Module")]
    public void GetRegistryValue_returns_null_if_path_and_value_does_not_exists_in_registry()
    {
        const string path = @"REGISTRY::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\CredentialsDelegation";
        const string value = "AllowFreshCredentials_NotExists";
        var actual = PsFactory.Create(HostState.Core)
            .Load(@"Modules\Registry.psm1")
            .Execute("Get-RegistryValue", new Dictionary<string, string>
            {
                {"Path", path},
                {"Name", value}
            })
            .Result;
        Assert.IsNull(actual[0]);
    }

    [TestMethod]
    [TestCategory("SharePointDSCAdministration Module")]
    public void GetRegistryValue_returns_value_if_path_and_key_exists_in_registry()
    {
        const string path = @"REGISTRY::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\CredentialsDelegation";
        const string value = "AllowFreshCredentials";
        int current = default(int);
        var reg = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
            Microsoft.Win32.RegistryView.Default);
        var key = reg.OpenSubKey(@"Software\Policies\Microsoft\Windows\CredentialsDelegation");
        if (key != null)
        {
            var keyValue = key.GetValue("AllowFreshCredentials", null);
            if (keyValue != null)
            {
                Int32.TryParse(keyValue.ToString(), out current);
            }
        }

        var actual = PsFactory.Create(HostState.Core)
            .Load(@"Modules\Registry.psm1")
            .Execute("Get-RegistryValue", new Dictionary<string, string>
            {
                {"Path", path},
                {"Name", value}
            })
            .FirstResultItemAs<int>();
        Assert.AreEqual(actual, current);
    }
}

2 comments:

Bjorn Houben said...

Have you also looked into PSRemoteRegistry?
http://psremoteregistry.codeplex.com/

Prajeesh Prathap said...

Hi Houben,

Or you can use PSRemoteRegistry :)