From 1a8dfcfe662a698bc783842cec090a08028bb848 Mon Sep 17 00:00:00 2001 From: Mariah Breakey Date: Fri, 16 Dec 2016 20:56:52 -0800 Subject: [PATCH] porting registry --- .../MSFT_RegistryResource.psm1 | 1449 ++++++ .../MSFT_RegistryResource.schema.mof | 11 + .../en-US/MSFT_RegistryResource.schema.mfl | 11 + .../en-US/MSFT_RegistryResource.strings.psd1 | 38 + Examples/Sample_RegistryResource_AddKey.ps1 | 19 + ...mple_RegistryResource_AddOrModifyValue.ps1 | 27 + .../Sample_RegistryResource_RemoveKey.ps1 | 19 + .../Sample_RegistryResource_RemoveValue.ps1 | 19 + PSDscResources.psd1 | 2 +- README.md | 33 +- .../MSFT_RegistryResource.EndToEnd.Tests.ps1 | 318 ++ ...SFT_RegistryResource.Integration.Tests.ps1 | 362 ++ ...RegistryResource_KeyAndNameOnly.config.ps1 | 39 + ...egistryResource_WithDataAndType.config.ps1 | 51 + .../MSFT_RegistryResource.TestHelper.psm1 | 402 ++ Tests/Unit/MSFT_RegistryResource.Tests.ps1 | 4108 +++++++++++++++++ 16 files changed, 6906 insertions(+), 2 deletions(-) create mode 100644 DscResources/MSFT_RegistryResource/MSFT_RegistryResource.psm1 create mode 100644 DscResources/MSFT_RegistryResource/MSFT_RegistryResource.schema.mof create mode 100644 DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.schema.mfl create mode 100644 DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.strings.psd1 create mode 100644 Examples/Sample_RegistryResource_AddKey.ps1 create mode 100644 Examples/Sample_RegistryResource_AddOrModifyValue.ps1 create mode 100644 Examples/Sample_RegistryResource_RemoveKey.ps1 create mode 100644 Examples/Sample_RegistryResource_RemoveValue.ps1 create mode 100644 Tests/Integration/MSFT_RegistryResource.EndToEnd.Tests.ps1 create mode 100644 Tests/Integration/MSFT_RegistryResource.Integration.Tests.ps1 create mode 100644 Tests/Integration/MSFT_RegistryResource_KeyAndNameOnly.config.ps1 create mode 100644 Tests/Integration/MSFT_RegistryResource_WithDataAndType.config.ps1 create mode 100644 Tests/TestHelpers/MSFT_RegistryResource.TestHelper.psm1 create mode 100644 Tests/Unit/MSFT_RegistryResource.Tests.ps1 diff --git a/DscResources/MSFT_RegistryResource/MSFT_RegistryResource.psm1 b/DscResources/MSFT_RegistryResource/MSFT_RegistryResource.psm1 new file mode 100644 index 0000000..3e0fe32 --- /dev/null +++ b/DscResources/MSFT_RegistryResource/MSFT_RegistryResource.psm1 @@ -0,0 +1,1449 @@ +$errorActionPreference = 'Stop' +Set-StrictMode -Version 'Latest' + +# Import CommonResourceHelper for Get-LocalizedData +$script:dscResourcesFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonResourceHelperFilePath = Join-Path -Path $script:dscResourcesFolderFilePath -ChildPath 'CommonResourceHelper.psm1' +Import-Module -Name $script:commonResourceHelperFilePath + +# Localized messages for verbose and error statements in this resource +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_RegistryResource' + +$script:registryDriveRoots = @{ + 'HKCC' = 'HKEY_CURRENT_CONFIG' + 'HKCR' = 'HKEY_CLASSES_ROOT' + 'HKCU' = 'HKEY_CURRENT_USER' + 'HKLM' = 'HKEY_LOCAL_MACHINE' + 'HKUS' = 'HKEY_USERS' +} + +<# + .SYNOPSIS + Retrieves the current state of the Registry resource with the given Key. + + .PARAMETER Key + The path of the registry key to retrieve the state of. + This path must include the registry hive. + + .PARAMETER ValueName + The name of the registry value to retrieve the state of. + + .PARAMETER ValueData + Used only as a boolean flag (along with ValueType) to determine if the target entity is the + Default Value or the key itself. + + .PARAMETER ValueType + Used only as a boolean flag (along with ValueData) to determine if the target entity is the + Default Value or the key itself. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Key, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [String] + [AllowEmptyString()] + $ValueName, + + [String[]] + $ValueData, + + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $ValueType + ) + + Write-Verbose -Message ($script:localizedData.GetTargetResourceStartMessage -f $Key) + + $registryResource = @{ + Key = $Key + Ensure = 'Absent' + ValueName = $null + ValueType = $null + ValueData = $null + } + + # Retrieve the registry key at the specified path + $registryKey = Get-RegistryKey -RegistryKeyPath $Key + + # Check if the registry key exists + if ($null -eq $registryKey) + { + Write-Verbose -Message ($script:localizedData.RegistryKeyDoesNotExist -f $Key) + } + else + { + Write-Verbose -Message ($script:localizedData.RegistryKeyExists -f $Key) + + # Check if the user specified a value name to retrieve + $valueNameSpecified = (-not [String]::IsNullOrEmpty($ValueName)) -or $PSBoundParameters.ContainsKey('ValueType') -or $PSBoundParameters.ContainsKey('ValueData') + + if ($valueNameSpecified) + { + $valueDisplayName = Get-RegistryKeyValueDisplayName -RegistryKeyValueName $ValueName + $registryResource['ValueName'] = $valueDisplayName + + # If a value name was specified, retrieve the value with the specified name from the retrieved registry key + $registryKeyValue = Get-RegistryKeyValue -RegistryKey $registryKey -RegistryKeyValueName $ValueName + + # Check if the registry key value exists + if ($null -eq $registryKeyValue) + { + Write-Verbose -Message ($script:localizedData.RegistryKeyValueDoesNotExist -f $Key, $valueDisplayName) + } + else + { + Write-Verbose -Message ($script:localizedData.RegistryKeyValueExists -f $Key, $valueDisplayName) + + # If the registry key value exists, retrieve its type + $actualValueType = Get-RegistryKeyValueType -RegistryKey $registryKey -RegistryKeyValueName $ValueName + + # If the registry key value exists, convert it to a readable string + $registryKeyValueAsReadableString = ConvertTo-ReadableString -RegistryKeyValue $registryKeyValue -RegistryKeyValueType $actualValueType + + $registryResource['Ensure'] = 'Present' + $registryResource['ValueType'] = $actualValueType + $registryResource['ValueData'] = $registryKeyValueAsReadableString + } + } + else + { + $registryResource['Ensure'] = 'Present' + } + } + + Write-Verbose -Message ($script:localizedData.GetTargetResourceEndMessage -f $Key) + + return $registryResource +} + +<# + .SYNOPSIS + Sets the Registry resource with the given Key to the specified state. + + .PARAMETER Key + The path of the registry key to set the state of. + This path must include the registry hive. + + .PARAMETER ValueName + The name of the registry value to set. + + To add or remove a registry key, specify this property as an empty string without + specifying ValueType or ValueData. To modify or remove the default value of a registry key, + specify this property as an empty string while also specifying ValueType or ValueData. + + .PARAMETER Ensure + Specifies whether or not the registry key with the given path and the registry key value with the given name should exist. + + To ensure that the registry key and value exists, set this property to Present. + To ensure that the registry key and value do not exist, set this property to Absent. + + The default value is Present. + + .PARAMETER ValueData + The data to set as the registry key value. + + .PARAMETER ValueType + The type of the value to set. + + The supported types are: + String (REG_SZ) + Binary (REG-BINARY) + Dword 32-bit (REG_DWORD) + Qword 64-bit (REG_QWORD) + Multi-string (REG_MULTI_SZ) + Expandable string (REG_EXPAND_SZ) + + .PARAMETER Hex + Specifies whether or not the value data should be expressed in hexadecimal format. + + If specified, DWORD/QWORD value data is presented in hexadecimal format. + Not valid for other value types. + + The default value is $false. + + .PARAMETER Force + Specifies whether or not to overwrite the registry key with the given path with the new + value if it is already present. +#> +function Set-TargetResource +{ + [CmdletBinding(SupportsShouldProcess = $true)] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Key, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [String] + [AllowEmptyString()] + $ValueName, + + [ValidateSet('Present', 'Absent')] + [String] + $Ensure = 'Present', + + [ValidateNotNull()] + [String[]] + $ValueData = @(), + + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $ValueType = 'String', + + [Boolean] + $Hex = $false, + + [Boolean] + $Force = $false + ) + + Write-Verbose -Message ($script:localizedData.SetTargetResourceStartMessage -f $Key) + + # Retrieve the registry key at the specified path + $registryKey = Get-RegistryKey -RegistryKeyPath $Key -WriteAccessAllowed + + # Check if the registry key exists + if ($null -eq $registryKey) + { + Write-Verbose -Message ($script:localizedData.RegistryKeyDoesNotExist -f $Key) + + # Check if the user wants the registry key to exist + if ($Ensure -eq 'Present') + { + Write-Verbose -Message ($script:localizedData.CreatingRegistryKey -f $Key) + $registryKey = New-RegistryKey -RegistryKeyPath $Key + } + } + + # Check if the registry key exists + if ($null -ne $registryKey) + { + Write-Verbose -Message ($script:localizedData.RegistryKeyExists -f $Key) + + $valueNameSpecified = (-not [String]::IsNullOrEmpty($ValueName)) -or $PSBoundParameters.ContainsKey('ValueType') -or $PSBoundParameters.ContainsKey('ValueData') + + # Check if the user wants to set a registry key value + if ($valueNameSpecified) + { + # Retrieve the display name of the specified registry key value + $valueDisplayName = Get-RegistryKeyValueDisplayName -RegistryKeyValueName $ValueName + + # Retrieve the existing registry key value + $actualRegistryKeyValue = Get-RegistryKeyValue -RegistryKey $registryKey -RegistryKeyValueName $ValueName + + # Check if the user wants to add/modify or remove the registry key value + if ($Ensure -eq 'Present') + { + # Convert the specified registry key value to the specified type + $expectedRegistryKeyValue = switch ($ValueType) + { + 'Binary' { ConvertTo-Binary -RegistryKeyValue $ValueData; break } + 'DWord' { ConvertTo-DWord -RegistryKeyValue $ValueData -Hex $Hex; break } + 'MultiString' { ConvertTo-MultiString -RegistryKeyValue $ValueData; break } + 'QWord' { ConvertTo-QWord -RegistryKeyValue $ValueData -Hex $Hex; break } + default { ConvertTo-String -RegistryKeyValue $ValueData} + } + + # Retrieve the name of the registry key + $registryKeyName = Get-RegistryKeyName -RegistryKey $registryKey + + # Check if the registry key value exists + if ($null -eq $actualRegistryKeyValue) + { + # If the registry key value does not exist, set the new value + Write-Verbose -Message ($script:localizedData.SettingRegistryKeyValue -f $valueDisplayName, $Key) + $null = Set-RegistryKeyValue -RegistryKeyName $registryKeyName -RegistryKeyValueName $ValueName -RegistryKeyValue $expectedRegistryKeyValue -ValueType $ValueType + } + else + { + # If the registry key value exists, check if the specified registry key value matches the retrieved registry key value + if (Test-RegistryKeyValuesMatch -ExpectedRegistryKeyValue $expectedRegistryKeyValue -ActualRegistryKeyValue $actualRegistryKeyValue -RegistryKeyValueType $ValueType) + { + # If the specified registry key value matches the retrieved registry key value, no change is needed + Write-Verbose -Message ($script:localizedData.RegistryKeyValueAlreadySet -f $valueDisplayName, $Key) + } + else + { + # If the specified registry key value matches the retrieved registry key value, check if the user wants to overwrite the value + if (-not $Force) + { + # If the user does not want to overwrite the value, throw an error + New-InvalidOperationException -Message ($script:localizedData.CannotOverwriteExistingRegistryKeyValueWithoutForce -f $Key, $valueDisplayName) + } + else + { + # If the user does want to overwrite the value, overwrite the value + Write-Verbose -Message ($script:localizedData.OverwritingRegistryKeyValue -f $valueDisplayName, $Key) + $null = Set-RegistryKeyValue -RegistryKeyName $registryKeyName -RegistryKeyValueName $ValueName -RegistryKeyValue $expectedRegistryKeyValue -ValueType $ValueType + } + } + } + } + else + { + # Check if the registry key value exists + if ($null -ne $actualRegistryKeyValue) + { + Write-Verbose -Message ($script:localizedData.RemovingRegistryKeyValue -f $valueDisplayName, $Key) + + # If the specified registry key value exists, check if the user specified a registry key value with a name to remove + if (-not [String]::IsNullOrEmpty($ValueName)) + { + # If the user specified a registry key value with a name to remove, remove the registry key value with the specified name + $null = Remove-ItemProperty -Path $Key -Name $ValueName -Force + } + else + { + # If the user did not specify a registry key value with a name to remove, remove the default registry key value + $null = Remove-DefaultRegistryKeyValue -RegistryKey $registryKey + } + } + } + } + else + { + # Check if the user wants to remove the registry key + if ($Ensure -eq 'Absent') + { + # Retrieve the number of subkeys the registry key has + $registryKeySubKeyCount = Get-RegistryKeySubKeyCount -RegistryKey $registryKey + + # Check if the registry key has subkeys and the user does not want to forcibly remove the registry key + if ($registryKeySubKeyCount -gt 0 -and -not $Force) + { + New-InvalidOperationException -Message ($script:localizedData.CannotRemoveExistingRegistryKeyWithSubKeysWithoutForce -f $Key) + } + else + { + # Remove the registry key + Write-Verbose -Message ($script:localizedData.RemovingRegistryKey -f $Key) + $null = Remove-Item -Path $Key -Recurse -Force + } + } + } + } + + Write-Verbose -Message ($script:localizedData.SetTargetResourceEndMessage -f $Key) +} + +<# + .SYNOPSIS + Tests if the Registry resource with the given key is in the specified state. + + .PARAMETER Key + The path of the registry key to test the state of. + This path must include the registry hive. + + .PARAMETER ValueName + The name of the registry value to check for. + Specify this property as an empty string ('') to check the default value of the registry key. + + .PARAMETER Ensure + Specifies whether or not the registry key and value should exist. + + To test that they exist, set this property to "Present". + To test that they do not exist, set the property to "Absent". + The default value is "Present". + + .PARAMETER ValueData + The data the registry key value should have. + + .PARAMETER ValueType + The type of the value. + + The supported types are: + String (REG_SZ) + Binary (REG-BINARY) + Dword 32-bit (REG_DWORD) + Qword 64-bit (REG_QWORD) + Multi-string (REG_MULTI_SZ) + Expandable string (REG_EXPAND_SZ) + + .PARAMETER Hex + Not used in Test-TargetResource. + + .PARAMETER Force + Not used in Test-TargetResource. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Key, + + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [ValidateNotNull()] + [String] + $ValueName, + + [ValidateSet('Present', 'Absent')] + [String] + $Ensure = 'Present', + + [ValidateNotNull()] + [String[]] + $ValueData = @(), + + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $ValueType = 'String', + + [Boolean] + $Hex = $false, + + [Boolean] + $Force = $false + ) + + Write-Verbose -Message ($script:localizedData.TestTargetResourceStartMessage -f $Key) + + $registryResourceInDesiredState = $false + + $getTargetResourceParameters = @{ + Key = $Key + ValueName = $ValueName + } + + if ($PSBoundParameters.ContainsKey('ValueType')) + { + $getTargetResourceParameters['ValueType'] = $ValueType + } + + if ($PSBoundParameters.ContainsKey('ValueData')) + { + $getTargetResourceParameters['ValueData'] = $ValueData + } + + $registryResource = Get-TargetResource @getTargetResourceParameters + + # Check if the user specified a value name to retrieve + $valueNameSpecified = (-not [String]::IsNullOrEmpty($ValueName)) -or $PSBoundParameters.ContainsKey('ValueType') -or $PSBoundParameters.ContainsKey('ValueData') + + if ($valueNameSpecified) + { + $valueDisplayName = Get-RegistryKeyValueDisplayName -RegistryKeyValueName $ValueName + + if ($registryResource.Ensure -eq 'Absent') + { + Write-Verbose -Message ($script:localizedData.RegistryKeyValueDoesNotExist -f $Key, $valueDisplayName) + $registryResourceInDesiredState = $Ensure -eq 'Absent' + } + else + { + Write-Verbose -Message ($script:localizedData.RegistryKeyValueExists -f $Key, $valueDisplayName) + + if ($Ensure -eq 'Absent') + { + $registryResourceInDesiredState = $false + } + elseif ($PSBoundParameters.ContainsKey('ValueType') -and $ValueType -ne $registryResource.ValueType) + { + Write-Verbose -Message ($script:localizedData.RegistryKeyValueTypeDoesNotMatch -f $valueDisplayName, $Key, $ValueType, $registryResource.ValueType) + + $registryResourceInDesiredState = $false + } + elseif ($PSBoundParameters.ContainsKey('ValueData')) + { + # Need to get the actual registry key value since Get-TargetResource returns + $registryKey = Get-RegistryKey -RegistryKeyPath $Key + $actualRegistryKeyValue = Get-RegistryKeyValue -RegistryKey $registryKey -RegistryKeyValueName $ValueName + + if (-not $PSBoundParameters.ContainsKey('ValueType') -and $null -ne $registryResource.ValueType) + { + $ValueType = $registryResource.ValueType + } + + # Convert the specified registry key value to the specified type + $expectedRegistryKeyValue = switch ($ValueType) + { + 'Binary' { ConvertTo-Binary -RegistryKeyValue $ValueData; break } + 'DWord' { ConvertTo-DWord -RegistryKeyValue $ValueData -Hex $Hex; break } + 'MultiString' { ConvertTo-MultiString -RegistryKeyValue $ValueData; break } + 'QWord' { ConvertTo-QWord -RegistryKeyValue $ValueData -Hex $Hex; break } + default { ConvertTo-String -RegistryKeyValue $ValueData; break } + } + + if (-not (Test-RegistryKeyValuesMatch -ExpectedRegistryKeyValue $expectedRegistryKeyValue -ActualRegistryKeyValue $actualRegistryKeyValue -RegistryKeyValueType $ValueType)) + { + Write-Verbose -Message ($script:localizedData.RegistryKeyValueDoesNotMatch -f $valueDisplayName, $Key, $ValueData, $registryResource.ValueData) + + $registryResourceInDesiredState = $false + } + else + { + $registryResourceInDesiredState = $true + } + } + else + { + $registryResourceInDesiredState = $true + } + } + } + else + { + if ($registryResource.Ensure -eq 'Present') + { + Write-Verbose -Message ($script:localizedData.RegistryKeyExists -f $Key) + $registryResourceInDesiredState = $Ensure -eq 'Present' + } + else + { + Write-Verbose -Message ($script:localizedData.RegistryKeyDoesNotExist -f $Key) + $registryResourceInDesiredState = $Ensure -eq 'Absent' + } + } + + Write-Verbose -Message ($script:localizedData.TestTargetResourceEndMessage -f $Key) + + return $registryResourceInDesiredState +} + +<# + .SYNOPSIS + Retrieves the root of the specified path. + + .PARAMETER Path + The path to retrieve the root of. +#> +function Get-PathRoot +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Path + ) + + $pathParent = Split-Path -Path $Path -Parent + $pathRoot = $Path + + while (-not [String]::IsNullOrEmpty($pathParent)) + { + $pathRoot = Split-Path -Path $pathParent -Leaf + $pathParent = Split-Path -Path $pathParent -Parent + } + + return $pathRoot +} + +<# + .SYNOPSIS + Converts the specified registry drive root to its corresponding registry drive name. + + .PARAMETER RegistryDriveRoot + The registry drive root to convert. +#> +function ConvertTo-RegistryDriveName +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $RegistryDriveRoot + ) + + $registryDriveName = $null + + if ($script:registryDriveRoots.ContainsValue($RegistryDriveRoot)) + { + foreach ($registryDriveRootsKey in $script:registryDriveRoots.Keys) + { + if ($script:registryDriveRoots[$registryDriveRootsKey] -ieq $RegistryDriveRoot) + { + $registryDriveName = $registryDriveRootsKey + break + } + } + } + + return $registryDriveName +} + +<# + .SYNOPSIS + Retrieves the name of the registry drive at the root of the the specified registry key path. + + .PARAMETER RegistryKeyPath + The registry key path to retrieve the registry drive name from. +#> +function Get-RegistryDriveName +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $RegistryKeyPath + ) + + $registryKeyPathRoot = Get-PathRoot -Path $RegistryKeyPath + $registryKeyPathRoot = $registryKeyPathRoot.TrimEnd('\') + + if ($registryKeyPathRoot.Contains(':')) + { + $registryDriveName = $registryKeyPathRoot.TrimEnd(':') + + if (-not $script:registryDriveRoots.ContainsKey($registryDriveName)) + { + New-InvalidArgumentException -ArgumentName 'Key' -Message ($script:localizedData.InvalidRegistryDrive -f $registryDriveName) + } + } + else + { + $registryDriveName = ConvertTo-RegistryDriveName -RegistryDriveRoot $registryKeyPathRoot + + if ([String]::IsNullOrEmpty($registryDriveName)) + { + New-InvalidArgumentException -ArgumentName 'Key' -Message ($script:localizedData.InvalidRegistryDrive -f $registryKeyPathRoot) + } + } + + return $registryDriveName +} + +<# + .SYNOPSIS + Mounts the registry drive with the specified name. + + .PARAMETER RegistryKeyName + The name of the registry drive to mount. +#> +function Mount-RegistryDrive +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $RegistryDriveName + ) + + $registryDriveInfo = Get-PSDrive -Name $RegistryDriveName -ErrorAction 'SilentlyContinue' + + if ($null -eq $registryDriveInfo) + { + $newPSDriveParameters = @{ + Name = $RegistryDriveName + Root = $script:registryDriveRoots[$RegistryDriveName] + PSProvider = 'Registry' + Scope = 'Script' + } + + $registryDriveInfo = New-PSDrive @newPSDriveParameters + } + + # Validate that the specified PSDrive is valid + if (($null -eq $registryDriveInfo) -or ($null -eq $registryDriveInfo.Provider) -or ($registryDriveInfo.Provider.Name -ine 'Registry')) + { + New-InvalidOperationException -Message ($script:localizedData.RegistryDriveCouldNotBeMounted -f $RegistryDriveName) + } +} + +<# + .SYNOPSIS + Opens the specified registry sub key under the specified registry parent key. + This is a wrapper function for unit testing. + + .PARAMETER ParentKey + The parent registry key which contains the sub key to open. + + .PARAMETER SubKey + The sub key to open. + + .PARAMETER WriteAccessAllowed + Specifies whether or not to open the sub key with permissions to write to it. +#> +function Open-RegistrySubKey +{ + [OutputType([Microsoft.Win32.RegistryKey])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $ParentKey, + + [Parameter(Mandatory = $true)] + [String] + [AllowEmptyString()] + $SubKey, + + [Parameter()] + [Switch] + $WriteAccessAllowed + ) + + return $ParentKey.OpenSubKey($SubKey, $WriteAccessAllowed) +} + +<# + .SYNOPSIS + Opens and retrieves the registry key at the specified path. + + .PARAMETER RegistryKeyPath + The path to the registry key to open. + The path must include the registry drive. + + .PARAMETER WriteAccessAllowed + Specifies whether or not to open the key with permissions to write to it. + + .NOTES + This method is used instead of Get-Item so that there is no ambiguity between + forward slashes as path separators vs literal characters in a key name + (which is valid in the registry). + +#> +function Get-RegistryKey +{ + [OutputType([Microsoft.Win32.RegistryKey])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $RegistryKeyPath, + + [Switch] + $WriteAccessAllowed + ) + + # Parse the registry drive from the specified registry key path + $registryDriveName = Get-RegistryDriveName -RegistryKeyPath $RegistryKeyPath + + # Mount the registry drive if needed + Mount-RegistryDrive -RegistryDriveName $registryDriveName + + # Retrieve the registry drive key + $registryDriveKey = Get-Item -LiteralPath ($registryDriveName + ':') + + # Parse the registry drive subkey from the specified registry key path + $indexOfBackSlashInPath = $RegistryKeyPath.IndexOf('\') + if ($indexOfBackSlashInPath -ge 0 -and $indexOfBackSlashInPath -lt ($RegistryKeyPath.Length - 1)) + { + $registryDriveSubKey = $RegistryKeyPath.Substring($RegistryKeyPath.IndexOf('\') + 1) + } + else + { + $registryDriveSubKey = '' + } + + # Open the registry drive subkey + $registryKey = Open-RegistrySubKey -ParentKey $registryDriveKey -SubKey $registryDriveSubKey -WriteAccessAllowed:$WriteAccessAllowed + + # Return the opened registry key + return $registryKey +} + +<# + .SYNOPSIS + Retrieves the display name of the default registry key value if needed. + + .PARAMETER RegistryKeyValueName + The name of the registry key value to retrieve the display name of. +#> +function Get-RegistryKeyValueDisplayName +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [String] + [AllowNull()] + [AllowEmptyString()] + $RegistryKeyValueName + ) + + $registryKeyValueDisplayName = $RegistryKeyValueName + + if ([String]::IsNullOrEmpty($RegistryKeyValueName)) + { + $registryKeyValueDisplayName = $script:localizedData.DefaultValueDisplayName + } + + return $registryKeyValueDisplayName +} + +<# + .SYNOPSIS + Retrieves the registry key value with the specified name from the specified registry key. + This is a wrapper function for unit testing. + + .PARAMETER RegistryKey + The registry key to retrieve the value from. + + .PARAMETER RegistryKeyValueName + The name of the registry key value to retrieve. +#> +function Get-RegistryKeyValue +{ + [OutputType([Object[]])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $RegistryKey, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [String] + [AllowEmptyString()] + $RegistryKeyValueName + ) + + $registryValueOptions = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames + $registryKeyValue = $RegistryKey.GetValue($RegistryKeyValueName, $null, $registryValueOptions) + return ,$registryKeyValue +} + +<# + .SYNOPSIS + Retrieves the type of the registry key value with the specified name from the the specified + registry key. + This is a wrapper function for unit testing. + + .PARAMETER RegistryKey + The registry key to retrieve the type of the value from. + + .PARAMETER RegistryKeyValueName + The name of the registry key value to retrieve the type of. +#> +function Get-RegistryKeyValueType +{ + [OutputType([Type])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $RegistryKey, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [String] + [AllowEmptyString()] + $RegistryKeyValueName + ) + + return $RegistryKey.GetValueKind($RegistryKeyValueName) +} + +<# + .SYNOPSIS + Converts the specified byte array to a hex string. + + .PARAMETER ByteArray + The byte array to convert. +#> +function Convert-ByteArrayToHexString +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [Object[]] + [AllowEmptyCollection()] + $ByteArray + ) + + $hexString = '' + + foreach ($byte in $ByteArray) + { + $hexString += ('{0:x2}' -f $byte) + } + + return $hexString +} + +<# + .SYNOPSIS + Converts the specified registry key value to a readable string. + + .PARAMETER RegistryKeyValue + The registry key value to convert. + + .PARAMETER RegistryKeyValueType + The type of the registry key value to convert. +#> +function ConvertTo-ReadableString +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Object[]] + [AllowNull()] + [AllowEmptyCollection()] + $RegistryKeyValue, + + [Parameter(Mandatory = $true)] + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $RegistryKeyValueType + ) + + $registryKeyValueAsString = [String]::Empty + + if ($null -ne $RegistryKeyValue) + { + # For Binary type data, convert the received bytes back to a readable hex-string + if ($RegistryKeyValueType -eq 'Binary') + { + $RegistryKeyValue = Convert-ByteArrayToHexString -ByteArray $RegistryKeyValue + } + + if ($RegistryKeyValueType -ne 'MultiString') + { + $RegistryKeyValue = [String[]]@() + $RegistryKeyValue + } + + if ($RegistryKeyValue.Count -eq 1 -and -not [String]::IsNullOrEmpty($RegistryKeyValue[0])) + { + $registryKeyValueAsString = $RegistryKeyValue[0].ToString() + } + elseif ($RegistryKeyValue.Count -gt 1) + { + $registryKeyValueAsString = "($($RegistryKeyValue -join ', '))" + } + } + + return $registryKeyValueAsString +} + +<# + .SYNOPSIS + Creates a new subkey with the specified name under the specified registry key. + This is a wrapper function for unit testing. + + .PARAMETER ParentRegistryKey + The parent registry key to create the new subkey under. + + .PARAMETER SubKeyName + The name of the new subkey to create. +#> +function New-RegistrySubKey +{ + [OutputType([Microsoft.Win32.RegistryKey])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $ParentRegistryKey, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $SubKeyName + ) + + return $ParentRegistryKey.CreateSubKey($SubKeyName) +} + +<# + .SYNOPSIS + Creates a new registry key at the specified registry key path. + + .PARAMETER RegistryKeyPath + The path at which to create the registry key. + This path must include the registry drive. +#> +function New-RegistryKey +{ + [OutputType([Microsoft.Win32.RegistryKey])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $RegistryKeyPath + ) + + $parentRegistryKeyPath = Split-Path -Path $RegistryKeyPath -Parent + $newRegistryKeyName = Split-Path -Path $RegistryKeyPath -Leaf + + $parentRegistryKey = Get-RegistryKey -RegistryKeyPath $parentRegistryKeyPath -WriteAccessAllowed + + if ($null -eq $parentRegistryKey) + { + # If the parent registry key does not exist, create it + $parentRegistryKey = New-RegistryKey -RegistryKeyPath $parentRegistryKeyPath + } + + $newRegistryKey = New-RegistrySubKey -ParentRegistryKey $parentRegistryKey -SubKeyName $newRegistryKeyName + + return $newRegistryKey +} + +<# + .SYNOPSIS + Retrieves the name of the specified registry key. + This is a wrapper function for unit testing. + + .PARAMETER RegistryKey + The registry key to retrieve the name of. +#> +function Get-RegistryKeyName +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $RegistryKey + ) + + return $RegistryKey.Name +} + +<# + .SYNOPSIS + Converts the specified registry key value to a byte array for the Binary registry type. + + .PARAMETER RegistryKeyValue + The registry key value to convert. +#> +function ConvertTo-Binary +{ + [OutputType([Byte[]])] + [CmdletBinding()] + param + ( + [Parameter()] + [AllowNull()] + [String[]] + [AllowEmptyCollection()] + $RegistryKeyValue + ) + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -gt 1)) + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.ArrayNotAllowedForExpectedType -f 'Binary') + } + + $binaryRegistryKeyValue = [Byte[]] @() + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -eq 1) -and (-not [String]::IsNullOrEmpty($RegistryKeyValue[0]))) + { + $singleRegistryKeyValue = $RegistryKeyValue[0] + + if ($singleRegistryKeyValue.StartsWith('0x')) + { + $singleRegistryKeyValue = $singleRegistryKeyValue.Substring('0x'.Length) + } + + if (($singleRegistryKeyValue.Length % 2) -ne 0) + { + $singleRegistryKeyValue = $singleRegistryKeyValue.PadLeft($singleRegistryKeyValue.Length + 1, '0') + } + + try + { + for ($singleRegistryKeyValueIndex = 0 ; $singleRegistryKeyValueIndex -lt ($singleRegistryKeyValue.Length - 1) ; $singleRegistryKeyValueIndex = $singleRegistryKeyValueIndex + 2) + { + $binaryRegistryKeyValue += [Byte]::Parse($singleRegistryKeyValue.Substring($singleRegistryKeyValueIndex, 2), 'HexNumber') + } + } + catch + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.BinaryDataNotInHexFormat -f $singleRegistryKeyValue) + } + } + + return $binaryRegistryKeyValue +} + +<# + .SYNOPSIS + Converts the specified registry key value to an Int32 for the DWord registry type. + + .PARAMETER RegistryKeyValue + The registry key value to convert. +#> +function ConvertTo-DWord +{ + [OutputType([System.Int32])] + [CmdletBinding()] + param + ( + [Parameter()] + [AllowNull()] + [String[]] + [AllowEmptyCollection()] + $RegistryKeyValue, + + [Parameter()] + [Boolean] + $Hex = $false + ) + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -gt 1)) + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.ArrayNotAllowedForExpectedType -f 'Dword') + } + + $dwordRegistryKeyValue = [System.Int32] 0 + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -eq 1) -and (-not [String]::IsNullOrEmpty($RegistryKeyValue[0]))) + { + $singleRegistryKeyValue = $RegistryKeyValue[0] + + if ($Hex) + { + if ($singleRegistryKeyValue.StartsWith('0x')) + { + $singleRegistryKeyValue = $singleRegistryKeyValue.Substring('0x'.Length) + } + + $currentCultureInfo = [System.Globalization.CultureInfo]::CurrentCulture + $referenceValue = $null + + if ([System.Int32]::TryParse($singleRegistryKeyValue, 'HexNumber', $currentCultureInfo, [Ref] $referenceValue)) + { + $dwordRegistryKeyValue = $referenceValue + } + else + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.DWordDataNotInHexFormat -f $singleRegistryKeyValue) + } + } + else + { + $dwordRegistryKeyValue = [System.Int32]::Parse($singleRegistryKeyValue) + } + } + + return $dwordRegistryKeyValue +} + +<# + .SYNOPSIS + Converts the specified registry key value to a string array for the MultiString registry type. + + .PARAMETER RegistryKeyValue + The registry key value to convert. +#> +function ConvertTo-MultiString +{ + [OutputType([String[]])] + [CmdletBinding()] + param + ( + [Parameter()] + [AllowNull()] + [String[]] + [AllowEmptyCollection()] + $RegistryKeyValue + ) + + $multiStringRegistryKeyValue = [String[]] @() + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Length -gt 0)) + { + $multiStringRegistryKeyValue = [String[]]$RegistryKeyValue + } + + return $multiStringRegistryKeyValue +} + +<# + .SYNOPSIS + Converts the specified registry key value to an Int64 for the QWord registry type. + + .PARAMETER RegistryKeyValue + The registry key value to convert. +#> +function ConvertTo-QWord +{ + [OutputType([System.Int64])] + [CmdletBinding()] + param + ( + [Parameter()] + [AllowNull()] + [String[]] + [AllowEmptyCollection()] + $RegistryKeyValue, + + [Parameter()] + [Boolean] + $Hex = $false + ) + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -gt 1)) + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.ArrayNotAllowedForExpectedType -f 'Qword') + } + + $qwordRegistryKeyValue = [System.Int64] 0 + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -eq 1) -and (-not [String]::IsNullOrEmpty($RegistryKeyValue[0]))) + { + $singleRegistryKeyValue = $RegistryKeyValue[0] + + if ($Hex) + { + if ($singleRegistryKeyValue.StartsWith('0x')) + { + $singleRegistryKeyValue = $singleRegistryKeyValue.Substring('0x'.Length) + } + + $currentCultureInfo = [System.Globalization.CultureInfo]::CurrentCulture + $referenceValue = $null + + if ([System.Int64]::TryParse($singleRegistryKeyValue, 'HexNumber', $currentCultureInfo, [Ref] $referenceValue)) + { + $qwordRegistryKeyValue = $referenceValue + } + else + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.QWordDataNotInHexFormat -f $singleRegistryKeyValue) + } + } + else + { + $qwordRegistryKeyValue = [System.Int64]::Parse($singleRegistryKeyValue) + } + } + + return $qwordRegistryKeyValue +} + +<# + .SYNOPSIS + Converts the specified registry key value to a string for the String or ExpandString registry types. + + .PARAMETER RegistryKeyValue + The registry key value to convert. +#> +function ConvertTo-String +{ + [OutputType([String])] + [CmdletBinding()] + param + ( + [Parameter()] + [AllowNull()] + [String[]] + [AllowEmptyCollection()] + $RegistryKeyValue + ) + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -gt 1)) + { + New-InvalidArgumentException -ArgumentName 'ValueData' -Message ($script:localizedData.ArrayNotAllowedForExpectedType -f 'String or ExpandString') + } + + $registryKeyValueAsString = [String]::Empty + + if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -eq 1)) + { + $registryKeyValueAsString = [String]$RegistryKeyValue[0] + } + + return $registryKeyValueAsString +} + +<# + .SYNOPSIS + Sets the specified registry key value with the specified name to the specified value. + This is a wrapper function for unit testing. + + .PARAMETER RegistryKeyName + The name of the registry key that the value to set is under. + + .PARAMETER RegistryKeyValueName + The name of the registry key value to set. + + .PARAMETER RegistryKeyValue + The new value to set the registry key value to. + + .PARAMETER RegistryKeyValueType + The type of the new value to set the registry key value to. +#> +function Set-RegistryKeyValue +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $RegistryKeyName, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [String] + [AllowEmptyString()] + $RegistryKeyValueName, + + [Parameter(Mandatory = $true)] + [Object] + [AllowNull()] + $RegistryKeyValue, + + [Parameter(Mandatory = $true)] + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $ValueType + ) + + if ($ValueType -eq 'Binary') + { + $RegistryKeyValue = [Byte[]]$RegistryKeyValue + } + elseif ($ValueType -eq 'MultiString') + { + $RegistryKeyValue = [String[]]$RegistryKeyValue + } + + $null = [Microsoft.Win32.Registry]::SetValue($RegistryKeyName, $RegistryKeyValueName, $RegistryKeyValue, $ValueType) +} + +<# + .SYNOPSIS + Tests if the actual registry key value matches the expected registry key value. + + .PARAMETER ExpectedRegistryKeyValue + The expected registry key value to test against. + + .PARAMETER ActualRegistryKeyValue + The actual registry key value to test. + + .PARAMETER RegistryKeyValueType + The type of the registry key values. +#> +function Test-RegistryKeyValuesMatch +{ + [OutputType([Boolean])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Object] + [AllowNull()] + $ExpectedRegistryKeyValue, + + [Parameter(Mandatory = $true)] + [Object] + [AllowNull()] + $ActualRegistryKeyValue, + + [Parameter(Mandatory = $true)] + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $RegistryKeyValueType + ) + + $registryKeyValuesMatch = $true + + if ($RegistryKeyValueType -eq 'Multistring' -or $RegistryKeyValueType -eq 'Binary') + { + if ($null -eq $ExpectedRegistryKeyValue) + { + $ExpectedRegistryKeyValue = @() + } + + if ($null -eq $ActualRegistryKeyValue) + { + $ActualRegistryKeyValue = @() + } + + $registryKeyValuesMatch = $null -eq (Compare-Object -ReferenceObject $ExpectedRegistryKeyValue -DifferenceObject $ActualRegistryKeyValue) + } + else + { + if ($null -eq $ExpectedRegistryKeyValue) + { + $ExpectedRegistryKeyValue = '' + } + + if ($null -eq $ActualRegistryKeyValue) + { + $ActualRegistryKeyValue = '' + } + + $registryKeyValuesMatch = $ExpectedRegistryKeyValue -ieq $ActualRegistryKeyValue + } + + return $registryKeyValuesMatch +} + +<# + .SYNOPSIS + Removes the default value of the specified registry key. + This is a wrapper function for unit testing. + + .PARAMETER RegistryKey + The registry key to remove the default value of. +#> +function Remove-DefaultRegistryKeyValue +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $RegistryKey + ) + + $null = $RegistryKey.DeleteValue('') +} + +<# + .SYNOPSIS + Retrieves the number of subkeys under the specified registry key. + This is a wrapper function for unit testing. + + .PARAMETER RegistryKey + The registry key to retrieve the subkeys of. +#> +function Get-RegistryKeySubKeyCount +{ + [OutputType([Int])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [Microsoft.Win32.RegistryKey] + $RegistryKey + ) + + return $RegistryKey.SubKeyCount +} diff --git a/DscResources/MSFT_RegistryResource/MSFT_RegistryResource.schema.mof b/DscResources/MSFT_RegistryResource/MSFT_RegistryResource.schema.mof new file mode 100644 index 0000000..be7b77f --- /dev/null +++ b/DscResources/MSFT_RegistryResource/MSFT_RegistryResource.schema.mof @@ -0,0 +1,11 @@ +[ClassVersion("1.0.0"), FriendlyName("Registry")] +class MSFT_xRegistryResource : OMI_BaseResource +{ + [Key, Description("The path of the registry key to add, modify, or remove. This path must include the registry hive/drive.")] String Key; + [Key, Description("The name of the registry value. To add or remove a registry key, specify this property as an empty string without specifying ValueType or ValueData. To modify or remove the default value of a registry key, specify this property as an empty string while also specifying ValueType or ValueData.")] String ValueName; + [Write, Description("The data the specified registry key value should have as a string or an array of strings (MultiString only).")] String ValueData[]; + [Write, Description("The type the specified registry key value should have."), ValueMap{"String", "Binary", "DWord", "QWord", "MultiString", "ExpandString"},Values{"String", "Binary", "DWord", "QWord", "MultiString", "ExpandString"}] String ValueType; + [Write, Description("Specifies whether or not the registry key or value should exist. To add or modify a registry key or value, set this property to Present. To remove a registry key or value, set the property to Absent."), ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure; + [Write, Description("Specifies whether or not the specified DWord or QWord registry key data is provided in a hexadecimal format. Not valid for types other than DWord and QWord. The default value is $false.")] Boolean Hex; + [Write, Description("Specifies whether or not to overwrite the specified registry key value if it already has a value or whether or not to delete a registry key that has subkeys. The default value is $false.")] Boolean Force; +}; diff --git a/DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.schema.mfl b/DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.schema.mfl new file mode 100644 index 0000000..6e3f051 --- /dev/null +++ b/DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.schema.mfl @@ -0,0 +1,11 @@ +[Description("Provides a mechanism to manage registry keys and values on a target node.") : Amended,AMENDMENT, LOCALE("MS_409")] +class MSFT_RegistryResource : OMI_BaseResource +{ + [Key, Description("The path of the registry key to add, modify, or remove. This path must include the registry hive/drive.") : Amended] String Key; + [Key, Description("The name of the registry value. To add or remove a registry key, specify this property as an empty string without specifying ValueType or ValueData. To modify or remove the default value of a registry key, specify this property as an empty string while also specifying ValueType or ValueData.") : Amended] String ValueName; + [Description("The data the specified registry key value should have as a string or an array of strings (MultiString only).") : Amended] String ValueData[]; + [Description("The type the specified registry key value should have.") : Amended] String ValueType; + [Description("Specifies whether or not the registry key or value should exist. To add or modify a registry key or value, set this property to Present. To remove a registry key or value, set the property to Absent.") : Amended] String Ensure; + [Description("Specifies whether or not the specified DWord or QWord registry key data is provided in a hexadecimal format. Not valid for types other than DWord and QWord. The default value is $false.") : Amended] Boolean Hex; + [Description("Specifies whether or not to overwrite the specified registry key value if it already has a value or whether or not to delete a registry key that has subkeys. The default value is $false.") : Amended] Boolean Force; +}; diff --git a/DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.strings.psd1 b/DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.strings.psd1 new file mode 100644 index 0000000..6ceb0ad --- /dev/null +++ b/DscResources/MSFT_RegistryResource/en-US/MSFT_RegistryResource.strings.psd1 @@ -0,0 +1,38 @@ +# Localized resources for MSFT_RegistryResource + +ConvertFrom-StringData @' + DefaultValueDisplayName = (Default) + + GetTargetResourceStartMessage = Get-TargetResource is starting for Registry resource with Key {0} + GetTargetResourceEndMessage = Get-TargetResource has finished for Registry resource with Key {0} + RegistryKeyDoesNotExist = The registry key at path {0} does not exist. + RegistryKeyExists = The registry key at path {0} exists. + RegistryKeyValueDoesNotExist = The registry key at path {0} does not have a value named {1}. + RegistryKeyValueExists = The registry key at path {0} has a value named {1}. + + SetTargetResourceStartMessage = Set-TargetResource is starting for Registry resource with Key {0} + SetTargetResourceEndMessage = Set-TargetResource has finished for Registry resource with Key {0} + CreatingRegistryKey = Creating registry key at path {0}... + SettingRegistryKeyValue = Setting the value {0} under the registry key at path {1}... + OverwritingRegistryKeyValue = Overwriting the value {0} under the registry key at path {1}... + RemovingRegistryKey = Removing registry key at path {0}... + RegistryKeyValueAlreadySet = The value {0} under the registry key at path {1} has already been set to the specified value. + RemovingRegistryKeyValue = Removeing the value {0} from the registry key at path {1}... + + TestTargetResourceStartMessage = Test-TargetResource is starting for Registry resource with Key {0} + TestTargetResourceEndMessage = Test-TargetResource has finished for Registry resource with Key {0} + RegistryKeyValueTypeDoesNotMatch = The type of the value {0} under the registry key at path {1} does not match the expected type. Expected {2} but was {3}. + RegistryKeyValueDoesNotMatch = The value {0} under the registry key at path {1} does not match the expected value. Expected {2} but was {3}. + + CannotRemoveExistingRegistryKeyWithSubKeysWithoutForce = The registry key at path {0} has subkeys. To remove this registry key please specifiy the Force parameter as $true. + CannotOverwriteExistingRegistryKeyValueWithoutForce = The registry key at path {0} already has a value with the name {1}. To overwrite this registry key value please specifiy the Force parameter as $true. + CannotRemoveExistingRegistryKeyValueWithoutForce = The registry key at path {0} already has a value with the name {1}. To remove this registry key value please specifiy the Force parameter as $true. + RegistryDriveInvalid = The registry drive specified in the registry key path {0} is missing or invalid. + ArrayNotAllowedForExpectedType = The specified value data has been declared as a string array, but the registry key type {0} cannot be converted from an array. Please declare the value data as only one string or use the registry type MultiString. + DWordDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as an Int32 (dword). + QWordDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as an Int64 (qword). + BinaryDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as a Byte array (Binary). + InvalidRegistryDrive = The registry drive {0} is invalid. Please update the Key parameter to include a valid registry drive. + InvalidRegistryDriveAbbreviation = The registry drive abbreviation {0} is invalid. Please update the Key parameter to include a valid registry drive. + RegistryDriveCouldNotBeMounted = The registry drive with the abbreviation {0} could not be mounted. +'@ diff --git a/Examples/Sample_RegistryResource_AddKey.ps1 b/Examples/Sample_RegistryResource_AddKey.ps1 new file mode 100644 index 0000000..138d78f --- /dev/null +++ b/Examples/Sample_RegistryResource_AddKey.ps1 @@ -0,0 +1,19 @@ +<# + .SYNOPSIS + Create a new registry key called MyNewKey as a subkey under the key + 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'. +#> +Configuration Sample_RegistryResource_AddKey +{ + Import-DscResource -ModuleName 'PSDscResources' + + Node localhost + { + Registry Registry1 + { + Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\MyNewKey' + Ensure = 'Present' + ValueName = '' + } + } +} diff --git a/Examples/Sample_RegistryResource_AddOrModifyValue.ps1 b/Examples/Sample_RegistryResource_AddOrModifyValue.ps1 new file mode 100644 index 0000000..df89a0a --- /dev/null +++ b/Examples/Sample_RegistryResource_AddOrModifyValue.ps1 @@ -0,0 +1,27 @@ +<# + .SYNOPSIS + If the registry key value MyValue under the key + 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' does not exist, + creates it with the Binary value 0. + + If the registry key value MyValue under the key + 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' already exists, + overwrites it with the Binary value 0. +#> +Configuration Sample_RegistryResource_AddOrModifyValue +{ + Import-DscResource -ModuleName 'PSDscResources' + + Node localhost + { + Registry Registry1 + { + Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + Ensure = 'Present' + ValueName = 'MyValue' + ValueType = 'Binary' + ValueData = '0x00' + Force = $true + } + } +} diff --git a/Examples/Sample_RegistryResource_RemoveKey.ps1 b/Examples/Sample_RegistryResource_RemoveKey.ps1 new file mode 100644 index 0000000..79a2d17 --- /dev/null +++ b/Examples/Sample_RegistryResource_RemoveKey.ps1 @@ -0,0 +1,19 @@ +<# + .SYNOPSIS + Removes the registry key called MyNewKey under the parent key + 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'. +#> +Configuration Sample_RegistryResource_RemoveKey +{ + Import-DscResource -ModuleName 'PSDscResources' + + Node localhost + { + Registry Registry1 + { + Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\MyNewKey' + Ensure = 'Absent' + ValueName = '' + } + } +} diff --git a/Examples/Sample_RegistryResource_RemoveValue.ps1 b/Examples/Sample_RegistryResource_RemoveValue.ps1 new file mode 100644 index 0000000..2dec121 --- /dev/null +++ b/Examples/Sample_RegistryResource_RemoveValue.ps1 @@ -0,0 +1,19 @@ +<# + .SYNOPSIS + Removes the registry key value MyValue from the key + 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'. +#> +Configuration Sample_RegistryResource_RemoveValue +{ + Import-DscResource -ModuleName 'PSDscResources' + + Node localhost + { + Registry Registry1 + { + Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + Ensure = 'Absent' + ValueName = 'MyValue' + } + } +} diff --git a/PSDscResources.psd1 b/PSDscResources.psd1 index 5f356ca..7f2e109 100644 --- a/PSDscResources.psd1 +++ b/PSDscResources.psd1 @@ -73,7 +73,7 @@ VariablesToExport = '*' AliasesToExport = @() # DSC resources to export from this module -DscResourcesToExport = 'Group', 'GroupSet', 'Script', 'Service', 'ServiceSet', 'User', 'WindowsFeature', 'WindowsFeatureSet', 'WindowsOptionalFeature', 'WindowsOptionalFeature', 'WindowsPackageCab', 'WindowsProcess', 'ProcessSet' +DscResourcesToExport = 'Group', 'GroupSet', 'Registry', 'Script', 'Service', 'ServiceSet', 'User', 'WindowsFeature', 'WindowsFeatureSet', 'WindowsOptionalFeature', 'WindowsOptionalFeature', 'WindowsPackageCab', 'WindowsProcess', 'ProcessSet' # List of all modules packaged with this module # ModuleList = @() diff --git a/README.md b/README.md index 9408b97..897e776 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Please check out the common DSC Resources [contributing guidelines](https://gith * [Group](#group): Provides a mechanism to manage local groups on a target node. * [GroupSet](#groupset): Provides a mechanism to configure and manage multiple Group resources with common settings but different names. +* [Registry](#registry): Provides a mechanism to manage registry keys and values on a target node. * [Script](#script): Provides a mechanism to run PowerShell script blocks on a target node. * [Service](#service): Provides a mechanism to configure and manage Windows services on a target node. * [ServiceSet](#serviceset): Provides a mechanism to configure and manage multiple Service resources with common settings but different names. @@ -113,6 +114,35 @@ None * [Add members to multiple groups](https://github.com/PowerShell/PSDscResources/blob/master/Examples/Sample_GroupSet_AddMembers.ps1) +### Registry + +Provides a mechanism to manage registry keys and values on a target node. + +#### Requirements + +None + +#### Parameters + +* **[String] Key** _(Key)_: The path of the registry key to add, modify, or remove. This path must include the registry hive/drive (e.g. HKEY_LOCAL_MACHINE, HKLM:). +* **[String] ValueName** _(Key)_: The name of the registry value. To add or remove a registry key, specify this property as an empty string without specifying ValueType or ValueData. To modify or remove the default value of a registry key, specify this property as an empty string while also specifying ValueType or ValueData. +* **[String] Ensure** _(Write)_: Specifies whether or not the registry key or value should exist. To add or modify a registry key or value, set this property to Present. To remove a registry key or value, set this property to Absent. { *Present* | Absent }. +* **[String] ValueData** _(Write)_: The data the specified registry key value should have as a string or an array of strings (MultiString only). +* **[String] ValueType** _(Write)_: The type the specified registry key value should have. { *String* | Binary | DWord | QWord | MultiString | ExpandString } +* **[Boolean] Hex** _(Write)_: Specifies whether or not the specified DWord or QWord registry key data is provided in a hexadecimal format. Not valid for types other than DWord and QWord. The default value is $false. +* **[Boolean] Force** _(Write)_: Specifies whether or not to overwrite the specified registry key value if it already has a value or whether or not to delete a registry key that has subkeys. The default value is $false. + +#### Read-Only Properties from Get-TargetResource + +None + +#### Examples + +* [Add a registry key](https://github.com/PowerShell/PSDscResources/blob/master/Examples/Sample_RegistryResource_AddKey.ps1) +* [Add or modify a registry key value](https://github.com/PowerShell/PSDscResources/blob/master/Examples/Sample_RegistryResource_AddOrModifyValue.ps1) +* [Remove a registry key value](https://github.com/PowerShell/PSDscResources/blob/master/Examples/Sample_RegistryResource_RemoveValue.ps1) +* [Remove a registry key](https://github.com/PowerShell/PSDscResources/blob/master/Examples/Sample_RegistryResource_RemoveKey.ps1) + ### Script Provides a mechanism to run PowerShell script blocks on a target node. @@ -453,11 +483,12 @@ The following parameters will be the same for each process in the set: * Updated resource module, unit tests, integration tests, and examples to reflect the changes made in xPSDesiredStateConfiguration. * Group: * Updated resource module, examples, and integration tests to reflect the changes made in xPSDesiredStateConfiguration. -* Added Script +* Added Script. * Added GroupSet, ServiceSet, WindowsFeatureSet, WindowsOptionalFeatureSet, and ProcessSet. * Added Set-StrictMode -'Latest' and $errorActionPreference -'Stop' to Group, Service, User, WindowsFeature, WindowsOptionalFeature, WindowsPackageCab. * Fixed bug in WindowsFeature in which is was checking the 'Count' property of an object that was not always an array. * Cleaned Group and Service resources and tests. +* Added Registry. ### 2.1.0.0 diff --git a/Tests/Integration/MSFT_RegistryResource.EndToEnd.Tests.ps1 b/Tests/Integration/MSFT_RegistryResource.EndToEnd.Tests.ps1 new file mode 100644 index 0000000..c9a31d9 --- /dev/null +++ b/Tests/Integration/MSFT_RegistryResource.EndToEnd.Tests.ps1 @@ -0,0 +1,318 @@ +<# + WARNING: DO NOT RUN THESE TESTS ON A VALUABLE MACHINE! + Running on a disposable VM or AppVeyor is strongly recommended. + If these tests go awry, your machine's registry could be corrupted which will brick your machine! + If this happens to you, it is fixable, but the fix is difficult and time-consuming. +#> + +$errorActionPreference = 'Stop' +Set-StrictMode -Version 'Latest' + +# Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +$script:testEnvironment = Enter-DscResourceTestEnvironment ` + -DscResourceModuleName 'PSDscResources' ` + -DscResourceName 'MSFT_RegistryResource' ` + -TestType 'Integration' + +try +{ + Describe 'Registry End to End Tests' { + BeforeAll { + # Import Registry resource module for Get-TargetResource, Test-TargetResource, Set-TargetResource + $moduleRootFilePath = Split-Path -Path $script:testsFolderFilePath -Parent + $dscResourcesFolderFilePath = Join-Path -Path $moduleRootFilePath -ChildPath 'DscResources' + $registryResourceFolderFilePath = Join-Path -Path $dscResourcesFolderFilePath -ChildPath 'MSFT_RegistryResource' + $registryResourceModuleFilePath = Join-Path -Path $registryResourceFolderFilePath -ChildPath 'MSFT_RegistryResource.psm1' + Import-Module -Name $registryResourceModuleFilePath -Force + + $script:registryKeyValueTypes = @( 'String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString' ) + $script:testRegistryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\TestKey2' + + # Force is specified as true for both of these configurations + $script:confgurationFilePathKeyAndNameOnly = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_RegistryResource_KeyAndNameOnly.config.ps1' + $script:confgurationFilePathWithDataAndType = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_RegistryResource_WithDataAndType.config.ps1' + } + + Context 'Create a new registry key' { + $configurationName = 'CreateRegistryKey' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Present' + ValueName = '' + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathKeyAndNameOnly -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKey = Get-Item -Path $registryParameters.Key -ErrorAction 'SilentlyContinue' + + It 'Should have created the registry key' { + $registryKey | Should Not Be $null + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + + Context 'Create a registry key value with no data or type' { + $configurationName = 'CreateRegistryKeyValueNoDataOrType' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Present' + ValueName = 'TestValue' + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathKeyAndNameOnly -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKeyValue = Get-ItemProperty -Path $registryParameters.Key -Name $registryParameters.ValueName -ErrorAction 'SilentlyContinue' + + It 'Should have created the registry key value' { + $registryKeyValue | Should Not Be $null + } + + It 'Should not have set the registry key value' { + $registryKeyValue.($registryParameters.ValueName) | Should Be '' + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + + Context 'Set registry key value with data and String type' { + $configurationName = 'SetRegistryKeyValueString' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Present' + ValueName = 'TestValue' + ValueType = 'String' + ValueData = 'TestString1' + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathWithDataAndType -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKeyValue = Get-ItemProperty -Path $registryParameters.Key -Name $registryParameters.ValueName -ErrorAction 'SilentlyContinue' + + It 'Should have created the registry key value' { + $registryKeyValue | Should Not Be $null + } + + It 'Should have set the registry key value to the specified String value' { + $registryKeyValue.($registryParameters.ValueName) | Should Be $registryParameters.ValueData + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + + foreach ($registryKeyValueType in $script:registryKeyValueTypes) + { + $registryKeyValueData = switch ($registryKeyValueType) + { + 'String' { 'TestString2'; break } + 'Binary' { '0xCAC1111'; break } + 'DWord' { [Int32]::MaxValue.ToString(); break } + 'QWord' { [Int64]::MaxValue.ToString(); break } + 'MultiString' { @('MultiString1', 'MultiString2'); break } + 'ExpandString' { '%WINDIR%'; break } + } + + $expectedRegistryKeyValue = switch ($registryKeyValueType) + { + 'String' { 'TestString2'; break } + 'Binary' { [Byte[]]@( 12, 172, 17, 17 ); break } + 'DWord' { [Int32]::MaxValue; break } + 'QWord' { [Int64]::MaxValue; break } + 'MultiString' { [String[]]@('MultiString1', 'MultiString2'); break } + 'ExpandString' { 'C:\windows'; break } + } + + Context "Overwrite a registry key value with a $registryKeyValueType value" { + $configurationName = "OverwriteRegistryKeyValue$registryKeyValueType" + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Present' + ValueName = 'TestValue' + ValueType = $registryKeyValueType + ValueData = $registryKeyValueData + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathWithDataAndType -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKeyValue = Get-ItemProperty -Path $registryParameters.Key -Name $registryParameters.ValueName -ErrorAction 'SilentlyContinue' + + It 'Should be able to retrieve the registry key value' { + $registryKeyValue | Should Not Be $null + } + + It 'Should have set the registry key value to the specified value' { + Compare-Object -ReferenceObject $expectedRegistryKeyValue -DifferenceObject $registryKeyValue.($registryParameters.ValueName) | Should Be $null + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + } + + Context 'Set the registry key default value to a Binary value of 0' { + $configurationName = 'SetDefaultRegistryKeyValueBinary0' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Present' + ValueName = '' + ValueType = 'Binary' + ValueData = '0x00' + } + + $expectedRegistryKeyValue = [Byte[]]@(0) + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathWithDataAndType -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKeyValue = Get-ItemProperty -Path $registryParameters.Key -Name $registryParameters.ValueName -ErrorAction 'SilentlyContinue' + + It 'Should be able to retrieve the registry key value' { + $registryKeyValue | Should Not Be $null + } + + It 'Should have set the registry key value to the specified Binary value' { + Compare-Object -ReferenceObject $expectedRegistryKeyValue -DifferenceObject $registryKeyValue.'(default)' | Should Be $null + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + + Context 'Remove a registry key value' { + $configurationName = 'RemoveRegistryKeyValue' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Absent' + ValueName = 'TestValue' + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathKeyAndNameOnly -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKeyValue = Get-ItemProperty -Path $registryParameters.Key -Name $registryParameters.ValueName -ErrorAction 'SilentlyContinue' + + It 'Should have removed the registry key value' { + $registryKeyValue | Should Be $null + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + + Context 'Remove a default registry key value' { + $configurationName = 'RemoveDefaultRegistryKeyValue' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Absent' + ValueName = '' + ValueType = 'Binary' + ValueData = '0' + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathWithDataAndType -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKeyValue = Get-ItemProperty -Path $registryParameters.Key -Name $registryParameters.ValueName -ErrorAction 'SilentlyContinue' + + It 'Should have removed the registry key value' { + $registryKeyValue | Should Be $null + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + + Context 'Remove a registry key' { + $configurationName = 'RemoveRegistryKey' + + $registryParameters = @{ + Key = $script:testRegistryKeyPath + Ensure = 'Absent' + ValueName = '' + } + + It 'Should compile and run configuration' { + { + . $script:confgurationFilePathKeyAndNameOnly -ConfigurationName $configurationName + & $configurationName -OutputPath $TestDrive @registryParameters + Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force + } | Should Not Throw + } + + $registryKey = Get-Item -Path $registryParameters.Key -ErrorAction 'SilentlyContinue' + + It 'Should have removed the registry key value' { + $registryKey | Should Be $null + } + + It 'Should return true from Test-TargetResource with the same parameters' { + MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true + } + } + } +} +finally +{ + Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment +} + diff --git a/Tests/Integration/MSFT_RegistryResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_RegistryResource.Integration.Tests.ps1 new file mode 100644 index 0000000..ea6883d --- /dev/null +++ b/Tests/Integration/MSFT_RegistryResource.Integration.Tests.ps1 @@ -0,0 +1,362 @@ +<# + WARNING: DO NOT RUN THESE TESTS ON A VALUABLE MACHINE! + Running on a disposable VM or AppVeyor is strongly recommended. + If these tests go awry, your machine's registry could be corrupted which will brick your machine! + If this happens to you, it is fixable, but the fix is difficult and time-consuming. +#> + +$errorActionPreference = 'Stop' +Set-StrictMode -Version 'Latest' + +# Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:testHelpersPath = Join-Path -Path $script:testFolderFilePath -ChildPath 'TestHelpers' +Import-Module -Name (Join-Path -Path $script:testHelpersPath -ChildPath 'CommonTestHelper.psm1') + +$script:testEnvironment = Enter-DscResourceTestEnvironment ` + -DscResourceModuleName 'PSDscResources' ` + -DscResourceName 'MSFT_RegistryResource' ` + -TestType 'Integration' + +try +{ + Describe 'Registry Integration Tests' { + BeforeAll { + # Import Registry test helper + $registryTestHelperFilePath = Join-Path -Path $script:testHelpersPath -ChildPath 'MSFT_RegistryResource.TestHelper.psm1' + Import-Module -Name $registryTestHelperFilePath -Force + + # Import Registry resource module for Get-TargetResource, Test-TargetResource, Set-TargetResource + $moduleRootFilePath = Split-Path -Path $script:testsFolderFilePath -Parent + $dscResourcesFolderFilePath = Join-Path -Path $moduleRootFilePath -ChildPath 'DscResources' + $registryResourceFolderFilePath = Join-Path -Path $dscResourcesFolderFilePath -ChildPath 'MSFT_RegistryResource' + $registryResourceModuleFilePath = Join-Path -Path $registryResourceFolderFilePath -ChildPath 'MSFT_RegistryResource.psm1' + Import-Module -Name $registryResourceModuleFilePath -Force + + $baseRegistryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\TestKey' + $script:registryKeyPath = $baseRegistryKeyPath + + $script:doNotDeleteRegistryKey = $false + $script:registryDriveOriginallyMounted = $true + + $loopTimeoutMinutes = 1 + + $startLoopTime = Get-Date + while ((Test-RegistryKeyExists -KeyPath $script:registryKeyPath) -and $loopMinutes -lt $loopTimeoutMinutes) + { + $randomNumber = Get-Random + $script:registryKeyPath = $baseRegistryKeyPath + $randomNumber + $loopMinutes = ((Get-Date) - $startLoopTime).Minutes + } + + if (Test-RegistryKeyExists -KeyPath $script:registryKeyPath) + { + $script:doNotDeleteRegistryKey = $true + throw "Timed out while attempting to set up a non-destructive registry key for testing. Last testing key attempted: $script:registryKeyPath" + return + } + } + + BeforeEach { + # Remove the test registry key if it already exists + if (Test-RegistryKeyExists -KeyPath $script:registryKeyPath) + { + Remove-RegistryKey -KeyPath $script:registryKeyPath + } + } + + AfterAll { + # Remove the test registry key if it already exists + if ((Test-RegistryKeyExists -KeyPath $script:registryKeyPath) -and -not $script:doNotDeleteRegistryKey) + { + Remove-RegistryKey -KeyPath $script:registryKeyPath + } + } + + Context 'Old tests' { + # Get-TargetResource + It 'Should return Present when retrieving a blank value from an existing registry key' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $getTargetResourceResult = Get-TargetResource -Key $registryKeyPath -ValueName '' + $getTargetResourceResult.Ensure | Should Be 'Present' + } + + It 'Should return Absent when retrieving a blank value from a registry key that does not exist' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environmental' + $getTargetResourceResult = Get-TargetResource -Key $registryKeyPath -ValueName '' + $getTargetResourceResult.Ensure | Should Be 'Absent' + } + + It 'Should return Present when retrieving an existing value from an existing registry key' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $registryValueName = 'Path' + $getTargetResourceResult = Get-TargetResource -Key $registryKeyPath -ValueName $registryValueName + $getTargetResourceResult.Ensure | Should Be 'Present' + } + + It 'Should return Absent when retrieving a nonexistant value from an existing registry key' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $registryValueName = 'PsychoPath' + $getTargetResourceResult = Get-TargetResource -Key $registryKeyPath -ValueName $registryValueName + $getTargetResourceResult.Ensure | Should Be 'Absent' + } + + $commonRegistryKeys = @( 'HKEY_CURRENT_USER', 'HKEY_CLASSES_ROOT', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG' ) + foreach ($commonRegistryKey in $commonRegistryKeys) + { + It "Should return Present when retrieving a blank value from $commonRegistryKey" { + $getTargetResourceResult = Get-TargetResource -Key $commonRegistryKey -ValueName '' + $getTargetResourceResult.Ensure | Should Be 'Present' + } + } + + # Set-TargetResource + It 'Should create a new registry key' { + Set-TargetResource -Key $script:registryKeyPath -ValueName '' + + # Verify that the registry key has been created + $registryKeyExists = Test-RegistryKeyExists -KeyPath $script:registryKeyPath + $registryKeyExists | Should Be $true + } + + It 'Should create a new registry key tree' { + $registryKeyTreePath = Join-Path -Path (Join-Path -Path (Join-Path -Path $script:registryKeyPath -ChildPath 'A') -ChildPath 'B') -ChildPath 'C' + + Set-TargetResource -Key $registryKeyTreePath -ValueName '' + + # Verify that the registry key has been created + $registryKeyExists = Test-RegistryKeyExists -KeyPath $registryKeyTreePath + $registryKeyExists | Should Be $true + } + + It 'Should remove a registry key' { + # Create the test registry key + New-TestRegistryKey -KeyPath $script:registryKeyPath + + # Verify that the registry key exists before removal + $registryKeyExists = Test-RegistryKeyExists -KeyPath $script:registryKeyPath + $registryKeyExists | Should Be $true + + # Now remove the TestKey + Set-TargetResource -Key $script:registryKeyPath -ValueName '' -Ensure 'Absent' + + # Verify that the registry key has been removed + $registryKeyExists = Test-RegistryKeyExists -KeyPath $script:registryKeyPath + $registryKeyExists | Should Be $false + } + + It 'Should remove a registry key tree' { + $registryKeyTreePath = Join-Path -Path (Join-Path -Path (Join-Path -Path $script:registryKeyPath -ChildPath 'A') -ChildPath 'B') -ChildPath 'C' + + # Create the test registry key + New-TestRegistryKey -KeyPath $registryKeyTreePath + + # Verify that the registry key tree exists before removal + $registryKeyExists = Test-RegistryKeyExists -KeyPath $registryKeyTreePath + $registryKeyExists | Should Be $true + + # Remove the test registry key tree + Set-TargetResource -Key $registryKeyTreePath -ValueName '' -Ensure 'Absent' + + # Verify that the registry key tree has been removed + $registryKeyExists = Test-RegistryKeyExists -KeyPath $registryKeyTreePath + $registryKeyExists | Should Be $false + } + + It 'Should create a new string registry key value' { + $valueName = 'TestValue' + $valueData = 'TestData' + $valueType = 'String' + + # Create the new registry key value + Set-TargetResource -Key $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + + # Verify that the registry key value has been created with the correct data and type + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + $registryValueExists | Should Be $true + } + + It 'Should create a new binary registry key value' { + $valueName = 'TestValue' + $valueData = 'aabbcc' + $valueType = 'Binary' + + # Create the new registry key value + Set-TargetResource -Key $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType -Hex $true + + # Verify that the registry key value has been created with the correct data and type + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + $registryValueExists | Should Be $true + } + + It 'Should set the default value of a registry key' { + $valueName = '' + $valueData = 'DefaultValue' + + # Create the new registry key value + Set-TargetResource -Key $script:registryKeyPath -ValueName $valueName -ValueData $valueData + + # Verify that the registry key value has been created with the correct data and type + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName '(default)' -ValueData $valueData -ValueType 'String' + $registryValueExists | Should Be $true + } + + It 'Should remove a registry key value' { + $valueName = 'TestValue' + $valueData = 'TestData' + $valueType = 'String' + + # Create the test registry value + New-RegistryValue -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + + # Verify that the registry key value exists before removal + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName $valueName + $registryValueExists | Should Be $true + + # Remove the registry value + Set-TargetResource -Key $script:registryKeyPath -ValueName $valueName -Ensure 'Absent' + + # Verify that the registry key value has been removed + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName $valueName + $registryValueExists | Should Be $false + } + + It 'Should remove the default value for a registry key' { + $valueName = '' + $valueData = 'DefaultValue' + $valueType = 'String' + + # Create the test registry value + New-RegistryValue -KeyPath $script:registryKeyPath -ValueName '(default)' -ValueData $valueData -ValueType $valueType + + # Verify that the registry key value exists before removal + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName '(default)' + $registryValueExists | Should Be $true + + # Remove the registry value + Set-TargetResource -Key $script:registryKeyPath -ValueName $valueName -Ensure 'Absent' + + # Verify that the registry key value has been removed + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName '(default)' + $registryValueExists | Should Be $false + } + + It 'Should create a new key and value with path containing forward slashes' { + $registryKeyPathWithForwardSlashes = $script:registryKeyPath + '/Test/Key' + $valueName = 'Testing' + $valueData = 'TestValue' + + # Create the new registry key value + Set-TargetResource -Key $registryKeyPathWithForwardSlashes -ValueName $valueName -ValueData $valueData + + # Verify that the registry key value has been created with the correct data and type + $registryValueExists = Test-RegistryValueExists -KeyPath $registryKeyPathWithForwardSlashes -ValueName $valueName -ValueData $valueData + $registryValueExists | Should Be $true + } + + # Test-TargetResource + It 'Should return true for an existing registry key' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName '' + $testTargetResourceResult | Should Be $true + } + + It 'Should return false for a registry key that does not exist' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environmentally' + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName '' + $testTargetResourceResult | Should Be $false + } + + It 'Should return true for an existing registry value' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $valueName = 'path' + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName $valueName + $testTargetResourceResult | Should Be $true + } + + It 'Should return false for a registry value that does not exist' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $valueName = 'NonExisting' + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName $valueName + $testTargetResourceResult | Should Be $false + } + + It 'Should return true when Ensure is Absent and registry key does not exist' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environmentally' + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName '' -Ensure 'Absent' + $testTargetResourceResult | Should Be $true + } + + It 'Should return false when Ensure is Absent and registry key exists' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName '' -Ensure 'Absent' + $testTargetResourceResult | Should Be $false + } + + It 'Should return false when Ensure is Absent and registry value exists with invalid data' { + $registryKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $valueName = 'path' + $valueData = 'FakePath' + + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName $valueName -ValueData $valueData -Ensure 'Absent' + $testTargetResourceResult | Should Be $false + } + + It 'Should return true for a multi-string registry value' { + $valueName = 'TestValue' + $valueData = @('a', 'b', 'c') + $valueType = 'MultiString' + + # Create the test registry value + New-RegistryValue -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + + $testTargetResourceResult = Test-TargetResource -Key $registryKeyPath -ValueName $valueName -ValueData $valueData + $testTargetResourceResult | Should Be $true + } + + It 'Should return true for a binary registry value' { + $valueName = 'TestValue' + $valueData = 'abcd123' + $valueType = 'Binary' + + # Create the test registry value + New-RegistryValue -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + + $testTargetResourceResult = Test-TargetResource -Key $script:registryKeyPath -ValueName $valueName -ValueData $valueData + $testTargetResourceResult | Should Be $true + } + + It 'Should return true for an empty binary registry value' { + $valueName = 'TestValue' + $valueData = '' + $valueType = 'Binary' + + # Create the test registry value + New-RegistryValue -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + + # Verify that the registry key value has been created with the correct data and type + $registryValueExists = Test-RegistryValueExists -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + $registryValueExists | Should Be $true + + $testTargetResourceResult = Test-TargetResource -Key $script:registryKeyPath -ValueName $valueName -ValueData $valueData + $testTargetResourceResult | Should Be $true + } + + It 'Should return true for binary registry value with zeroes' { + $valueName = 'TestValue' + $valueData = 'abcd0123' + $valueType = 'Binary' + + # Create the test registry value + New-RegistryValue -KeyPath $script:registryKeyPath -ValueName $valueName -ValueData $valueData -ValueType $valueType + + $testTargetResourceResult = Test-TargetResource -Key $script:registryKeyPath -ValueName $valueName -ValueData $valueData + $testTargetResourceResult | Should Be $true + } + } + } +} +finally +{ + Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment +} diff --git a/Tests/Integration/MSFT_RegistryResource_KeyAndNameOnly.config.ps1 b/Tests/Integration/MSFT_RegistryResource_KeyAndNameOnly.config.ps1 new file mode 100644 index 0000000..30622fa --- /dev/null +++ b/Tests/Integration/MSFT_RegistryResource_KeyAndNameOnly.config.ps1 @@ -0,0 +1,39 @@ +param +( + [Parameter(Mandatory = $true)] + [String] + $ConfigurationName +) + +Configuration $ConfigurationName +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Key, + + [ValidateSet('Present', 'Absent')] + [String] + $Ensure = 'Present', + + [Parameter(Mandatory = $true)] + [String] + [AllowEmptyString()] + $ValueName + ) + + Import-DscResource -ModuleName 'PSDscResources' + + Node localhost + { + Registry Registry1 + { + Key = $Key + Ensure = $Ensure + ValueName = $ValueName + Force = $true + } + } +} diff --git a/Tests/Integration/MSFT_RegistryResource_WithDataAndType.config.ps1 b/Tests/Integration/MSFT_RegistryResource_WithDataAndType.config.ps1 new file mode 100644 index 0000000..16615a4 --- /dev/null +++ b/Tests/Integration/MSFT_RegistryResource_WithDataAndType.config.ps1 @@ -0,0 +1,51 @@ +param +( + [Parameter(Mandatory = $true)] + [String] + $ConfigurationName +) + +Configuration $ConfigurationName +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Key, + + [ValidateSet('Present', 'Absent')] + [String] + $Ensure = 'Present', + + [Parameter(Mandatory = $true)] + [String] + [AllowEmptyString()] + $ValueName, + + [Parameter(Mandatory = $true)] + [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] + [String] + $ValueType, + + [Parameter(Mandatory = $true)] + [String[]] + [AllowEmptyCollection()] + $ValueData + ) + + Import-DscResource -ModuleName 'PSDscResources' + + Node localhost + { + Registry Registry1 + { + Key = $Key + Ensure = $Ensure + ValueName = $ValueName + ValueType = $ValueType + ValueData = $ValueData + Force = $true + } + } +} diff --git a/Tests/TestHelpers/MSFT_RegistryResource.TestHelper.psm1 b/Tests/TestHelpers/MSFT_RegistryResource.TestHelper.psm1 new file mode 100644 index 0000000..18029f6 --- /dev/null +++ b/Tests/TestHelpers/MSFT_RegistryResource.TestHelper.psm1 @@ -0,0 +1,402 @@ +<# + .SYNOPSIS + Tests if a registry key exists. + + .PARAMETER KeyPath + The path to the registry key to test for existence. + Must include the registry hive. +#> +function Test-RegistryKeyExists +{ + [CmdletBinding()] + [OutputType([Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath + ) + + return Test-Path -Path $KeyPath +} + +<# + .SYNOPSIS + Tests if a registry key value exists. + + .PARAMETER KeyPath + The path to the registry key that should contain the value to test for existence. + Must include the registry hive. + + .PARAMETER ValueName + The name of the value to test for existence. + + .PARAMETER ValueData + The data the existing value should contain. + + .PARAMETER ValueType + The value type that the registry value should have. +#> +function Test-RegistryValueExists +{ + [CmdletBinding()] + [OutputType([Boolean])] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [AllowEmptyString()] + [String] + $ValueName, + + [String] + $ValueData, + + [ValidateNotNullOrEmpty()] + [String] + $ValueType + ) + + try + { + $registryValue = Get-ItemProperty -Path $KeyPath -Name $ValueName -ErrorAction 'SilentlyContinue' + Write-Verbose -Message "Test-RegistryValueExists - Registry key value: $registryKeyValue" + + $registryValueExists = $null -ne $registryValue + + Write-Verbose -Message "Test-RegistryValueExists - Registry value is not null: $registryValueExists" + + if (-not $registryValueExists) + { + return $false + } + + $registryValue = $registryValue.$ValueName + + if ($PSBoundParameters.ContainsKey('ValueType')) + { + Write-Verbose -Message "Test-RegistryValueExists - Registry value type: $($registryValue.GetType().Name)" + + if ($ValueType -eq 'Binary') + { + $registryValueExists = $registryValueExists -and ($registryValue.GetType().Name -eq 'Byte[]') + $registryValue = Convert-ByteArrayToHexString -Data $registryValue + } + else + { + $registryValueExists = $registryValueExists -and ($registryValue.GetType().Name -eq $ValueType) + } + } + + if ($PSBoundParameters.ContainsKey('ValueData')) + { + Write-Verbose -Message "Test-RegistryValueExists - Registry value data: $registryValue" + + $registryValueExists = $registryValueExists -and ($ValueData -eq $registryValue) + } + + return $registryValueExists + } + catch + { + return $false + } +} + +<# + .SYNOPSIS + Creates a registry key. + + .PARAMETER KeyPath + The path to the registry key to be created. + Must include the registry hive. +#> +function New-TestRegistryKey +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath + ) + + $parentPath = Split-Path -Path $KeyPath -Parent + + if (-not (Test-RegistryKeyExists -KeyPath $parentPath)) + { + New-TestRegistryKey -KeyPath $parentPath + } + + Write-Verbose -Message "New-TestRegistryKey - Creating new registry key at: $KeyPath" + + $null = New-Item -Path $KeyPath +} + +<# + .SYNOPSIS + Creates a registry key. + + .PARAMETER KeyPath + The path to the registry key to be created. + Must include the registry hive. + + .PARAMETER ValueName + The name of the value to add + + .PARAMETER ValueData + The data of the value to add. + + .PARAMETER ValueType + The type of the value to add. +#> +function New-RegistryValue +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [AllowEmptyString()] + [String] + $ValueName, + + [Object] + $ValueData, + + [ValidateNotNullOrEmpty()] + [String] + $ValueType + ) + + if (-not (Test-Path -Path $KeyPath)) + { + New-TestRegistryKey -KeyPath $KeyPath + } + + if ($ValueType -ieq 'Binary') + { + $convertedValueData = @() + + if (($ValueData.Length % 2) -eq 1) + { + $ValueData = '0' + $ValueData + } + + for($index = 0; $index -lt $ValueData.Length - 1; $index += 2) + { + $convertedValueData += [Convert]::ToInt32($ValueData.Substring($index, 2), 16) + } + + $ValueData = [Byte[]] $convertedValueData + + Write-Verbose -Message "New-RegistryValue - Binary data: $ValueData" + } + + $null = New-ItemProperty -Path $KeyPath -Name $ValueName -Value $ValueData -PropertyType $ValueType +} + +<# + .SYNOPSIS + Removes a registry key. + + .PARAMETER KeyPath + The path to the registry key to remove + Must include the registry hive. +#> +function Remove-RegistryKey +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath + ) + + $null = Remove-Item -Path $KeyPath -Recurse -Force +} + +<# + .SYNOPSIS + Removes a registry value. + + .PARAMETER KeyPath + The path to the registry key that contains the value to remove. + Must include the registry hive. + + .PARAMETER ValueName + The name of the value to remove. +#> +function Remove-RegistryValue +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [AllowEmptyString()] + [String] + $ValueName + ) + + $null = Remove-ItemProperty -Path $KeyPath -Name $ValueName -Force +} + +<# + .SYNOPSIS + Mounts the registry drive of the given registry key path. + + .PARAMETER KeyPath + The registry key path that contains the registry drive to mount. +#> +function Mount-RegistryDrive +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath + ) + + $driveName = (Split-Path -Path $KeyPath -Qualifier).TrimEnd(':') + Write-Verbose -Message "Mount-RegistryDrive - Drive name: $driveName" + + $registryDriveRootMappings = @{ + 'HKCR' = 'HKEY_CLASSES_ROOT' + 'HKUS' = 'HKEY_USERS' + 'HKCC' = 'HKEY_CURRENT_CONFIG' + 'HKCU' = 'HKEY_CURRENT_USER' + 'HKLM' = 'HKEY_LOCAL_MACHINE' + } + + if ($registryDriveRootMappings.ContainsKey($driveName)) + { + # Abbreviated name was given. Use this as the new PSDrive name and the elongated name as the root + $null = New-PSDrive -Name $driveName -Root $registryDriveRootMappings[$driveName] -PSProvider 'Registry' -Scope 'Script' + } + elseif ($registryDriveRootMappings.ContainsValue($driveName)) + { + $mappingKey = $null + + # Find the abbreviated key that goes with the given registry drive path + foreach ($key in $registryDriveRootMappings.Keys) + { + if ($registryDriveRootMappings[$key] -ieq $driveName) + { + $mappingKey = $key + break + } + } + + # Mount the PSDrive with the abbreviated name as the Name and the elongated name as the root + $null = New-PSDrive -Name $mappingKey -Root $driveName -PSProvider 'Registry' -Scope 'Script' + } + else + { + throw "Mount-RegistryDrive - Invalid registry drive in key path provided: $KeyPath" + } +} + +<# + .SYNOPSIS + Removes a registry drive. + + .PARAMETER KeyPath + The registry key path that contains the registry drive to remove. +#> +function Dismount-RegistryDrive +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath + ) + + $driveName = Split-Path -Path $KeyPath -Qualifier + Write-Verbose -Message "Dismount-RegistryDrive - Drive name: $driveName" + + $null = Remove-PSDrive -Name $driveName -PSProvider 'Registry' -Scope 'Script' -Force +} + +<# + .SYNOPSIS + Tests if the registry drive of the given registry key path is mounted. + + .PARAMETER KeyPath + The registry key path that contains the registry drive to test. +#> +function Test-RegistryDriveMounted +{ + [CmdletBinding()] + [OutputType([Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $KeyPath + ) + + $driveName = Split-Path -Path $KeyPath -Qualifier + Write-Verbose -Message "Test-RegistryDriveMounted - Drive name: $driveName" + + $psDriveNames = (Get-PSDrive).Name.ToUpperInvariant() + + return $psDriveNames -icontains $driveName +} + +<# + .SYNOPSIS + Helper function to convert a byte array to its hex string representation + + .PARAMETER Data + Specifies the byte array to be converted. +#> +function Convert-ByteArrayToHexString +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [System.Object] + $Data + ) + + $hexString = '' + $Data | ForEach-Object { $hexString += ('{0:x2}' -f $_) } + + return $hexString +} + +Export-ModuleMember -Function ` + 'Test-RegistryKeyExists', ` + 'Test-RegistryValueExists', ` + 'New-TestRegistryKey', ` + 'New-RegistryValue', ` + 'Remove-RegistryKey', ` + 'Remove-RegistryValue', ` + 'Dismount-RegistryDrive', ` + 'Test-RegistryDriveMounted' diff --git a/Tests/Unit/MSFT_RegistryResource.Tests.ps1 b/Tests/Unit/MSFT_RegistryResource.Tests.ps1 new file mode 100644 index 0000000..364e311 --- /dev/null +++ b/Tests/Unit/MSFT_RegistryResource.Tests.ps1 @@ -0,0 +1,4108 @@ +$errorActionPreference = 'Stop' +Set-StrictMode -Version 'Latest' + +# Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:testHelpersPath = Join-Path -Path $script:testFolderFilePath -ChildPath 'TestHelpers' +Import-Module -Name (Join-Path -Path $script:testHelpersPath -ChildPath 'CommonTestHelper.psm1') + +$script:testEnvironment = Enter-DscResourceTestEnvironment ` + -DscResourceModuleName 'PSDscResources' ` + -DscResourceName 'MSFT_RegistryResource' ` + -TestType 'Unit' + +try +{ + InModuleScope 'MSFT_RegistryResource' { + $script:registryKeyValueTypes = @( 'String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString' ) + + $script:validRegistryDriveRoots = @( 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG' ) + $script:validRegistryDriveNames = @( 'HKCR', 'HKCU', 'HKLM', 'HKUS', 'HKCC' ) + + # This registry key is used ONLY for its type (Microsoft.Win32.RegistryKey). It is not actually accessed in any way during these tests. + $script:testRegistryKey = [Microsoft.Win32.Registry]::CurrentConfig + + $script:defaultValueType = 'String' + $script:defaultValueData = @() + + Describe 'Registry\Get-TargetResource' { + Mock -CommandName 'Get-RegistryKey' -MockWith { } + Mock -CommandName 'Get-RegistryKeyValueDisplayName' -MockWith { return $RegistryKeyValueName } + Mock -CommandName 'Get-RegistryKeyValue' -MockWith { } + Mock -CommandName 'Get-RegistryKeyValueType' -MockWith { } + Mock -CommandName 'ConvertTo-ReadableString' -MockWith { return $RegistryKeyValue } + + Context 'Registry key at specified path does not exist' { + $getTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + } + + It 'Should not throw' { + { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value type' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueType' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the registry key value to a readable string' { + Assert-MockCalled -CommandName 'ConvertTo-ReadableString' -Times 0 -Scope 'Context' + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + It 'Should return a hashtable' { + $getTargetResourceResult -is [Hashtable] | Should Be $true + } + + It 'Should return 5 hashtable properties' { + $getTargetResourceResult.Keys.Count | Should Be 5 + } + + It 'Should return the Key property as the given registry key path' { + $getTargetResourceResult.Key | Should Be $getTargetResourceParameters.Key + } + + It 'Should return the Ensure property as Absent' { + $getTargetResourceResult.Ensure | Should Be 'Absent' + } + + It 'Should return the ValueName property as null' { + $getTargetResourceResult.ValueName | Should Be $null + } + + It 'Should return the ValueType property as null' { + $getTargetResourceResult.ValueType | Should Be $null + } + + It 'Should return the ValueData property as null' { + $getTargetResourceResult.ValueData | Should Be $null + } + } + + Mock -CommandName 'Get-RegistryKey' -MockWith { return $script:testRegistryKey } + + Context 'Specified registry key exists, registry key value name specified as an empty string, and registry key value data and type not specified' { + $getTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + } + + It 'Should not throw' { + { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value type' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueType' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the registry key value to a readable string' { + Assert-MockCalled -CommandName 'ConvertTo-ReadableString' -Times 0 -Scope 'Context' + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + It 'Should return a hashtable' { + $getTargetResourceResult -is [Hashtable] | Should Be $true + } + + It 'Should return 5 hashtable properties' { + $getTargetResourceResult.Keys.Count | Should Be 5 + } + + It 'Should return the Key property as the given registry key path' { + $getTargetResourceResult.Key | Should Be $getTargetResourceParameters.Key + } + + It 'Should return the Ensure property as Present' { + $getTargetResourceResult.Ensure | Should Be 'Present' + } + + It 'Should return the ValueName property as null' { + $getTargetResourceResult.ValueName | Should Be $null + } + + It 'Should return the ValueType property as null' { + $getTargetResourceResult.ValueType | Should Be $null + } + + It 'Should return the ValueData property as null' { + $getTargetResourceResult.ValueData | Should Be $null + } + } + + Context 'Specified registry key exists and specified registry key value does not exist' { + $getTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestValueName' + } + + It 'Should not throw' { + { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + + return $registryKeyParameterCorrect -and $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value type' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueType' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the registry key value to a readable string' { + Assert-MockCalled -CommandName 'ConvertTo-ReadableString' -Times 0 -Scope 'Context' + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + It 'Should return a hashtable' { + $getTargetResourceResult -is [Hashtable] | Should Be $true + } + + It 'Should return 5 hashtable properties' { + $getTargetResourceResult.Keys.Count | Should Be 5 + } + + It 'Should return the Key property as the given registry key path' { + $getTargetResourceResult.Key | Should Be $getTargetResourceParameters.Key + } + + It 'Should return the Ensure property as Absent' { + $getTargetResourceResult.Ensure | Should Be 'Absent' + } + + It 'Should return the ValueName property as the specified value name' { + $getTargetResourceResult.ValueName | Should Be $getTargetResourceParameters.ValueName + } + + It 'Should return the ValueType property as null' { + $getTargetResourceResult.ValueType | Should Be $null + } + + It 'Should return the ValueData property as null' { + $getTargetResourceResult.ValueData | Should Be $null + } + } + + $testRegistryKeyValue = 'TestRegistryKeyValue' + $testRegistryValueType = 'String' + Mock -CommandName 'Get-RegistryKeyValue' -MockWith { return $testRegistryKeyValue } + Mock -CommandName 'Get-RegistryKeyValueType' -MockWith { return $testRegistryValueType } + + Context 'Specified registry key exists and specified registry key value exists as a string' { + $getTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestValueName' + } + + It 'Should not throw' { + { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + + return $registryKeyParameterCorrect -and $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value type' { + $getRegistryKeyValueTypeParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + + return $registryKeyParameterCorrect -and $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueType' -ParameterFilter $getRegistryKeyValueTypeParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should convert the registry key value to a readable string' { + $convertToReadableStringParameterFilter = { + $registryKeyValueParameterCorrect = $testRegistryKeyValue -eq $RegistryKeyValue + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $testRegistryValueType + return $registryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-ReadableString' -ParameterFilter $convertToReadableStringParameterFilter -Times 1 -Scope 'Context' + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + It 'Should return a hashtable' { + $getTargetResourceResult -is [Hashtable] | Should Be $true + } + + It 'Should return 5 hashtable properties' { + $getTargetResourceResult.Keys.Count | Should Be 5 + } + + It 'Should return the Key property as the given registry key path' { + $getTargetResourceResult.Key | Should Be $getTargetResourceParameters.Key + } + + It 'Should return the Ensure property as Present' { + $getTargetResourceResult.Ensure | Should Be 'Present' + } + + It 'Should return the ValueName property as specified value display name' { + $getTargetResourceResult.ValueName | Should Be $getTargetResourceParameters.ValueName + } + + It 'Should return the ValueType property as the retrieved value type' { + $getTargetResourceResult.ValueType | Should Be $testRegistryValueType + } + + It 'Should return the ValueData property as the retrieved value' { + $getTargetResourceResult.ValueData | Should Be $testRegistryKeyValue + } + } + + Context 'Specified registry key exists and registry key default value exists as a string' { + $getTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueType = 'String' + ValueData = 'TestValueData' + } + + It 'Should not throw' { + { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + + return $registryKeyParameterCorrect -and $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value type' { + $getRegistryKeyValueTypeParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $getTargetResourceParameters.ValueName + + return $registryKeyParameterCorrect -and $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueType' -ParameterFilter $getRegistryKeyValueTypeParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should convert the registry key value to a readable string' { + $convertToReadableStringParameterFilter = { + $registryKeyValueParameterCorrect = $testRegistryKeyValue -eq $RegistryKeyValue + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $testRegistryValueType + return $registryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-ReadableString' -ParameterFilter $convertToReadableStringParameterFilter -Times 1 -Scope 'Context' + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + It 'Should return a hashtable' { + $getTargetResourceResult -is [Hashtable] | Should Be $true + } + + It 'Should return 5 hashtable properties' { + $getTargetResourceResult.Keys.Count | Should Be 5 + } + + It 'Should return the Key property as the given registry key path' { + $getTargetResourceResult.Key | Should Be $getTargetResourceParameters.Key + } + + It 'Should return the Ensure property as Present' { + $getTargetResourceResult.Ensure | Should Be 'Present' + } + + It 'Should return the ValueName property as specified value display name' { + $getTargetResourceResult.ValueName | Should Be $getTargetResourceParameters.ValueName + } + + It 'Should return the ValueType property as the retrieved value type' { + $getTargetResourceResult.ValueType | Should Be $testRegistryValueType + } + + It 'Should return the ValueData property as the retrieved value' { + $getTargetResourceResult.ValueData | Should Be $testRegistryKeyValue + } + } + } + + Describe 'Registry\Set-TargetResource' { + Mock -CommandName 'Get-RegistryKey' -MockWith { } + Mock -CommandName 'New-RegistryKey' -MockWith { return $script:testRegistryKey } + Mock -CommandName 'Get-RegistryKeyValueDisplayName' -MockWith { return $RegistryKeyValueName } + Mock -CommandName 'Get-RegistryKeyValue' -MockWith { } + Mock -CommandName 'ConvertTo-Binary' -MockWith { return $RegistryKeyValue } + Mock -CommandName 'ConvertTo-Dword' -MockWith { return $RegistryKeyValue } + Mock -CommandName 'ConvertTo-MultiString' -MockWith { return $RegistryKeyValue } + Mock -CommandName 'ConvertTo-Qword' -MockWith { return $RegistryKeyValue } + Mock -CommandName 'ConvertTo-String' -MockWith { return $RegistryKeyValue } + Mock -CommandName 'Get-RegistryKeyName' -MockWith { return $setTargetResourceParameters.Key } + Mock -CommandName 'Set-RegistryKeyValue' -MockWith { } + Mock -CommandName 'Test-RegistryKeyValuesMatch' -MockWith { return $true } + Mock -CommandName 'Remove-ItemProperty' -MockWith { } + Mock -CommandName 'Remove-DefaultRegistryKeyValue' -MockWith { } + Mock -CommandName 'Get-RegistryKeySubKeyCount' -MockWith { return 0 } + Mock -CommandName 'Remove-Item' -MockWith { } + + + Context 'Registry key does not exist and Ensure specified as Absent' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Absent' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key does not exist, Ensure specified as Present, registry value name specified as empty string, and registry value type and data not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Present' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should create a new registry key' { + $newRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'New-RegistryKey' -ParameterFilter $newRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Mock -CommandName 'Get-RegistryKey' -MockWith { return $script:testRegistryKey } + + Context 'Registry key exists with no subkeys, Ensure specified as Absent, registry value name specified as empty string, and registry value type and data not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Absent' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key subkey count' { + $getRegistryKeySubCountParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -ParameterFilter $getRegistryKeySubCountParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should remove the registry key' { + $removeItemParameterFilter = { + $pathParameterCorrect = $Path -eq $setTargetResourceParameters.Key + $recurseParameterCorrect = $Recurse -eq $true + $forceParameterCorrect = $Force -eq $true + + return $pathParameterCorrect -and $recurseParameterCorrect -and $forceParameterCorrect + } + + Assert-MockCalled -CommandName 'Remove-Item' -ParameterFilter $removeItemParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Mock -CommandName 'Get-RegistryKeySubKeyCount' -MockWith { return 2 } + + Context 'Registry key exists with subkeys, Ensure specified as Absent, registry value name specified as empty string, registry value type and data not specified, and Force not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Absent' + } + + It 'Should throw error for removal of registry key with subkeys without specifying Force as True' { + $errorMessage = $script:localizedData.CannotRemoveExistingRegistryKeyWithSubKeysWithoutForce -f $setTargetResourceParameters.Key + + { Set-TargetResource @setTargetResourceParameters } | Should Throw $errorMessage + } + } + + Context 'Registry key exists with subkeys, Ensure specified as Absent, registry value name specified as empty string, registry value type and data not specified, and Force specified as True' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Absent' + Force = $true + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key subkey count' { + $getRegistryKeySubCountParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -ParameterFilter $getRegistryKeySubCountParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should remove the registry key' { + $removeItemParameterFilter = { + $pathParameterCorrect = $Path -eq $setTargetResourceParameters.Key + $recurseParameterCorrect = $Recurse -eq $true + $forceParameterCorrect = $Force -eq $true + + return $pathParameterCorrect -and $recurseParameterCorrect -and $forceParameterCorrect + } + + Assert-MockCalled -CommandName 'Remove-Item' -ParameterFilter $removeItemParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key exists, Ensure specified as Absent, specified registry value does not exist, and registry value type and data not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Absent' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key exists, Ensure specified as Present, registry value name specified as empty string, and registry value type and data not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Present' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key exists, Ensure specified as Present, specified registry value does not exist, and registry value type and data not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Present' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should convert the specified registry key value to a string' { + $convertToStringParameterFilter = { + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:defaultValueData -DifferenceObject $RegistryKeyValue) + return $registryKeyValueParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-String' -ParameterFilter $convertToStringParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key name' { + $getRegistryKeyNameParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -ParameterFilter $getRegistryKeyNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should set the registry key value' { + $setRegistryKeyValueParameterFilter = { + $registryKeyNameParameterCorrect = $RegistryKeyName -eq $setTargetResourceParameters.Key + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + $registryKeyValueParameterCorrect = $null -eq $RegistryKeyValue + $valueTypeParameterCorrect = $ValueType -eq $script:defaultValueType + + return $registryKeyNameParameterCorrect -and $registryKeyValueNameParameterCorrect -and $registryKeyValueParameterCorrect -and $valueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -ParameterFilter $setRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key exists, Ensure specified as Present, default registry value does not exist, registry value type specified as binary, and value data specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueType = 'Binary' + ValueData = @( [Byte]::MinValue.ToString(), [Byte]::MaxValue.ToString() ) + Ensure = 'Present' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should convert the specified registry key value to binary data' { + $convertToBinaryParameterFilter = { + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + return $registryKeyValueParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-Binary' -ParameterFilter $convertToBinaryParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key name' { + $getRegistryKeyNameParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -ParameterFilter $getRegistryKeyNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should set the registry key value' { + $setRegistryKeyValueParameterFilter = { + $registryKeyNameParameterCorrect = $RegistryKeyName -eq $setTargetResourceParameters.Key + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $valueTypeParameterCorrect = $ValueType -eq $setTargetResourceParameters.ValueType + + return $registryKeyNameParameterCorrect -and $registryKeyValueNameParameterCorrect -and $registryKeyValueParameterCorrect -and $valueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -ParameterFilter $setRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Mock -CommandName 'Get-RegistryKeyValue' -MockWith { return $setTargetResourceParameters.ValueData } + + Context 'Registry key exists, Ensure specified as Present, specified registry value exists and matches specified multi-string value data' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryeyValueName' + ValueType = 'MultiString' + ValueData = @( 'TestValueData1', 'TestValueData2' ) + Ensure = 'Present' + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should convert the specified registry key value to a multi-string' { + $convertToMultiStringParameterFilter = { + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + return $registryKeyValueParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -ParameterFilter $convertToMultiStringParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should test if the specified registry key value matches the retrieved registry key value' { + $testRegistryKeyValuesMatchParameterFilter = { + $expectedRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $ExpectedRegistryKeyValue) + $actualRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $ActualRegistryKeyValue) + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $setTargetResourceParameters.ValueType + + return $expectedRegistryKeyValueParameterCorrect -and $actualRegistryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -ParameterFilter $testRegistryKeyValuesMatchParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key name' { + $getRegistryKeyNameParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -ParameterFilter $getRegistryKeyNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Mock -CommandName 'Test-RegistryKeyValuesMatch' -MockWith { return $false } + + Context 'Registry key exists, Ensure specified as Present, specified registry value exists and does not match specified expand string value data, and Force not specified' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryeyValueName' + ValueType = 'ExpandString' + ValueData = 'TestValueData1' + Ensure = 'Present' + } + + It 'Should throw error for trying to overwrite existing registry key value without specifying Force as True' { + $errorMessage = $script:localizedData.CannotOverwriteExistingRegistryKeyValueWithoutForce -f $setTargetResourceParameters.Key, $setTargetResourceParameters.ValueName + + { Set-TargetResource @setTargetResourceParameters } | Should Throw $errorMessage + } + } + + Context 'Registry key exists, Ensure specified as Present, specified registry value exists and does not match specified dword value data, and Hex and Force specified as True' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryeyValueName' + ValueType = 'DWord' + ValueData = 'x9A' + Ensure = 'Present' + Hex = $true + Force = $true + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should convert the specified registry key value to a dword' { + $convertToDwordParameterFilter = { + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $hexParameterCorrect = $Hex -eq $setTargetResourceParameters.Hex + + return $registryKeyValueParameterCorrect -and $hexParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-Dword' -ParameterFilter $convertToDwordParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should test if the specified registry key value matches the retrieved registry key value' { + $testRegistryKeyValuesMatchParameterFilter = { + $expectedRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $ExpectedRegistryKeyValue) + $actualRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $ActualRegistryKeyValue) + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $setTargetResourceParameters.ValueType + + return $expectedRegistryKeyValueParameterCorrect -and $actualRegistryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -ParameterFilter $testRegistryKeyValuesMatchParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key name' { + $getRegistryKeyNameParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -ParameterFilter $getRegistryKeyNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should set the registry key value' { + $setRegistryKeyValueParameterFilter = { + $registryKeyNameParameterCorrect = $RegistryKeyName -eq $setTargetResourceParameters.Key + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $valueTypeParameterCorrect = $ValueType -eq $setTargetResourceParameters.ValueType + + return $registryKeyNameParameterCorrect -and $registryKeyValueNameParameterCorrect -and $registryKeyValueParameterCorrect -and $valueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -ParameterFilter $setRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key exists, Ensure specified as Present, specified registry value exists and does not match specified qword value data, and Hex and Force specified as True' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryeyValueName' + ValueType = 'QWord' + ValueData = 'x9A' + Ensure = 'Present' + Hex = $true + Force = $true + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should convert the specified registry key value to a qword' { + $convertToQwordParameterFilter = { + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $hexParameterCorrect = $Hex -eq $setTargetResourceParameters.Hex + + return $registryKeyValueParameterCorrect -and $hexParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-Qword' -ParameterFilter $convertToQwordParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should test if the specified registry key value matches the retrieved registry key value' { + $testRegistryKeyValuesMatchParameterFilter = { + $expectedRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $ExpectedRegistryKeyValue) + $actualRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $ActualRegistryKeyValue) + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $setTargetResourceParameters.ValueType + + return $expectedRegistryKeyValueParameterCorrect -and $actualRegistryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -ParameterFilter $testRegistryKeyValuesMatchParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key name' { + $getRegistryKeyNameParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -ParameterFilter $getRegistryKeyNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should set the registry key value' { + $setRegistryKeyValueParameterFilter = { + $registryKeyNameParameterCorrect = $RegistryKeyName -eq $setTargetResourceParameters.Key + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $valueTypeParameterCorrect = $ValueType -eq $setTargetResourceParameters.ValueType + + return $registryKeyNameParameterCorrect -and $registryKeyValueNameParameterCorrect -and $registryKeyValueParameterCorrect -and $valueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -ParameterFilter $setRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Mock -CommandName 'Get-RegistryKeyValue' -MockWith { return 'NotNull' } + + Context 'Registry key exists, Ensure specified as Absent specified registry value exists' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Absent' + Force = $true + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should remove the registry key value' { + $removeItemPropertyParameterFilter = { + $pathParameterCorrect = $Path -eq $setTargetResourceParameters.Key + $nameParameterCorrect = $Name -eq $setTargetResourceParameters.ValueName + $forceParameterCorrect = $Force -eq $true + + return $pathParameterCorrect -and $nameParameterCorrect -and $forceParameterCorrect + } + + Assert-MockCalled -CommandName 'Remove-ItemProperty' -ParameterFilter $removeItemPropertyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to remove the default registry key value' { + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + + Context 'Registry key exists, Ensure specified as Absent, default registry value exists, and Force specified as True' { + $setTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueType = 'String' + ValueData = 'TestValueData' + Ensure = 'Absent' + Force = $true + } + + It 'Should not throw' { + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $setTargetResourceParameters.Key + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a new registry key' { + Assert-MockCalled -CommandName 'New-RegistryKey' -Times 0 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $setTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value' { + $getRegistryKeyValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValue' -ParameterFilter $getRegistryKeyValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a string' { + Assert-MockCalled -CommandName 'ConvertTo-String' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to binary data' { + Assert-MockCalled -CommandName 'ConvertTo-Binary' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a dword' { + Assert-MockCalled -CommandName 'ConvertTo-Dword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a qword' { + Assert-MockCalled -CommandName 'ConvertTo-Qword' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to convert the specified registry key value to a multi-string' { + Assert-MockCalled -CommandName 'ConvertTo-MultiString' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to set the registry key value' { + Assert-MockCalled -CommandName 'Set-RegistryKeyValue' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key value' { + Assert-MockCalled -CommandName 'Remove-ItemProperty' -Times 0 -Scope 'Context' + } + + It 'Should remove the default registry key value' { + $removeReistryKeyDefaultValueParameterFilter = { + $registryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $RegistryKey) + return $registryKeyParameterCorrect + } + + Assert-MockCalled -CommandName 'Remove-DefaultRegistryKeyValue' -ParameterFilter $removeReistryKeyDefaultValueParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key subkey count' { + Assert-MockCalled -CommandName 'Get-RegistryKeySubKeyCount' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to remove the registry key' { + Assert-MockCalled -CommandName 'Remove-Item' -Times 0 -Scope 'Context' + } + + It 'Should not return' { + Set-TargetResource @setTargetResourceParameters | Should Be $null + } + } + } + + Describe 'Registry\Test-TargetResource' { + Mock -CommandName 'Get-RegistryKeyValueDisplayName' -MockWith { return $RegistryKeyValueName } + Mock -CommandName 'Test-RegistryKeyValuesMatch' -MockWith { return $true } + Mock -CommandName 'Get-RegistryKey' -MockWith { return $script:testRegistryKey } + + $testRegistryKeyValue = 'Something' + + Mock -CommandName 'Get-RegistryKeyValue' -MockWith { return $testRegistryKeyValue } + + Mock -CommandName 'Get-TargetResource' -MockWith { + return @{ + Ensure = 'Absent' + } + } + + Context 'Registry key does not exist and Ensure set to Absent' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Absent' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return True' { + $testTargetResourceResult | Should Be $true + } + } + + Context 'Registry key does not exist and Ensure set to Present' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + Mock -CommandName 'Get-TargetResource' -MockWith { + if ([String]::IsNullOrEmpty($ValueName)) + { + return @{ + Ensure = 'Present' + } + } + else + { + return @{ + Ensure = 'Absent' + } + } + } + + Context 'Registry key value does not exist and Ensure set to Absent' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Absent' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return True' { + $testTargetResourceResult | Should Be $true + } + } + + Context 'Registry key value does not exist and Ensure set to Present' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + Mock -CommandName 'Get-TargetResource' -MockWith { + return @{ + Ensure = 'Present' + } + } + + Context 'Registry key exists, Ensure set to Absent, and registry key value name, type, and data not specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Absent' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + Context 'Registry key exists, Ensure set to Present, and registry key value name, type, and data not specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to retrieve the registry key value display name' { + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -Times 0 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return True' { + $testTargetResourceResult | Should Be $true + } + } + + Context 'Registry key value exists, Enusre set to Absent, and registry key value name specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Absent' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + Context 'Registry key value exists, Enusre set to Absent, and registry key value type specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueType = 'String' + Ensure = 'Absent' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + Context 'Registry key value exists, Enusre set to Absent, and registry key value data specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueData = 'TestValueData' + Ensure = 'Absent' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + Context 'Registry key value exists, Enusre set to Present, and registry key value name specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = 'TestRegistryKeyValueName' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key and value name' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + + return $keyParameterCorrect -and $valueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return True' { + $testTargetResourceResult | Should Be $true + } + } + + Mock -CommandName 'Get-TargetResource' -MockWith { + return @{ + Ensure = 'Present' + ValueType = $testTargetResourceParameters.ValueType + } + } + + Context 'Registry key value exists, Enusre set to Present, and matching registry key value type specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueType = 'String' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key, value name, and value type' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + $valueTypeParameterCorrect = $ValueType -eq $testTargetResourceParameters.ValueType + + return $keyParameterCorrect -and $valueNameParameterCorrect -and $valueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return True' { + $testTargetResourceResult | Should Be $true + } + } + + Mock -CommandName 'Get-TargetResource' -MockWith { + return @{ + Ensure ='Present' + ValueData = $testTargetResourceParameters.ValueData + ValueType = $null + } + } + + Context 'Registry key value exists, Enusre set to Present, and matching registry key value data specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueData = 'TestValueData' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key, value name, and value data' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + $valueDataParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testTargetResourceParameters.ValueData -DifferenceObject $ValueData) + + return $keyParameterCorrect -and $valueNameParameterCorrect -and $valueDataParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should test if the specified registry key value matches the retrieved registry key value' { + $testRegistryKeyValuesMatchParameterFilter = { + $expectedRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testTargetResourceParameters.ValueData -DifferenceObject $ExpectedRegistryKeyValue) + $actualRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testRegistryKeyValue -DifferenceObject $ActualRegistryKeyValue) + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $script:defaultValueType + + return $expectedRegistryKeyValueParameterCorrect -and $actualRegistryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -ParameterFilter $testRegistryKeyValuesMatchParameterFilter -Times 1 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return True' { + $testTargetResourceResult | Should Be $true + } + } + + Mock -CommandName 'Get-TargetResource' -MockWith { + return @{ + Ensure ='Present' + ValueType = 'MismatchingValueType' + } + } + + Context 'Registry key value exists, Enusre set to Present, and mismatching registry key value type specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueType = 'String' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key, value name, and value type' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + $valueTypeParameterCorrect = $ValueType -eq $testTargetResourceParameters.ValueType + + return $keyParameterCorrect -and $valueNameParameterCorrect -and $valueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to test if the specified registry key value matches the retrieved registry key value' { + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -Times 0 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + + $mismatchingValueData = 'MismatchingValueData' + + Mock -CommandName 'Get-TargetResource' -MockWith { + return @{ + Ensure ='Present' + ValueData = $mismatchingValueData + ValueType = $null + } + } + + Mock -CommandName 'Test-RegistryKeyValuesMatch' -MockWith { return $false } + + Context 'Registry key value exists, Enusre set to Present, and mismatching registry key value data specified' { + $testTargetResourceParameters = @{ + Key = 'TestRegistryKey' + ValueName = '' + ValueData = 'TestValueData' + Ensure = 'Present' + } + + It 'Should not throw' { + { $null = Test-TargetResource @testTargetResourceParameters } | Should Not Throw + } + + It 'Should retrieve the registry resource with the specified reigstry key, value name, and value data' { + $getTargetResourceParameterFilter = { + $keyParameterCorrect = $Key -eq $testTargetResourceParameters.Key + $valueNameParameterCorrect = $ValueName -eq $testTargetResourceParameters.ValueName + $valueDataParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testTargetResourceParameters.ValueData -DifferenceObject $ValueData) + + return $keyParameterCorrect -and $valueNameParameterCorrect -and $valueDataParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-TargetResource' -ParameterFilter $getTargetResourceParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry key value display name' { + $getRegistryKeyValueDisplayNameParameterFilter = { + $registryKeyValueNameParameterCorrect = $RegistryKeyValueName -eq $testTargetResourceParameters.ValueName + return $registryKeyValueNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKeyValueDisplayName' -ParameterFilter $getRegistryKeyValueDisplayNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should test if the specified registry key value matches the retrieved registry key value' { + $testRegistryKeyValuesMatchParameterFilter = { + $expectedRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testTargetResourceParameters.ValueData -DifferenceObject $ExpectedRegistryKeyValue) + $actualRegistryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testRegistryKeyValue -DifferenceObject $ActualRegistryKeyValue) + $registryKeyValueTypeParameterCorrect = $RegistryKeyValueType -eq $script:defaultValueType + + return $expectedRegistryKeyValueParameterCorrect -and $actualRegistryKeyValueParameterCorrect -and $registryKeyValueTypeParameterCorrect + } + + Assert-MockCalled -CommandName 'Test-RegistryKeyValuesMatch' -ParameterFilter $testRegistryKeyValuesMatchParameterFilter -Times 1 -Scope 'Context' + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + + It 'Should return a boolean' { + $testTargetResourceResult -is [Boolean] | Should Be $true + } + + It 'Should return False' { + $testTargetResourceResult | Should Be $false + } + } + } + + Describe 'Registry\Get-PathRoot' { + Context 'Path without parent specified' { + $getPathRootParameters = @{ + Path = 'PathWithoutParent' + } + + It 'Should not throw' { + { $null = Get-PathRoot @getPathRootParameters } | Should Not Throw + } + + $getPathRootResult = Get-PathRoot @getPathRootParameters + + It 'Should return given path' { + $getPathRootResult | Should Be $getPathRootParameters.Path + } + } + + Context 'Path with one parent specified' { + $pathRoot = 'PathRoot' + + $getPathRootParameters = @{ + Path = Join-Path -Path $pathRoot -ChildPath 'PathLeaf' + } + + It 'Should not throw' { + { $null = Get-PathRoot @getPathRootParameters } | Should Not Throw + } + + $getPathRootResult = Get-PathRoot @getPathRootParameters + + It 'Should return the root of the given path' { + $getPathRootResult | Should Be $pathRoot + } + } + + Context 'Path with two parents specified' { + $pathRoot = 'PathRoot' + $pathMiddleParent = 'PathMiddleParent' + $parentPath = Join-Path -Path $pathRoot -ChildPath $pathMiddleParent + + $getPathRootParameters = @{ + Path = Join-Path -Path $parentPath -ChildPath 'PathLeaf' + } + + It 'Should not throw' { + { $null = Get-PathRoot @getPathRootParameters } | Should Not Throw + } + + $getPathRootResult = Get-PathRoot @getPathRootParameters + + It 'Should return the root of the given path' { + $getPathRootResult | Should Be $pathRoot + } + } + } + + Describe 'Registry\ConvertTo-RegistryDriveName' { + foreach ($validRegistryDriveRoot in $script:validRegistryDriveRoots) + { + Context "Valid registry drive root $validRegistryDriveRoot specified" { + $convertToRegistryDriveNameParameters = @{ + RegistryDriveRoot = $validRegistryDriveRoot + } + + It 'Should not throw' { + { $null = ConvertTo-RegistryDriveName @convertToRegistryDriveNameParameters } | Should Not Throw + } + + $expcetedRegistryDriveName = switch ($validRegistryDriveRoot) + { + 'HKEY_CLASSES_ROOT' { 'HKCR' } + 'HKEY_CURRENT_USER' { 'HKCU' } + 'HKEY_LOCAL_MACHINE' { 'HKLM' } + 'HKEY_USERS' { 'HKUS' } + 'HKEY_CURRENT_CONFIG' { 'HKCC' } + } + + $convertToRegistryDriveNameResult = ConvertTo-RegistryDriveName @convertToRegistryDriveNameParameters + + It "Should return correct registry drive name $expcetedRegistryDriveName" { + $convertToRegistryDriveNameResult | Should Be $expcetedRegistryDriveName + } + } + } + + Context 'Invalid registry drive root specified' { + $convertToRegistryDriveNameParameters = @{ + RegistryDriveRoot = 'HKEY_COAL_MINE' + } + + It 'Should not throw' { + { $null = ConvertTo-RegistryDriveName @convertToRegistryDriveNameParameters } | Should Not Throw + } + + $convertToRegistryDriveNameResult = ConvertTo-RegistryDriveName @convertToRegistryDriveNameParameters + + It 'Should return null' { + $convertToRegistryDriveNameResult | Should Be $null + } + } + } + + Describe 'Registry\Get-RegistryDriveName' { + Mock -CommandName 'Get-PathRoot' -MockWith { return Split-Path -Path $RegistryKeyPath -Parent } + Mock -CommandName 'ConvertTo-RegistryDriveName' { } + + Context 'Specified registry path contains an invalid registry drive root' { + $invalidRegistryDriveRoot = 'HKEY_COAL_MINE' + + $getRegistryDriveNameParameters = @{ + RegistryKeyPath = Join-Path -Path $invalidRegistryDriveRoot -ChildPath 'TestRegistryPath' + } + + It 'Should throw an error for invalid registry drive' { + $errorMessage = $script:localizedData.InvalidRegistryDrive -f $invalidRegistryDriveRoot + + { $null = Get-RegistryDriveName @getRegistryDriveNameParameters } | Should Throw $errorMessage + } + } + + Mock -CommandName 'ConvertTo-RegistryDriveName' { return $script:validRegistryDriveNames[0] } + + Context 'Specified registry key path contains a valid registry drive root' { + $validRegistryDriveRoot = $script:validRegistryDriveRoots[0] + + $getRegistryDriveNameParameters = @{ + RegistryKeyPath = Join-Path -Path $validRegistryDriveRoot -ChildPath 'TestRegistryPath' + } + + It 'Should not throw' { + { $null = Get-RegistryDriveName @getRegistryDriveNameParameters } | Should Not Throw + } + + It 'Should retrieve the path root' { + $getPathRootParameterFilter = { + $pathParameterCorrect = $Path -eq $getRegistryDriveNameParameters.RegistryKeyPath + return $pathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-PathRoot' -ParameterFilter $getPathRootParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should convert the registry drive root to a registry drive name' { + $convertToRegistryDriveNameParameterFilter = { + $registryDriveRootParameterCorrect = $RegistryDriveRoot -eq $validRegistryDriveRoot + return $registryDriveRootParameterCorrect + } + + Assert-MockCalled -CommandName 'ConvertTo-RegistryDriveName' -ParameterFilter $convertToRegistryDriveNameParameterFilter -Times 1 -Scope 'Context' + } + + $getDriveNameResult = Get-RegistryDriveName @getRegistryDriveNameParameters + + It 'Should return the retrieved registry drive name' { + $getDriveNameResult | Should Be $script:validRegistryDriveNames[0] + } + } + + Context 'Specified registry path contains an invalid registry drive name' { + $invalidRegistryDriveName = 'HKCM' + + # Join-Path will search for the drive and throw an error if the drive does not exist + $getRegistryDriveNameParameters = @{ + RegistryKeyPath = "$($invalidRegistryDriveName):\TestRegistryPath" + } + + It 'Should throw an error for invalid registry drive' { + $errorMessage = $script:localizedData.InvalidRegistryDrive -f $invalidRegistryDriveName + + { $null = Get-RegistryDriveName @getRegistryDriveNameParameters } | Should Throw $errorMessage + } + } + + foreach ($validRegistryDriveName in $script:validRegistryDriveNames) + { + Context "Specified registry key path contains the valid registry drive name $validRegistryDriveName" { + # Join-Path will search for the drive and throw an error if the drive does not exist + $getRegistryDriveNameParameters = @{ + RegistryKeyPath = "$($validRegistryDriveName):\TestRegistryPath" + } + + It 'Should not throw' { + { $null = Get-RegistryDriveName @getRegistryDriveNameParameters } | Should Not Throw + } + + It 'Should retrieve the path root' { + $getPathRootParameterFilter = { + $pathParameterCorrect = $Path -eq $getRegistryDriveNameParameters.RegistryKeyPath + return $pathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-PathRoot' -ParameterFilter $getPathRootParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to convert a registry drive root to a registry drive name' { + Assert-MockCalled -CommandName 'ConvertTo-RegistryDriveName' -Times 0 -Scope 'Context' + } + + $getDriveNameResult = Get-RegistryDriveName @getRegistryDriveNameParameters + + It 'Should return the retrieved registry drive name' { + $getDriveNameResult | Should Be $validRegistryDriveName + } + } + } + } + + Describe 'Registry\Mount-RegistryDrive' { + Mock -CommandName 'Get-PSDrive' -MockWith { } + Mock -CommandName 'New-PSDrive' -MockWith { } + + Context 'Registry drive with specified name does not exist and new drive creation fails' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'TestRegistryDriveName' + } + + It 'Should throw error for unmountable registry drive' { + $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName + + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Throw $errorMessage + } + } + + Mock -CommandName 'New-PSDrive' -MockWith { return @{ Name = 'NewRegistryDrive'; Provider = $null } } + + Context 'Registry drive with specified name does not exist and new drive does not have a provider' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'TestRegistryDriveName' + } + + It 'Should throw error for unmountable registry drive' { + $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName + + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Throw $errorMessage + } + } + + Mock -CommandName 'New-PSDrive' -MockWith { return @{ Provider = @{ Name = 'NotRegistry' } } } + + Context 'Registry drive with specified name does not exist and provider of the new drives is not the registry' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'TestRegistryDriveName' + } + + It 'Should throw error for unmountable registry drive' { + $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName + + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Throw $errorMessage + } + } + + Mock -CommandName 'New-PSDrive' -MockWith { return @{ Provider = @{ Name = 'Registry' } } } + + Context 'Registry drive with specified name does not exist and new drive creation succeeds' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'HKCR' + } + + $expectedRegistryDriveRoot = 'HKEY_CLASSES_ROOT' + + It 'Should not throw' { + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Not Throw + } + + It 'Should retrieve the registry drive with specified name' { + $getPSDriveParamterFilter = { + $nameParameterCorrect = $Name -eq $mountRegistryDriveParameters.RegistryDriveName + return $nameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-PSDrive' -ParameterFilter $getPSDriveParamterFilter -Times 1 -Scope 'Context' + } + + It 'Should create the registry drive with specified name' { + $newPSDriveParamterFilter = { + $nameParameterCorrect = $Name -eq $mountRegistryDriveParameters.RegistryDriveName + $rootParameterCorrect = $Root -eq $expectedRegistryDriveRoot + $psProviderParameterCorrect = $PSProvider -eq 'Registry' + $scopeParameterCorrect = $Scope -eq 'Script' + + return $nameParameterCorrect -and $rootParameterCorrect -and $psProviderParameterCorrect -and $scopeParameterCorrect + } + + Assert-MockCalled -CommandName 'New-PSDrive' -ParameterFilter $newPSDriveParamterFilter -Times 1 -Scope 'Context' + } + + It 'Should not return anything' { + Mount-RegistryDrive @mountRegistryDriveParameters | Should Be $null + } + } + + Mock -CommandName 'Get-PSDrive' -MockWith { return @{ Name = 'NewRegistryDrive'; Provider = $null } } + + Context 'Registry drive with specified name exists and does not have a provider' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'TestRegistryDriveName' + } + + It 'Should throw error for unmountable registry drive' { + $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName + + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Throw $errorMessage + } + } + + Mock -CommandName 'Get-PSDrive' -MockWith { return @{ Provider = @{ Name = 'NotRegistry' } } } + + Context 'Registry drive with specified name exists and its provider is not the registry' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'TestRegistryDriveName' + } + + It 'Should throw error for unmountable registry drive' { + $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName + + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Throw $errorMessage + } + } + + Mock -CommandName 'Get-PSDrive' -MockWith { return @{ Provider = @{ Name = 'Registry' } } } + + Context 'Registry drive with specified name exists' { + $mountRegistryDriveParameters = @{ + RegistryDriveName = 'HKCR' + } + + $expectedRegistryDriveRoot = 'HKEY_CLASSES_ROOT' + + It 'Should not throw' { + { Mount-RegistryDrive @mountRegistryDriveParameters } | Should Not Throw + } + + It 'Should retrieve the registry drive with specified name' { + $getPSDriveParamterFilter = { + $nameParameterCorrect = $Name -eq $mountRegistryDriveParameters.RegistryDriveName + return $nameParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-PSDrive' -ParameterFilter $getPSDriveParamterFilter -Times 1 -Scope 'Context' + } + + It 'Should not attempt to create a registry drive with specified name' { + Assert-MockCalled -CommandName 'New-PSDrive' -Times 0 -Scope 'Context' + } + + It 'Should not return anything' { + Mount-RegistryDrive @mountRegistryDriveParameters | Should Be $null + } + } + } + + Describe 'Registry\Get-RegistryKey' { + $expectedRegistryDriveName = 'RegistryDriveName' + Mock -CommandName 'Get-RegistryDriveName' -MockWith { return $expectedRegistryDriveName } + + Mock -CommandName 'Mount-RegistryDrive' -MockWith { } + Mock -CommandName 'Get-Item' -MockWith { return $script:testRegistryKey } + + $expectedGetRegistryKeyResult = $null + Mock -CommandName 'Open-RegistrySubKey' -MockWith { return $expectedGetRegistryKeyResult } + + Context 'Registry key at specified path does not exist' { + $getRegistryKeyParameters = @{ + RegistryKeyPath = 'TestRegistryKeyPath' + } + + It 'Should not throw' { + { $null = Get-RegistryKey @getRegistryKeyParameters } | Should Not Throw + } + + It 'Should retrieve the registry drive name of the specified registry key path' { + $getRegistryDriveNameParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getRegistryKeyParameters.RegistryKeyPath + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryDriveName' -ParameterFilter $getRegistryDriveNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should mount the registry drive with the retrieved name' { + $mountRegistryDriveParameterFilter = { + $registryDriveNameParameterCorrect = $RegistryDriveName -eq $expectedRegistryDriveName + return $registryDriveNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Mount-RegistryDrive' -ParameterFilter $mountRegistryDriveParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry drive key' { + $getItemParameterFilter = { + $literalPathParameterCorrect = $LiteralPath -eq ($expectedRegistryDriveName + ':') + return $literalPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-Item' -ParameterFilter $getItemParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should open the specified registry key' { + $openRegistrySubKeyParameterFilter = { + $parentKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $ParentKey) + $subKeyParameterCorrect = $SubKey -eq '' + $writeAccessAllowedParameterCorrect = $WriteAccessAllowed -eq $false + + return $parentKeyParameterCorrect -and $subKeyParameterCorrect -and $writeAccessAllowedParameterCorrect + } + + Assert-MockCalled -CommandName 'Open-RegistrySubKey' -ParameterFilter $openRegistrySubKeyParameterFilter -Times 1 -Scope 'Context' + } + + $getRegistryKeyResult = Get-RegistryKey @getRegistryKeyParameters + + It 'Should return the retrieved registry key' { + $getRegistryKeyResult | Should Be $expectedGetRegistryKeyResult + } + } + + $expectedGetRegistryKeyResult = 'TestRegistryKey' + + Context 'Registry key at specified path exists and WriteAccessAllowed not specified' { + $getRegistryKeyParameters = @{ + RegistryKeyPath = 'TestRegistryKeyPath\TestSubKey' + } + + It 'Should not throw' { + { $null = Get-RegistryKey @getRegistryKeyParameters } | Should Not Throw + } + + It 'Should retrieve the registry drive name of the specified registry key path' { + $getRegistryDriveNameParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getRegistryKeyParameters.RegistryKeyPath + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryDriveName' -ParameterFilter $getRegistryDriveNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should mount the registry drive with the retrieved name' { + $mountRegistryDriveParameterFilter = { + $registryDriveNameParameterCorrect = $RegistryDriveName -eq $expectedRegistryDriveName + return $registryDriveNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Mount-RegistryDrive' -ParameterFilter $mountRegistryDriveParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry drive key' { + $getItemParameterFilter = { + $literalPathParameterCorrect = $LiteralPath -eq ($expectedRegistryDriveName + ':') + return $literalPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-Item' -ParameterFilter $getItemParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should open the specified registry key' { + $openRegistrySubKeyParameterFilter = { + $parentKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $ParentKey) + $subKeyParameterCorrect = $SubKey -eq 'TestSubKey' + $writeAccessAllowedParameterCorrect = $WriteAccessAllowed -eq $false + + return $parentKeyParameterCorrect -and $subKeyParameterCorrect -and $writeAccessAllowedParameterCorrect + } + + Assert-MockCalled -CommandName 'Open-RegistrySubKey' -ParameterFilter $openRegistrySubKeyParameterFilter -Times 1 -Scope 'Context' + } + + $getRegistryKeyResult = Get-RegistryKey @getRegistryKeyParameters + + It 'Should return the retrieved registry key' { + $getRegistryKeyResult | Should Be $expectedGetRegistryKeyResult + } + } + + Context 'Registry key at specified path exists and WriteAccessAllowed specified' { + $getRegistryKeyParameters = @{ + RegistryKeyPath = 'TestRegistryKeyPath\TestSubKey' + WriteAccessAllowed = $true + } + + It 'Should not throw' { + { $null = Get-RegistryKey @getRegistryKeyParameters } | Should Not Throw + } + + It 'Should retrieve the registry drive name of the specified registry key path' { + $getRegistryDriveNameParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $getRegistryKeyParameters.RegistryKeyPath + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryDriveName' -ParameterFilter $getRegistryDriveNameParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should mount the registry drive with the retrieved name' { + $mountRegistryDriveParameterFilter = { + $registryDriveNameParameterCorrect = $RegistryDriveName -eq $expectedRegistryDriveName + return $registryDriveNameParameterCorrect + } + + Assert-MockCalled -CommandName 'Mount-RegistryDrive' -ParameterFilter $mountRegistryDriveParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should retrieve the registry drive key' { + $getItemParameterFilter = { + $literalPathParameterCorrect = $LiteralPath -eq ($expectedRegistryDriveName + ':') + return $literalPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-Item' -ParameterFilter $getItemParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should open the specified registry key' { + $openRegistrySubKeyParameterFilter = { + $parentKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $ParentKey) + $subKeyParameterCorrect = $SubKey -eq 'TestSubKey' + $writeAccessAllowedParameterCorrect = $WriteAccessAllowed -eq $getRegistryKeyParameters.WriteAccessAllowed + + return $parentKeyParameterCorrect -and $subKeyParameterCorrect -and $writeAccessAllowedParameterCorrect + } + + Assert-MockCalled -CommandName 'Open-RegistrySubKey' -ParameterFilter $openRegistrySubKeyParameterFilter -Times 1 -Scope 'Context' + } + + $getRegistryKeyResult = Get-RegistryKey @getRegistryKeyParameters + + It 'Should return the retrieved registry key' { + $getRegistryKeyResult | Should Be $expectedGetRegistryKeyResult + } + } + } + + Describe 'Registry\Get-RegistryKeyValueDisplayName' { + Context 'Specified registry key value name is null' { + $getRegistryKeyValueDisplayNameParameters = @{ + RegistryKeyValue = $null + } + + It 'Should not throw' { + { $null = Get-RegistryKeyValueDisplayName @getRegistryKeyValueDisplayNameParameters } | Should Not Throw + } + + $getRegistryKeyValueDisplayNameResult = Get-RegistryKeyValueDisplayName @getRegistryKeyValueDisplayNameParameters + + It 'Should return default registry key value name' { + $getRegistryKeyValueDisplayNameResult | Should Be $localizedData.DefaultValueDisplayName + } + } + + Context 'Specified registry key value name is an empty string' { + $getRegistryKeyValueDisplayNameParameters = @{ + RegistryKeyValue = [String]::Empty + } + + It 'Should not throw' { + { $null = Get-RegistryKeyValueDisplayName @getRegistryKeyValueDisplayNameParameters } | Should Not Throw + } + + $getRegistryKeyValueDisplayNameResult = Get-RegistryKeyValueDisplayName @getRegistryKeyValueDisplayNameParameters + + It 'Should return default registry key value name' { + $getRegistryKeyValueDisplayNameResult | Should Be $localizedData.DefaultValueDisplayName + } + } + + Context 'Specified registry key value name is a populated string' { + $getRegistryKeyValueDisplayNameParameters = @{ + RegistryKeyValue = 'TestRegistryKeyValueName' + } + + It 'Should not throw' { + { $null = Get-RegistryKeyValueDisplayName @getRegistryKeyValueDisplayNameParameters } | Should Not Throw + } + + $getRegistryKeyValueDisplayNameResult = Get-RegistryKeyValueDisplayName @getRegistryKeyValueDisplayNameParameters + + It 'Should return given registry key value name' { + $getRegistryKeyValueDisplayNameResult | Should Be $getRegistryKeyValueDisplayNameParameters.RegistryKeyValue + } + } + } + + Describe 'Registry\Convert-ByteArrayToHexString' { + Context 'Specified byte array is empty' { + $convertByteArrayToHexStringParameters = @{ + ByteArray = @() + } + + It 'Should not throw' { + { $null = Convert-ByteArrayToHexString @convertByteArrayToHexStringParameters } | Should Not Throw + } + + $convertByteArrayToHexStringResult = Convert-ByteArrayToHexString @convertByteArrayToHexStringParameters + + It 'Should return an empty string' { + $convertByteArrayToHexStringResult | Should Be ([String]::Empty) + } + } + + Context 'Specified byte array has one element' { + $convertByteArrayToHexStringParameters = @{ + ByteArray = @( [Byte]'1' ) + } + + It 'Should not throw' { + { $null = Convert-ByteArrayToHexString @convertByteArrayToHexStringParameters } | Should Not Throw + } + + $convertByteArrayToHexStringResult = Convert-ByteArrayToHexString @convertByteArrayToHexStringParameters + + It 'Should return the byte array as a single hex string' { + $convertByteArrayToHexStringResult | Should Be '01' + } + } + + Context 'Specified byte array has multiple elements' { + $convertByteArrayToHexStringParameters = @{ + ByteArray = @( 0, [Byte]::MaxValue ) + } + + It 'Should not throw' { + { $null = Convert-ByteArrayToHexString @convertByteArrayToHexStringParameters } | Should Not Throw + } + + $convertByteArrayToHexStringResult = Convert-ByteArrayToHexString @convertByteArrayToHexStringParameters + + It 'Should return the byte array as a single hex string' { + $convertByteArrayToHexStringResult | Should Be '00ff' + } + } + } + + Describe 'Registry\ConvertTo-ReadableString' { + Mock -CommandName 'Convert-ByteArrayToHexString' -MockWith { return $ByteArray } + + foreach ($registryKeyValueType in $script:registryKeyValueTypes) + { + Context "Registry key value specified as null and registry key type specified as $registryKeyValueType" { + $convertToReadableStringParameters = @{ + RegistryKeyValue = $null + RegistryKeyValueType = $registryKeyValueType + } + + It 'Should not throw' { + { $null = ConvertTo-ReadableString @convertToReadableStringParameters } | Should Not Throw + } + + It 'Should not attempt to convert registry key value to a hex string' { + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -Times 0 -Scope 'Context' + } + + $convertToReadableStringResult = ConvertTo-ReadableString @convertToReadableStringParameters + + It 'Should return an empty string' { + $convertToReadableStringResult | Should Be ([String]::Empty) + } + } + + Context "Registry key value specified as an empty array and registry key type specified as $registryKeyValueType" { + $convertToReadableStringParameters = @{ + RegistryKeyValue = @() + RegistryKeyValueType = $registryKeyValueType + } + + It 'Should not throw' { + { $null = ConvertTo-ReadableString @convertToReadableStringParameters } | Should Not Throw + } + + if ($registryKeyValueType -eq 'Binary') + { + It 'Should convert registry key value to a hex string' { + $convertByteArrayToHexStringParameterFilter = { + $byteArrayParameterCorrect = $null -eq (Compare-Object -ReferenceObject $convertToReadableStringParameters.RegistryKeyValue -DifferenceObject $ByteArray) + return $byteArrayParameterCorrect + } + + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -ParameterFilter $convertByteArrayToHexStringParameterFilter -Times 1 -Scope 'Context' + } + } + else + { + It 'Should not attempt to convert registry key value to a hex string' { + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -Times 0 -Scope 'Context' + } + } + + $convertToReadableStringResult = ConvertTo-ReadableString @convertToReadableStringParameters + + It 'Should return an empty string' { + $convertToReadableStringResult | Should Be ([String]::Empty) + } + } + + Context "Registry key value specified as an array with a single element and registry key type specified as $registryKeyValueType" { + $convertToReadableStringParameters = @{ + RegistryKeyValue = @( 'String1' ) + RegistryKeyValueType = $registryKeyValueType + } + + It 'Should not throw' { + { $null = ConvertTo-ReadableString @convertToReadableStringParameters } | Should Not Throw + } + + if ($registryKeyValueType -eq 'Binary') + { + It 'Should convert registry key value to a hex string' { + $convertByteArrayToHexStringParameterFilter = { + $byteArrayParameterCorrect = $null -eq (Compare-Object -ReferenceObject $convertToReadableStringParameters.RegistryKeyValue -DifferenceObject $ByteArray) + return $byteArrayParameterCorrect + } + + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -ParameterFilter $convertByteArrayToHexStringParameterFilter -Times 1 -Scope 'Context' + } + } + else + { + It 'Should not attempt to convert registry key value to a hex string' { + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -Times 0 -Scope 'Context' + } + } + + $convertToReadableStringResult = ConvertTo-ReadableString @convertToReadableStringParameters + + It 'Should return the specified string' { + $convertToReadableStringResult | Should Be $convertToReadableStringParameters.RegistryKeyValue[0] + } + } + + Context "Registry key value specified as an array with multiple elements and registry key type specified as $registryKeyValueType" { + $convertToReadableStringParameters = @{ + RegistryKeyValue = @( 'String1', 'String2' ) + RegistryKeyValueType = $registryKeyValueType + } + + It 'Should not throw' { + { $null = ConvertTo-ReadableString @convertToReadableStringParameters } | Should Not Throw + } + + if ($registryKeyValueType -eq 'Binary') + { + It 'Should convert registry key value to a hex string' { + $convertByteArrayToHexStringParameterFilter = { + $byteArrayParameterCorrect = $null -eq (Compare-Object -ReferenceObject $convertToReadableStringParameters.RegistryKeyValue -DifferenceObject $ByteArray) + return $byteArrayParameterCorrect + } + + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -ParameterFilter $convertByteArrayToHexStringParameterFilter -Times 1 -Scope 'Context' + } + } + else + { + It 'Should not attempt to convert registry key value to a hex string' { + Assert-MockCalled -CommandName 'Convert-ByteArrayToHexString' -Times 0 -Scope 'Context' + } + } + + $expectedReadProperty = '(String1, String2)' + $convertToReadableStringResult = ConvertTo-ReadableString @convertToReadableStringParameters + + It 'Should return the specified strings inside one string' { + $convertToReadableStringResult | Should Be $expectedReadProperty + } + } + } + } + + Describe 'Registry\New-RegistryKey' { + $registryRootKeyPath = 'RegistryRoot' + $newRegistryKeyPath = 'TestRegistryKeyPath' + + Mock -CommandName 'Get-RegistryKey' -MockWith { return $script:testRegistryKey } + Mock -CommandName 'New-RegistrySubKey' -MockWith { return $script:testRegistryKey } + + Context 'Parent registry key exists' { + $newRegistryKeyParameters = @{ + RegistryKeyPath = Join-Path -Path $registryRootKeyPath -ChildPath $newRegistryKeyPath + } + + It 'Should not throw' { + { $null = New-RegistryKey @newRegistryKeyParameters } | Should Not Throw + } + + It 'Should retrieve the parent registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $registryRootKeyPath + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should not create the parent registry key' { + Assert-MockCalled -CommandName 'Get-RegistryKey' -Exactly 1 -Scope 'Context' + } + + It 'Should create the registry key as a subkey of the parent registry key' { + $newRegistrySubKeyParameterFilter = { + $parentRegistryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $ParentRegistryKey) + $subKeyNameParameterCorrect = $SubKeyName -eq $newRegistryKeyPath + + return $parentRegistryKeyParameterCorrect -and $subKeyNameParameterCorrect + } + + Assert-MockCalled -CommandName 'New-RegistrySubKey' -ParameterFilter $newRegistrySubKeyParameterFilter -Times 1 -Scope 'Context' + } + + $newRegistryKeyResult = New-RegistryKey @newRegistryKeyParameters + + It 'Should return the created subkey' { + $newRegistryKeyResult | Should Be $script:testRegistryKey + } + } + + $newParentRegistryKeyPath = 'NewParentRegistryKey' + $testParentRegistryKeyPath = Join-Path -Path $registryRootKeyPath -ChildPath $newParentRegistryKeyPath + + Mock -CommandName 'Get-RegistryKey' -MockWith { + if ($RegistryKeyPath -eq $testParentRegistryKeyPath) + { + return $null + } + else + { + return $script:testRegistryKey + } + } + + Context 'Parent registry key does not exist' { + $newRegistryKeyParameters = @{ + RegistryKeyPath = Join-Path -Path $testParentRegistryKeyPath -ChildPath $newRegistryKeyPath + } + + It 'Should not throw' { + { $null = New-RegistryKey @newRegistryKeyParameters } | Should Not Throw + } + + It 'Should retrieve the parent registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $testParentRegistryKeyPath + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + } + + It 'Should create the parent registry key' { + $getRegistryKeyParameterFilter = { + $registryKeyPathParameterCorrect = $RegistryKeyPath -eq $registryRootKeyPath + return $registryKeyPathParameterCorrect + } + + Assert-MockCalled -CommandName 'Get-RegistryKey' -ParameterFilter $getRegistryKeyParameterFilter -Times 1 -Scope 'Context' + + $newRegistrySubKeyParameterFilter = { + $parentRegistryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $ParentRegistryKey) + $subKeyNameParameterCorrect = $SubKeyName -eq $newParentRegistryKeyPath + + return $parentRegistryKeyParameterCorrect -and $subKeyNameParameterCorrect + } + + Assert-MockCalled -CommandName 'New-RegistrySubKey' -ParameterFilter $newRegistrySubKeyParameterFilter -Times 1 -Scope 'Context' + + } + + It 'Should create the registry key as a subkey of the parent registry key' { + $newRegistrySubKeyParameterFilter = { + $parentRegistryKeyParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:testRegistryKey -DifferenceObject $ParentRegistryKey) + $subKeyNameParameterCorrect = $SubKeyName -eq $newRegistryKeyPath + + return $parentRegistryKeyParameterCorrect -and $subKeyNameParameterCorrect + } + + Assert-MockCalled -CommandName 'New-RegistrySubKey' -ParameterFilter $newRegistrySubKeyParameterFilter -Times 1 -Scope 'Context' + } + + $newRegistryKeyResult = New-RegistryKey @newRegistryKeyParameters + + It 'Should return the created subkey' { + $newRegistryKeyResult | Should Be $script:testRegistryKey + } + } + } + + Describe 'Registry\Test-RegistryKeyValuesMatch' { + foreach ($registryKeyValueType in $script:registryKeyValueTypes) + { + $expectedRegistryKeyValue = switch ($registryKeyValueType) + { + 'String' { 'String1' } + 'Binary' { [Byte[]]@( 12, 172, 17, 17 ) } + 'DWord' { 169 } + 'QWord' { 92 } + 'MultiString' { @( 'String1', 'String2' ) } + 'ExpandString' { '$expandMe' } + } + + $mismatchingActualRegistryKeyValue = switch ($registryKeyValueType) + { + 'String' { 'String2' } + 'Binary' { [Byte[]]@( 11, 172, 17, 1 ) } + 'DWord' { 12 } + 'QWord' { 64 } + 'MultiString' { @( 'String3', 'String2' ) } + 'ExpandString' { '$dontExpandMe' } + } + + Context "Registry key value type specified as $registryKeyValueType and registry key values match" { + $testRegistryKeyValuesMatchParameters = @{ + ExpectedRegistryKeyValue = $expectedRegistryKeyValue + ActualRegistryKeyValue = $expectedRegistryKeyValue + RegistryKeyValueType = $registryKeyValueType + } + + It 'Should not throw' { + { $null = Test-RegistryKeyValuesMatch @testRegistryKeyValuesMatchParameters } | Should Not Throw + } + + $testRegistryKeyValuesMatchResult = Test-RegistryKeyValuesMatch @testRegistryKeyValuesMatchParameters + + It 'Should return true' { + $testRegistryKeyValuesMatchResult | Should Be $true + } + } + + Context "Registry key value type specified as $registryKeyValueType and registry key values do not match" { + $testRegistryKeyValuesMatchParameters = @{ + ExpectedRegistryKeyValue = $expectedRegistryKeyValue + ActualRegistryKeyValue = $mismatchingActualRegistryKeyValue + RegistryKeyValueType = $registryKeyValueType + } + + It 'Should not throw' { + { $null = Test-RegistryKeyValuesMatch @testRegistryKeyValuesMatchParameters } | Should Not Throw + } + + $testRegistryKeyValuesMatchResult = Test-RegistryKeyValuesMatch @testRegistryKeyValuesMatchParameters + + It 'Should return false' { + $testRegistryKeyValuesMatchResult | Should Be $false + } + } + } + } + + Describe 'Registry\ConvertTo-Binary' { + Context 'Specified registry key value is null' { + $convertToBinaryParameters = @{ + RegistryKeyValue = $null + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return null' { + $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an empty array' { + $convertToBinaryParameters = @{ + RegistryKeyValue = @() + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return null' { + $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a single null element' { + $convertToBinaryParameters = @{ + RegistryKeyValue = @( $null ) + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return null' { + $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a valid single string of an odd length' { + $validBinaryString = '0xCAC1111' + $expectedByteArray = [Byte[]]@( 12, 172, 17, 17 ) + + $convertToBinaryParameters = @{ + RegistryKeyValue = @( $validBinaryString ) + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return the specified single string' { + Compare-Object -ReferenceObject $expectedByteArray -DifferenceObject $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a valid single string of an even length' { + $validBinaryString = '0x0CAC1111' + $expectedByteArray = [Byte[]]@( 12, 172, 17, 17 ) + + $convertToBinaryParameters = @{ + RegistryKeyValue = @( $validBinaryString ) + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return the specified single string' { + Compare-Object -ReferenceObject $expectedByteArray -DifferenceObject $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a valid single string of an even length not starting with 0x' { + $validBinaryString = '0CAC1111' + $expectedByteArray = [Byte[]]@( 12, 172, 17, 17 ) + + $convertToBinaryParameters = @{ + RegistryKeyValue = @( $validBinaryString ) + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return the specified single string' { + Compare-Object -ReferenceObject $expectedByteArray -DifferenceObject $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a valid single string of 0x00' { + $validBinaryString = '0x00' + $expectedByteArray = [Byte[]]@( 0 ) + + $convertToBinaryParameters = @{ + RegistryKeyValue = @( $validBinaryString ) + } + + It 'Should not throw' { + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Not Throw + } + + $convertToBinaryResult = ConvertTo-Binary @convertToBinaryParameters + + It 'Should return the specified single string' { + Compare-Object -ReferenceObject $expectedByteArray -DifferenceObject $convertToBinaryResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a invalid single string' { + $invalidBinaryString = 'InvalidBinaryValue' + + $convertToBinaryParameters = @{ + RegistryKeyValue = @( $invalidBinaryString ) + } + + It 'Should not throw' { + $errorMessage = $script:localizedData.BinaryDataNotInHexFormat -f $invalidBinaryString + + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Throw $errorMessage + } + } + + Context 'Specified registry key value is an array with more than one string' { + $convertToBinaryParameters = @{ + RegistryKeyValue = @( 'String1', 'String2' ) + } + + It 'Should throw an error for unexpected array' { + $errorMessage = $script:localizedData.ArrayNotAllowedForExpectedType -f 'Binary' + + { $null = ConvertTo-Binary @convertToBinaryParameters } | Should Throw $errorMessage + } + } + } + + Describe 'Registry\ConvertTo-DWord' { + Context 'Specified registry key value is null' { + $convertToDWordParameters = @{ + RegistryKeyValue = $null + } + + It 'Should not throw' { + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Not Throw + } + + $convertToDWordResult = ConvertTo-DWord @convertToDWordParameters + + It 'Should return 0 as an Int32' { + $convertToDWordResult | Should Be ([System.Int32] 0) + } + } + + Context 'Specified registry key value is an empty array' { + $convertToDWordParameters = @{ + RegistryKeyValue = @() + } + + It 'Should not throw' { + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Not Throw + } + + $convertToDWordResult = ConvertTo-DWord @convertToDWordParameters + + It 'Should return 0 as an Int32' { + $convertToDWordResult | Should Be ([System.Int32] 0) + } + } + + Context 'Specified registry key value is an array containing a single null element' { + $convertToDWordParameters = @{ + RegistryKeyValue = @( $null ) + } + + It 'Should not throw' { + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Not Throw + } + + $convertToDWordResult = ConvertTo-DWord @convertToDWordParameters + + It 'Should return 0 as an Int32' { + $convertToDWordResult | Should Be ([System.Int32] 0) + } + } + + $testDWord1 = [System.Int32]::MaxValue + + Context 'Specified registry key value is an array containing a valid single string and Hex not specified' { + $convertToDWordParameters = @{ + RegistryKeyValue = @( $testDWord1.ToString() ) + } + + It 'Should not throw' { + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Not Throw + } + + $convertToDWordResult = ConvertTo-DWord @convertToDWordParameters + + It 'Should return the specified double word' { + $convertToDWordResult | Should Be $testDWord1 + } + } + + Context 'Specified registry key value is an array containing an invalid single string and Hex specified as True' { + $invalidHexDWord = 'InvalidInt32' + $convertToDWordParameters = @{ + RegistryKeyValue = @( $invalidHexDWord ) + Hex = $true + } + + It 'Should throw an error for the invalid dword string' { + $errorMessage = $script:localizedData.DWordDataNotInHexFormat -f $invalidHexDWord + + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Throw $errorMessage + } + } + + Context 'Specified registry key value is an array containing a valid single string and Hex specified as True' { + $validHexDWord = '0xA9' + $expectedInt32Value = 169 + + $convertToDWordParameters = @{ + RegistryKeyValue = @( $validHexDWord.ToString() ) + Hex = $true + } + + It 'Should not throw' { + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Not Throw + } + + $convertToDWordResult = ConvertTo-DWord @convertToDWordParameters + + It 'Should return the specified double word converted from a Hex value' { + $convertToDWordResult | Should Be $expectedInt32Value + } + } + + Context 'Specified registry key value is an array containing a valid single string of 0x00 and Hex specified as True' { + $validHexDWord = '0x00' + $expectedInt32Value = 0 + + $convertToDWordParameters = @{ + RegistryKeyValue = @( $validHexDWord.ToString() ) + Hex = $true + } + + It 'Should not throw' { + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Not Throw + } + + $convertToDWordResult = ConvertTo-DWord @convertToDWordParameters + + It 'Should return the specified double word converted from a Hex value' { + $convertToDWordResult | Should Be $expectedInt32Value + } + } + + Context 'Specified registry key value is an array with more than one string' { + $testDWord2 = [System.Int32]::MinValue + + $convertToDWordParameters = @{ + RegistryKeyValue = @( $testDWord1.ToString(), $testDWord2.ToString() ) + } + + It 'Should throw an error for unexpected array' { + $errorMessage = $script:localizedData.ArrayNotAllowedForExpectedType -f 'Dword' + + { $null = ConvertTo-DWord @convertToDWordParameters } | Should Throw $errorMessage + } + } + } + + Describe 'Registry\ConvertTo-MultiString' { + Context 'Specified registry key value is null' { + $convertToMultiStringParameters = @{ + RegistryKeyValue = $null + } + + It 'Should not throw' { + { $null = ConvertTo-MultiString @convertToMultiStringParameters } | Should Not Throw + } + + $convertToMultiStringResult = ConvertTo-MultiString @convertToMultiStringParameters + + It 'Should return null' { + $convertToMultiStringResult | Should Be $null + } + } + + Context 'Specified registry key value is an empty array' { + $convertToMultiStringParameters = @{ + RegistryKeyValue = @() + } + + It 'Should not throw' { + { $null = ConvertTo-MultiString @convertToMultiStringParameters } | Should Not Throw + } + + $convertToMultiStringResult = ConvertTo-MultiString @convertToMultiStringParameters + + It 'Should return null' { + $convertToMultiStringResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a single null element' { + $convertToMultiStringParameters = @{ + RegistryKeyValue = @( $null ) + } + + It 'Should not throw' { + { $null = ConvertTo-MultiString @convertToMultiStringParameters } | Should Not Throw + } + + $convertToMultiStringResult = ConvertTo-MultiString @convertToMultiStringParameters + + It 'Should return an array containing null' { + Compare-Object -ReferenceObject ([String[]]@($null)) -DifferenceObject $convertToMultiStringResult | Should Be $null + } + } + + Context 'Specified registry key value is an array containing a single string' { + $convertToMultiStringParameters = @{ + RegistryKeyValue = @( 'String1' ) + } + + It 'Should not throw' { + { $null = ConvertTo-MultiString @convertToMultiStringParameters } | Should Not Throw + } + + $convertToMultiStringResult = ConvertTo-MultiString @convertToMultiStringParameters + + It 'Should return an array containing the specified single string' { + Compare-Object -ReferenceObject $convertToMultiStringParameters.RegistryKeyValue -DifferenceObject $convertToMultiStringResult | Should Be $null + } + } + + Context 'Specified registry key value is an array with more than one string' { + $convertToMultiStringParameters = @{ + RegistryKeyValue = @( 'String1', 'String2' ) + } + + It 'Should not throw' { + { $null = ConvertTo-MultiString @convertToMultiStringParameters } | Should Not Throw + } + + $convertToMultiStringResult = ConvertTo-MultiString @convertToMultiStringParameters + + It 'Should return an array containing the specified single string' { + Compare-Object -ReferenceObject $convertToMultiStringParameters.RegistryKeyValue -DifferenceObject $convertToMultiStringResult | Should Be $null + } + } + } + + Describe 'Registry\ConvertTo-QWord' { + Context 'Specified registry key value is null' { + $convertToQWordParameters = @{ + RegistryKeyValue = $null + } + + It 'Should not throw' { + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Not Throw + } + + $convertToQWordResult = ConvertTo-QWord @convertToQWordParameters + + It 'Should return 0 as an Int64' { + $convertToQWordResult | Should Be ([System.Int64] 0) + } + } + + Context 'Specified registry key value is an empty array' { + $convertToQWordParameters = @{ + RegistryKeyValue = @() + } + + It 'Should not throw' { + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Not Throw + } + + $convertToQWordResult = ConvertTo-QWord @convertToQWordParameters + + It 'Should return 0 as an Int64' { + $convertToQWordResult | Should Be ([System.Int64] 0) + } + } + + Context 'Specified registry key value is an array containing a single null element' { + $convertToQWordParameters = @{ + RegistryKeyValue = @( $null ) + } + + It 'Should not throw' { + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Not Throw + } + + $convertToQWordResult = ConvertTo-QWord @convertToQWordParameters + + It 'Should return 0 as an Int64' { + $convertToQWordResult | Should Be ([System.Int64] 0) + } + } + + $testDWord1 = [System.Int64]::MaxValue + + Context 'Specified registry key value is an array containing a valid single string and Hex not specified' { + $convertToQWordParameters = @{ + RegistryKeyValue = @( $testDWord1.ToString() ) + } + + It 'Should not throw' { + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Not Throw + } + + $convertToQWordResult = ConvertTo-QWord @convertToQWordParameters + + It 'Should return the specified quad word' { + $convertToQWordResult | Should Be $testDWord1 + } + } + + Context 'Specified registry key value is an array containing an invalid single string and Hex specified as True' { + $invalidHexDWord = 'InvalidInt32' + $convertToQWordParameters = @{ + RegistryKeyValue = @( $invalidHexDWord ) + Hex = $true + } + + It 'Should throw an error for the invalid qword string' { + $errorMessage = $script:localizedData.QWordDataNotInHexFormat -f $invalidHexDWord + + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Throw $errorMessage + } + } + + Context 'Specified registry key value is an array containing a valid single string and Hex specified as True' { + $validHexDWord = '0xA9' + $expectedInt64Value = 169 + + $convertToQWordParameters = @{ + RegistryKeyValue = @( $validHexDWord.ToString() ) + Hex = $true + } + + It 'Should not throw' { + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Not Throw + } + + $convertToQWordResult = ConvertTo-QWord @convertToQWordParameters + + It 'Should return the specified quad word converted from a Hex value' { + $convertToQWordResult | Should Be $expectedInt64Value + } + } + + Context 'Specified registry key value is an array containing a valid single string of 0x00 and Hex specified as True' { + $validHexDWord = '0x00' + $expectedInt64Value = 0 + + $convertToQWordParameters = @{ + RegistryKeyValue = @( $validHexDWord.ToString() ) + Hex = $true + } + + It 'Should not throw' { + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Not Throw + } + + $convertToQWordResult = ConvertTo-QWord @convertToQWordParameters + + It 'Should return the specified quad word converted from a Hex value' { + $convertToQWordResult | Should Be $expectedInt64Value + } + } + + Context 'Specified registry key value is an array with more than one string' { + $testDWord2 = [System.Int64]::MinValue + + $convertToQWordParameters = @{ + RegistryKeyValue = @( $testDWord1.ToString(), $testDWord2.ToString() ) + } + + It 'Should throw an error for unexpected array' { + $errorMessage = $script:localizedData.ArrayNotAllowedForExpectedType -f 'Qword' + + { $null = ConvertTo-QWord @convertToQWordParameters } | Should Throw $errorMessage + } + } + } + + Describe 'Registry\ConvertTo-String' { + Context 'Specified registry key value is null' { + $convertToStringParameters = @{ + RegistryKeyValue = $null + } + + It 'Should not throw' { + { $null = ConvertTo-String @convertToStringParameters } | Should Not Throw + } + + $convertToStringResult = ConvertTo-String @convertToStringParameters + + It 'Should return an empty string' { + $convertToStringResult | Should Be ([String]::Empty) + } + } + + Context 'Specified registry key value is an empty array' { + $convertToStringParameters = @{ + RegistryKeyValue = @() + } + + It 'Should not throw' { + { $null = ConvertTo-String @convertToStringParameters } | Should Not Throw + } + + $convertToStringResult = ConvertTo-String @convertToStringParameters + + It 'Should return an empty string' { + $convertToStringResult | Should Be ([String]::Empty) + } + } + + Context 'Specified registry key value is an array containing a single null element' { + $convertToStringParameters = @{ + RegistryKeyValue = @( $null ) + } + + It 'Should not throw' { + { $null = ConvertTo-String @convertToStringParameters } | Should Not Throw + } + + $convertToStringResult = ConvertTo-String @convertToStringParameters + + It 'Should return an empty string' { + $convertToStringResult | Should Be ([String]::Empty) + } + } + + Context 'Specified registry key value is an array containing a single string' { + $convertToStringParameters = @{ + RegistryKeyValue = @( 'String1' ) + } + + It 'Should not throw' { + { $null = ConvertTo-String @convertToStringParameters } | Should Not Throw + } + + $convertToStringResult = ConvertTo-String @convertToStringParameters + + It 'Should return the specified single string' { + $convertToStringResult | Should Be $convertToStringParameters.RegistryKeyValue[0] + } + } + + Context 'Specified registry key value is an array with more than one string' { + $convertToStringParameters = @{ + RegistryKeyValue = @( 'String1', 'String2' ) + } + + It 'Should throw an error for unexpected array' { + $errorMessage = $script:localizedData.ArrayNotAllowedForExpectedType -f 'String or ExpandString' + + { $null = ConvertTo-String @convertToStringParameters } | Should Throw $errorMessage + } + } + } + } +} +finally +{ + Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment +}