Wednesday, February 25, 2015

PowerShell - Get, Set and Remove environment variables


The environment provider made it easy to work with the environment variables on a local machine or a remote server to load the environment as a PSDrive and work on it. This simplifies accessing data from environment variables using PowerShell using the same cmdlets as working with files and directories like Get-Item, Set-Item, Remove-Item etc.
We'll make use of this feature to get, set and remove environment variables.

Function Test-EnvironmentPath
{
      [CmdletBinding()]
      param
      (
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [String]$Path
      )
     
      if(-not ($Path.StartsWith("Env:\")))
      {
            $Path = "Env:\$($Path)"
      }
      Test-Path $Path   
}

Function Get-EnvironmentPath
{
      [CmdletBinding()]
      param
      (
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [String]$Path
      )
   
      if(-not ($Path.StartsWith("Env:\")))
      {
            $Path = "Env:\$($Path)"
      }
      if(Test-EnvironmentPath -Path $Path)
      {
            (Get-Item $Path).Value
      }   
      else
      {
            $null
      }
}

Function Set-EnvironmentPath
{
      [CmdletBinding()]
      param
      (
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [String]$Path,
           
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [String]$Value
      )
      if(-not ($Path.StartsWith("Env:")))
      {
            $Path = "Env:$($Path)"
      }
      Set-Item -Path $Path -Value $Value
}

Function Remove-EnvironmentPath
{
      [CmdletBinding()]
      param
      (
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [String]$Path
      )
      if(-not ($Path.StartsWith("Env:\")))
      {
            $Path = "Env:\$($Path)"
      }
      if(Test-EnvironmentPath -Path $Path)
      {
            Remove-Item $Path
      }
}

To test the scripts I’ve used the FluentShellUnit project and have my test cases as
[TestMethod]
[TestCategory("xPowerPack Module")]
public void TestEnvironmentPath_returns_true_if_value_exists()
{
    var expected = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine));

    var actual = PsFactory.Create(HostState.Core)
        .Load(@"Modules\Environment.psm1")
        .Execute("Test-EnvironmentPath", new Dictionary<string, string>
        {
            {"Path", "Path"}
        })
        .FirstResultItemAs<bool>();
    Assert.IsTrue(actual == expected);
}

[TestMethod]
[TestCategory("xPowerPack Module")]
public void GetEnvironmentPath_returns_value_from_environment_variable()
{
    var expected = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);

    var actual = PsFactory.Create(HostState.Core)
        .Load(@"Modules\Environment.psm1")
        .Execute("Get-EnvironmentPath", new Dictionary<string, string>
        {
            {"Path", "Path"}
        })
        .FirstResultItemAs<string>();
    Assert.IsTrue(actual == expected);
}

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);
    }
}