win reg - Support special chars in path (#53305)

* win reg - Support special chars in path

* Added deprecation warning for path separators
This commit is contained in:
Jordan Borean
2019-03-08 05:41:52 +10:00
committed by GitHub
parent e4f2e15b96
commit 072fa54b50
12 changed files with 881 additions and 773 deletions

View File

@@ -15,122 +15,112 @@ $result = @{
changed = $false
}
Function Get-NetHiveName($hive) {
# Will also check that the hive passed in the path is a known hive
switch ($hive.ToUpper()) {
"HKCR" {"ClassesRoot"}
"HKCC" {"CurrentConfig"}
"HKCU" {"CurrentUser"}
"HKLM" {"LocalMachine"}
"HKU" {"Users"}
default {"unsupported"}
}
}
Function Get-PropertyValue {
param(
[Parameter(Mandatory=$true)][Microsoft.Win32.RegistryKey]$Key,
[String]$Name
)
Function Get-PropertyType($hive, $path, $property) {
$type = (Get-Item REGISTRY::$hive\$path).GetValueKind($property)
switch ($type) {
"Binary" {"REG_BINARY"}
"String" {"REG_SZ"}
"DWord" {"REG_DWORD"}
"QWord" {"REG_QWORD"}
"MultiString" {"REG_MULTI_SZ"}
"ExpandString" {"REG_EXPAND_SZ"}
"None" {"REG_NONE"}
default {"Unknown"}
$value = $Key.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::None)
if ($null -eq $value) {
# Property does not exist or the key's (Default) is not set
return $null
}
}
Function Get-PropertyObject($hive, $net_hive, $path, $property) {
$value = (Get-ItemProperty REGISTRY::$hive\$path).$property
$type = Get-PropertyType -hive $hive -path $path -property $property
If ($type -eq 'REG_EXPAND_SZ') {
$raw_value = [Microsoft.Win32.Registry]::$net_hive.OpenSubKey($path).GetValue($property, $false, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
} ElseIf ($type -eq 'REG_BINARY' -or $type -eq 'REG_NONE') {
$raw_value = @()
$raw_value = $Key.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
if ($Name -eq "") {
# The key's (Default) will fail on GetValueKind
$type = [Microsoft.Win32.RegistryValueKind]::String
} else {
$type = $Key.GetValueKind($Name)
}
if ($type -in @([Microsoft.Win32.RegistryValueKind]::Binary, [Microsoft.Win32.RegistryValueKind]::None)) {
$formatted_raw_value = [System.Collections.Generic.List`1[String]]@()
foreach ($byte in $value) {
$hex_value = ('{0:x}' -f $byte).PadLeft(2, '0')
$raw_value += "0x$hex_value"
$formatted_raw_value.Add("0x{0:x2}" -f $byte)
}
} Else {
$raw_value = $formatted_raw_value
} elseif ($type -eq [Microsoft.Win32.RegistryValueKind]::DWord) {
# .NET returns the value as a signed integer, we need to make it unsigned
$value = [UInt32]("0x{0:x}" -f $value)
$raw_value = $value
} elseif ($type -eq [Microsoft.Win32.RegistryValueKind]::QWord) {
$value = [UInt64]("0x{0:x}" -f $value)
$raw_value = $value
}
$object = @{
raw_value = $raw_value
$return_type = switch($type.ToString()) {
"Binary" { "REG_BINARY" }
"String" { "REG_SZ" }
"DWord" { "REG_DWORD" }
"QWord" { "REG_QWORD" }
"MultiString" { "REG_MULTI_SZ" }
"ExpandString" { "REG_EXPAND_SZ" }
"None" { "REG_NONE" }
default { "Unknown - $($type.ToString())" }
}
return @{
type = $return_type
value = $value
type = $type
}
$object
}
Function Test-RegistryProperty($hive, $path, $property) {
Try {
$type = (Get-Item REGISTRY::$hive\$path).GetValueKind($property)
} Catch {
$type = $null
}
If ($type -eq $null) {
$false
} Else {
$true
raw_value = $raw_value
}
}
# Will validate the key parameter to make sure it matches known format
if ($path -match "^([a-zA-Z_]*):\\(.*)$") {
$hive = $matches[1]
$reg_path = $matches[2]
} else {
Fail-Json $result "path does not match format 'HIVE:\KEY_PATH'"
if ($path -notmatch "^HK(CC|CR|CU|LM|U):\\") {
Fail-Json -obj $result -message "path: $path is not a valid registry path, see module documentation for examples."
}
# Used when getting the actual REG_EXPAND_SZ value as well as checking the hive is a known value
$net_hive = Get-NetHiveName -hive $hive
if ($net_hive -eq 'unsupported') {
Fail-Json $result "the hive in path is '$hive'; must be 'HKCR', 'HKCC', 'HKCU', 'HKLM' or 'HKU'"
$registry_path = (Split-Path -Path $path -NoQualifier).Substring(1) # removes the hive: and leading \
$registry_hive = switch(Split-Path -Path $path -Qualifier) {
"HKCR:" { [Microsoft.Win32.Registry]::ClassesRoot }
"HKCC:" { [Microsoft.Win32.Registry]::CurrentConfig }
"HKCU:" { [Microsoft.Win32.Registry]::CurrentUser }
"HKLM:" { [Microsoft.Win32.Registry]::LocalMachine }
"HKU" { [Microsoft.Win32.Registry]::Users }
}
if (Test-Path REGISTRY::$hive\$reg_path) {
if ($name -eq $null) {
$property_info = @{}
$properties = Get-ItemProperty REGISTRY::$hive\$reg_path
$key = $null
try {
$key = $registry_hive.OpenSubKey($registry_path, $false)
foreach ($property in $properties.PSObject.Properties) {
# Powershell adds in some metadata we need to filter out
$real_property = Test-RegistryProperty -hive $hive -path $reg_path -property $property.Name
if ($real_property -eq $true) {
$property_object = Get-PropertyObject -hive $hive -net_hive $net_hive -path $reg_path -property $property.Name
$property_info.Add($property.Name, $property_object)
if ($null -ne $key) {
if ($null -eq $name) {
$property_info = @{}
foreach ($property in $key.GetValueNames()) {
$property_info.$property = Get-PropertyValue -Key $key -Name $property
}
# Return the key's (Default) property if it has been defined
$default_value = Get-PropertyValue -Key $key -Name ""
if ($null -ne $default_value) {
$property_info."" = $default_value
}
$result.exists = $true
$result.properties = $property_info
$result.sub_keys = $key.GetSubKeyNames()
} else {
$property_value = Get-PropertyValue -Key $key -Name $name
if ($null -ne $property_value) {
$result.exists = $true
$result += $property_value
} else {
$result.exists = $false
}
}
$sub_keys = @()
$sub_keys_raw = Get-ChildItem REGISTRY::$hive\$reg_path -ErrorAction SilentlyContinue
foreach ($sub_key in $sub_keys_raw) {
$sub_keys += $sub_key.PSChildName
}
$result.exists = $true
$result.sub_keys = $sub_keys
$result.properties = $property_info
} else {
$exists = Test-RegistryProperty -hive $hive -path $reg_path -property $name
if ($exists -eq $true) {
$propertyObject = Get-PropertyObject -hive $hive -net_hive $net_hive -path $reg_path -property $name
$result.exists = $true
$result.raw_value = $propertyObject.raw_value
$result.value = $propertyObject.value
$result.type = $propertyObject.type
} else {
$result.exists = $false
}
$result.exists = $false
}
} else {
$result.exists = $false
} finally {
if ($key) {
$key.Dispose()
}
$registry_hive.Dispose()
}
Exit-Json $result
Exit-Json -obj $result

View File

@@ -29,8 +29,12 @@ options:
name:
description:
- The registry property name to get information for, the return json will not include the sub_keys and properties entries for the I(key) specified.
- Set to an empty string to target the registry key's C((Default)) property value.
type: str
aliases: [ entry, value, property ]
notes:
- The C(properties) return value will contain an empty string key C("") that refers to the key's C(Default) value. If
the value has not been set then this key is not returned.
seealso:
- module: win_regedit
- module: win_regmerge
@@ -49,6 +53,12 @@ EXAMPLES = r'''
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
name: CommonFilesDir
register: common_files_dir
- name: Obtain the registry key's (Default) property
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
name: ''
register: current_version_default
'''
RETURN = r'''
@@ -67,6 +77,11 @@ properties:
returned: success, path exists and property not specified
type: dict
sample: {
"" : {
"raw_value": "",
"type": "REG_SZ",
"value": ""
},
"binary_property" : {
"raw_value": ["0x01", "0x16"],
"type": "REG_BINARY",
@@ -77,7 +92,7 @@ properties:
"type": "REG_MULTI_SZ",
"value": ["a", "b"]
}
}
}
sub_keys:
description: A list of all the sub keys of the key specified.
returned: success, path exists and property not specified

View File

@@ -8,8 +8,6 @@
#Requires -Module Ansible.ModuleUtils.Legacy
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
$ErrorActionPreference = "Stop"
$params = Parse-Args -arguments $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
@@ -31,7 +29,8 @@ $result = @{
if ($diff_mode) {
$result.diff = @{
prepared = ""
before = ""
after = ""
}
}
@@ -40,12 +39,20 @@ using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ansible.RegEdit
namespace Ansible.WinRegedit
{
public enum HKEY : uint
internal class NativeMethods
{
LOCAL_MACHINE = 0x80000002,
USERS = 0x80000003
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegLoadKeyW(
UInt32 hKey,
string lpSubKey,
string lpFile);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegUnLoadKeyW(
UInt32 hKey,
string lpSubKey);
}
public class Win32Exception : System.ComponentModel.Win32Exception
@@ -60,41 +67,48 @@ namespace Ansible.RegEdit
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
}
public class Hive
public class Hive : IDisposable
{
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegLoadKey(
HKEY hKey,
string lpSubKey,
string lpFile);
private const UInt32 SCOPE = 0x80000002; // HKLM
private string hiveKey;
private bool loaded = false;
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegUnLoadKey(
HKEY hKey,
string lpSubKey);
public static void LoadHive(string lpSubKey, string lpFile)
public Hive(string hiveKey, string hivePath)
{
int ret;
ret = RegLoadKey(HKEY.LOCAL_MACHINE, lpSubKey, lpFile);
this.hiveKey = hiveKey;
int ret = NativeMethods.RegLoadKeyW(SCOPE, hiveKey, hivePath);
if (ret != 0)
throw new Win32Exception(ret, String.Format("Failed to load registry hive at {0}", lpFile));
throw new Win32Exception(ret, String.Format("Failed to load registry hive at {0}", hivePath));
loaded = true;
}
public static void UnloadHive(string lpSubKey)
public static void UnloadHive(string hiveKey)
{
GC.Collect();
int ret;
ret = RegUnLoadKey(HKEY.LOCAL_MACHINE, lpSubKey);
int ret = NativeMethods.RegUnLoadKeyW(SCOPE, hiveKey);
if (ret != 0)
throw new Win32Exception(ret, String.Format("Failed to unload registry hive at {0}", lpSubKey));
throw new Win32Exception(ret, String.Format("Failed to unload registry hive at {0}", hiveKey));
}
public void Dispose()
{
if (loaded)
{
// Make sure the garbage collector disposes all unused handles and waits until it is complete
GC.Collect();
GC.WaitForPendingFinalizers();
UnloadHive(hiveKey);
loaded = false;
}
GC.SuppressFinalize(this);
}
~Hive() { this.Dispose(); }
}
}
'@
# fire a warning if the property name isn't specified, the (Default) key ($null) can only be a string
if ($name -eq $null -and $type -ne "string") {
if ($null -eq $name -and $type -ne "string") {
Add-Warning -obj $result -message "the data type when name is not specified can only be 'string', the type has automatically been converted"
$type = "string"
}
@@ -104,16 +118,13 @@ if ($path -notmatch "^HK(CC|CR|CU|LM|U):\\") {
Fail-Json $result "path: $path is not a valid powershell path, see module documentation for examples."
}
# Create the required PSDrives if missing
$registry_hive = Split-Path -Path $path -Qualifier
if ($registry_hive -eq "HKCR:" -and (-not (Test-Path HKCR:\))) {
New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT
}
if ($registry_hive -eq "HKU:" -and (-not (Test-Path HKU:\))) {
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS
}
if ($registry_hive -eq "HKCC:" -and (-not (Test-Path HKCC:\))) {
New-PSDrive -Name HKCC -PSProvider Registry -Root HKEY_CURRENT_CONFIG
# Add a warning if the path does not contains a \ and is not the leaf path
$registry_path = (Split-Path -Path $path -NoQualifier).Substring(1) # removes the hive: and leading \
$registry_leaf = Split-Path -Path $path -Leaf
if ($registry_path -ne $registry_leaf -and -not $registry_path.Contains('\')) {
$msg = "path is not using '\' as a separator, support for '/' as a separator will be removed in a future Ansible version"
Add-DeprecationWarning -obj $result -message $msg -version 2.12
$registry_path = $registry_path.Replace('/', '\')
}
# Simplified version of Convert-HexStringToByteArray from
@@ -148,62 +159,202 @@ Function Convert-RegExportHexStringToByteArray($string) {
}
}
Function Test-RegistryProperty($path, $name) {
# will validate if the registry key contains the property, returns true
# if the property exists and false if the property does not
try {
$reg_key = Get-Item -Path $path
$value = $reg_key.GetValue($name)
# need to do it this way return ($value -eq $null) does not work
if ($value -eq $null) {
return $false
} else {
return $true
}
} catch [System.Management.Automation.ItemNotFoundException] {
# key didn't exist so the property mustn't
return $false
} finally {
if ($reg_key) {
$reg_key.Close()
}
}
}
Function Compare-RegistryProperties($existing, $new) {
$mismatch = $false
# Outputs $true if the property values don't match
if ($existing -is [Array]) {
if ((Compare-Object -ReferenceObject $existing -DifferenceObject $new -SyncWindow 0).Length -ne 0) {
$mismatch = $true
}
(Compare-Object -ReferenceObject $existing -DifferenceObject $new -SyncWindow 0).Length -ne 0
} else {
if ($existing -cne $new) {
$mismatch = $true
}
$existing -cne $new
}
return $mismatch
}
Function Get-DiffValueString($type, $value) {
Function Get-DiffValue {
param(
[Parameter(Mandatory=$true)][Microsoft.Win32.RegistryValueKind]$Type,
[Parameter(Mandatory=$true)][Object]$Value
)
$diff = @{ type = $Type.ToString(); value = $Value }
$enum = [Microsoft.Win32.RegistryValueKind]
if ($type -in @($enum::Binary, $enum::None)) {
$hex_values = @()
foreach ($dec_value in $value) {
$hex_values += "0x$("{0:x2}" -f $dec_value)"
if ($Type -in @($enum::Binary, $enum::None)) {
$diff.value = [System.Collections.Generic.List`1[String]]@()
foreach ($dec_value in $Value) {
$diff.value.Add("0x{0:x2}" -f $dec_value)
}
$diff_value = "$($type):[$($hex_values -join ", ")]"
} elseif ($type -eq $enum::DWord) {
$diff_value = "$($type):0x$("{0:x8}" -f $value)"
} elseif ($type -eq $enum::QWord) {
$diff_value = "$($type):0x$("{0:x16}" -f $value)"
} elseif ($type -eq $enum::MultiString) {
$diff_value = "$($type):[$($value -join ", ")]"
} else {
$diff_value = "$($type):$value"
} elseif ($Type -eq $enum::DWord) {
$diff.value = "0x{0:x8}" -f $Value
} elseif ($Type -eq $enum::QWord) {
$diff.value = "0x{0:x16}" -f $Value
}
return $diff_value
return $diff
}
Function Set-StateAbsent {
param(
# Used for diffs and exception messages to match up against Ansible input
[Parameter(Mandatory=$true)][String]$PrintPath,
[Parameter(Mandatory=$true)][Microsoft.Win32.RegistryKey]$Hive,
[Parameter(Mandatory=$true)][String]$Path,
[String]$Name,
[Switch]$DeleteKey
)
$key = $Hive.OpenSubKey($Path, $true)
if ($null -eq $key) {
# Key does not exist, no need to delete anything
return
}
try {
if ($DeleteKey -and -not $Name) {
# delete_key=yes is set and name is null/empty, so delete the entire key
$key.Dispose()
$key = $null
if (-not $check_mode) {
try {
$Hive.DeleteSubKeyTree($Path, $false)
} catch {
Fail-Json -obj $result -message "failed to delete registry key at $($PrintPath): $($_.Exception.Message)"
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.before = @{$PrintPath = @{}}
$result.diff.after = @{}
}
} else {
# delete_key=no or name is not null/empty, delete the property not the full key
$property = $key.GetValue($Name)
if ($null -eq $property) {
# property does not exist
return
}
$property_type = $key.GetValueKind($Name) # used for the diff
if (-not $check_mode) {
try {
$key.DeleteValue($Name)
} catch {
Fail-Json -obj $result -message "failed to delete registry property '$Name' at $($PrintPath): $($_.Exception.Message)"
}
}
$result.changed = $true
if ($diff_mode) {
$diff_value = Get-DiffValue -Type $property_type -Value $property
$result.diff.before = @{ $PrintPath = @{ $Name = $diff_value } }
$result.diff.after = @{ $PrintPath = @{} }
}
}
} finally {
if ($key) {
$key.Dispose()
}
}
}
Function Set-StatePresent {
param(
[Parameter(Mandatory=$true)][String]$PrintPath,
[Parameter(Mandatory=$true)][Microsoft.Win32.RegistryKey]$Hive,
[Parameter(Mandatory=$true)][String]$Path,
[String]$Name,
[Object]$Data,
[Microsoft.Win32.RegistryValueKind]$Type
)
$key = $Hive.OpenSubKey($Path, $true)
try {
if ($null -eq $key) {
# the key does not exist, create it so the next steps work
if (-not $check_mode) {
try {
$key = $Hive.CreateSubKey($Path)
} catch {
Fail-Json -obj $result -message "failed to create registry key at $($PrintPath): $($_.Exception.Message)"
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.before = @{}
$result.diff.after = @{$PrintPath = @{}}
}
} elseif ($diff_mode) {
# Make sure the diff is in an expected state for the key
$result.diff.before = @{$PrintPath = @{}}
$result.diff.after = @{$PrintPath = @{}}
}
if ($null -eq $key -or $null -eq $Data) {
# Check mode and key was created above, we cannot do any more work, or $Data is $null which happens when
# we create a new key but haven't explicitly set the data
return
}
$property = $key.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
if ($null -ne $property) {
# property exists, need to compare the values and type
$existing_type = $key.GetValueKind($name)
$change_value = $false
if ($Type -ne $existing_type) {
$change_value = $true
$result.data_type_changed = $true
$data_mismatch = Compare-RegistryProperties -existing $property -new $Data
if ($data_mismatch) {
$result.data_changed = $true
}
} else {
$data_mismatch = Compare-RegistryProperties -existing $property -new $Data
if ($data_mismatch) {
$change_value = $true
$result.data_changed = $true
}
}
if ($change_value) {
if (-not $check_mode) {
try {
$key.SetValue($Name, $Data, $Type)
} catch {
Fail-Json -obj $result -message "failed to change registry property '$Name' at $($PrintPath): $($_.Exception.Message)"
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.before.$PrintPath.$Name = Get-DiffValue -Type $existing_type -Value $property
$result.diff.after.$PrintPath.$Name = Get-DiffValue -Type $Type -Value $Data
}
} elseif ($diff_mode) {
$diff_value = Get-DiffValue -Type $existing_type -Value $property
$result.diff.before.$PrintPath.$Name = $diff_value
$result.diff.after.$PrintPath.$Name = $diff_value
}
} else {
# property doesn't exist just create a new one
if (-not $check_mode) {
try {
$key.SetValue($Name, $Data, $Type)
} catch {
Fail-Json -obj $result -message "failed to create registry property '$Name' at $($PrintPath): $($_.Exception.Message)"
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.after.$PrintPath.$Name = Get-DiffValue -Type $Type -Value $Data
}
}
} finally {
if ($key) {
$key.Dispose()
}
}
}
# convert property names "" to $null as "" refers to (Default)
@@ -213,7 +364,7 @@ if ($name -eq "") {
# convert the data to the required format
if ($type -in @("binary", "none")) {
if ($data -eq $null) {
if ($null -eq $data) {
$data = ""
}
@@ -230,7 +381,7 @@ if ($type -in @("binary", "none")) {
}
} elseif ($type -in @("dword", "qword")) {
# dword's and dword's don't allow null values, set to 0
if ($data -eq $null) {
if ($null -eq $data) {
$data = 0
}
@@ -260,14 +411,15 @@ if ($type -in @("binary", "none")) {
}
$data = [Int64]$data
}
} elseif ($type -in @("string", "expandstring")) {
} elseif ($type -in @("string", "expandstring") -and $name) {
# a null string or expandstring must be empty quotes
if ($data -eq $null) {
# Only do this if $name has been defined (not the default key)
if ($null -eq $data) {
$data = ""
}
} elseif ($type -eq "multistring") {
# convert the data for a multistring to a String[] array
if ($data -eq $null) {
if ($null -eq $data) {
$data = [String[]]@()
} elseif ($data -isnot [Array]) {
$new_data = New-Object -TypeName String[] -ArgumentList 1
@@ -285,209 +437,59 @@ if ($type -in @("binary", "none")) {
# convert the type string to the .NET class
$type = [System.Enum]::Parse([Microsoft.Win32.RegistryValueKind], $type, $true)
if ($hive) {
if (-not (Test-Path $hive)) {
Fail-Json -obj $result -message "hive at path '$hive' is not valid or accessible, cannot load hive"
}
$original_tmp = $env:TMP
$env:TMP = $_remote_tmp
Add-Type -TypeDefinition $registry_util
$env:TMP = $original_tmp
try {
Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to enable SeBackupPrivilege and SeRestorePrivilege for the current process: $($_.Exception.Message)"
}
if (Test-Path -Path HKLM:\ANSIBLE) {
Add-Warning -obj $result -message "hive already loaded at HKLM:\ANSIBLE, had to unload hive for win_regedit to continue"
try {
[Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE")
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
}
}
try {
[Ansible.RegEdit.Hive]::LoadHive("ANSIBLE", $hive)
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to load registry hive from '$hive' to HKLM:\ANSIBLE: $($_.Exception.Message)"
}
$registry_hive = switch(Split-Path -Path $path -Qualifier) {
"HKCR:" { [Microsoft.Win32.Registry]::ClassesRoot }
"HKCC:" { [Microsoft.Win32.Registry]::CurrentConfig }
"HKCU:" { [Microsoft.Win32.Registry]::CurrentUser }
"HKLM:" { [Microsoft.Win32.Registry]::LocalMachine }
"HKU" { [Microsoft.Win32.Registry]::Users }
}
$loaded_hive = $null
try {
if ($state -eq "present") {
if (-not (Test-Path -path $path)) {
# the key doesn't exist, create it so the next steps work
if ($hive) {
if (-not (Test-Path -LiteralPath $hive)) {
Fail-Json -obj $result -message "hive at path '$hive' is not valid or accessible, cannot load hive"
}
$original_tmp = $env:TMP
$env:TMP = $_remote_tmp
Add-Type -TypeDefinition $registry_util
$env:TMP = $original_tmp
try {
Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to enable SeBackupPrivilege and SeRestorePrivilege for the current process: $($_.Exception.Message)"
}
if (Test-Path -Path HKLM:\ANSIBLE) {
Add-Warning -obj $result -message "hive already loaded at HKLM:\ANSIBLE, had to unload hive for win_regedit to continue"
try {
$new_key = New-Item -Path $path -Type directory -Force -WhatIf:$check_mode
} catch {
Fail-Json $result "failed to create registry key at $($path): $($_.Exception.Message)"
} finally {
if ($new_key) {
$new_key.Close()
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.prepared += @"
+[$path]
"@
[Ansible.WinRegedit.Hive]::UnloadHive("ANSIBLE")
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
}
}
if (Test-RegistryProperty -path $path -name $name) {
# property exists, need to compare the values and type
$existing_key = Get-Item -Path $path
$existing_type = $existing_key.GetValueKind($name)
$existing_data = $existing_key.GetValue($name, $false, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$existing_key.Close()
$change_value = $false
if ($type -ne $existing_type) {
$change_value = $true
$result.data_type_changed = $true
$data_mismatch = Compare-RegistryProperties -existing $existing_data -new $data
if ($data_mismatch) {
$result.data_changed = $true
}
} else {
$data_mismatch = Compare-RegistryProperties -existing $existing_data -new $data
if ($data_mismatch) {
$change_value = $true
$result.data_changed = $true
}
}
if ($change_value) {
if (-not $check_mode) {
$reg_key = Get-Item -Path $path
try {
$sub_key = $reg_key.OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree)
try {
$sub_key.SetValue($name, $data, $type)
} finally {
$sub_key.Close()
}
} catch {
Fail-Json $result "failed to change registry property '$name' at $($path): $($_.Exception.Message)"
} finally {
$reg_key.Close()
}
}
$result.changed = $true
if ($diff_mode) {
if ($result.diff.prepared) {
$key_prefix = "+"
} else {
$key_prefix = ""
}
$result.diff.prepared = @"
$key_prefix[$path]
-"$name" = "$(Get-DiffValueString -type $existing_type -value $existing_data)"
+"$name" = "$(Get-DiffValueString -type $type -value $data)"
"@
}
}
} else {
# property doesn't exist just create a new one
if (-not $check_mode) {
$reg_key = Get-Item -Path $path
try {
$sub_key = $reg_key.OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree)
try {
$sub_key.SetValue($name, $data, $type)
} finally {
$sub_key.Close()
}
} catch {
Fail-Json $result "failed to change registry property '$name' at $($path): $($_.Exception.Message)"
} finally {
$reg_key.Close()
}
}
$result.changed = $true
if ($diff_mode) {
if ($result.diff.prepared) {
$key_prefix = "+"
} else {
$key_prefix = ""
}
$result.diff.prepared = @"
$key_prefix[$path]
+"$name" = "$(Get-DiffValueString -type $type -value $data)"
"@
}
try {
$loaded_hive = New-Object -TypeName Ansible.WinRegedit.Hive -ArgumentList "ANSIBLE", $hive
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to load registry hive from '$hive' to HKLM:\ANSIBLE: $($_.Exception.Message)"
}
}
if ($state -eq "present") {
Set-StatePresent -PrintPath $path -Hive $registry_hive -Path $registry_path -Name $name -Data $data -Type $type
} else {
if (Test-Path -path $path) {
if ($delete_key -and $name -eq $null) {
# the clear_key flag is set and name is null so delete the entire key
try {
$null = Remove-Item -Path $path -Force -Recurse -WhatIf:$check_mode
} catch {
Fail-Json $result "failed to delete registry key at $($path): $($_.Exception.Message)"
}
$result.changed = $true
if ($diff_mode) {
$result.diff.prepared += @"
-[$path]
"@
}
} else {
# the clear_key flag is set or name is not null, check whether we need to delete a property
if (Test-RegistryProperty -path $path -name $name) {
$existing_key = Get-Item -Path $path
$existing_type = $existing_key.GetValueKind($name)
$existing_data = $existing_key.GetValue($name, $false, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$existing_key.Close()
# cannot use Remove-ItemProperty as it fails when deleting the (Default) key ($name = $null)
if (-not $check_mode) {
$reg_key = Get-Item -Path $path
try {
$sub_key = $reg_key.OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree)
try {
$sub_key.DeleteValue($name)
} finally {
$sub_key.Close()
}
} catch {
Fail-Json $result "failed to delete registry property '$name' at $($path): $($_.Exception.Message)"
} finally {
$reg_key.Close()
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.prepared += @"
[$path]
-"$name" = "$(Get-DiffValueString -type $existing_type -value $existing_data)"
"@
}
}
}
}
Set-StateAbsent -PrintPath $path -Hive $registry_hive -Path $registry_path -Name $name -DeleteKey:$delete_key
}
} finally {
if ($hive) {
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
try {
[Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE")
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
}
$registry_hive.Dispose()
if ($loaded_hive) {
$loaded_hive.Dispose()
}
}
Exit-Json $result