Windows: new win_netbios module (#58331)
* Initial commit win_netbios module * Ansibot sanity improvements. * Corrected file permissions. * Windows: module description update * Adding integration tests * Added missing alias file * Improvements made to tests and WMI efficiency. * Correction from sanity check failures * Shippable corrections * Moved reboot flag. * Slight tweak to return value conditoin
This commit is contained in:
parent
fe3459ef86
commit
a589cac18a
6 changed files with 907 additions and 0 deletions
72
lib/ansible/modules/windows/win_netbios.ps1
Normal file
72
lib/ansible/modules/windows/win_netbios.ps1
Normal file
|
@ -0,0 +1,72 @@
|
|||
#!powershell
|
||||
|
||||
# Copyright: (c) 2019, Thomas Moore (@tmmruk) <hi@tmmr.uk>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
state = @{ type = "str"; choices = "enabled", "disabled", "default"; required = $true }
|
||||
adapter_names = @{ type = "list"; required = $false }
|
||||
}
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
$module.Result.reboot_required = $false
|
||||
|
||||
$state = $module.Params.state
|
||||
$adapter_names = $module.Params.adapter_names
|
||||
|
||||
switch ( $state )
|
||||
{
|
||||
'default'{ $netbiosoption = 0 }
|
||||
enabled { $netbiosoption = 1 }
|
||||
disabled { $netbiosoption = 2 }
|
||||
}
|
||||
|
||||
if(-not $adapter_names)
|
||||
{
|
||||
# Target all network adapters on the system
|
||||
$get_params = @{
|
||||
ClassName = 'Win32_NetworkAdapterConfiguration'
|
||||
Filter = 'IPEnabled=true'
|
||||
Property = @('MacAddress', 'TcpipNetbiosOptions')
|
||||
}
|
||||
$target_adapters_config = Get-CimInstance @get_params
|
||||
}
|
||||
else
|
||||
{
|
||||
$get_params = @{
|
||||
Class = 'Win32_NetworkAdapter'
|
||||
Filter = ($adapter_names | ForEach-Object -Process { "NetConnectionId='$_'" }) -join " OR "
|
||||
KeyOnly = $true
|
||||
}
|
||||
$target_adapters_config = Get-CimInstance @get_params | Get-CimAssociatedInstance -ResultClass 'Win32_NetworkAdapterConfiguration'
|
||||
if(($target_adapters_config | Measure-Object).Count -ne $adapter_names.Count)
|
||||
{
|
||||
$module.FailJson("Not all of the target adapter names could be found on the system. No configuration changes have been made. $adapter_names")
|
||||
}
|
||||
}
|
||||
|
||||
foreach($adapter in $target_adapters_config)
|
||||
{
|
||||
if($adapter.TcpipNetbiosOptions -ne $netbiosoption)
|
||||
{
|
||||
if(-not $module.CheckMode)
|
||||
{
|
||||
$result = Invoke-CimMethod -InputObject $adapter -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions=$netbiosoption}
|
||||
switch ( $result.ReturnValue )
|
||||
{
|
||||
0 { <# Success no reboot required #> }
|
||||
1 { $module.Result.reboot_required = $true }
|
||||
100 { $module.Warn("DHCP not enabled on adapter $($adapter.MacAddress). Unable to set default. Try using disabled or enabled options instead.") }
|
||||
default { $module.FailJson("An error occurred while setting TcpipNetbios options on adapter $($adapter.MacAddress). Return code $($result.ReturnValue).") }
|
||||
}
|
||||
}
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
80
lib/ansible/modules/windows/win_netbios.py
Normal file
80
lib/ansible/modules/windows/win_netbios.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Thomas Moore (@tmmruk)
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# this is a windows documentation stub. actual code lives in the .ps1
|
||||
# file of the same name
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_netbios
|
||||
version_added: '2.9'
|
||||
short_description: Manage NetBIOS over TCP/IP settings on Windows.
|
||||
description:
|
||||
- Enables or disables NetBIOS on Windows network adapters.
|
||||
- Can be used to protect a system against NBT-NS poisoning and avoid NBNS broadcast storms.
|
||||
- Settings can be applied system wide or per adapter.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Whether NetBIOS should be enabled, disabled, or default (use setting from DHCP server or if static IP address is assigned enable NetBIOS).
|
||||
choices:
|
||||
- enabled
|
||||
- disabled
|
||||
- default
|
||||
required: yes
|
||||
type: str
|
||||
adapter_names:
|
||||
description:
|
||||
- List of adapter names for which to manage NetBIOS settings. If this option is omitted then configuration is applied to all adapters on the system.
|
||||
- The adapter name used is the connection caption in the Network Control Panel or via C(Get-NetAdapter), eg C(Ethernet 2).
|
||||
type: list
|
||||
required: no
|
||||
|
||||
author:
|
||||
- Thomas Moore (@tmmruk)
|
||||
notes:
|
||||
- Changing NetBIOS settings does not usually require a reboot and will take effect immediately.
|
||||
- UDP port 137/138/139 will no longer be listening once NetBIOS is disabled.
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Disable NetBIOS system wide
|
||||
win_netbios:
|
||||
state: disabled
|
||||
|
||||
- name: Disable NetBIOS on Ethernet2
|
||||
win_netbios:
|
||||
state: disabled
|
||||
adapter_names:
|
||||
- Ethernet2
|
||||
|
||||
- name: Enable NetBIOS on Public and Backup adapters
|
||||
win_netbios:
|
||||
state: enabled
|
||||
adapter_names:
|
||||
- Public
|
||||
- Backup
|
||||
|
||||
- name: Set NetBIOS to system default on all adapters
|
||||
win_netbios:
|
||||
state: default
|
||||
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
reboot_required:
|
||||
description: Boolean value stating whether a system reboot is required.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: true
|
||||
'''
|
1
test/integration/targets/win_netbios/aliases
Normal file
1
test/integration/targets/win_netbios/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
shippable/windows/group6
|
541
test/integration/targets/win_netbios/library/win_device.ps1
Normal file
541
test/integration/targets/win_netbios/library/win_device.ps1
Normal file
|
@ -0,0 +1,541 @@
|
|||
#!powershell
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
#Requires -Module Ansible.ModuleUtils.AddType
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
hardware_id = @{ type = "str" }
|
||||
name = @{ type = "str" }
|
||||
path = @{ type = "path" }
|
||||
state = @{ type = "str"; choices = @("absent", "present"); default = "present" }
|
||||
}
|
||||
required_if = @(
|
||||
@("state", "present", @("path", "hardware_id"), $true),
|
||||
@("state", "absent", @(,"name"))
|
||||
)
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$hardware_id = $module.Params.hardware_id
|
||||
$name = $module.Params.name
|
||||
$path = $module.Params.path
|
||||
$state = $module.Params.state
|
||||
|
||||
$module.Result.reboot_required = $false
|
||||
|
||||
Add-CSharpType -References @'
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ansible.Device
|
||||
{
|
||||
public class NativeHelpers
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class SP_DEVINFO_DATA
|
||||
{
|
||||
public UInt32 cbSize;
|
||||
public Guid ClassGuid;
|
||||
public UInt32 DevInst;
|
||||
public IntPtr Reserved;
|
||||
|
||||
public SP_DEVINFO_DATA()
|
||||
{
|
||||
this.cbSize = (UInt32)Marshal.SizeOf(this);
|
||||
this.ClassGuid = Guid.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DeviceInfoCreationFlags : uint
|
||||
{
|
||||
DICD_GENERATE_ID = 0x00000001,
|
||||
DICD_INHERIT_CLASSDRVS = 0x00000002,
|
||||
}
|
||||
|
||||
public enum DeviceProperty : uint
|
||||
{
|
||||
SPDRP_DEVICEDESC = 0x0000000,
|
||||
SPDRP_HARDWAREID = 0x0000001,
|
||||
SPDRP_COMPATIBLEIDS = 0x0000002,
|
||||
SPDRP_UNUSED0 = 0x0000003,
|
||||
SPDRP_SERVICE = 0x0000004,
|
||||
SPDRP_UNUSED1 = 0x0000005,
|
||||
SPDRP_UNUSED2 = 0x0000006,
|
||||
SPDRP_CLASS = 0x0000007, // Read only - tied to ClassGUID
|
||||
SPDRP_CLASSGUID = 0x0000008,
|
||||
SPDRP_DRIVER = 0x0000009,
|
||||
SPDRP_CONFIGFLAGS = 0x000000a,
|
||||
SPDRP_MFG = 0x000000b,
|
||||
SPDRP_FRIENDLYNAME = 0x000000c,
|
||||
SPDRP_LOCATION_INFORMATION = 0x000000d,
|
||||
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x000000e, // Read only
|
||||
SPDRP_CAPABILITIES = 0x000000f, // Read only
|
||||
SPDRP_UI_NUMBER = 0x0000010, // Read only
|
||||
SPDRP_UPPERFILTERS = 0x0000011,
|
||||
SPDRP_LOWERFILTERS = 0x0000012,
|
||||
SPDRP_BUSTYPEGUID = 0x0000013, // Read only
|
||||
SPDRP_LEGACYBUSTYPE = 0x0000014, // Read only
|
||||
SPDRP_BUSNUMBER = 0x0000015, // Read only
|
||||
SPDRP_ENUMERATOR_NAME = 0x0000016, // Read only
|
||||
SPDRP_SECURITY = 0x0000017,
|
||||
SPDRP_SECURITY_SDS = 0x0000018,
|
||||
SPDRP_DEVTYPE = 0x0000019,
|
||||
SPDRP_EXCLUSIVE = 0x000001a,
|
||||
SPDRP_CHARACTERISTICS = 0x000001b,
|
||||
SPDRP_ADDRESS = 0x000001c, // Read only
|
||||
SPDRP_UI_NUMBER_DESC_FORMAT = 0x000001d,
|
||||
SPDRP_DEVICE_POWER_DATA = 0x000001e, // Read only
|
||||
SPDRP_REMOVAL_POLICY = 0x000001f, // Read only
|
||||
SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x0000020, // Read only
|
||||
SPDRP_REMOVAL_POLICY_OVERRIDE = 0x0000021,
|
||||
SPDRP_INSTALL_STATE = 0x0000022, // Read only
|
||||
SPDRP_LOCATION_PATHS = 0x0000023, // Read only
|
||||
SPDRP_BASE_CONTAINERID = 0x0000024, // Read only
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/previous-versions/ff549793%28v%3dvs.85%29
|
||||
public enum DifCodes : uint
|
||||
{
|
||||
DIF_SELECTDIVE = 0x00000001,
|
||||
DIF_INSTALLDEVICE = 0x00000002,
|
||||
DIF_ASSIGNRESOURCES = 0x00000003,
|
||||
DIF_PROPERTIES = 0x00000004,
|
||||
DIF_REMOVE = 0x00000005,
|
||||
DIF_FIRSTTIMESETUP = 0x00000006,
|
||||
DIF_FOUNDDEVICE = 0x00000007,
|
||||
DIF_SELECTCLASSDRIVERS = 0x00000008,
|
||||
DIF_VALIDATECLASSDRIVERS = 0x00000009,
|
||||
DIF_INSTALLCLASSDRIVERS = 0x0000000a,
|
||||
DIF_CALCDISKSPACE = 0x0000000b,
|
||||
DIF_DESTROYPRIVATEDATA = 0x0000000c,
|
||||
DIF_VALIDATEDRIVER = 0x0000000d,
|
||||
DIF_DETECT = 0x0000000f,
|
||||
DIF_INSTALLWIZARD = 0x00000010,
|
||||
DIF_DESTROYWIZARDDATA = 0x00000011,
|
||||
DIF_PROPERTYCHANGE = 0x00000012,
|
||||
DIF_ENABLECLASS = 0x00000013,
|
||||
DIF_DETECTVERIFY = 0x00000014,
|
||||
DIF_INSTALLDEVICEFILES = 0x00000015,
|
||||
DIF_UNREMOVE = 0x00000016,
|
||||
DIF_SELECTBESTCOMPATDRV = 0x00000017,
|
||||
DIF_ALLOW_INSTALL = 0x00000018,
|
||||
DIF_REGISTERDEVICE = 0x00000019,
|
||||
DIF_NEWDEVICEWIZARD_PRESELECT = 0x0000001a,
|
||||
DIF_NEWDEVICEWIZARD_SELECT = 0x0000001b,
|
||||
DIF_NEWDEVICEWIZARD_PREANALYZE = 0x0000001c,
|
||||
DIF_NEWDEVICEWIZARD_POSTANALYZE = 0x0000001d,
|
||||
DIF_NEWDEVICEWIZARD_FINISHINSTALL = 0x0000001e,
|
||||
DIF_UNUSED1 = 0x0000001e,
|
||||
DIF_INSTALLINTERFACES = 0x00000020,
|
||||
DIF_DETECTCANCEL = 0x00000021,
|
||||
DIF_REGISTER_COINSTALLERS = 0x00000022,
|
||||
DIF_ADDPROPERTYPAGE_ADVANCED = 0x00000023,
|
||||
DIF_ADDPROPERTYPAGE_BASIC = 0x00000024,
|
||||
DIF_RESERVED1 = 0x00000025,
|
||||
DIF_TROUBLESHOOTER = 0x00000026,
|
||||
DIF_POWERMESSAGEWAKE = 0x00000027,
|
||||
DIF_ADDREMOTEPROPERTYPAGE_ADVANCED = 0x00000028,
|
||||
DIF_UPDATEDRIVER_UI = 0x00000029,
|
||||
DIF_FINISHINSTALL_ACTION = 0x0000002a,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum GetClassFlags : uint
|
||||
{
|
||||
DIGCF_DEFAULT = 0x00000001,
|
||||
DIGCF_PRESENT = 0x00000002,
|
||||
DIGCF_ALLCLASSES = 0x00000004,
|
||||
DIGCF_PROFILE = 0x00000008,
|
||||
DIGCF_DEVICEINTERFACE = 0x00000010,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum InstallFlags : uint
|
||||
{
|
||||
INSTALLFLAG_FORCE = 0x00000001,
|
||||
INSTALLFLAG_READONLY = 0x00000002,
|
||||
INSTALLFLAG_NONINTERACTIVE = 0x00000004,
|
||||
INSTALLFLAG_BITS = 0x00000007,
|
||||
}
|
||||
}
|
||||
|
||||
public class NativeMethods
|
||||
{
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiCallClassInstaller(
|
||||
NativeHelpers.DifCodes InstallFunction,
|
||||
SafeDeviceInfoSet DeviceInfoSet,
|
||||
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern SafeDeviceInfoSet SetupDiCreateDeviceInfoList(
|
||||
Guid ClassGuid,
|
||||
IntPtr hwndParent);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiCreateDeviceInfoW(
|
||||
SafeDeviceInfoSet DeviceInfoSet,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string DeviceName,
|
||||
Guid ClassGuid,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string DeviceDescription,
|
||||
IntPtr hwndParent,
|
||||
NativeHelpers.DeviceInfoCreationFlags CreationFlags,
|
||||
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiDestroyDeviceInfoList(
|
||||
IntPtr DeviceInfoSet);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiEnumDeviceInfo(
|
||||
SafeDeviceInfoSet DeviceInfoSet,
|
||||
UInt32 MemberIndex,
|
||||
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern SafeDeviceInfoSet SetupDiGetClassDevsW(
|
||||
Guid ClassGuid,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string Enumerator,
|
||||
IntPtr hwndParent,
|
||||
NativeHelpers.GetClassFlags Flags);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiGetDeviceRegistryPropertyW(
|
||||
SafeDeviceInfoSet DeviceInfoSet,
|
||||
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData,
|
||||
NativeHelpers.DeviceProperty Property,
|
||||
out UInt32 PropertyRegDataType,
|
||||
SafeMemoryBuffer PropertyBuffer,
|
||||
UInt32 PropertyBufferSize,
|
||||
ref UInt32 RequiredSize);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiGetINFClassW(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string InfName,
|
||||
ref Guid ClassGuid,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder ClassName,
|
||||
UInt32 ClassNameSize,
|
||||
ref UInt32 RequiredSize);
|
||||
|
||||
[DllImport("Setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiSetDeviceRegistryPropertyW(
|
||||
SafeDeviceInfoSet DeviceInfoSet,
|
||||
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData,
|
||||
NativeHelpers.DeviceProperty Property,
|
||||
SafeMemoryBuffer PropertyBuffer,
|
||||
UInt32 PropertyBufferSize);
|
||||
|
||||
[DllImport("Newdev.dll", SetLastError = true)]
|
||||
public static extern bool UpdateDriverForPlugAndPlayDevicesW(
|
||||
IntPtr hwndParent,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string HardwareId,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string FullInfPath,
|
||||
NativeHelpers.InstallFlags InstallFlags,
|
||||
ref bool bRebootRequired);
|
||||
}
|
||||
|
||||
public class SafeDeviceInfoSet : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public SafeDeviceInfoSet() : base(true) { }
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return NativeMethods.SetupDiDestroyDeviceInfoList(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public int Length = 0;
|
||||
|
||||
public SafeMemoryBuffer() : base(true) { }
|
||||
|
||||
public SafeMemoryBuffer(int cb) : base(true)
|
||||
{
|
||||
Length = cb;
|
||||
base.SetHandle(Marshal.AllocHGlobal(cb));
|
||||
}
|
||||
|
||||
public SafeMemoryBuffer(string sz) : base(true)
|
||||
{
|
||||
Length = sz.Length * sizeof(char);
|
||||
base.SetHandle(Marshal.StringToHGlobalUni(sz));
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Marshal.FreeHGlobal(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceUtil
|
||||
{
|
||||
public static string GetDeviceFriendlyName(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo)
|
||||
{
|
||||
string friendlyName = GetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_FRIENDLYNAME);
|
||||
|
||||
// Older Windows versions may not have a friendly name set. This seems to be the case when the device has
|
||||
// a unique description so we fallback to that value.
|
||||
if (null == friendlyName)
|
||||
friendlyName = GetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_DEVICEDESC);
|
||||
|
||||
return friendlyName;
|
||||
}
|
||||
|
||||
public static void SetDeviceHardwareId(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
|
||||
string hardwareId)
|
||||
{
|
||||
SetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_HARDWAREID, hardwareId);
|
||||
}
|
||||
|
||||
private static string GetDeviceStringProp(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
|
||||
NativeHelpers.DeviceProperty property)
|
||||
{
|
||||
using (SafeMemoryBuffer memBuf = GetDeviceProperty(devInfoSet, devInfo, property))
|
||||
{
|
||||
if (memBuf.IsInvalid) // Property does not exist so just return null.
|
||||
return null;
|
||||
|
||||
return Marshal.PtrToStringUni(memBuf.DangerousGetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
private static SafeMemoryBuffer GetDeviceProperty(SafeDeviceInfoSet devInfoSet,
|
||||
NativeHelpers.SP_DEVINFO_DATA devInfo, NativeHelpers.DeviceProperty property)
|
||||
{
|
||||
UInt32 requiredSize = 0;
|
||||
UInt32 regDataType = 0;
|
||||
if (!NativeMethods.SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfo, property,
|
||||
out regDataType, new SafeMemoryBuffer(0), 0, ref requiredSize))
|
||||
{
|
||||
int errCode = Marshal.GetLastWin32Error();
|
||||
if (errCode == 0x0000000D) // ERROR_INVALID_DATA
|
||||
return new SafeMemoryBuffer(); // The FRIENDLYNAME property does not exist
|
||||
else if (errCode != 0x0000007A) // ERROR_INSUFFICIENT_BUFFER
|
||||
throw new Win32Exception(errCode);
|
||||
}
|
||||
|
||||
SafeMemoryBuffer memBuf = new SafeMemoryBuffer((int)requiredSize);
|
||||
if (!NativeMethods.SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfo, property,
|
||||
out regDataType, memBuf, requiredSize, ref requiredSize))
|
||||
{
|
||||
int errCode = Marshal.GetLastWin32Error();
|
||||
memBuf.Dispose();
|
||||
|
||||
throw new Win32Exception(errCode);
|
||||
}
|
||||
|
||||
return memBuf;
|
||||
}
|
||||
|
||||
private static void SetDeviceStringProp(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
|
||||
NativeHelpers.DeviceProperty property, string value)
|
||||
{
|
||||
using (SafeMemoryBuffer buffer = new SafeMemoryBuffer(value))
|
||||
SetDeviceProperty(devInfoSet, devInfo, property, buffer);
|
||||
}
|
||||
|
||||
private static void SetDeviceProperty(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
|
||||
NativeHelpers.DeviceProperty property, SafeMemoryBuffer buffer)
|
||||
{
|
||||
if (!NativeMethods.SetupDiSetDeviceRegistryPropertyW(devInfoSet, devInfo, property, buffer,
|
||||
(UInt32)buffer.Length))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
Function Get-Win32ErrorMessage {
|
||||
Param ([System.Int32]$ErrorCode)
|
||||
|
||||
$exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode
|
||||
return ("{0} (Win32 ErrorCode {1} - 0x{1:X8}" -f $exp.Message, $ErrorCode)
|
||||
}
|
||||
|
||||
# Determine if the device is already installed
|
||||
$dev_info_set = [Ansible.Device.NativeMethods]::SetupDiGetClassDevsW(
|
||||
[Guid]::Empty,
|
||||
[NullString]::Value,
|
||||
[System.IntPtr]::Zero,
|
||||
[Ansible.Device.NativeHelpers+GetClassFlags]::DIGCF_ALLCLASSES
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
try {
|
||||
if ($dev_info_set.IsInvalid) {
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to get device information set for installed devices: $msg")
|
||||
}
|
||||
|
||||
$dev_info = $null
|
||||
if ($null -ne $name) {
|
||||
# Loop through the set of all devices and compare the name
|
||||
$idx = 0
|
||||
while ($true) {
|
||||
$dev_info = New-Object -TypeName Ansible.Device.NativeHelpers+SP_DEVINFO_DATA
|
||||
$res = [Ansible.Device.NativeMethods]::SetupDiEnumDeviceInfo(
|
||||
$dev_info_set,
|
||||
$idx,
|
||||
$dev_info
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if (-not $res) {
|
||||
$dev_info = $null
|
||||
if ($err -eq 0x00000103) { # ERROR_NO_MORE_ITEMS
|
||||
break
|
||||
}
|
||||
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to enumerate device information set at index $($idx): $msg")
|
||||
}
|
||||
|
||||
$device_name = [Ansible.Device.DeviceUtil]::GetDeviceFriendlyName($dev_info_set, $dev_info)
|
||||
if ($device_name -eq $name) {
|
||||
break
|
||||
}
|
||||
|
||||
$dev_info = $null
|
||||
$idx++
|
||||
}
|
||||
}
|
||||
|
||||
if ($state -eq "absent" -and $null -ne $dev_info) {
|
||||
if (-not $module.CheckMode) {
|
||||
$res = [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller(
|
||||
[Ansible.Device.NativeHelpers+DifCodes]::DIF_REMOVE,
|
||||
$dev_info_set,
|
||||
$dev_info
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if (-not $res) {
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to remove device $($name): $msg")
|
||||
}
|
||||
}
|
||||
|
||||
$module.Result.changed = $true
|
||||
} elseif ($state -eq "present" -and $null -eq $dev_info) {
|
||||
# Populate the class guid and display name if the path to an inf file was set.
|
||||
$class_id = [Guid]::Empty
|
||||
$class_name = $null
|
||||
if ($path) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
$module.FailJson("Could not find the inf file specified at '$path'")
|
||||
}
|
||||
|
||||
$class_name_sb = New-Object -TypeName System.Text.StringBuilder -ArgumentList 32 # MAX_CLASS_NAME_LEN
|
||||
$required_size = 0
|
||||
$res = [Ansible.Device.NativeMethods]::SetupDiGetINFClassW(
|
||||
$path,
|
||||
[ref]$class_id,
|
||||
$class_name_sb,
|
||||
$class_name_sb.Capacity,
|
||||
[ref]$required_size
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if (-not $res) {
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to parse driver inf at '$path': $msg")
|
||||
}
|
||||
|
||||
$class_name = $class_name_sb.ToString()
|
||||
}
|
||||
|
||||
# When creating a new device we want to start with a blank device information set.
|
||||
$dev_info_set.Dispose()
|
||||
|
||||
$dev_info_set = [Ansible.Device.NativeMethods]::SetupDiCreateDeviceInfoList(
|
||||
$class_id,
|
||||
[System.IntPtr]::Zero
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if ($dev_info_set.IsInvalid) {
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to create device info set for the class $($class_id): $msg")
|
||||
}
|
||||
|
||||
# Create the new device element and add it to the device info set
|
||||
$dev_info = New-Object -TypeName Ansible.Device.NativeHelpers+SP_DEVINFO_DATA
|
||||
$res = [Ansible.Device.NativeMethods]::SetupDiCreateDeviceInfoW(
|
||||
$dev_info_set,
|
||||
$class_name,
|
||||
$class_id,
|
||||
$null,
|
||||
[System.IntPtr]::Zero,
|
||||
[Ansible.Device.NativeHelpers+DeviceInfoCreationFlags]::DICD_GENERATE_ID,
|
||||
$dev_info
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if (-not $res) {
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to create new device element for class $($class_name): $msg")
|
||||
}
|
||||
|
||||
# Set the hardware id of the new device so we can load the proper driver.
|
||||
[Ansible.Device.DeviceUtil]::SetDeviceHardwareId($dev_info_set, $dev_info, $hardware_id)
|
||||
|
||||
if (-not $module.CheckMode) {
|
||||
# Install the device
|
||||
$res = [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller(
|
||||
[Ansible.Device.NativeHelpers+DifCodes]::DIF_REGISTERDEVICE,
|
||||
$dev_info_set,
|
||||
$dev_info
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if (-not $res) {
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to register new device for class $($class_name): $msg")
|
||||
}
|
||||
|
||||
# Load the drivers for the new device
|
||||
$reboot_required = $false
|
||||
$res = [Ansible.Device.NativeMethods]::UpdateDriverForPlugAndPlayDevicesW(
|
||||
[System.IntPtr]::Zero,
|
||||
$hardware_id,
|
||||
$path,
|
||||
[Ansible.Device.NativeHelpers+InstallFlags]'INSTALLFLAG_FORCE, INSTALLFLAG_NONINTERACTIVE',
|
||||
[ref]$reboot_required
|
||||
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
|
||||
if (-not $res) {
|
||||
# On a failure make sure we cleanup the "installed" device
|
||||
[Ansible.Device.NativeMethods]::SetupDiCallClassInstaller(
|
||||
[Ansible.Device.NativeHelpers+DifCodes]::DIF_REMOVE,
|
||||
$dev_info_set,
|
||||
$dev_info
|
||||
) > $null
|
||||
|
||||
$msg = Get-Win32ErrorMessage -ErrorCode $err
|
||||
$module.FailJson("Failed to update device driver: $msg")
|
||||
}
|
||||
|
||||
$module.Result.reboot_required = $reboot_required
|
||||
|
||||
# Now get the name of the newly created device which we return back to Ansible.
|
||||
$name = [Ansible.Device.DeviceUtil]::GetDeviceFriendlyName($dev_info_set, $dev_info)
|
||||
} else {
|
||||
# Generate random name for check mode output
|
||||
$name = "Check mode generated device for $($class_name)"
|
||||
}
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
} finally {
|
||||
$dev_info_set.Dispose()
|
||||
}
|
||||
|
||||
$module.Result.name = $name
|
||||
|
||||
$module.ExitJson()
|
54
test/integration/targets/win_netbios/tasks/main.yml
Normal file
54
test/integration/targets/win_netbios/tasks/main.yml
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Test code for win_netbios module
|
||||
# Copyright: (c) 2019, Thomas Moore <hi@tmmr.uk>
|
||||
|
||||
# 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/>.
|
||||
|
||||
|
||||
- name: ensure netbios is set to default to start with
|
||||
win_netbios:
|
||||
state: default
|
||||
|
||||
- name: create dummy network adapter device
|
||||
win_device:
|
||||
path: '%WinDir%\Inf\netloop.inf'
|
||||
hardware_id: '*msloop'
|
||||
state: present
|
||||
register: test_device_name
|
||||
|
||||
- set_fact:
|
||||
test_device_name: '{{ test_device_name.name }}'
|
||||
|
||||
- block:
|
||||
- name: get name of the dummy network adapter
|
||||
win_shell: (Get-CimInstance -Class Win32_NetworkAdapter -Filter "Name='{{ test_device_name }}'").NetConnectionID
|
||||
changed_when: False
|
||||
register: test_adapter
|
||||
|
||||
- set_fact:
|
||||
test_adapter: '{{ test_adapter.stdout | trim }}'
|
||||
|
||||
- name: run tests
|
||||
include_tasks: tests.yml
|
||||
|
||||
always:
|
||||
- name: remove dummy network adapter device
|
||||
win_device:
|
||||
name: '{{ test_device_name }}'
|
||||
state: absent
|
||||
|
||||
- name: set netbios back to default after tests
|
||||
win_netbios:
|
||||
state: default
|
159
test/integration/targets/win_netbios/tasks/tests.yml
Normal file
159
test/integration/targets/win_netbios/tasks/tests.yml
Normal file
|
@ -0,0 +1,159 @@
|
|||
# Test code for win_netbios module
|
||||
# Copyright: (c) 2019, Thomas Moore <hi@tmmr.uk>
|
||||
|
||||
# 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/>.
|
||||
|
||||
- set_fact:
|
||||
get_netbios_script: |
|
||||
$adapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetConnectionID='{{ test_adapter }}'"
|
||||
$config = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index=$($adapter.DeviceID)"
|
||||
$config.TcpipNetbiosOptions
|
||||
|
||||
- name: disable netbios single adapter (check mode)
|
||||
win_netbios:
|
||||
adapter_names: '{{ test_adapter }}'
|
||||
state: disabled
|
||||
register: set_single_check
|
||||
check_mode: yes
|
||||
|
||||
- name: get result of disable a single adapter test (check mode)
|
||||
win_shell: '{{ get_netbios_script }}'
|
||||
changed_when: no
|
||||
register: set_single_actual_check
|
||||
|
||||
- name: assert disable a single adapter (check mode)
|
||||
assert:
|
||||
that:
|
||||
- set_single_check is changed
|
||||
- set_single_actual_check.stdout_lines == ["0"]
|
||||
|
||||
- name: disable netbios single adapter
|
||||
win_netbios:
|
||||
adapter_names: '{{ test_adapter }}'
|
||||
state: disabled
|
||||
register: set_single
|
||||
|
||||
- name: get result of disable a single adapter test
|
||||
win_shell: '{{ get_netbios_script }}'
|
||||
changed_when: no
|
||||
register: set_single_actual
|
||||
|
||||
- name: assert disable a single adapter
|
||||
assert:
|
||||
that:
|
||||
- set_single_check is changed
|
||||
- set_single_actual.stdout_lines == ["2"]
|
||||
|
||||
- name: fail with invalid network adapter name
|
||||
win_netbios:
|
||||
state: disabled
|
||||
adapter_names:
|
||||
- FakeAdapterName
|
||||
register: invalid_adapter
|
||||
failed_when: invalid_adapter.msg != "Not all of the target adapter names could be found on the system. No configuration changes have been made. FakeAdapterName"
|
||||
|
||||
- name: disable netbios all adapters (check mode)
|
||||
win_netbios:
|
||||
state: disabled
|
||||
check_mode: yes
|
||||
register: disable_check
|
||||
|
||||
- name: assert disable netbios (check mode)
|
||||
assert:
|
||||
that:
|
||||
- disable_check.changed
|
||||
|
||||
- name: disable netbios all adapters
|
||||
win_netbios:
|
||||
state: disabled
|
||||
register: netbios_disable
|
||||
|
||||
- name: assert netbios disabled
|
||||
assert:
|
||||
that:
|
||||
- netbios_disable.changed
|
||||
|
||||
- name: test disable idempotence
|
||||
win_netbios:
|
||||
state: disabled
|
||||
register: netbios_disable
|
||||
|
||||
- name: test disable idempotence
|
||||
assert:
|
||||
that:
|
||||
- not netbios_disable.changed
|
||||
|
||||
- name: enable netbios all adapters (check mode)
|
||||
win_netbios:
|
||||
state: enabled
|
||||
check_mode: yes
|
||||
register: enable_check
|
||||
|
||||
- name: assert enable netbios all adapters (check mode)
|
||||
assert:
|
||||
that:
|
||||
- enable_check.changed
|
||||
|
||||
- name: enable netbios all adapters
|
||||
win_netbios:
|
||||
state: enabled
|
||||
register: netbios_enable
|
||||
|
||||
- name: assert netbios enabled
|
||||
assert:
|
||||
that:
|
||||
- netbios_enable.changed
|
||||
|
||||
- name: test enable idempotence
|
||||
win_netbios:
|
||||
state: enabled
|
||||
register: netbios_enable
|
||||
|
||||
- name: assert enable idempotence
|
||||
assert:
|
||||
that:
|
||||
- not netbios_enable.changed
|
||||
|
||||
- name: default netbios all adapters (check mode)
|
||||
win_netbios:
|
||||
state: default
|
||||
check_mode: yes
|
||||
register: default_check
|
||||
|
||||
- name: assert default netbios (check mode)
|
||||
assert:
|
||||
that:
|
||||
- default_check.changed
|
||||
|
||||
- name: default netbios all adapters
|
||||
win_netbios:
|
||||
state: default
|
||||
register: default_enable
|
||||
|
||||
- name: assert netbios default all adapters
|
||||
assert:
|
||||
that:
|
||||
- default_enable.changed
|
||||
|
||||
- name: test default idempotence
|
||||
win_netbios:
|
||||
state: default
|
||||
register: netbios_default
|
||||
|
||||
- name: assert default idempotence
|
||||
assert:
|
||||
that:
|
||||
- not netbios_default.changed
|
Loading…
Reference in a new issue