Skip to content
This repository has been archived by the owner on Jun 14, 2024. It is now read-only.

Added support for Exclusive flag parameter which ensures the a value is the EXCLUSIVE value in a given key. #64

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 93 additions & 2 deletions DscResources/MSFT_RegistryResource/MSFT_RegistryResource.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ function Get-TargetResource
{
$registryResource['Ensure'] = 'Present'
}

$valueNames = Get-RegistryKeyValueNames -RegistryKey $registryKey
$otherValueNames = @($valueNames | Where-Object {$_ -ne $ValueName})

if ($otherValueNames.Count -gt 0)
{
Write-Verbose -Message ($script:localizedData.Exclusive_IsNotExclusive -f $Key, $ValueName)
$registryResource['Exclusive'] = $false
}
elseif ($valueNames -eq $valueName)
{
Write-Verbose -Message ($script:localizedData.Exclusive_IsExclusive -f $Key, $ValueName)
$registryResource['Exclusive'] = $true
}
}

Write-Verbose -Message ($script:localizedData.GetTargetResourceEndMessage -f $Key)
Expand Down Expand Up @@ -206,7 +220,10 @@ function Set-TargetResource
$Hex = $false,

[Boolean]
$Force = $false
$Force = $false,

[Boolean]
$Exclusive = $false
)

Write-Verbose -Message ($script:localizedData.SetTargetResourceStartMessage -f $Key)
Expand Down Expand Up @@ -259,6 +276,20 @@ function Set-TargetResource
# Retrieve the name of the registry key
$registryKeyName = Get-RegistryKeyName -RegistryKey $registryKey

# Retrieve other registry key value names.
$valueNames = Get-RegistryKeyValueNames -RegistryKey $registryKey

if ($Exclusive)
{
$otherValueNames = $valueNames | Where-Object {$_ -ne $ValueName}

foreach ($name in $otherValueNames)
{
Write-Verbose -Message ($script:localizedData.Exclusive_RemovingRegistryValueName -f $Key, $Name)
$null = Remove-ItemProperty -Path $Key -Name $name -Force
}
}

# Check if the registry key value exists
if ($null -eq $actualRegistryKeyValue)
{
Expand Down Expand Up @@ -332,6 +363,20 @@ function Set-TargetResource
$null = Remove-Item -Path $Key -Recurse -Force
}
}
else
{
if ($Exclusive)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why are removing:

  • key exist
  • value name not specified
  • Exclusive specified

So remove any values.

{
# Retrieve other registry key value names.
$valueNames = Get-RegistryKeyValueNames -RegistryKey $registryKey

foreach ($name in $valueNames)
{
Write-Verbose -Message ($script:localizedData.Exclusive_RemovingRegistryValueName -f $Key, $Name)
$null = Remove-ItemProperty -Path $Key -Name $name -Force
}
}
}
}
}

Expand Down Expand Up @@ -410,7 +455,10 @@ function Test-TargetResource
$Hex = $false,

[Boolean]
$Force = $false
$Force = $false,

[Boolean]
$Exclusive = $false
)

Write-Verbose -Message ($script:localizedData.TestTargetResourceStartMessage -f $Key)
Expand Down Expand Up @@ -512,6 +560,27 @@ function Test-TargetResource
}
}

if ($Exclusive)
{
# Need to get the actual registry key value since Get-TargetResource returns
$registryKey = Get-RegistryKey -RegistryKeyPath $Key

# Retrieve other registry key value names.
$valueNames = Get-RegistryKeyValueNames -RegistryKey $registryKey

$otherValueNames = @($valueNames | Where-Object {$_ -ne $ValueName})

if ($otherValueNames.Count -gt 0)
{
Write-Verbose -Message ($script:localizedData.Exclusive_TESTIsNotExclusive -f $key, $ValueName)
$registryResourceInDesiredState = $false
}
else
{
Write-Verbose -Message ($script:localizedData.Exclusive_TESTIsExclusive -f $key, $ValueName)
}
}

Write-Verbose -Message ($script:localizedData.TestTargetResourceEndMessage -f $Key)

return $registryResourceInDesiredState
Expand Down Expand Up @@ -793,6 +862,28 @@ function Get-RegistryKeyValueDisplayName
return $registryKeyValueDisplayName
}

<#
.SYNOPSIS
Retrieves all the values in a Registry Key.

.PARAMETER RegistryKey
The registry key to retrieve the values from.
#>
function Get-RegistryKeyValueNames
{
[OutputType([Object[]])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[Microsoft.Win32.RegistryKey]
$RegistryKey
)

$registryKeyValue = $RegistryKey.GetValueNames()
return ,$registryKeyValue
}

<#
.SYNOPSIS
Retrieves the registry key value with the specified name from the specified registry key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ class MSFT_RegistryResource : OMI_BaseResource
[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;
[Write, Description("Specifies whether the ValueName specified should be made to be the ONLY value in the Key (recreation of **delval functionality)")] Boolean Exclusive;
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,10 @@ ConvertFrom-StringData @'
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.

Exclusive_RemovingRegistryValueName = Enforcing Exclusive Flag by removing Value Name ({1}) from key ({0}).
Exclusive_IsExclusive = ValueName ({1}) IS EXCLUSIVE within key ({0}).
Exclusive_IsNotExclusive = ValueName ({1}) IS NOT EXCLUSIVE within key ({0}).
Exclusive_TESTIsExclusive = ValueName ({1}) IS EXCLUSIVE within Key ({0}).
Exclusive_TESTIsNotExclusive = The ValueName ({1}) IS NOT EXCLUSIVE within Key ({0}).
'@
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ None
* **[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.
* **[Boolean] Exclusive** _(Write)_: Specifies whether the specified value should be made to be the ONLY value in a Key (support for **delvals). The default value is $false.

#### Read-Only Properties from Get-TargetResource

Expand Down
74 changes: 74 additions & 0 deletions Tests/Integration/MSFT_RegistryResource.EndToEnd.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ try

# Force is specified as true for both of these configurations
$script:confgurationFilePathKeyAndNameOnly = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_RegistryResource_KeyAndNameOnly.config.ps1'
$script:confgurationFilePathKeyAndNameOnlyExclusive = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_RegistryResource_KeyAndNameOnly_Exclusive.config.ps1'
$script:confgurationFilePathWithDataAndType = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_RegistryResource_WithDataAndType.config.ps1'
}

Expand Down Expand Up @@ -316,6 +317,79 @@ try
}
}

Context 'Enforce Exclusivity using Flag Specifying ValueName' {
$configurationName = 'EnforceExclusivity'

New-ItemProperty -Path $script:testRegistryKeyPath -Name "Test" -Value 1
New-ItemProperty -Path $script:testRegistryKeyPath -Name "Test2" -Value 1

$registryParameters = @{
Key = $script:testRegistryKeyPath
Ensure = 'Present'
ValueName = 'Test_Exclusive'
Exclusive = $true
Force = $true
}

It 'Should compile and run configuration' {
{
. $script:confgurationFilePathKeyAndNameOnlyExclusive -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @registryParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}

It 'Should be able to call Get-DscConfiguration without throwing' {
{ Get-DscConfiguration -ErrorAction 'Stop' } | Should Not Throw
}

$registryKeyValues = Get-ItemProperty -Path $registryParameters.Key -Name Test1, Test2 -ErrorAction SilentlyContinue

It 'Should have removed all the registry key value names' {
$registryKeyValues | Should Be $null
}

It 'Should return true from Test-TargetResource with the same parameters' {
MSFT_RegistryResource\Test-TargetResource @registryParameters | Should Be $true
}
}

Context 'Enforce Exclusivity using Flag Specifying Empty ValueName' {
$configurationName = 'EnforceExclusivity'

New-ItemProperty -Path $script:testRegistryKeyPath -Name "Test" -Value 1
New-ItemProperty -Path $script:testRegistryKeyPath -Name "Test2" -Value 1

$registryParameters = @{
Key = $script:testRegistryKeyPath
Ensure = 'Present'
ValueName = ''
Exclusive = $true
}

It 'Should compile and run configuration' {
{
. $script:confgurationFilePathKeyAndNameOnlyExclusive -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @registryParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}

It 'Should be able to call Get-DscConfiguration without throwing' {
{ Get-DscConfiguration -ErrorAction 'Stop' } | Should Not Throw
}

$registryKeyValues = Get-ItemProperty -Path $registryParameters.Key -Name * -ErrorAction 'SilentlyContinue' | Where-Object {$_.Name -ne "Test_Exclusive"}

It 'Should have removed all the registry key value names' {
$registryKeyValues | 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'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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()]
[Boolean]
$Exclusive=$false,

[Parameter()]
[Boolean]
$Force=$true
)

Import-DscResource -ModuleName 'PSDscResources'

Node localhost
{
Registry Registry1
{
Key = $Key
Ensure = $Ensure
ValueName = $ValueName
Force = $Force
Exclusive = $Exclusive
}
}
}
Loading