mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
win_regedit: added function to load a dat file for editing (#31289)
* win_regedit: added function to load a dat file for editing * try to make the tests more resiliant * more stability changes
This commit is contained in:
parent
6c3339402a
commit
743ff4897a
6 changed files with 597 additions and 129 deletions
|
@ -1,24 +1,11 @@
|
||||||
#!powershell
|
#!powershell
|
||||||
# This file is part of Ansible
|
# This file is part of Ansible
|
||||||
#
|
|
||||||
# (c) 2015, Adam Keech <akeech@chathamfinancial.com>, Josh Ludwig <jludwig@chathamfinancial.com>
|
# (c) 2015, Adam Keech <akeech@chathamfinancial.com>, Josh Ludwig <jludwig@chathamfinancial.com>
|
||||||
# (c) 2017, Jordan Borean <jborean93@gmail.com>
|
# (c) 2017, Jordan Borean <jborean93@gmail.com>
|
||||||
#
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
# Ansible is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Ansible is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# WANT_JSON
|
#Requires -Module Ansible.ModuleUtils.Legacy.psm1
|
||||||
# POWERSHELL_COMMON
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
@ -32,6 +19,7 @@ $data = Get-AnsibleParam -obj $params -name "data"
|
||||||
$type = Get-AnsibleParam -obj $params -name "type" -type "str" -default "string" -validateset "none","binary","dword","expandstring","multistring","string","qword" -aliases "datatype"
|
$type = Get-AnsibleParam -obj $params -name "type" -type "str" -default "string" -validateset "none","binary","dword","expandstring","multistring","string","qword" -aliases "datatype"
|
||||||
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"
|
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"
|
||||||
$delete_key = Get-AnsibleParam -obj $params -name "delete_key" -type "bool" -default $true
|
$delete_key = Get-AnsibleParam -obj $params -name "delete_key" -type "bool" -default $true
|
||||||
|
$hive = Get-AnsibleParam -obj $params -name "hive" -type "path"
|
||||||
|
|
||||||
$result = @{
|
$result = @{
|
||||||
changed = $false
|
changed = $false
|
||||||
|
@ -45,6 +33,147 @@ if ($diff_mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$registry_util = @'
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ansible
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct LUID
|
||||||
|
{
|
||||||
|
public UInt32 LowPart;
|
||||||
|
public Int32 HighPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct TOKEN_PRIVILEGES
|
||||||
|
{
|
||||||
|
public UInt32 PrivilegeCount;
|
||||||
|
public LUID Luid;
|
||||||
|
public UInt32 Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HKEY : uint
|
||||||
|
{
|
||||||
|
LOCAL_MACHINE = 0x80000002,
|
||||||
|
USERS = 0x80000003
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Win32Exception : System.ComponentModel.Win32Exception
|
||||||
|
{
|
||||||
|
private string _msg;
|
||||||
|
public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
|
||||||
|
public Win32Exception(int errorCode, string message) : base(errorCode)
|
||||||
|
{
|
||||||
|
_msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
|
||||||
|
}
|
||||||
|
public override string Message { get { return _msg; } }
|
||||||
|
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegistryUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
|
||||||
|
public const int TOKEN_QUERY = 0x00000008;
|
||||||
|
public const int SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern IntPtr GetCurrentProcess();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern bool CloseHandle(
|
||||||
|
IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern bool OpenProcessToken(
|
||||||
|
IntPtr ProcessHandle,
|
||||||
|
UInt32 DesiredAccess,
|
||||||
|
out IntPtr TokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern bool LookupPrivilegeValue(
|
||||||
|
string lpSystemName,
|
||||||
|
string lpName,
|
||||||
|
[MarshalAs(UnmanagedType.Struct)] out LUID lpLuid);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern bool AdjustTokenPrivileges(
|
||||||
|
IntPtr TokenHandle,
|
||||||
|
[MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
|
||||||
|
ref TOKEN_PRIVILEGES NewState,
|
||||||
|
UInt32 BufferLength,
|
||||||
|
IntPtr PreviousState,
|
||||||
|
IntPtr ReturnLength);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
private static extern int RegLoadKey(
|
||||||
|
HKEY hKey,
|
||||||
|
string lpSubKey,
|
||||||
|
string lpFile);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
private static extern int RegUnLoadKey(
|
||||||
|
HKEY hKey,
|
||||||
|
string lpSubKey);
|
||||||
|
|
||||||
|
public static void EnablePrivileges()
|
||||||
|
{
|
||||||
|
List<String> privileges = new List<String>()
|
||||||
|
{
|
||||||
|
"SeRestorePrivilege",
|
||||||
|
"SeBackupPrivilege"
|
||||||
|
};
|
||||||
|
foreach (string privilege in privileges)
|
||||||
|
{
|
||||||
|
IntPtr hToken;
|
||||||
|
LUID luid;
|
||||||
|
TOKEN_PRIVILEGES tkpPrivileges;
|
||||||
|
|
||||||
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken))
|
||||||
|
throw new Win32Exception("OpenProcessToken() failed");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!LookupPrivilegeValue(null, privilege, out luid))
|
||||||
|
throw new Win32Exception("LookupPrivilegeValue() failed");
|
||||||
|
|
||||||
|
tkpPrivileges.PrivilegeCount = 1;
|
||||||
|
tkpPrivileges.Luid = luid;
|
||||||
|
tkpPrivileges.Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
|
|
||||||
|
if (!AdjustTokenPrivileges(hToken, false, ref tkpPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
|
||||||
|
throw new Win32Exception(String.Format("AdjustTokenPrivileges() failed to adjust privilege {0}", privilege));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseHandle(hToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadHive(string lpSubKey, string lpFile)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
ret = RegLoadKey(HKEY.LOCAL_MACHINE, lpSubKey, lpFile);
|
||||||
|
if (ret != 0)
|
||||||
|
throw new Win32Exception(ret, String.Format("Failed to load registry hive at {0}", lpFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UnloadHive(string lpSubKey)
|
||||||
|
{
|
||||||
|
GC.Collect();
|
||||||
|
int ret;
|
||||||
|
ret = RegUnLoadKey(HKEY.LOCAL_MACHINE, lpSubKey);
|
||||||
|
if (ret != 0)
|
||||||
|
throw new Win32Exception(ret, String.Format("Failed to unload registry hive at {0}", lpSubKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
# Fix HCCC:\ PSDrive for pre-2.3 compatibility
|
# Fix HCCC:\ PSDrive for pre-2.3 compatibility
|
||||||
if ($path -match "^HCCC:\\") {
|
if ($path -match "^HCCC:\\") {
|
||||||
Add-DeprecationWarning -obj $result -message "Please use path: HKCC:\... instead of path: $path" -version 2.6
|
Add-DeprecationWarning -obj $result -message "Please use path: HKCC:\... instead of path: $path" -version 2.6
|
||||||
|
@ -110,7 +239,8 @@ Function Test-RegistryProperty($path, $name) {
|
||||||
# will validate if the registry key contains the property, returns true
|
# will validate if the registry key contains the property, returns true
|
||||||
# if the property exists and false if the property does not
|
# if the property exists and false if the property does not
|
||||||
try {
|
try {
|
||||||
$value = (Get-Item -Path $path).GetValue($name)
|
$reg_key = Get-Item -Path $path
|
||||||
|
$value = $reg_key.GetValue($name)
|
||||||
# need to do it this way return ($value -eq $null) does not work
|
# need to do it this way return ($value -eq $null) does not work
|
||||||
if ($value -eq $null) {
|
if ($value -eq $null) {
|
||||||
return $false
|
return $false
|
||||||
|
@ -120,6 +250,10 @@ Function Test-RegistryProperty($path, $name) {
|
||||||
} catch [System.Management.Automation.ItemNotFoundException] {
|
} catch [System.Management.Automation.ItemNotFoundException] {
|
||||||
# key didn't exist so the property mustn't
|
# key didn't exist so the property mustn't
|
||||||
return $false
|
return $false
|
||||||
|
} finally {
|
||||||
|
if ($reg_key) {
|
||||||
|
$reg_key.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,59 +369,121 @@ 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 ($state -eq "present") {
|
if ($hive) {
|
||||||
if (-not (Test-Path -path $path)) {
|
if (-not (Test-Path $hive)) {
|
||||||
# the key doesn't exist, create it so the next steps work
|
Fail-Json -obj $result -message "hive at path '$hive' is not valid or accessible, cannot load hive"
|
||||||
try {
|
}
|
||||||
$null = New-Item -Path $path -Type directory -Force -WhatIf:$check_mode
|
Add-Type -TypeDefinition $registry_util
|
||||||
} catch {
|
try {
|
||||||
Fail-Json $result "failed to create registry key at $($path): $($_.Exception.Message)"
|
[Ansible.RegistryUtil]::EnablePrivileges()
|
||||||
}
|
} catch [System.ComponentModel.Win32Exception] {
|
||||||
$result.changed = $true
|
Fail-Json -obj $result -message "failed to enable SeRestorePrivilege and SeRestorePrivilege for the current process: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
if ($diff_mode) {
|
if (Test-Path -Path HKLM:\ANSIBLE) {
|
||||||
$result.diff.prepared += @"
|
Add-Warning -obj $result -message "hive already loaded at HKLM:\ANSIBLE, had to unload hive for win_regedit to continue"
|
||||||
+[$path]
|
try {
|
||||||
"@
|
[Ansible.RegistryUtil]::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) {
|
try {
|
||||||
# property exists, need to compare the values and type
|
[Ansible.RegistryUtil]::LoadHive("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)
|
}
|
||||||
$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) {
|
try {
|
||||||
if (-not $check_mode) {
|
if ($state -eq "present") {
|
||||||
try {
|
if (-not (Test-Path -path $path)) {
|
||||||
(Get-Item -Path $path).OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree).SetValue($name, $data, $type)
|
# the key doesn't exist, create it so the next steps work
|
||||||
} catch {
|
try {
|
||||||
Fail-Json $result "failed to change registry property '$name' at $($path): $($_.Exception.Message)"
|
$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]
|
||||||
|
"@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
$reg_key.OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree).SetValue($name, $data, $type)
|
||||||
|
} 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 {
|
||||||
|
$reg_key.OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree).SetValue($name, $data, $type)
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to change registry property '$name' at $($path): $($_.Exception.Message)"
|
||||||
|
} finally {
|
||||||
|
$reg_key.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$result.changed = $true
|
$result.changed = $true
|
||||||
|
|
||||||
if ($diff_mode) {
|
if ($diff_mode) {
|
||||||
if ($result.diff.prepared) {
|
if ($result.diff.prepared) {
|
||||||
$key_prefix = "+"
|
$key_prefix = "+"
|
||||||
|
@ -297,76 +493,66 @@ if ($state -eq "present") {
|
||||||
|
|
||||||
$result.diff.prepared = @"
|
$result.diff.prepared = @"
|
||||||
$key_prefix[$path]
|
$key_prefix[$path]
|
||||||
-"$name" = "$(Get-DiffValueString -type $existing_type -value $existing_data)"
|
|
||||||
+"$name" = "$(Get-DiffValueString -type $type -value $data)"
|
+"$name" = "$(Get-DiffValueString -type $type -value $data)"
|
||||||
"@
|
"@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
# property doesn't exist just create a new one
|
if (Test-Path -path $path) {
|
||||||
if (-not $check_mode) {
|
if ($delete_key -and $name -eq $null) {
|
||||||
try {
|
# the clear_key flag is set and name is null so delete the entire key
|
||||||
(Get-Item -Path $path).OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree).SetValue($name, $data, $type)
|
try {
|
||||||
} catch {
|
$null = Remove-Item -Path $path -Force -Recurse -WhatIf:$check_mode
|
||||||
Fail-Json $result "failed to change registry property '$name' at $($path): $($_.Exception.Message)"
|
} catch {
|
||||||
}
|
Fail-Json $result "failed to delete registry key at $($path): $($_.Exception.Message)"
|
||||||
}
|
|
||||||
$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)"
|
|
||||||
"@
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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)
|
|
||||||
|
|
||||||
# cannot use Remove-ItemProperty as it fails when deleting the (Default) key ($name = $null)
|
|
||||||
if (-not $check_mode) {
|
|
||||||
try {
|
|
||||||
(Get-Item -Path $path).OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree).DeleteValue($name)
|
|
||||||
} catch {
|
|
||||||
Fail-Json $result "failed to delete registry property '$name' at $($path): $($_.Exception.Message)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$result.changed = $true
|
$result.changed = $true
|
||||||
|
|
||||||
if ($diff_mode) {
|
if ($diff_mode) {
|
||||||
$result.diff.prepared += @"
|
$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 {
|
||||||
|
$reg_key.OpenSubKey($null, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree).DeleteValue($name)
|
||||||
|
} 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]
|
[$path]
|
||||||
-"$name" = "$(Get-DiffValueString -type $existing_type -value $existing_data)"
|
-"$name" = "$(Get-DiffValueString -type $existing_type -value $existing_data)"
|
||||||
"@
|
"@
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
if ($hive) {
|
||||||
|
[GC]::Collect()
|
||||||
|
try {
|
||||||
|
[Ansible.RegistryUtil]::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
|
||||||
|
|
|
@ -83,10 +83,21 @@ options:
|
||||||
type: bool
|
type: bool
|
||||||
default: 'yes'
|
default: 'yes'
|
||||||
version_added: '2.4'
|
version_added: '2.4'
|
||||||
|
hive:
|
||||||
|
description:
|
||||||
|
- A path to a hive key like C:\Users\Default\NTUSER.DAT to load in the
|
||||||
|
registry.
|
||||||
|
- This hive is loaded under the HKLM:\ANSIBLE key which can then be used
|
||||||
|
in I(name) like any other path.
|
||||||
|
- This can be used to load the default user profile registry hive or any
|
||||||
|
other hive saved as a file.
|
||||||
|
- Using this function requires the user to have the C(SeRestorePrivilege)
|
||||||
|
and C(SeBackupPrivilege) privileges enabled.
|
||||||
|
version_added: '2.5'
|
||||||
notes:
|
notes:
|
||||||
- Check-mode C(-C/--check) and diff output C(-D/--diff) are supported, so that you can test every change against the active configuration before
|
- Check-mode C(-C/--check) and diff output C(-D/--diff) are supported, so that you can test every change against the active configuration before
|
||||||
applying changes.
|
applying changes.
|
||||||
- Beware that some registry hives (C(HKEY_USERS) in particular) do not allow to create new registry paths.
|
- Beware that some registry hives (C(HKEY_USERS) in particular) do not allow to create new registry paths in the root folder.
|
||||||
- Since ansible 2.4, when checking if a string registry value has changed, a case-sensitive test is used. Previously the test was case-insensitive.
|
- Since ansible 2.4, when checking if a string registry value has changed, a case-sensitive test is used. Previously the test was case-insensitive.
|
||||||
author:
|
author:
|
||||||
- Adam Keech (@smadam813)
|
- Adam Keech (@smadam813)
|
||||||
|
@ -178,6 +189,15 @@ EXAMPLES = r'''
|
||||||
path: HKCU:\Software\MyCompany
|
path: HKCU:\Software\MyCompany
|
||||||
name: hello
|
name: hello
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
|
- name: Change default mouse trailing settings for new users
|
||||||
|
win_regedit:
|
||||||
|
path: HKLM:\ANSIBLE\Control Panel\Mouse
|
||||||
|
name: MouseTrails
|
||||||
|
data: 10
|
||||||
|
type: string
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = r'''
|
RETURN = r'''
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
test_win_regedit_local_key: HKLM:\Software\Cow Corp
|
test_win_regedit_local_key: HKLM:\Software\Cow Corp
|
||||||
test_win_regedit_classes_key: HKCR:\.test-ansible
|
test_win_regedit_classes_key: HKCR:\.test-ansible
|
||||||
|
test_win_regedit_hive_key: HKLM:\ANSIBLE\NewKey
|
||||||
|
|
18
test/integration/targets/win_regedit/tasks/cleanup.yml
Normal file
18
test/integration/targets/win_regedit/tasks/cleanup.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
- name: load HKLM:\ANSIBLE with test hive
|
||||||
|
win_command: reg.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: make sure testing keys are removed before test
|
||||||
|
win_regedit:
|
||||||
|
path: '{{item}}'
|
||||||
|
delete_key: yes
|
||||||
|
state: absent
|
||||||
|
with_items:
|
||||||
|
- '{{test_win_regedit_local_key}}'
|
||||||
|
- '{{test_win_regedit_classes_key}}'
|
||||||
|
- '{{test_win_regedit_hive_key}}'
|
||||||
|
|
||||||
|
- name: ensure HKLM:\ANSIBLE is unloaded
|
||||||
|
win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
failed_when: false
|
|
@ -1,12 +1,6 @@
|
||||||
---
|
---
|
||||||
- name: make sure testing keys are removed before test
|
- name: make sure we start on a blank state
|
||||||
win_regedit:
|
include_tasks: cleanup.yml
|
||||||
path: '{{item}}'
|
|
||||||
delete_key: yes
|
|
||||||
state: absent
|
|
||||||
with_items:
|
|
||||||
- '{{test_win_regedit_local_key}}'
|
|
||||||
- '{{test_win_regedit_classes_key}}'
|
|
||||||
|
|
||||||
- block:
|
- block:
|
||||||
- name: run tests for each property type
|
- name: run tests for each property type
|
||||||
|
@ -81,10 +75,4 @@
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: make sure testing keys are removed after test
|
- name: make sure testing keys are removed after test
|
||||||
win_regedit:
|
include_tasks: cleanup.yml
|
||||||
path: '{{item}}'
|
|
||||||
delete_key: yes
|
|
||||||
state: absent
|
|
||||||
with_items:
|
|
||||||
- '{{test_win_regedit_local_key}}'
|
|
||||||
- '{{test_win_regedit_classes_key}}'
|
|
||||||
|
|
|
@ -458,3 +458,258 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- not create_prop_with_json_again|changed
|
- not create_prop_with_json_again|changed
|
||||||
|
|
||||||
|
- name: create new key in loaded hive (check mode)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: new_key_in_hive_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get result of create new key in loaded hive (check mode)
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
Test-Path -Path HKLM:\ANSIBLE\NewKey
|
||||||
|
exit 0
|
||||||
|
register: new_key_in_hive_result_check
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of create new key in loaded hive (check mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- new_key_in_hive_check|changed
|
||||||
|
- new_key_in_hive_result_check.stdout == "False\r\n"
|
||||||
|
|
||||||
|
- name: create new key in loaded hive
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: new_key_in_hive
|
||||||
|
|
||||||
|
- name: get result of create new key in loaded hive
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
Test-Path -Path HKLM:\ANSIBLE\NewKey
|
||||||
|
exit 0
|
||||||
|
register: new_key_in_hive_result
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of create new key in loaded hive
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- new_key_in_hive|changed
|
||||||
|
- new_key_in_hive_result.stdout == "True\r\n"
|
||||||
|
|
||||||
|
- name: create new key in loaded hive (idempotent)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: new_key_in_hive_again
|
||||||
|
|
||||||
|
- name: assert result of create new key in loaded hive (idempotent)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not new_key_in_hive_again|changed
|
||||||
|
|
||||||
|
- name: set hive key property (check mode)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
name: TestProp
|
||||||
|
data: string
|
||||||
|
type: string
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: new_prop_in_hive_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get result of set hive key property (check mode)
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
$prop = Get-ItemProperty -Path HKLM:\ANSIBLE\NewKey -Name TestProp -ErrorAction SilentlyContinue
|
||||||
|
if ($prop) {
|
||||||
|
$prop.TestProp
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
|
register: new_prop_in_hive_result_check
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of set hive key property (check mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- new_prop_in_hive_check|changed
|
||||||
|
- new_prop_in_hive_result_check.stdout == ""
|
||||||
|
|
||||||
|
- name: set hive key property
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
name: TestProp
|
||||||
|
data: string
|
||||||
|
type: string
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: new_prop_in_hive
|
||||||
|
|
||||||
|
- name: get result of set hive key property
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
$prop = Get-ItemProperty -Path HKLM:\ANSIBLE\NewKey -Name TestProp -ErrorAction SilentlyContinue
|
||||||
|
if ($prop) {
|
||||||
|
$prop.TestProp
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
|
register: new_prop_in_hive_result
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of set hive key property
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- new_prop_in_hive|changed
|
||||||
|
- new_prop_in_hive_result.stdout == "string\r\n"
|
||||||
|
|
||||||
|
- name: set hive key property (idempotent)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
name: TestProp
|
||||||
|
data: string
|
||||||
|
type: string
|
||||||
|
state: present
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: new_prop_in_hive_again
|
||||||
|
|
||||||
|
- name: assert result of set hive key property (idempotent)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not new_prop_in_hive_again|changed
|
||||||
|
|
||||||
|
- name: remove hive key property (check mode)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
name: TestProp
|
||||||
|
state: absent
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: remove_prop_in_hive_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get result of remove hive key property (check mode)
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
$prop = Get-ItemProperty -Path HKLM:\ANSIBLE\NewKey -Name TestProp -ErrorAction SilentlyContinue
|
||||||
|
if ($prop) {
|
||||||
|
$prop.TestProp
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
|
register: remove_prop_in_hive_result_check
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of remove hive key property (check mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- remove_prop_in_hive_check|changed
|
||||||
|
- remove_prop_in_hive_result_check.stdout == "string\r\n"
|
||||||
|
|
||||||
|
- name: remove hive key property
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
name: TestProp
|
||||||
|
state: absent
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: remove_prop_in_hive
|
||||||
|
|
||||||
|
- name: get result of remove hive key property
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
$prop = Get-ItemProperty -Path HKLM:\ANSIBLE\NewKey -Name TestProp -ErrorAction SilentlyContinue
|
||||||
|
if ($prop) {
|
||||||
|
$prop.TestProp
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
|
register: remove_prop_in_hive_result
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of remove hive key property
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- remove_prop_in_hive|changed
|
||||||
|
- remove_prop_in_hive_result.stdout == ""
|
||||||
|
|
||||||
|
- name: remove hive key property (idempotent)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
name: TestProp
|
||||||
|
state: absent
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: remove_prop_in_hive_again
|
||||||
|
|
||||||
|
- name: assert result of set hive key property (idempotent)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not remove_prop_in_hive_again|changed
|
||||||
|
|
||||||
|
- name: remove key in loaded hive (check mode)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
state: absent
|
||||||
|
delete_yes: yes
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: remove_key_in_hive_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get result of remove key in loaded hive (check mode)
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
Test-Path -Path HKLM:\ANSIBLE\NewKey
|
||||||
|
exit 0
|
||||||
|
register: remove_key_in_hive_result_check
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of removekey in loaded hive (check mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- remove_key_in_hive_check|changed
|
||||||
|
- remove_key_in_hive_result_check.stdout == "True\r\n"
|
||||||
|
|
||||||
|
- name: remove key in loaded hive
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
state: absent
|
||||||
|
delete_yes: yes
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: remove_key_in_hive
|
||||||
|
|
||||||
|
- name: get result of remove key in loaded hive
|
||||||
|
win_shell: |
|
||||||
|
®.exe load HKLM\ANSIBLE C:\Users\Default\NTUSER.dat > $null
|
||||||
|
Test-Path -Path HKLM:\ANSIBLE\NewKey
|
||||||
|
exit 0
|
||||||
|
register: remove_key_in_hive_result
|
||||||
|
|
||||||
|
- win_command: reg.exe unload HKLM\ANSIBLE
|
||||||
|
|
||||||
|
- name: assert result of remove key in loaded hive
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- remove_key_in_hive|changed
|
||||||
|
- remove_key_in_hive_result.stdout == "False\r\n"
|
||||||
|
|
||||||
|
- name: remove key in loaded hive (idempotent)
|
||||||
|
win_regedit:
|
||||||
|
path: '{{test_win_regedit_hive_key}}'
|
||||||
|
state: absent
|
||||||
|
delete_yes: yes
|
||||||
|
hive: C:\Users\Default\NTUSER.dat
|
||||||
|
register: remove_key_in_hive_again
|
||||||
|
|
||||||
|
- name: assert result of remove key in loaded hive (idempotent)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not remove_key_in_hive_again|changed
|
||||||
|
|
Loading…
Reference in a new issue