1 # This PS module contains functions for Desired State Configuration (DSC) Registry provider. It enables querying, creation, removal and update of Windows registry keys through Get, Set and Test operations on DSC managed nodes.
3 # Fallback message strings in en-US
7 ConvertFrom-StringData @'
8 ParameterValueInvalid = (ERROR) Parameter '{0}' has an invalid value '{1}' for type '{2}'
9 InvalidPSDriveSpecified = (ERROR) Invalid PSDrive '{0}' specified in registry key '{1}'
10 InvalidRegistryHiveSpecified = (ERROR) Invalid registry hive was specified in registry key '{0}'
11 SetRegValueFailed = (ERROR) Failed to set registry key value '{0}' to value '{1}' of type '{2}'
12 SetRegValueUnchanged = (UNCHANGED) No change to registry key value '{0}' containing '{1}'
13 SetRegKeyUnchanged = (UNCHANGED) No change to registry key '{0}'
14 SetRegValueSucceeded = (SET) Set registry key value '{0}' to '{1}' of type '{2}'
15 SetRegKeySucceeded = (SET) Create registry key '{0}'
16 SetRegKeyFailed = (ERROR) Failed to created registry key '{0}'
17 RemoveRegKeyTreeFailed = (ERROR) Registry Key '{0}' has subkeys, cannot remove without Force flag
18 RemoveRegKeySucceeded = (REMOVAL) Registry key '{0}' removed
19 RemoveRegKeyFailed = (ERROR) Failed to remove registry key '{0}'
20 RemoveRegValueSucceeded = (REMOVAL) Registry key value '{0}' removed
21 RemoveRegValueFailed = (ERROR) Failed to remove registry key value '{0}'
22 RegKeyDoesNotExist = Registry key '{0}' does not exist
23 RegKeyExists = Registry key '{0}' exists
24 RegValueExists = Found registry key value '{0}' with type '{1}' and data '{2}'
25 RegValueDoesNotExist = Registry key value '{0}' does not exist
26 RegValueTypeMismatch = Registry key value '{0}' of type '{1}' does not exist
27 RegValueDataMismatch = Registry key value '{0}' of type '{1}' does not contain data '{2}'
28 DefaultValueDisplayName = (Default)
31 Import-LocalizedData LocalizedData -filename MSFT_RegistryResource.strings.psd1
33 #--------------------------------------
34 # The Get-TargetResourceInternal cmdlet
35 #--------------------------------------
36 FUNCTION Get-TargetResourceInternal
40 [parameter(Mandatory = $true)]
41 [ValidateNotNullOrEmpty()]
45 # Default is [String]::Empty to cater for the (Default) RegValue
47 $ValueName = [String]::Empty
50 # Perform any required setup steps for the provider
51 SetupProvider -KeyName ([ref]$Key)
53 $ValueNameSpecified = $PSBoundParameters.ContainsKey("ValueName")
55 # First check if the specified key exists
56 $keyInfo = Get-Item -Path $Key -ErrorAction SilentlyContinue
58 # If $keyInfo is $null, the registry key doesn't exist
59 if ($keyInfo -eq $null)
61 Write-Verbose ($localizedData.RegKeyDoesNotExist -f $Key)
63 $retVal = @{Ensure='Absent'; Key=$Key}
68 # If the control reaches here, the key has been found at least
69 $retVal = @{Ensure='Present'; Key=$Key; Data=$keyInfo}
71 # If $ValueName parameter has not been specified then we simply report success on finding the $Key
72 if (!$ValueNameSpecified)
74 Write-Verbose ($localizedData.RegKeyExists -f $Key)
79 # If the control reaches here, the $ValueName has been specified as a parameter and we should query it now
80 $valData = $keyInfo.GetValue($ValueName, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
82 # If $ValueName is not found in the specified $Key
83 if($valData -eq $null)
85 Write-Verbose ($localizedData.RegValueDoesNotExist -f "$Key\$ValueName")
87 $retVal = @{Ensure='Absent'; Key=$Key; ValueName=(GetValueDisplayName -ValueName $ValueName)}
92 # Finalize name, type and data to be returned
93 $finalName = GetValueDisplayName -ValueName $ValueName
94 $finalType = $keyInfo.GetValueKind($ValueName)
97 # Special case: For Binary type data we convert the received bytes back to a readable hex-strin
98 if ($finalType -ieq "Binary")
100 $finalData = ConvertByteArrayToHexString -Data $valData
103 # Populate all config in the return object
104 $retVal.ValueName = $finalName
105 $retVal.ValueType = $finalType
106 $retVal.Data = $finalData
108 # If the control reaches here, both the $Key and the $ValueName have been found, query is fully successful
109 Write-Verbose ($localizedData.RegValueExists -f "$Key\$ValueName", $retVal.ValueType, (ArrayToString $retVal.Data))
114 #------------------------------
115 # The Get-TargetResource cmdlet
116 #------------------------------
117 FUNCTION Get-TargetResource
121 [Parameter(Mandatory)]
122 [ValidateNotNullOrEmpty()]
126 [Parameter(Mandatory)]
132 # Special-case: Used only as a boolean flag (along with ValueType) to determine if the target entity is the Default Value or the key itself.
136 # Special-case: Used only as a boolean flag (along with ValueData) to determine if the target entity is the Default Value or the key itself.
141 # If $ValueName is "" and ValueType and ValueData are both not specified, then we target the key itself (not Default Value)
142 if ($ValueName -eq "" -and !$PSBoundParameters.ContainsKey("ValueType") -and !$PSBoundParameters.ContainsKey("ValueData"))
144 $retVal = Get-TargetResourceInternal -Key $Key
148 $retVal = Get-TargetResourceInternal -Key $Key -ValueName $ValueName
150 if ($retVal.Ensure -eq 'Present')
152 [string[]]$retVal.ValueData += $retVal.Data
154 if ($retVal.ValueType -ieq "MultiString")
156 $retVal.ValueData = $retVal.Data
161 $retVal.Remove("Data")
167 #------------------------------
168 # The Set-TargetResource cmdlet
169 #------------------------------
170 FUNCTION Set-TargetResource
172 [CmdletBinding(SupportsShouldProcess=$true)]
175 [Parameter(Mandatory)]
176 [ValidateNotNullOrEmpty()]
180 [Parameter(Mandatory)]
186 [ValidateSet("Present", "Absent")]
194 [ValidateSet("String", "Binary", "DWord", "QWord", "MultiString", "ExpandString")]
196 $ValueType = "String",
205 # Perform any required setup steps for the provider
206 SetupProvider -KeyName ([ref]$Key)
208 # Query if the RegVal related parameters have been specified
209 $ValueNameSpecified = $PSBoundParameters.ContainsKey("ValueName")
210 $ValueTypeSpecified = $PSBoundParameters.ContainsKey("ValueType")
211 $ValueDataSpecified = $PSBoundParameters.ContainsKey("ValueData")
214 # If an empty string ValueName has been specified and no ValueType and no ValueData has been specified,
215 # treat this case as if ValueName was not specified and target the Key itself. This is to cater the limitation
216 # that both Key and ValueName are mandatory now and we must special-case like this to target the Key only.
217 if ($ValueName -eq "" -and !$ValueTypeSpecified -and !$ValueDataSpecified)
219 $ValueNameSpecified = $false
222 # Now, query the specified key
223 $keyInfo = Get-TargetResourceInternal -Key $Key -Verbose:$false
227 if ($Ensure -ieq "Present")
229 # If key doesn't exist, attempt to create it
230 if ($keyInfo.Ensure -ieq "Absent")
232 if ($PSCmdlet.ShouldProcess(($localizedData.SetRegKeySucceeded -f "$Key"), $null, $null))
236 $keyInfo = CreateRegistryKey -Key $Key
241 Write-Verbose ($localizedData.SetRegKeyFailed -f "$Key")
248 # If $ValueName, $ValueType and $ValueData are not specified, the simple existence/creation of the Regkey satisfies the Ensure=Present condition, just return
249 if (!$ValueNameSpecified -and !$ValueDataSpecified -and !$ValueTypeSpecified)
253 Write-Log ($localizedData.SetRegKeyUnchanged -f "$Key")
259 # If $ValueType and $ValueData are both not specified, but $ValueName is specified, check if the Value exists, if yes return with status unchanged, otherwise report input error
260 if (!$ValueTypeSpecified -and !$ValueDataSpecified -and $ValueNameSpecified)
262 $valData = $keyInfo.Data.GetValue($ValueName)
264 if ($valData -ne $null)
266 Write-Log ($localizedData.SetRegValueUnchanged -f "$Key\$ValueName", (ArrayToString -Value $valData))
272 # Create a strongly-typed object (in accordance with the specified $ValueType)
274 GetTypedObject -Type $ValueType -Data $ValueData -Hex $Hex -ReturnValue ([ref]$setVal)
276 # Get the appropriate display name for the specified ValueName (to handle the Default RegValue case)
277 $valDisplayName = GetValueDisplayName -ValueName $ValueName
279 if ($PSCmdlet.ShouldProcess(($localizedData.SetRegValueSucceeded -f "$Key\$valDisplayName", (ArrayToString -Value $setVal), $ValueType), $null, $null))
283 # Finally set the $ValueName here
284 [Microsoft.Win32.Registry]::SetValue($keyInfo.Data.Name, $ValueName, $setVal, $ValueType)
288 Write-Verbose ($localizedData.SetRegValueFailed -f "$Key\$valDisplayName", (ArrayToString -Value $setVal), $ValueType)
297 elseif ($Ensure -ieq "Absent")
299 # If key doesn't exist, no action is required
300 if ($keyInfo.Ensure -ieq "Absent")
302 Write-Log ($localizedData.RegKeyDoesNotExist -f "$Key")
307 # If the code reaches here, the key exists
309 # If ValueName is "" and ValueType and ValueData have not been specified, target the key for removal
310 if(!$ValueNameSpecified -and !$ValueTypeSpecified -and !$ValueDataSpecified)
312 # If this is not a Force removal and the Key contains subkeys, report no change and return
313 if (!$Force -and ($keyInfo.Data.SubKeyCount -gt 0))
315 $errorMessage = $localizedData.RemoveRegKeyTreeFailed -f "$Key"
317 Write-Log $errorMessage
319 ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $errorMessage -ExceptionObject $Force -ErrorId "CannotRemoveKeyTreeWithoutForceFlag" -ErrorCategory NotSpecified
322 # If the control reaches here, either the $Force flag was specified or the Regkey has no subkeys. In either case we simply remove it.
324 if ($PSCmdlet.ShouldProcess(($localizedData.RemoveRegKeySucceeded -f $Key), $null, $null))
328 # Formulate hiveName and subkeyName compatible with .NET APIs
329 $hiveName = $keyInfo.Data.PSDrive.Root.Replace("_","").Replace("HKEY","")
330 $subkeyName = $keyInfo.Data.Name.Substring($keyInfo.Data.Name.IndexOf("\")+1)
332 # Finally remove the subkeytree
333 [Microsoft.Win32.Registry]::$hiveName.DeleteSubKeyTree($subkeyName)
337 Write-Verbose ($localizedData.RemoveRegKeyFailed -f "$Key")
346 # If the control reaches here, ValueName has been specified so a RegValue needs be removed (if found)
348 # Get the appropriate display name for the specified ValueName (to handle the Default RegValue case)
349 $valDisplayName = GetValueDisplayName -ValueName $ValueName
351 # Query the specified $ValueName
352 $valData = $keyInfo.Data.GetValue($ValueName)
354 # If $ValueName is not found in the specified $Key
355 if($valData -eq $null)
357 Write-Log ($localizedData.RegValueDoesNotExist -f "$Key\$valDisplayName")
362 # If the control reaches here, the specified Value has been found and should be removed.
364 if ($PSCmdlet.ShouldProcess(($localizedData.RemoveRegValueSucceeded -f "$Key\$valDisplayName"), $null, $null))
368 # Formulate hiveName and subkeyName compatible with .NET APIs
369 $hiveName = $keyInfo.Data.PSDrive.Root.Replace("_","").Replace("HKEY","")
370 $subkeyName = $keyInfo.Data.Name.Substring($keyInfo.Data.Name.IndexOf("\")+1)
372 # Finally open the subkey and remove the RegValue in subkey
373 $subkey = [Microsoft.Win32.Registry]::$hiveName.OpenSubKey($subkeyName, $true)
374 $subkey.DeleteValue($ValueName)
379 Write-Verbose ($localizedData.RemoveRegValueFailed -f "$Key\$valDisplayName")
388 #-------------------------------
389 # The Test-TargetResource cmdlet
390 #-------------------------------
391 FUNCTION Test-TargetResource
395 [parameter(Mandatory)]
396 [ValidateNotNullOrEmpty()]
400 [parameter(Mandatory)]
406 [ValidateSet("Present", "Absent")]
414 [ValidateSet("String", "Binary", "DWord", "QWord", "MultiString", "ExpandString")]
416 $ValueType = "String",
421 # Force is not used in Test-TargetResource but is required by DSC engine to keep parameter-sets in parity for both SET and TEST
426 # Perform any required setup steps for the provider
427 SetupProvider -KeyName ([ref]$Key)
429 # Query if the RegVal related parameters have been specified
430 $ValueNameSpecified = $PSBoundParameters.ContainsKey("ValueName")
431 $ValueTypeSpecified = $PSBoundParameters.ContainsKey("ValueType")
432 $ValueDataSpecified = $PSBoundParameters.ContainsKey("ValueData")
434 # If an empty string ValueName has been specified and no ValueType and no ValueData has been specified,
435 # treat this case as if ValueName was not specified and target the Key itself. This is to cater the limitation
436 # that both Key and ValueName are mandatory now and we must special-case like this to target the Key only.
437 if (($ValueName -eq "") -and !$ValueTypeSpecified -and !$ValueDataSpecified)
439 $ValueNameSpecified = $false
442 # Now, query the specified key
443 $keyInfo = Get-TargetResourceInternal -Key $Key -Verbose:$false
447 if ($Ensure -ieq "Present")
449 # If key doesn't exist, the test fails
450 if ($keyInfo.Ensure -ieq "Absent")
452 Write-Verbose ($localizedData.RegKeyDoesNotExist -f $Key)
457 # If $ValueName, $ValueType and $ValueData are not specified, the simple existence of the Regkey satisfies the Ensure=Present condition, test is successful
458 if (!$ValueNameSpecified -and !$ValueDataSpecified -and !$ValueTypeSpecified)
460 Write-Verbose ($localizedData.RegKeyExists -f $Key)
465 # IF THE CONTROL REACHED HERE, THE KEY EXISTS AND A REGVALUE ATTRIBUTE HAS BEEN SPECIFIED
467 # Get the appropriate display name for the specified ValueName (to handle the Default RegValue case)
468 $valDisplayName = GetValueDisplayName -ValueName $ValueName
470 # Now query the specified Reg Value
471 $valData = Get-TargetResourceInternal -Key $Key -ValueName $ValueName -Verbose:$false
473 # If the Value doesn't exist, the test has failed
474 if ($valData.Ensure -ieq "Absent")
476 Write-Verbose ($localizedData.RegValueDoesNotExist -f "$Key\$valDisplayName")
481 # IF THE CONTROL REACHED HERE, THE KEY EXISTS AND THE SPECIFIED (or Default) VALUE EXISTS
483 # If the $ValueType has been specified and it doesn't match the type of the found RegValue, test fails
484 if ($ValueTypeSpecified -and ($ValueType -ine $valData.ValueType))
486 Write-Verbose ($localizedData.RegValueTypeMismatch -f "$Key\$valDisplayName", $ValueType)
491 # If an explicit ValueType has not been specified, given the Value already exists in Registry, assume the ValueType to be of the existing Value
492 if (!$ValueTypeSpecified)
494 $ValueType = $valData.ValueType
497 # If $ValueData has been specified, match the data of the found Regvalue.
498 if ($ValueDataSpecified -and !(ValueDataMatches -RetrievedValue $valData -ValueType $ValueType -ValueData $ValueData))
500 # Since the $ValueData specified didn't match the data of the found RegValue, test failed
501 Write-Verbose ($localizedData.RegValueDataMismatch -f "$Key\$valDisplayName", $ValueType, (ArrayToString -Value $ValueData))
506 # IF THE CONTROL REACHED HERE, ALL TESTS HAVE PASSED FOR THE SPECIFIED REGISTRY VALUE AND IT COMPLETELY MATCHES, REPORT SUCCESS
508 Write-Verbose ($localizedData.RegValueExists -f "$Key\$valDisplayName", $valData.ValueType, (ArrayToString -Value $valData.Data))
515 elseif ($Ensure -ieq "Absent")
517 # If key doesn't exist, test is successful
518 if ($keyInfo.Ensure -ieq "Absent")
520 Write-Log ($localizedData.RegKeyDoesNotExist -f "$Key")
525 # IF CONTROL REACHED HERE, THE SPECIFIED KEY EXISTS
527 # If $ValueName, $ValueType and $ValueData are not specified, the simple existence of the Regkey fails the test
528 if (!$ValueNameSpecified -and !$ValueDataSpecified -and !$ValueTypeSpecified)
530 Write-Verbose ($localizedData.RegKeyExists -f $Key)
535 # IF THE CONTROL REACHED HERE, THE KEY EXISTS AND A REGVALUE ATTRIBUTE HAS BEEN SPECIFIED
537 # Get the appropriate display name for the specified ValueName (to handle the Default RegValue case)
538 $valDisplayName = GetValueDisplayName -ValueName $ValueName
540 # Now query the specified RegValue
541 $valData = Get-TargetResourceInternal -Key $Key -ValueName $ValueName -Verbose:$false
543 # If the Value doesn't exist, the test has passed
544 if ($valData.Ensure -ieq "Absent")
546 Write-Verbose ($localizedData.RegValueDoesNotExist -f "$Key\$valDisplayName")
551 # IF THE CONTROL REACHED HERE, THE KEY EXISTS AND THE SPECIFIED (or Default) VALUE EXISTS, THUS REPORT FAILURE
553 Write-Verbose ($localizedData.RegValueExists -f "$Key\$valDisplayName", $valData.ValueType, (ArrayToString -Value $valData.Data))
560 #--------------------------------------------
561 # Utility to create an arbitrary registry key
562 #--------------------------------------------
563 FUNCTION CreateRegistryKey
567 [parameter(Mandatory = $true)]
568 [ValidateNotNullOrEmpty()]
573 # Trim any "\" back-slash(es) at the end of the specified RegKey
574 $Key = ([string]$Key).TrimEnd('\')
576 # Extract the parent-key
577 $slashIndex = $Key.LastIndexOf('\')
578 $parentKey = $Key.Substring(0, $slashIndex)
580 # Check if the parent-key exists, if not first create that (recurse).
581 if ((Get-TargetResourceInternal -Key $parentKey -Verbose:$false).Ensure -eq "Absent")
583 CreateRegistryKey -Key $parentKey | Out-Null
587 $retVal = New-Item -Path $Key 2>&1
590 if ($retVal -and $retVal.GetType().Name -ieq "ErrorRecord")
595 # If the control reaches here, the key was created successfully
596 return (Get-TargetResourceInternal -Key $Key -Verbose:$false)
600 #-------------------------------------------
601 # Validate PSDrive specified in Registry Key
602 #-------------------------------------------
603 FUNCTION ValidatePSDrive
607 [parameter(Mandatory = $true)]
608 [ValidateNotNullOrEmpty()]
613 # Extract the PSDriveName from the specified Key
614 $psDriveName = $Key.Substring(0, $Key.IndexOf(':'))
616 # Query the specified PSDrive
617 $psDrive = Get-PSDrive $psDriveName -ErrorAction SilentlyContinue
619 # Validate that the specified psdrive is a valid
620 if (($psDrive -eq $null) -or ($psDrive.Provider -eq $null) -or ($psDrive.Provider.Name -ine "Registry") -or !(IsValidRegistryRoot -PSDriveRoot $psDrive.Root))
622 $errorMessage = $localizedData.InvalidPSDriveSpecified -f $psDriveName, $Key
623 ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $errorMessage -ExceptionObject $Key -ErrorId "InvalidPSDrive" -ErrorCategory InvalidArgument
628 #--------------------------------------------------
629 # Check if the PSDriveRoot is a valid registry root
630 #--------------------------------------------------
631 FUNCTION IsValidRegistryRoot
639 # List of valid registry roots
640 $validRegistryRoots = @("HKEY_CLASSES_ROOT", "HKEY_CURRENT_USER", "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CURRENT_CONFIG")
642 # Extract the base of the PSDrive root
643 if ($PSDriveRoot.Contains('\'))
645 $PSDriveRoot = $PSDriveRoot.Substring(0, $PSDriveRoot.IndexOf('\'))
648 return ($validRegistryRoots -icontains $PSDriveRoot)
652 #----------------------------------------
653 # Utility to write WhatIf or Verbose logs
654 #----------------------------------------
657 [CmdletBinding(SupportsShouldProcess=$true)]
660 [parameter(Mandatory = $true)]
661 [ValidateNotNullOrEmpty()]
666 if ($PSCmdlet.ShouldProcess($Message, $null, $null))
668 Write-Verbose $Message
673 #------------------------------------
674 # Utility to throw an error/exception
675 #------------------------------------
681 [parameter(Mandatory = $true)]
682 [ValidateNotNullOrEmpty()]
686 [parameter(Mandatory = $true)]
687 [ValidateNotNullOrEmpty()]
694 [parameter(Mandatory = $true)]
695 [ValidateNotNullOrEmpty()]
699 [parameter(Mandatory = $true)]
701 [System.Management.Automation.ErrorCategory]
705 $exception = New-Object $ExceptionName $ExceptionMessage;
706 $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject
711 #----------------------------------------------------------------------
712 # Utility to construct a strongly-typed object based on specified $Type
713 #----------------------------------------------------------------------
714 FUNCTION GetTypedObject
718 [parameter(Mandatory = $true)]
719 [ValidateNotNullOrEmpty()]
733 $ArgumentExceptionScriptBlock =
737 $errorMessage = $localizedData.ParameterValueInvalid -f "ValueData", (ArrayToString -Value $Data), $Type
738 Write-Verbose $errorMessage
739 ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $errorMessage -ExceptionObject $Data -ErrorId $ErrorId -ErrorCategory InvalidArgument
742 # The the $Type specified is not a multistring then we always expect a non-array $Data. If this is not the case, throw an error and let the user know.
743 if (($Type -ine "Multistring") -and ($Data -ne $null) -and ($Data.Count -gt 1))
745 Invoke-Command -ScriptBlock $ArgumentExceptionScriptBlock -ArgumentList ([String]::Format("ArrayNotExpectedForType{0}", $Type))
753 if (($Data -eq $null) -or ($Data.Length -eq 0))
755 $ReturnValue.Value = [String]::Empty
760 $ReturnValue.Value = [String]$Data[0]
766 if (($Data -eq $null) -or ($Data.Length -eq 0))
768 $ReturnValue.Value = [String]::Empty
773 $ReturnValue.Value = [String]$Data[0]
779 if (($Data -eq $null) -or ($Data.Length -eq 0))
781 $ReturnValue.Value = [String[]]@()
786 $ReturnValue.Value = [String[]]$Data
792 if (($Data -eq $null) -or ($Data.Length -eq 0))
794 $ReturnValue.Value = [Int32]0
799 $val = $Data[0].TrimStart("0x")
801 if ([Int32]::TryParse($val, "HexNumber", [System.Globalization.CultureInfo]::CurrentCulture, [ref] $retVal))
803 $ReturnValue.Value = $retVal
807 Invoke-Command -ScriptBlock $ArgumentExceptionScriptBlock -ArgumentList "ValueDataNotInHexFormat"
812 $ReturnValue.Value = [Int32]::Parse($Data[0])
819 if (($Data -eq $null) -or ($Data.Length -eq 0))
821 $ReturnValue.Value = [Int64]0
826 $val = $Data[0].TrimStart("0x")
828 if ([Int64]::TryParse($val, "HexNumber", [System.Globalization.CultureInfo]::CurrentCulture, [ref] $retVal))
830 $ReturnValue.Value = $retVal
834 Invoke-Command -ScriptBlock $ArgumentExceptionScriptBlock -ArgumentList "ValueDataNotInHexFormat"
839 $ReturnValue.Value = [Int64]::Parse($Data[0])
846 if (($Data -eq $null) -or ($Data.Length -eq 0))
848 $ReturnValue.Value = [Byte[]]@()
854 $val = $Data[0].TrimStart("0x")
855 if ($val.Length % 2 -ne 0)
857 $val = $val.PadLeft($val.Length+1, "0")
862 $byteArray = [Byte[]]@()
864 for ($i = 0 ; $i -lt ($val.Length-1) ; $i = $i+2)
866 $byteArray += [Byte]::Parse($val.Substring($i, 2), "HexNumber")
869 $ReturnValue.Value = [Byte[]]$byteArray
873 Invoke-Command -ScriptBlock $ArgumentExceptionScriptBlock -ArgumentList "ValueDataNotInHexFormat"
880 #-------------------------------------------------------
881 # Utility to convert an array to a string representation
882 #-------------------------------------------------------
883 FUNCTION ArrayToString
887 [parameter(Mandatory = $true)]
888 [AllowEmptyCollection()]
894 if (!$Value.GetType().IsArray)
896 return $Value.ToString()
898 if ($Value.Length -eq 1)
900 return $Value[0].ToString()
903 [System.Text.StringBuilder]$retString = "("
905 $Value | % {$retString = ($retString.ToString() + $_.ToString() + ", ")}
907 $retString = $retString.ToString().TrimEnd(", ") + ")"
909 return $retString.ToString()
913 #-------------------------------------------------------
914 # Utility to convert an array to a string representation
915 #-------------------------------------------------------
916 FUNCTION ConvertByteArrayToHexString
920 [parameter(Mandatory = $true)]
927 $Data | % {$retString += [String]::Format("{0:x2}", $_)}
933 #--------------------------------------------------------------
934 # Utility to handle the display name for the (Default) RegValue
935 #--------------------------------------------------------------
936 FUNCTION GetValueDisplayName
944 if ([String]::IsNullOrEmpty($ValueName))
946 return $localizedData.DefaultValueDisplayName
953 #---------------------------------------------------------
954 # Utility to mount the optional Registry hives as PSDrives
955 #---------------------------------------------------------
956 FUNCTION MountRequiredRegistryHives
960 [parameter(Mandatory = $true)]
961 [ValidateNotNullOrEmpty()]
966 $psDriveNames = (Get-PSDrive).Name.ToUpperInvariant()
968 if ($KeyName.StartsWith("HKCR","OrdinalIgnoreCase") -and !$psDriveNames.Contains("HKCR"))
970 New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -Scope "Script" -WhatIf:$false | Out-Null
972 elseif ($KeyName.StartsWith("HKUS","OrdinalIgnoreCase") -and !$psDriveNames.Contains("HKUS"))
974 New-PSDrive -Name HKUS -PSProvider Registry -Root HKEY_USERS -Scope "Script" -WhatIf:$false | Out-Null
976 elseif ($KeyName.StartsWith("HKCC","OrdinalIgnoreCase") -and !$psDriveNames.Contains("HKCC"))
978 New-PSDrive -Name HKCC -PSProvider Registry -Root HKEY_CURRENT_CONFIG -Scope "Script" -WhatIf:$false | Out-Null
980 elseif ($KeyName.StartsWith("HKCU","OrdinalIgnoreCase") -and !$psDriveNames.Contains("HKCU"))
982 New-PSDrive -Name HKCU -PSProvider Registry -Root HKEY_CURRENT_USER -Scope "Script" -WhatIf:$false | Out-Null
984 elseif ($KeyName.StartsWith("HKLM","OrdinalIgnoreCase") -and !$psDriveNames.Contains("HKLM"))
986 New-PSDrive -Name HKLM -PSProvider Registry -Root HKEY_LOCAL_MACHINE -Scope "Script" -WhatIf:$false | Out-Null
991 #---------------------------------------------------------
992 # Utility to mount the optional Registry hives as PSDrives
993 #---------------------------------------------------------
994 FUNCTION SetupProvider
1002 # Fix $KeyName if required
1003 if (!$KeyName.Value.ToString().Contains(":"))
1005 if ($KeyName.Value.ToString().StartsWith("hkey_users","OrdinalIgnoreCase"))
1007 $KeyName.Value = $KeyName.Value.ToString() -replace "hkey_users", "HKUS:"
1009 elseif ($KeyName.Value.ToString().StartsWith("hkey_current_config","OrdinalIgnoreCase"))
1011 $KeyName.Value = $KeyName.Value.ToString() -replace "hkey_current_config", "HKCC:"
1013 elseif ($KeyName.Value.ToString().StartsWith("hkey_classes_root","OrdinalIgnoreCase"))
1015 $KeyName.Value = $KeyName.Value.ToString() -replace "hkey_classes_root", "HKCR:"
1017 elseif ($KeyName.Value.ToString().StartsWith("hkey_local_machine","OrdinalIgnoreCase"))
1019 $KeyName.Value = $KeyName.Value.ToString() -replace "hkey_local_machine", "HKLM:"
1021 elseif ($KeyName.Value.ToString().StartsWith("hkey_current_user","OrdinalIgnoreCase"))
1023 $KeyName.Value = $KeyName.Value.ToString() -replace "hkey_current_user", "HKCU:"
1027 $errorMessage = $localizedData.InvalidRegistryHiveSpecified -f $Key
1028 ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $errorMessage -ExceptionObject $KeyName -ErrorId "InvalidRegistryHive" -ErrorCategory InvalidArgument
1032 # Mount any required registry hives
1033 MountRequiredRegistryHives -KeyName $KeyName.Value.ToString()
1035 # Check the target PSDrive to be a valid Registry Hive root
1036 ValidatePSDrive -Key $KeyName.Value.ToString()
1039 #----------------------------------------------------------------------------------------
1040 # Refactored utility to decide if the ValueData specified matches the ValueData retrieved
1041 #----------------------------------------------------------------------------------------
1042 FUNCTION ValueDataMatches
1046 [parameter(Mandatory = $true)]
1051 [parameter(Mandatory = $true)]
1052 [ValidateNotNullOrEmpty()]
1060 # Convert the specified $ValueData into strongly-typed data for correct comparsion
1061 $specifiedData = $null
1062 $retrievedData = $RetrievedValue.Data
1064 GetTypedObject -Type $ValueType -Data $ValueData -Hex $Hex -ReturnValue ([ref]$specifiedData)
1066 # Special case for binary comparison (do hex-string comparison)
1067 if ($ValueType -ieq "Binary")
1069 $specifiedData = $ValueData[0].PadLeft($retrievedData.Length, '0')
1072 # If the ValueType is not multistring, do a simple comparison
1073 if ($ValueType -ine "Multistring")
1075 return ($specifiedData -ieq $retrievedData)
1078 # IF THE CONTROL REACHES HERE, THE ValueType IS A "MultiString" and we need a size-based and element-by-element comparsion for it
1080 # Array-size comparison
1081 if ($specifiedData.Length -ne $retrievedData.Length)
1087 # Element-by-Element comparison
1088 for ($i = 0 ; $i -lt $specifiedData.Length ; $i++)
1090 if ($specifiedData[$i] -ine $retrievedData[$i])
1096 # IF THE CONTROL REACHED HERE, THE Multistring COMPARISON WAS SUCCESSFUL
1100 Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource