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

* win reg - Support special chars in path

* Added deprecation warning for path separators
pull/53478/head
Jordan Borean 6 years ago committed by GitHub
parent e4f2e15b96
commit 072fa54b50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
bugfixes:
- win_regedit - Fix issue where creating a new key would set the ``(Default)`` key property to an empty string instead of undefined
- win_regedit - Support registry paths with special characters - https://github.com/ansible/ansible/issues/41791
- win_reg_stat - Support registry paths with special characters - https://github.com/ansible/ansible/issues/41791
- win_reg_stat - Fix issue where the key's ``(Default)`` property was not being returned if it was set

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

@ -29,8 +29,12 @@ options:
name: name:
description: 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. - 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 type: str
aliases: [ entry, value, property ] 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: seealso:
- module: win_regedit - module: win_regedit
- module: win_regmerge - module: win_regmerge
@ -49,6 +53,12 @@ EXAMPLES = r'''
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
name: CommonFilesDir name: CommonFilesDir
register: common_files_dir 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''' RETURN = r'''
@ -67,6 +77,11 @@ properties:
returned: success, path exists and property not specified returned: success, path exists and property not specified
type: dict type: dict
sample: { sample: {
"" : {
"raw_value": "",
"type": "REG_SZ",
"value": ""
},
"binary_property" : { "binary_property" : {
"raw_value": ["0x01", "0x16"], "raw_value": ["0x01", "0x16"],
"type": "REG_BINARY", "type": "REG_BINARY",
@ -77,7 +92,7 @@ properties:
"type": "REG_MULTI_SZ", "type": "REG_MULTI_SZ",
"value": ["a", "b"] "value": ["a", "b"]
} }
} }
sub_keys: sub_keys:
description: A list of all the sub keys of the key specified. description: A list of all the sub keys of the key specified.
returned: success, path exists and property not specified returned: success, path exists and property not specified

@ -8,8 +8,6 @@
#Requires -Module Ansible.ModuleUtils.Legacy #Requires -Module Ansible.ModuleUtils.Legacy
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil #Requires -Module Ansible.ModuleUtils.PrivilegeUtil
$ErrorActionPreference = "Stop"
$params = Parse-Args -arguments $args -supports_check_mode $true $params = Parse-Args -arguments $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $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 $diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
@ -31,7 +29,8 @@ $result = @{
if ($diff_mode) { if ($diff_mode) {
$result.diff = @{ $result.diff = @{
prepared = "" before = ""
after = ""
} }
} }
@ -40,12 +39,20 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ansible.RegEdit namespace Ansible.WinRegedit
{ {
public enum HKEY : uint internal class NativeMethods
{ {
LOCAL_MACHINE = 0x80000002, [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
USERS = 0x80000003 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 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 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 const UInt32 SCOPE = 0x80000002; // HKLM
private static extern int RegLoadKey( private string hiveKey;
HKEY hKey, private bool loaded = false;
string lpSubKey,
string lpFile);
[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; this.hiveKey = hiveKey;
ret = RegLoadKey(HKEY.LOCAL_MACHINE, lpSubKey, lpFile); int ret = NativeMethods.RegLoadKeyW(SCOPE, hiveKey, hivePath);
if (ret != 0) 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 = NativeMethods.RegUnLoadKeyW(SCOPE, hiveKey);
int ret;
ret = RegUnLoadKey(HKEY.LOCAL_MACHINE, lpSubKey);
if (ret != 0) 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 # 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" 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" $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." Fail-Json $result "path: $path is not a valid powershell path, see module documentation for examples."
} }
# Create the required PSDrives if missing # Add a warning if the path does not contains a \ and is not the leaf path
$registry_hive = Split-Path -Path $path -Qualifier $registry_path = (Split-Path -Path $path -NoQualifier).Substring(1) # removes the hive: and leading \
if ($registry_hive -eq "HKCR:" -and (-not (Test-Path HKCR:\))) { $registry_leaf = Split-Path -Path $path -Leaf
New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT 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"
if ($registry_hive -eq "HKU:" -and (-not (Test-Path HKU:\))) { Add-DeprecationWarning -obj $result -message $msg -version 2.12
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS $registry_path = $registry_path.Replace('/', '\')
}
if ($registry_hive -eq "HKCC:" -and (-not (Test-Path HKCC:\))) {
New-PSDrive -Name HKCC -PSProvider Registry -Root HKEY_CURRENT_CONFIG
} }
# Simplified version of Convert-HexStringToByteArray from # Simplified version of Convert-HexStringToByteArray from
@ -148,62 +159,202 @@ Function Convert-RegExportHexStringToByteArray($string) {
} }
} }
Function Test-RegistryProperty($path, $name) { Function Compare-RegistryProperties($existing, $new) {
# will validate if the registry key contains the property, returns true # Outputs $true if the property values don't match
# if the property exists and false if the property does not if ($existing -is [Array]) {
(Compare-Object -ReferenceObject $existing -DifferenceObject $new -SyncWindow 0).Length -ne 0
} else {
$existing -cne $new
}
}
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)) {
$diff.value = [System.Collections.Generic.List`1[String]]@()
foreach ($dec_value in $Value) {
$diff.value.Add("0x{0:x2}" -f $dec_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
}
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 { try {
$reg_key = Get-Item -Path $path if ($DeleteKey -and -not $Name) {
$value = $reg_key.GetValue($name) # delete_key=yes is set and name is null/empty, so delete the entire key
# need to do it this way return ($value -eq $null) does not work $key.Dispose()
if ($value -eq $null) { $key = $null
return $false 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 { } else {
return $true # 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 = @{} }
}
} }
} catch [System.Management.Automation.ItemNotFoundException] {
# key didn't exist so the property mustn't
return $false
} finally { } finally {
if ($reg_key) { if ($key) {
$reg_key.Close() $key.Dispose()
} }
} }
} }
Function Compare-RegistryProperties($existing, $new) { Function Set-StatePresent {
$mismatch = $false param(
if ($existing -is [Array]) { [Parameter(Mandatory=$true)][String]$PrintPath,
if ((Compare-Object -ReferenceObject $existing -DifferenceObject $new -SyncWindow 0).Length -ne 0) { [Parameter(Mandatory=$true)][Microsoft.Win32.RegistryKey]$Hive,
$mismatch = $true [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 = @{}}
} }
} else {
if ($existing -cne $new) { if ($null -eq $key -or $null -eq $Data) {
$mismatch = $true # 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
} }
}
return $mismatch $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
Function Get-DiffValueString($type, $value) { if ($Type -ne $existing_type) {
$enum = [Microsoft.Win32.RegistryValueKind] $change_value = $true
if ($type -in @($enum::Binary, $enum::None)) { $result.data_type_changed = $true
$hex_values = @() $data_mismatch = Compare-RegistryProperties -existing $property -new $Data
foreach ($dec_value in $value) { if ($data_mismatch) {
$hex_values += "0x$("{0:x2}" -f $dec_value)" $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()
} }
$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"
} }
return $diff_value
} }
# convert property names "" to $null as "" refers to (Default) # convert property names "" to $null as "" refers to (Default)
@ -213,7 +364,7 @@ if ($name -eq "") {
# convert the data to the required format # convert the data to the required format
if ($type -in @("binary", "none")) { if ($type -in @("binary", "none")) {
if ($data -eq $null) { if ($null -eq $data) {
$data = "" $data = ""
} }
@ -230,7 +381,7 @@ if ($type -in @("binary", "none")) {
} }
} elseif ($type -in @("dword", "qword")) { } elseif ($type -in @("dword", "qword")) {
# dword's and dword's don't allow null values, set to 0 # dword's and dword's don't allow null values, set to 0
if ($data -eq $null) { if ($null -eq $data) {
$data = 0 $data = 0
} }
@ -260,14 +411,15 @@ if ($type -in @("binary", "none")) {
} }
$data = [Int64]$data $data = [Int64]$data
} }
} elseif ($type -in @("string", "expandstring")) { } elseif ($type -in @("string", "expandstring") -and $name) {
# a null string or expandstring must be empty quotes # 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 = "" $data = ""
} }
} elseif ($type -eq "multistring") { } elseif ($type -eq "multistring") {
# convert the data for a multistring to a String[] array # convert the data for a multistring to a String[] array
if ($data -eq $null) { if ($null -eq $data) {
$data = [String[]]@() $data = [String[]]@()
} elseif ($data -isnot [Array]) { } elseif ($data -isnot [Array]) {
$new_data = New-Object -TypeName String[] -ArgumentList 1 $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 # convert the type string to the .NET class
$type = [System.Enum]::Parse([Microsoft.Win32.RegistryValueKind], $type, $true) $type = [System.Enum]::Parse([Microsoft.Win32.RegistryValueKind], $type, $true)
if ($hive) { $registry_hive = switch(Split-Path -Path $path -Qualifier) {
if (-not (Test-Path $hive)) { "HKCR:" { [Microsoft.Win32.Registry]::ClassesRoot }
Fail-Json -obj $result -message "hive at path '$hive' is not valid or accessible, cannot load hive" "HKCC:" { [Microsoft.Win32.Registry]::CurrentConfig }
} "HKCU:" { [Microsoft.Win32.Registry]::CurrentUser }
"HKLM:" { [Microsoft.Win32.Registry]::LocalMachine }
$original_tmp = $env:TMP "HKU" { [Microsoft.Win32.Registry]::Users }
$env:TMP = $_remote_tmp }
Add-Type -TypeDefinition $registry_util $loaded_hive = $null
$env:TMP = $original_tmp try {
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"
}
try { $original_tmp = $env:TMP
Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true $env:TMP = $_remote_tmp
Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true Add-Type -TypeDefinition $registry_util
} catch [System.ComponentModel.Win32Exception] { $env:TMP = $original_tmp
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 { try {
[Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE") Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true
} catch [System.ComponentModel.Win32Exception] { } catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)" Fail-Json -obj $result -message "failed to enable SeBackupPrivilege and SeRestorePrivilege for the current process: $($_.Exception.Message)"
} }
}
try { if (Test-Path -Path HKLM:\ANSIBLE) {
[Ansible.RegEdit.Hive]::LoadHive("ANSIBLE", $hive) Add-Warning -obj $result -message "hive already loaded at HKLM:\ANSIBLE, had to unload hive for win_regedit to continue"
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to load registry hive from '$hive' to HKLM:\ANSIBLE: $($_.Exception.Message)"
}
}
try {
if ($state -eq "present") {
if (-not (Test-Path -path $path)) {
# the key doesn't exist, create it so the next steps work
try { try {
$new_key = New-Item -Path $path -Type directory -Force -WhatIf:$check_mode [Ansible.WinRegedit.Hive]::UnloadHive("ANSIBLE")
} catch { } catch [System.ComponentModel.Win32Exception] {
Fail-Json $result "failed to create registry key at $($path): $($_.Exception.Message)" Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
} finally {
if ($new_key) {
$new_key.Close()
}
}
$result.changed = $true
if ($diff_mode) {
$result.diff.prepared += @"
+[$path]
"@
} }
} }
if (Test-RegistryProperty -path $path -name $name) { try {
# property exists, need to compare the values and type $loaded_hive = New-Object -TypeName Ansible.WinRegedit.Hive -ArgumentList "ANSIBLE", $hive
$existing_key = Get-Item -Path $path } catch [System.ComponentModel.Win32Exception] {
$existing_type = $existing_key.GetValueKind($name) Fail-Json -obj $result -message "failed to load registry hive from '$hive' to HKLM:\ANSIBLE: $($_.Exception.Message)"
$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)"
"@
}
} }
}
if ($state -eq "present") {
Set-StatePresent -PrintPath $path -Hive $registry_hive -Path $registry_path -Name $name -Data $data -Type $type
} else { } else {
if (Test-Path -path $path) { Set-StateAbsent -PrintPath $path -Hive $registry_hive -Path $registry_path -Name $name -DeleteKey:$delete_key
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)"
"@
}
}
}
}
} }
} finally { } finally {
if ($hive) { $registry_hive.Dispose()
[GC]::Collect() if ($loaded_hive) {
[GC]::WaitForPendingFinalizers() $loaded_hive.Dispose()
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)"
}
} }
} }
Exit-Json $result Exit-Json $result

@ -0,0 +1,2 @@
---
test_reg_path: Test Key / [&Ansible*]

@ -1,24 +0,0 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Test]
[HKEY_CURRENT_USER\Test\nested]
"string"="test"
"binary"=hex:01,16
"dword"=dword:00000001
"qword"=hex(b):01,00,00,00,00,00,00,00
"multi"=hex(7):61,00,2c,00,20,00,62,00,00,00,63,00,00,00,00,00
"expand"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,64,00,69,\
00,72,00,00,00
[HKEY_CURRENT_USER\Test\nested\nest1]
"dontcare"=""
[HKEY_CURRENT_USER\Test\nested\nest2]
[HKEY_CURRENT_USER\Test\single]
"string1"=""
"string2"="abc123"
"none"=hex(0):
"none1"=hex(0):00

@ -1,322 +1,29 @@
--- ---
- name: test
win_file:
path: "{{win_output_dir}}"
state: absent
- name: make sure win output dir exists
win_file:
path: "{{win_output_dir}}"
state: directory
- name: template out test registry structure
win_copy:
src: test_reg.reg
dest: "{{win_output_dir}}\\raw_test_reg.reg"
- name: convert the line endings to the windows variant
win_shell: Get-Content "{{win_output_dir}}\raw_test_reg.reg" | Set-Content "{{win_output_dir}}\test_reg.reg"
- name: import test registry structure
win_regmerge:
path: "{{win_output_dir}}\\test_reg.reg"
- name: get value of expand string %windir% - name: get value of expand string %windir%
win_command: powershell.exe $env:windir win_command: powershell.exe $env:windir
register: win_dir_value register: win_dir_value
- name: expect failure when not passing in path option - name: template out test registry structure
win_reg_stat: win_template:
name: a src: test_reg.reg.j2
register: actual dest: '{{ win_output_dir }}\test_reg.reg'
failed_when: "actual.msg != 'Get-AnsibleParam: Missing required argument: path'"
- name: expect failure when passing in an invalid hive
win_reg_stat:
path: ABCD:\test
register: actual
failed_when: actual.msg != "the hive in path is 'ABCD'; must be 'HKCR', 'HKCC', 'HKCU', 'HKLM' or 'HKU'"
- name: get known nested reg key structure for testing with short hive form
win_reg_stat:
path: HKCU:\Test\nested
register: actual_short
- name: get known nested reg key structure for testing with quoted yaml
win_reg_stat:
path: "HKCU:\\Test\\nested"
register: actual_quoted
- name: set expected value for reg structure
set_fact:
expected:
changed: false
exists: true
failed: false
properties:
binary: { raw_value: ["0x01", "0x16"], type: 'REG_BINARY', value: [1, 22] }
dword: { raw_value: 1, type: 'REG_DWORD', value: 1 }
expand: { raw_value: '%windir%\dir', type: 'REG_EXPAND_SZ', value: "{{win_dir_value.stdout_lines[0]}}\\dir" }
multi: { raw_value: ['a, b', 'c'], type: 'REG_MULTI_SZ', value: ['a, b', 'c'] }
qword: { raw_value: 1, type: 'REG_QWORD', value: 1 }
string: { raw_value: 'test', type: 'REG_SZ', value: 'test' }
sub_keys:
- nest1
- nest2
- name: validate test
assert:
that:
- "actual_short == expected"
- "actual_quoted == expected"
- name: get known reg key with no sub keys but some properties
win_reg_stat:
path: HKCU:\Test\single
register: actual
- name: set expected value for reg key with no sub keys but some properties
set_fact:
expected:
changed: false
exists: true
failed: false
properties:
none: { raw_value: [], type: 'REG_NONE', value: [] }
none1: { raw_value: ["0x00"], type: 'REG_NONE', value: [0] }
string1: { raw_value: '', type: 'REG_SZ', value: '' }
string2: { raw_value: 'abc123', type: 'REG_SZ', value: 'abc123' }
sub_keys: []
- name: validate test
assert:
that:
- "actual == expected"
- name: get known reg key without sub keys and properties
win_reg_stat:
path: HKCU:\Test\nested\nest2
register: actual
- name: set expected value for reg key without sub keys or properties
set_fact:
expected:
changed: false
exists: true
failed: false
properties: {}
sub_keys: []
register: expected
- name: validate test
assert:
that:
- "actual == expected"
- name: get non-existent reg key
win_reg_stat:
path: HKCU:\Test\Thispathwillneverexist
register: actual
- name: set expected value for non-existent reg key
set_fact:
expected:
changed: false
exists: false
failed: false
- name: validate test
assert:
that:
- "actual == expected"
- name: get string property
win_reg_stat:
path: HKCU:\Test\nested
name: string
register: actual
- name: set expected string property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: 'test'
type: 'REG_SZ'
value: 'test'
- name: validate test
assert:
that:
- "actual == expected"
- name: get expand string property
win_reg_stat:
path: HKCU:\Test\nested
name: expand
register: actual
- name: set expected expand string property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: '%windir%\dir'
type: 'REG_EXPAND_SZ'
value: "{{win_dir_value.stdout_lines[0]}}\\dir"
- name: validate test
assert:
that:
- "actual == expected"
- name: get multi string property
win_reg_stat:
path: HKCU:\Test\nested
name: multi
register: actual
- name: set expected multi string property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: ['a, b', 'c']
type: 'REG_MULTI_SZ'
value: ['a, b', 'c']
- name: validate test
assert:
that:
- "actual == expected"
- name: get binary property
win_reg_stat:
path: HKCU:\Test\nested
name: binary
register: actual
- name: set expected binary property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: ["0x01", "0x16"]
type: 'REG_BINARY'
value: [1, 22]
- name: validate test
assert:
that:
- "actual == expected"
- name: get dword property
win_reg_stat:
path: HKCU:\Test\nested
name: dword
register: actual
- name: set expected dword property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: 1
type: 'REG_DWORD'
value: 1
- name: validate test
assert:
that:
- "actual == expected"
- name: get qword property
win_reg_stat:
path: HKCU:\Test\nested
name: qword
register: actual
- name: set expected qword property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: 1
type: 'REG_QWORD'
value: 1
- name: validate test
assert:
that:
- "actual == expected"
- name: get none property
win_reg_stat:
path: HKCU:\Test\single
name: none
register: actual
- name: set expected none property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: []
type: 'REG_NONE'
value: []
- name: validate test
assert:
that:
- "actual == expected"
- name: get none with value property
win_reg_stat:
path: HKCU:\Test\single
name: none1
register: actual
- name: set expected none with value property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: ["0x00"]
type: 'REG_NONE'
value: [0]
- name: validate test
assert:
that:
- "actual == expected"
- name: get non-existence property
win_reg_stat:
path: HKCU:\Test\single
name: doesnotexist
register: actual
- name: set expected non-existence property
set_fact:
expected:
changed: false
exists: false
failed: false
- name: validate test
assert:
that:
- "actual == expected"
- name: remove registry entry - name: import test registry structure
win_regedit: win_regmerge:
path: HKCU:\Test path: '{{ win_output_dir }}\test_reg.reg'
state: absent
- block:
- name: run tests
import_tasks: tests.yml
always:
- name: remove test registry key
win_regedit:
path: HKCU:\{{ test_reg_path }}
state: absent
delete_key: True
- name: remove template registry file
win_file:
path: '{{ win_output_dir }}\test_reg.reg'
state: absent

@ -0,0 +1,364 @@
---
- name: expect failure when not passing in path option
win_reg_stat:
name: a
register: actual
failed_when: "actual.msg != 'Get-AnsibleParam: Missing required argument: path'"
- name: expect failure when passing in an invalid hive
win_reg_stat:
path: ABCD:\test
register: actual
failed_when: 'actual.msg != "path: ABCD:\\test is not a valid registry path, see module documentation for examples."'
- name: get known nested reg key structure
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
register: actual
- name: set expected value for reg structure
set_fact:
expected:
changed: false
exists: true
failed: false
properties:
binary: { raw_value: ["0x01", "0x16"], type: 'REG_BINARY', value: [1, 22] }
dword: { raw_value: 1, type: 'REG_DWORD', value: 1 }
expand: { raw_value: '%windir%\dir', type: 'REG_EXPAND_SZ', value: "{{win_dir_value.stdout_lines[0]}}\\dir" }
large_dword: { raw_value: 4294967295, type: 'REG_DWORD', value: 4294967295 }
large_qword: { raw_value: 18446744073709551615, type: 'REG_QWORD', value: 18446744073709551615 }
multi: { raw_value: ['a, b', 'c'], type: 'REG_MULTI_SZ', value: ['a, b', 'c'] }
qword: { raw_value: 1, type: 'REG_QWORD', value: 1 }
string: { raw_value: 'test', type: 'REG_SZ', value: 'test' }
sub_keys:
- nest1
- nest2
- name: assert get known nested reg key structure
assert:
that:
- actual == expected
- name: get known reg key with no sub keys but some properties
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\single
register: actual
- name: set expected value for reg key with no sub keys but some properties
set_fact:
expected:
changed: false
exists: true
failed: false
properties:
none: { raw_value: [], type: 'REG_NONE', value: [] }
none1: { raw_value: ["0x00"], type: 'REG_NONE', value: [0] }
string1: { raw_value: '', type: 'REG_SZ', value: '' }
string2: { raw_value: 'abc123', type: 'REG_SZ', value: 'abc123' }
sub_keys: []
- name: assert get known reg key with no sub keys but some properties
assert:
that:
- actual == expected
- name: get known reg key without sub keys and properties
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested\nest2
register: actual
- name: set expected value for reg key without sub keys or properties
set_fact:
expected:
changed: false
exists: true
failed: false
properties: {}
sub_keys: []
register: expected
- name: assert get known reg key without sub keys and properties
assert:
that:
- actual == expected
- name: get non-existent reg key
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Thispathwillneverexist
register: actual
- name: set expected value for non-existent reg key
set_fact:
expected:
changed: false
exists: false
failed: false
- name: assert get non-existent reg key
assert:
that:
- actual == expected
- name: get string property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
name: string
register: actual
- name: set expected string property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: 'test'
type: 'REG_SZ'
value: 'test'
- name: assert get string property
assert:
that:
- actual == expected
- name: get expand string property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
name: expand
register: actual
- name: set expected expand string property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: '%windir%\dir'
type: 'REG_EXPAND_SZ'
value: "{{win_dir_value.stdout_lines[0]}}\\dir"
- name: assert get expand string property
assert:
that:
- actual == expected
- name: get multi string property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
name: multi
register: actual
- name: set expected multi string property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: ['a, b', 'c']
type: 'REG_MULTI_SZ'
value: ['a, b', 'c']
- name: assert get multi string property
assert:
that:
- actual == expected
- name: get binary property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
name: binary
register: actual
- name: set expected binary property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: ["0x01", "0x16"]
type: 'REG_BINARY'
value: [1, 22]
- name: assert get binary property
assert:
that:
- actual == expected
- name: get dword property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
name: dword
register: actual
- name: set expected dword property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: 1
type: 'REG_DWORD'
value: 1
- name: assert get dword property
assert:
that:
- actual == expected
- name: get qword property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\nested
name: qword
register: actual
- name: set expected qword property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: 1
type: 'REG_QWORD'
value: 1
- name: assert get qword property
assert:
that:
- actual == expected
- name: get none property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\single
name: none
register: actual
- name: set expected none property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: []
type: 'REG_NONE'
value: []
- name: assert get none property
assert:
that:
- actual == expected
- name: get none with value property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\single
name: none1
register: actual
- name: set expected none with value property
set_fact:
expected:
changed: false
exists: true
failed: false
raw_value: ["0x00"]
type: 'REG_NONE'
value: [0]
- name: assert get non with value property
assert:
that:
- actual == expected
- name: get non-existent property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\single
name: doesnotexist
register: actual
- name: set expected non-existent property
set_fact:
expected:
changed: false
exists: false
failed: false
- name: assert get non-existent property
assert:
that:
- actual == expected
- name: get key with default property set
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Duplicate Default
register: actual
- name: assert get key with default property set
assert:
that:
- actual.properties[""]['raw_value'] == "default"
- actual.properties[""]['type'] == "REG_SZ"
- actual.properties[""]['value'] == "default"
- actual.properties['(Default)'].raw_value == "custom"
- actual.properties['(Default)'].type == "REG_SZ"
- actual.properties['(Default)'].value == "custom"
- name: get default property
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Duplicate Default
name: ''
register: actual
- name: assert get default property
assert:
that:
- actual.value == "default"
- actual.raw_value == "default"
- actual.type == "REG_SZ"
- name: get key with blank property set
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Blank Default
register: actual
- name: assert get key with blank property set
assert:
that:
- actual.properties[""].raw_value == ""
- actual.properties[""].type == "REG_SZ"
- actual.properties[""].value == ""
- actual.properties['(Default)'].raw_value == ""
- actual.properties['(Default)'].type == "REG_SZ"
- actual.properties['(Default)'].value == ""
- name: get default property as empty string
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Blank Default
name: ''
register: actual
- name: assert get default property as empty string
assert:
that:
- actual.value == ""
- actual.raw_value == ""
- actual.type == "REG_SZ"
- name: get key with no properties set
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Empty Default
register: actual
- name: assert get key with no properties set
assert:
that:
- actual.properties == {}
- name: get default property that has not been set
win_reg_stat:
path: HKCU:\{{ test_reg_path }}\Empty Default
name: ''
register: actual
- name: assert get default property that has not been set
assert:
that:
- not actual.exists

@ -0,0 +1,37 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\{{ test_reg_path }}]
[HKEY_CURRENT_USER\{{ test_reg_path }}\nested]
"string"="test"
"binary"=hex:01,16
"dword"=dword:00000001
"qword"=hex(b):01,00,00,00,00,00,00,00
"large_dword"=dword:ffffffff
"large_qword"=hex(b):ff,ff,ff,ff,ff,ff,ff,ff
"multi"=hex(7):61,00,2c,00,20,00,62,00,00,00,63,00,00,00,00,00
"expand"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,5c,00,64,00,69,\
00,72,00,00,00
[HKEY_CURRENT_USER\{{ test_reg_path }}\nested\nest1]
"dontcare"=""
[HKEY_CURRENT_USER\{{ test_reg_path }}\nested\nest2]
[HKEY_CURRENT_USER\{{ test_reg_path }}\single]
"string1"=""
"string2"="abc123"
"none"=hex(0):
"none1"=hex(0):00
[HKEY_CURRENT_USER\{{ test_reg_path }}\Empty Default]
[HKEY_CURRENT_USER\{{ test_reg_path }}\Blank Default]
@=""
"(Default)"=""
[HKEY_CURRENT_USER\{{ test_reg_path }}\Duplicate Default]
@="default"
"(Default)"="custom"

@ -1,3 +1,3 @@
test_win_regedit_local_key: HKLM:\Software\Cow Corp test_win_regedit_local_key: 'HKLM:\Software\Moo []{}!@#$%^&*()-_=+/key''"?<>'
test_win_regedit_classes_key: HKCR:\.test-ansible test_win_regedit_classes_key: HKCR:\.test-ansible
test_win_regedit_hive_key: HKLM:\ANSIBLE\NewKey test_win_regedit_hive_key: HKLM:\ANSIBLE\NewKey

@ -131,14 +131,6 @@
- data_create_actual.type == test_win_regedit_key_expected_type - data_create_actual.type == test_win_regedit_key_expected_type
when: test_win_regedit_key_type not in ['dword', 'qword'] when: test_win_regedit_key_type not in ['dword', 'qword']
- name: debug 1
debug:
var: test_win_regedit_key_expected_value1
- name: debug 2
debug:
var: test_win_regedit_key_expected_value1|int
- name: assert create a {{test_win_regedit_key_type}} for dword or qword - name: assert create a {{test_win_regedit_key_type}} for dword or qword
assert: assert:
that: that:

@ -1,4 +1,17 @@
--- ---
- name: check warning is fired if path with / as separators is used
win_regedit:
path: HKLM:\SOFTWARE/Microsoft
state: present
register: forward_separator_warn
- name: assert warning is fired if / is used as a separator
assert:
that:
- forward_separator_warn.deprecations|length == 1
- forward_separator_warn.deprecations[0].msg == "path is not using '\\' as a separator, support for '/' as a separator will be removed in a future Ansible version"
- forward_separator_warn.deprecations[0].version == 2.12
- name: fail run win_regedit with larger dword - name: fail run win_regedit with larger dword
win_regedit: win_regedit:
path: '{{test_win_regedit_local_key}}' path: '{{test_win_regedit_local_key}}'
@ -82,16 +95,17 @@
register: modify_default_check register: modify_default_check
check_mode: yes check_mode: yes
# win_reg_stat struggles with the (Default) property just use powershell
- name: get actual modify the (Default) key property check - name: get actual modify the (Default) key property check
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
path: '{{ test_win_regedit_local_key }}'
name: ''
register: modify_default_actual_check register: modify_default_actual_check
- name: assert modify the (Default) key property check - name: assert modify the (Default) key property check
assert: assert:
that: that:
- modify_default_check is changed - modify_default_check is changed
- modify_default_actual_check.stdout == "" - not modify_default_actual_check.exists
- name: modify the (Default) key property - name: modify the (Default) key property
win_regedit: win_regedit:
@ -101,14 +115,16 @@
register: modify_default register: modify_default
- name: get actual modify the (Default) key property - name: get actual modify the (Default) key property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
path: '{{ test_win_regedit_local_key }}'
name: ''
register: modify_default_actual register: modify_default_actual
- name: assert modify the (Default) key property - name: assert modify the (Default) key property
assert: assert:
that: that:
- modify_default is changed - modify_default is changed
- modify_default_actual.stdout == "default value\r\n" - modify_default_actual.value == "default value"
- name: create an actual property called (Default) - name: create an actual property called (Default)
win_regedit: win_regedit:
@ -120,20 +136,17 @@
register: create_specific_default_check register: create_specific_default_check
check_mode: yes check_mode: yes
- name: get actual value for (Default) property to ensure it didn't change check - name: get actual value for (Default) property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
register: create_specific_default_actual_default_check path: '{{ test_win_regedit_local_key }}'
- name: get actual for create specific property called (Default) check
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue('(Default)', $null)"
register: create_specific_default_actual_check register: create_specific_default_actual_check
- name: assert create specific property called (Default) check - name: assert create specific property called (Default) check
assert: assert:
that: that:
- create_specific_default_check is changed - create_specific_default_check is changed
- create_specific_default_actual_default_check.stdout == "default value\r\n" - create_specific_default_actual_check.properties[""].value == "default value"
- create_specific_default_actual_check.stdout == "" - not "(Default)" in create_specific_default_actual_check.properties
- name: create an actual property called (Default) - name: create an actual property called (Default)
win_regedit: win_regedit:
@ -144,20 +157,17 @@
state: present state: present
register: create_specific_default register: create_specific_default
- name: get actual value for (Default) property to ensure it didn't change - name: get actual value for (Default) property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
register: create_specific_default_actual_default path: '{{ test_win_regedit_local_key }}'
- name: get actual for specific property called (Default)
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue('(Default)', $null)"
register: create_specific_default_actual register: create_specific_default_actual
- name: assert create specific property called (Default) - name: assert create specific property called (Default)
assert: assert:
that: that:
- create_specific_default is changed - create_specific_default is changed
- create_specific_default_actual_default.stdout == "default value\r\n" - create_specific_default_actual.properties[""].value == "default value"
- create_specific_default_actual.stdout == "custom default value\r\n" - create_specific_default_actual.properties["(Default)"].value == "custom default value"
- name: delete property check - name: delete property check
win_regedit: win_regedit:
@ -219,19 +229,16 @@
check_mode: yes check_mode: yes
- name: get actual of key's (Default) property check - name: get actual of key's (Default) property check
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
path: '{{ test_win_regedit_local_key }}'
register: delete_default_actual_check register: delete_default_actual_check
- name: get actual of custom (Default) property check
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue('(Default)', $null)"
register: delete_default_custom_actual_check
- name: assert delete the key's (Default) property check - name: assert delete the key's (Default) property check
assert: assert:
that: that:
- delete_default_check is changed - delete_default_check is changed
- delete_default_actual_check.stdout == "default value\r\n" - delete_default_actual_check.properties[""].value == "default value"
- delete_default_custom_actual_check.stdout == "custom default value\r\n" - delete_default_actual_check.properties["(Default)"].value == "custom default value"
- name: delete the key's (Default) property - name: delete the key's (Default) property
win_regedit: win_regedit:
@ -241,19 +248,16 @@
register: delete_default register: delete_default
- name: get actual of key's (Default) property - name: get actual of key's (Default) property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
path: '{{ test_win_regedit_local_key }}'
register: delete_default_actual register: delete_default_actual
- name: get actual of custom (Default) property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue('(Default)', $null)"
register: delete_default_custom_actual
- name: assert delete the key's (Default) property - name: assert delete the key's (Default) property
assert: assert:
that: that:
- delete_default is changed - delete_default is changed
- delete_default_actual.stdout == "" - not "" in delete_default_actual.properties
- delete_default_custom_actual.stdout == "custom default value\r\n" - delete_default_actual.properties["(Default)"].value == "custom default value"
- name: recreate the key's (Default) property for next test - name: recreate the key's (Default) property for next test
win_regedit: win_regedit:
@ -270,19 +274,16 @@
check_mode: yes check_mode: yes
- name: get actual of key's (Default) property check - name: get actual of key's (Default) property check
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
path: '{{ test_win_regedit_local_key }}'
register: delete_custom_default_actual_check register: delete_custom_default_actual_check
- name: get actual of custom (Default) property check
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue('(Default)', $null)"
register: delete_custom_default_custom_actual_check
- name: assert delete the custom (Default) property check - name: assert delete the custom (Default) property check
assert: assert:
that: that:
- delete_custom_default_check is changed - delete_custom_default_check is changed
- delete_custom_default_actual_check.stdout == "default value\r\n" - delete_custom_default_actual_check.properties[""].value == "default value"
- delete_custom_default_custom_actual_check.stdout == "custom default value\r\n" - delete_custom_default_actual_check.properties["(Default)"].value == "custom default value"
- name: delete the custom (Default) property - name: delete the custom (Default) property
win_regedit: win_regedit:
@ -293,19 +294,16 @@
register: delete_custom_default register: delete_custom_default
- name: get actual of key's (Default) property - name: get actual of key's (Default) property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue($null, $null)" win_reg_stat:
path: '{{ test_win_regedit_local_key }}'
register: delete_custom_default_actual register: delete_custom_default_actual
- name: get actual of custom (Default) property
win_command: powershell.exe "(Get-Item -Path '{{test_win_regedit_local_key}}').GetValue('(Default)', $null)"
register: delete_custom_default_custom_actual
- name: assert delete the custom (Default) property - name: assert delete the custom (Default) property
assert: assert:
that: that:
- delete_custom_default is changed - delete_custom_default is changed
- delete_custom_default_actual.stdout == "default value\r\n" - delete_custom_default_actual.properties[""].value == "default value"
- delete_custom_default_custom_actual.stdout == "" - not "(Default)" in delete_custom_default_actual.properties
- name: add some nested keys for later deletion - name: add some nested keys for later deletion
win_regedit: win_regedit:
@ -366,6 +364,26 @@
that: that:
- delete_key_again is not changed - delete_key_again is not changed
- name: create a new key without specifying the property
win_regedit:
path: '{{ test_win_regedit_local_key }}\new'
state: present
register: create_key_no_prop
- name: get result of create a new key without specifying the property
win_reg_stat:
path: '{{ test_win_regedit_local_key }}\new'
register: create_key_no_prop_actual
- name: assert create a new key without specifying the property
assert:
that:
- create_key_no_prop is changed
- not create_key_no_prop.data_changed
- not create_key_no_prop.data_type_changed
- create_key_no_prop_actual.exists
- create_key_no_prop_actual.properties == {}
- name: create key in HKEY_CLASSES_ROOT check - name: create key in HKEY_CLASSES_ROOT check
win_regedit: win_regedit:
path: '{{test_win_regedit_classes_key}}' path: '{{test_win_regedit_classes_key}}'

Loading…
Cancel
Save