Added win_http_proxy and win_inet_proxy (#54631)

* Added win_http_proxy and win_inet_proxy

* Fix up docs sanity issues

* removed duplicate doc entry

* Fix docs issues and fix for empty proxy

* Removed <-loopback> for win_http_proxy

* doc changes from review
This commit is contained in:
Jordan Borean 2019-04-05 11:19:30 +10:00 committed by GitHub
parent a39051067b
commit 10a9cf59dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 2439 additions and 0 deletions

View file

@ -0,0 +1,267 @@
#!powershell
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
$spec = @{
options = @{
bypass = @{ type = "list" }
proxy = @{ type = "raw" }
source = @{ type = "str"; choices = @("ie") }
}
mutually_exclusive = @(
@("proxy", "source"),
@("bypass", "source")
)
required_by = @{
bypass = @("proxy")
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$proxy = $module.Params.proxy
$bypass = $module.Params.bypass
$source = $module.Params.source
# Parse the raw value, it should be a Dictionary or String
if ($proxy -is [System.Collections.IDictionary]) {
$valid_keys = [System.Collections.Generic.List`1[String]]@("http", "https", "ftp", "socks")
# Check to make sure we don't have any invalid keys in the dict
$invalid_keys = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $proxy.Keys) {
if ($k -notin $valid_keys) {
$invalid_keys.Add($k)
}
}
if ($invalid_keys.Count -gt 0) {
$invalid_keys = $invalid_keys | Sort-Object # So our test assertion doesn't fail due to random ordering
$module.FailJson("Invalid keys found in proxy: $($invalid_keys -join ', '). Valid keys are $($valid_keys -join ', ').")
}
# Build the proxy string in the form 'protocol=host;', the order of valid_keys is also important
$proxy_list = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $valid_keys) {
if ($proxy.ContainsKey($k)) {
$proxy_list.Add("$k=$($proxy.$k)")
}
}
$proxy = $proxy_list -join ";"
} elseif ($null -ne $proxy) {
$proxy = $proxy.ToString()
}
if ($bypass) {
if ([System.String]::IsNullOrEmpty($proxy)) {
$module.FailJson("missing parameter(s) required by ''bypass'': proxy")
}
$bypass = $bypass -join ';'
}
$win_http_invoke = @'
using System;
using System.Runtime.InteropServices;
namespace Ansible.WinHttpProxy
{
internal class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class WINHTTP_CURRENT_USER_IE_PROXY_CONFIG : IDisposable
{
public bool fAutoDetect;
public IntPtr lpszAutoConfigUrl;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;
public void Dispose()
{
if (lpszAutoConfigUrl != IntPtr.Zero)
Marshal.FreeHGlobal(lpszAutoConfigUrl);
if (lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxy);
if (lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxyBypass);
GC.SuppressFinalize(this);
}
~WINHTTP_CURRENT_USER_IE_PROXY_CONFIG() { this.Dispose(); }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class WINHTTP_PROXY_INFO : IDisposable
{
public UInt32 dwAccessType;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;
public void Dispose()
{
if (lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxy);
if (lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxyBypass);
GC.SuppressFinalize(this);
}
~WINHTTP_PROXY_INFO() { this.Dispose(); }
}
}
internal class NativeMethods
{
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpGetDefaultProxyConfiguration(
[Out] NativeHelpers.WINHTTP_PROXY_INFO pProxyInfo);
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpGetIEProxyConfigForCurrentUser(
[Out] NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG pProxyConfig);
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpSetDefaultProxyConfiguration(
NativeHelpers.WINHTTP_PROXY_INFO pProxyInfo);
}
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 WinINetProxy
{
public bool AutoDetect;
public string AutoConfigUrl;
public string Proxy;
public string ProxyBypass;
}
public class WinHttpProxy
{
public string Proxy;
public string ProxyBypass;
public WinHttpProxy()
{
Refresh();
}
public void Set()
{
using (NativeHelpers.WINHTTP_PROXY_INFO proxyInfo = new NativeHelpers.WINHTTP_PROXY_INFO())
{
if (String.IsNullOrEmpty(Proxy))
proxyInfo.dwAccessType = 1; // WINHTTP_ACCESS_TYPE_NO_PROXY
else
{
proxyInfo.dwAccessType = 3; // WINHTTP_ACCESS_TYPE_NAMED_PROXY
proxyInfo.lpszProxy = Marshal.StringToHGlobalUni(Proxy);
if (!String.IsNullOrEmpty(ProxyBypass))
proxyInfo.lpszProxyBypass = Marshal.StringToHGlobalUni(ProxyBypass);
}
if (!NativeMethods.WinHttpSetDefaultProxyConfiguration(proxyInfo))
throw new Win32Exception("WinHttpSetDefaultProxyConfiguration() failed");
}
}
public void Refresh()
{
using (NativeHelpers.WINHTTP_PROXY_INFO proxyInfo = new NativeHelpers.WINHTTP_PROXY_INFO())
{
if (!NativeMethods.WinHttpGetDefaultProxyConfiguration(proxyInfo))
throw new Win32Exception("WinHttpGetDefaultProxyConfiguration() failed");
Proxy = Marshal.PtrToStringUni(proxyInfo.lpszProxy);
ProxyBypass = Marshal.PtrToStringUni(proxyInfo.lpszProxyBypass);
}
}
public static WinINetProxy GetIEProxyConfig()
{
using (NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy = new NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG())
{
if (!NativeMethods.WinHttpGetIEProxyConfigForCurrentUser(ieProxy))
throw new Win32Exception("WinHttpGetIEProxyConfigForCurrentUser() failed");
return new WinINetProxy
{
AutoDetect = ieProxy.fAutoDetect,
AutoConfigUrl = Marshal.PtrToStringUni(ieProxy.lpszAutoConfigUrl),
Proxy = Marshal.PtrToStringUni(ieProxy.lpszProxy),
ProxyBypass = Marshal.PtrToStringUni(ieProxy.lpszProxyBypass),
};
}
}
}
}
'@
Add-CSharpType -References $win_http_invoke -AnsibleModule $module
$actual_proxy = New-Object -TypeName Ansible.WinHttpProxy.WinHttpProxy
$module.Diff.before = @{
proxy = $actual_proxy.Proxy
bypass = $actual_proxy.ProxyBypass
}
if ($source -eq "ie") {
# If source=ie we need to get the server and bypass values from the IE configuration
$ie_proxy = [Ansible.WinHttpProxy.WinHttpProxy]::GetIEProxyConfig()
$proxy = $ie_proxy.Proxy
$bypass = $ie_proxy.ProxyBypass
}
$previous_proxy = $actual_proxy.Proxy
$previous_bypass = $actual_proxy.ProxyBypass
# Make sure an empty string is converted to $null for easier comparisons
if ([String]::IsNullOrEmpty($proxy)) {
$proxy = $null
}
if ([String]::IsNullOrEmpty($bypass)) {
$bypass = $null
}
if ($previous_proxy -ne $proxy -or $previous_bypass -ne $bypass) {
$actual_proxy.Proxy = $proxy
$actual_proxy.ProxyBypass = $bypass
if (-not $module.CheckMode) {
$actual_proxy.Set()
# Validate that the change was made correctly and revert if it wasn't. The Set() method won't fail on invalid
# values so we need to check again to make sure all was good.
$actual_proxy.Refresh()
if ($actual_proxy.Proxy -ne $proxy -or $actual_proxy.ProxyBypass -ne $bypass) {
$actual_proxy.Proxy = $previous_proxy
$actual_proxy.ProxyBypass = $previous_bypass
$actual_proxy.Set()
$module.FailJson("Unknown error when trying to set proxy '$proxy' or bypass '$bypass'")
}
}
$module.Result.changed = $true
}
$module.Diff.after = @{
proxy = $proxy
bypass = $bypass
}
$module.ExitJson()

View file

@ -0,0 +1,103 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_http_proxy
version_added: '2.8'
short_description: Manages proxy settings for WinHTTP
description:
- Used to set, remove, or import proxy settings for Windows HTTP Services
C(WinHTTP).
- WinHTTP is a framework used by applications or services, typically .NET
applications or non-interactive services, to make web requests.
options:
bypass:
description:
- A list of hosts that will bypass the set proxy when being accessed.
- Use C(<local>) to match hostnames that are not fully qualified domain
names. This is useful when needing to connect to intranet sites using
just the hostname.
- Omit, set to null or an empty string/list to remove the bypass list.
- If this is set then I(proxy) must also be set.
type: list
proxy:
description:
- A string or dict that specifies the proxy to be set.
- If setting a string, should be in the form C(hostname), C(hostname:port),
or C(protocol=hostname:port).
- If the port is undefined, the default port for the protocol in use is
used.
- If setting a dict, the keys should be the protocol and the values should
be the hostname and/or port for that protocol.
- Valid protocols are C(http), C(https), C(ftp), and C(socks).
- Omit, set to null or an empty string to remove the proxy settings.
source:
description:
- Instead of manually specifying the I(proxy) and/or I(bypass), set this to
import the proxy from a set source like Internet Explorer.
- Using C(ie) will import the Internet Explorer proxy settings for the
current active network connection of the current user.
- Only IE's proxy URL and bypass list will be imported into WinHTTP.
- This is like running C(netsh winhttp import proxy source=ie).
- The value is imported when the module runs and will not automatically
be updated if the IE configuration changes in the future. The module will
have to be run again to sync the latest changes.
choices:
- ie
type: str
notes:
- This is not the same as the proxy settings set in Internet Explorer, also
known as C(WinINet); use the M(win_inet_proxy) module to manage that instead.
- These settings are set system wide and not per user, it will require
Administrative privileges to run.
seealso:
- module: win_inet_proxy
author:
- Jordan Borean (@jborean93)
'''
EXAMPLES = r'''
- name: Set a proxy to use for all protocols
win_http_proxy:
proxy: hostname
- name: Set a proxy with a specific port with a bypass list
win_http_proxy:
proxy: hostname:8080
bypass:
- server1
- server2
- <local>
- name: Set the proxy based on the IE proxy settings
win_http_proxy:
source: ie
- name: Set a proxy for specific protocols
win_http_proxy:
proxy:
http: hostname:8080
https: hostname:8443
- name: Set a proxy for specific protocols using a string
win_http_proxy:
proxy: http=hostname:8080;https=hostname:8443
bypass: server1,server2,<local>
- name: Remove any proxy settings
win_http_proxy:
proxy: ''
bypass: ''
'''
RETURN = r'''
#
'''

View file

@ -0,0 +1,495 @@
#!powershell
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
$spec = @{
options = @{
auto_detect = @{ type = "bool"; default = $true }
auto_config_url = @{ type = "str" }
proxy = @{ type = "raw" }
bypass = @{ type = "list" }
connection = @{ type = "str" }
}
required_by = @{
bypass = @("proxy")
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$auto_detect = $module.Params.auto_detect
$auto_config_url = $module.Params.auto_config_url
$proxy = $module.Params.proxy
$bypass = $module.Params.bypass
$connection = $module.Params.connection
# Parse the raw value, it should be a Dictionary or String
if ($proxy -is [System.Collections.IDictionary]) {
$valid_keys = [System.Collections.Generic.List`1[String]]@("http", "https", "ftp", "socks")
# Check to make sure we don't have any invalid keys in the dict
$invalid_keys = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $proxy.Keys) {
if ($k -notin $valid_keys) {
$invalid_keys.Add($k)
}
}
if ($invalid_keys.Count -gt 0) {
$invalid_keys = $invalid_keys | Sort-Object # So our test assertion doesn't fail due to random ordering
$module.FailJson("Invalid keys found in proxy: $($invalid_keys -join ', '). Valid keys are $($valid_keys -join ', ').")
}
# Build the proxy string in the form 'protocol=host;', the order of valid_keys is also important
$proxy_list = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $valid_keys) {
if ($proxy.ContainsKey($k)) {
$proxy_list.Add("$k=$($proxy.$k)")
}
}
$proxy = $proxy_list -join ";"
} elseif ($null -ne $proxy) {
$proxy = $proxy.ToString()
}
if ($bypass) {
if ([System.String]::IsNullOrEmpty($proxy)) {
$module.FailJson("missing parameter(s) required by ''bypass'': proxy")
}
$bypass = $bypass -join ';'
}
$win_inet_invoke = @'
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace Ansible.WinINetProxy
{
internal class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class INTERNET_PER_CONN_OPTION_LISTW : IDisposable
{
public UInt32 dwSize;
public IntPtr pszConnection;
public UInt32 dwOptionCount;
public UInt32 dwOptionError;
public IntPtr pOptions;
public INTERNET_PER_CONN_OPTION_LISTW()
{
dwSize = (UInt32)Marshal.SizeOf(this);
}
public void Dispose()
{
if (pszConnection != IntPtr.Zero)
Marshal.FreeHGlobal(pszConnection);
if (pOptions != IntPtr.Zero)
Marshal.FreeHGlobal(pOptions);
GC.SuppressFinalize(this);
}
~INTERNET_PER_CONN_OPTION_LISTW() { this.Dispose(); }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class INTERNET_PER_CONN_OPTIONW : IDisposable
{
public INTERNET_PER_CONN_OPTION dwOption;
public ValueUnion Value;
[StructLayout(LayoutKind.Explicit)]
public class ValueUnion
{
[FieldOffset(0)]
public UInt32 dwValue;
[FieldOffset(0)]
public IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
public void Dispose()
{
// We can't just check if Value.pszValue is not IntPtr.Zero as the union means it could be set even
// when the value is a UInt32 or FILETIME. We check against a known string option type and only free
// the value in those cases.
List<INTERNET_PER_CONN_OPTION> stringOptions = new List<INTERNET_PER_CONN_OPTION>
{
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL },
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS },
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER }
};
if (Value != null && Value.pszValue != IntPtr.Zero && stringOptions.Contains(dwOption))
Marshal.FreeHGlobal(Value.pszValue);
GC.SuppressFinalize(this);
}
~INTERNET_PER_CONN_OPTIONW() { this.Dispose(); }
}
public enum INTERNET_OPTION : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95,
}
public enum INTERNET_PER_CONN_OPTION : uint
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_FLAGS_UI = 10, // IE8+ - Included with Windows 7 and Server 2008 R2
}
[Flags]
public enum PER_CONN_FLAGS : uint
{
PROXY_TYPE_DIRECT = 0x00000001,
PROXY_TYPE_PROXY = 0x00000002,
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004,
PROXY_TYPE_AUTO_DETECT = 0x00000008,
}
}
internal class NativeMethods
{
[DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool InternetQueryOptionW(
IntPtr hInternet,
NativeHelpers.INTERNET_OPTION dwOption,
SafeMemoryBuffer lpBuffer,
ref UInt32 lpdwBufferLength);
[DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool InternetSetOptionW(
IntPtr hInternet,
NativeHelpers.INTERNET_OPTION dwOption,
SafeMemoryBuffer lpBuffer,
UInt32 dwBufferLength);
[DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 RasValidateEntryNameW(
string lpszPhonebook,
string lpszEntry);
}
internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeMemoryBuffer() : base(true) { }
public SafeMemoryBuffer(int cb) : base(true)
{
base.SetHandle(Marshal.AllocHGlobal(cb));
}
public SafeMemoryBuffer(IntPtr handle) : base(true)
{
base.SetHandle(handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
}
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 WinINetProxy
{
private string Connection;
public string AutoConfigUrl;
public bool AutoDetect;
public string Proxy;
public string ProxyBypass;
public WinINetProxy(string connection)
{
Connection = connection;
Refresh();
}
public static bool IsValidConnection(string name)
{
// RasValidateEntryName is used to verify is a name can be a valid phonebook entry. It returns 0 if no
// entry exists and 183 if it already exists. We just need to check if it returns 183 to verify the
// connection name.
return NativeMethods.RasValidateEntryNameW(null, name) == 183;
}
public void Refresh()
{
using (var connFlags = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS_UI))
using (var autoConfigUrl = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL))
using (var server = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER))
using (var bypass = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS))
{
NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options = new NativeHelpers.INTERNET_PER_CONN_OPTIONW[]
{
connFlags, autoConfigUrl, server, bypass
};
try
{
QueryOption(options, Connection);
}
catch (Win32Exception e)
{
if (e.NativeErrorCode == 87) // ERROR_INVALID_PARAMETER
{
// INTERNET_PER_CONN_FLAGS_UI only works for IE8+, try the fallback in case we are still working
// with an ancient version.
connFlags.dwOption = NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS;
QueryOption(options, Connection);
}
else
throw;
}
NativeHelpers.PER_CONN_FLAGS flags = (NativeHelpers.PER_CONN_FLAGS)connFlags.Value.dwValue;
AutoConfigUrl = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL)
? Marshal.PtrToStringUni(autoConfigUrl.Value.pszValue) : null;
AutoDetect = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT);
if (flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_PROXY))
{
Proxy = Marshal.PtrToStringUni(server.Value.pszValue);
ProxyBypass = Marshal.PtrToStringUni(bypass.Value.pszValue);
}
else
{
Proxy = null;
ProxyBypass = null;
}
}
}
public void Set()
{
using (var connFlags = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS_UI))
using (var autoConfigUrl = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL))
using (var server = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER))
using (var bypass = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS))
{
List<NativeHelpers.INTERNET_PER_CONN_OPTIONW> options = new List<NativeHelpers.INTERNET_PER_CONN_OPTIONW>();
// PROXY_TYPE_DIRECT seems to always be set, need to verify
NativeHelpers.PER_CONN_FLAGS flags = NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_DIRECT;
if (AutoDetect)
flags |= NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT;
if (!String.IsNullOrEmpty(AutoConfigUrl))
{
flags |= NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL;
autoConfigUrl.Value.pszValue = Marshal.StringToHGlobalUni(AutoConfigUrl);
}
options.Add(autoConfigUrl);
if (!String.IsNullOrEmpty(Proxy))
{
flags |= NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_PROXY;
server.Value.pszValue = Marshal.StringToHGlobalUni(Proxy);
}
options.Add(server);
if (!String.IsNullOrEmpty(ProxyBypass))
bypass.Value.pszValue = Marshal.StringToHGlobalUni(ProxyBypass);
options.Add(bypass);
connFlags.Value.dwValue = (UInt32)flags;
options.Add(connFlags);
SetOption(options.ToArray(), Connection);
// Tell IE that the proxy settings have been changed.
if (!NativeMethods.InternetSetOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PROXY_SETTINGS_CHANGED,
new SafeMemoryBuffer(IntPtr.Zero),
0))
{
throw new Win32Exception("InternetSetOptionW(INTERNET_OPTION_PROXY_SETTINGS_CHANGED) failed");
}
}
}
internal static NativeHelpers.INTERNET_PER_CONN_OPTIONW CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION option)
{
return new NativeHelpers.INTERNET_PER_CONN_OPTIONW
{
dwOption = option,
Value = new NativeHelpers.INTERNET_PER_CONN_OPTIONW.ValueUnion(),
};
}
internal static void QueryOption(NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection = null)
{
using (NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList = new NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW())
using (SafeMemoryBuffer optionListPtr = MarshalOptionList(optionList, options, connection))
{
UInt32 bufferSize = optionList.dwSize;
if (!NativeMethods.InternetQueryOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
optionListPtr,
ref bufferSize))
{
throw new Win32Exception("InternetQueryOptionW(INTERNET_OPTION_PER_CONNECTION_OPTION) failed");
}
for (int i = 0; i < options.Length; i++)
{
IntPtr opt = IntPtr.Add(optionList.pOptions, i * Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW)));
NativeHelpers.INTERNET_PER_CONN_OPTIONW option = (NativeHelpers.INTERNET_PER_CONN_OPTIONW)Marshal.PtrToStructure(opt,
typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW));
options[i].Value = option.Value;
option.Value = null; // Stops the GC from freeing the same memory twice
}
}
}
internal static void SetOption(NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection = null)
{
using (NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList = new NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW())
using (SafeMemoryBuffer optionListPtr = MarshalOptionList(optionList, options, connection))
{
if (!NativeMethods.InternetSetOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
optionListPtr,
optionList.dwSize))
{
throw new Win32Exception("InternetSetOptionW(INTERNET_OPTION_PER_CONNECTION_OPTION) failed");
}
}
}
internal static SafeMemoryBuffer MarshalOptionList(NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList,
NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection)
{
optionList.pszConnection = Marshal.StringToHGlobalUni(connection);
optionList.dwOptionCount = (UInt32)options.Length;
int optionSize = Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW));
optionList.pOptions = Marshal.AllocHGlobal(optionSize * options.Length);
for (int i = 0; i < options.Length; i++)
{
IntPtr option = IntPtr.Add(optionList.pOptions, i * optionSize);
Marshal.StructureToPtr(options[i], option, false);
}
SafeMemoryBuffer optionListPtr = new SafeMemoryBuffer((int)optionList.dwSize);
Marshal.StructureToPtr(optionList, optionListPtr.DangerousGetHandle(), false);
return optionListPtr;
}
}
}
'@
Add-CSharpType -References $win_inet_invoke -AnsibleModule $module
# We need to validate the connection because WinINet will just silently continue even if the connection does not
# already exist.
if ($null -ne $connection -and -not [Ansible.WinINetProxy.WinINetProxy]::IsValidConnection($connection)) {
$module.FailJson("The connection '$connection' does not exist.")
}
$actual_proxy = New-Object -TypeName Ansible.WinINetProxy.WinINetProxy -ArgumentList @(,$connection)
$module.Diff.before = @{
auto_config_url = $actual_proxy.AutoConfigUrl
auto_detect = $actual_proxy.AutoDetect
bypass = $actual_proxy.ProxyBypass
server = $actual_proxy.Proxy
}
# Make sure an empty string is converted to $null for easier comparisons
if ([String]::IsNullOrEmpty($auto_config_url)) {
$auto_config_url = $null
}
if ([String]::IsNullOrEmpty($proxy)) {
$proxy = $null
}
if ([String]::IsNullOrEmpty($bypass)) {
$bypass = $null
}
# Record the original values in case we need to revert on a failure
$previous_auto_config_url = $actual_proxy.AutoConfigUrl
$previous_auto_detect = $actual_proxy.AutoDetect
$previous_proxy = $actual_proxy.Proxy
$previous_bypass = $actual_proxy.ProxyBypass
$changed = $false
if ($auto_config_url -ne $previous_auto_config_url) {
$actual_proxy.AutoConfigUrl = $auto_config_url
$changed = $true
}
if ($auto_detect -ne $previous_auto_detect) {
$actual_proxy.AutoDetect = $auto_detect
$changed = $true
}
if ($proxy -ne $previous_proxy) {
$actual_proxy.Proxy = $proxy
$changed = $true
}
if ($bypass -ne $previous_bypass) {
$actual_proxy.ProxyBypass = $bypass
$changed = $true
}
if ($changed -and -not $module.CheckMode) {
$actual_proxy.Set()
# Validate that the change was made correctly and revert if it wasn't. THe Set() method won't fail on invalid
# values so we need to check again to make sure all was good
$actual_proxy.Refresh()
if ($actual_proxy.AutoConfigUrl -ne $auto_config_url -or
$actual_proxy.AutoDetect -ne $auto_detect -or
$actual_proxy.Proxy -ne $proxy -or
$actual_proxy.ProxyBypass -ne $bypass) {
$actual_proxy.AutoConfigUrl = $previous_auto_config_url
$actual_proxy.AutoDetect = $previous_auto_detect
$actual_proxy.Proxy = $previous_proxy
$actual_proxy.ProxyBypass = $previous_bypass
$actual_proxy.Set()
$module.FailJson("Unknown error when trying to set auto_config_url '$auto_config_url', proxy '$proxy', or bypass '$bypass'")
}
}
$module.Result.changed = $changed
$module.Diff.after = @{
auto_config_url = $auto_config_url
auto_detect = $auto_detect
bypass = $bypass
proxy = $proxy
}
$module.ExitJson()

View file

@ -0,0 +1,173 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_inet_proxy
version_added: '2.8'
short_description: Manages proxy settings for WinINet and Internet Explorer
description:
- Used to set or remove proxy settings for Windows INet which includes Internet
Explorer.
- WinINet is a framework used by interactive applications to submit web
requests through.
- The proxy settings can also be used by other applications like Firefox,
Chrome, and others but there is no definitive list.
options:
auto_detect:
description:
- Whether to configure WinINet to automatically detect proxy settings
through Web Proxy Auto-Detection C(WPAD).
- This corresponds to the checkbox I(Automatically detect settings) in the
connection settings window.
default: yes
type: bool
auto_config_url:
description:
- The URL of a proxy configuration script.
- Proxy configuration scripts are typically JavaScript files with the
C(.pac) extension that implement the C(FindProxyForURL(url, host)
function.
- Omit, set to null or an empty string to remove the auto config URL.
- This corresponds to the checkbox I(Use automatic configuration script) in
the connection settings window.
type: str
bypass:
description:
- A list of hosts that will bypass the set proxy when being accessed.
- Use C(<local>) to match hostnames that are not fully qualified domain
names. This is useful when needing to connect to intranet sites using
just the hostname. If defined, this should be the last entry in the
bypass list.
- Use C(<-loopback>) to stop automatically bypassing the proxy when
connecting through any loopback address like C(127.0.0.1), C(localhost),
or the local hostname.
- Omit, set to null or an empty string/list to remove the bypass list.
- If this is set then I(proxy) must also be set.
type: list
connection:
description:
- The name of the IE connection to set the proxy settings for.
- These are the connections under the I(Dial-up and Virtual Private Network)
header in the IE settings.
- When ommited, the default LAN connection is used.
type: str
proxy:
description:
- A string or dict that specifies the proxy to be set.
- If setting a string, should be in the form C(hostname), C(hostname:port),
or C(protocol=hostname:port).
- If the port is undefined, the default port for the protocol in use is
used.
- If setting a dict, the keys should be the protocol and the values should
be the hostname and/or port for that protocol.
- Valid protocols are C(http), C(https), C(ftp), and C(socks).
- Omit, set to null or an empty string to remove the proxy settings.
notes:
- This is not the same as the proxy settings set in WinHTTP through the
C(netsh) command. Use the M(win_http_proxy) module to manage that instead.
- These settings are by default set per user and not system wide. A registry
property must be set independently from this module if you wish to apply the
proxy for all users. See examples for more detail.
- If per user proxy settings are desired, use I(become) to become any local
user on the host. No password is needed to be set for this to work.
- If the proxy requires authentication, set the credentials using the
M(win_credential) module. This requires I(become) to be used so the
credential store can be accessed.
seealso:
- module: win_http_proxy
- module: win_credential
author:
- Jordan Borean (@jborean93)
'''
EXAMPLES = r'''
# This should be set before running the win_inet_proxy module
- name: Configure IE proxy settings to apply to all users
win_regedit:
path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings
name: ProxySettingsPerUser
data: 0
type: dword
state: present
# This should be set before running the win_inet_proxy module
- name: Configure IE proxy settings to apply per user
win_regedit:
path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings
name: ProxySettingsPerUser
data: 1
type: dword
state: present
- name: Configure IE proxy to use auto detected settings without an explicit proxy
win_inet_proxy:
auto_detect: yes
- name: Configure IE proxy to use auto detected settings with a configuration script
win_inet_proxy:
auto_detect: yes
auto_config_url: http://proxy.ansible.com/proxy.pac
- name: Configure IE to use explicit proxy host
win_inet_proxy:
auto_detect: yes
proxy: ansible.proxy
- name: Configure IE to use explicit proxy host with port and without auto detection
win_inet_proxy:
auto_detect: no
proxy: ansible.proxy:8080
- name: Configure IE to use a specific proxy per protocol
win_inet_proxy:
proxy:
http: ansible.proxy:8080
https: ansible.proxy:8443
- name: Configure IE to use a specific proxy per protocol using a string
win_inet_proxy:
proxy: http=ansible.proxy:8080;https=ansible.proxy:8443
- name: Set a proxy with a bypass list
win_inet_proxy:
proxy: ansible.proxy
bypass:
- server1
- server2
- <-loopback>
- <local>
- name: Remove any explicit proxies that are set
win_inet_proxy:
proxy: ''
bypass: ''
# This should be done after setting the IE proxy with win_inet_proxy
- name: Import IE proxy configuration to WinHTTP
win_http_proxy:
source: ie
# Explicit credentials can only be set per user and require become to work
- name: Set credential to use for proxy auth
win_credential:
name: ansible.proxy # The name should be the FQDN of the proxy host
type: generic_password
username: proxyuser
secret: proxypass
state: present
become: yes
become_user: '{{ ansible_user }}'
become_method: runas
'''
RETURN = r'''
#
'''

View file

@ -0,0 +1 @@
shippable/windows/group4

View file

@ -0,0 +1,14 @@
---
- name: make sure we start the tests with no proxy set
win_http_proxy:
- block:
- name: run tests
include_tasks: tests.yml
always:
- name: remove any explicit proxy settings
win_http_proxy:
- name: reset WinINet proxy settings
win_inet_proxy:

View file

@ -0,0 +1,265 @@
---
- name: ensure we fail when proxy is not set with bypass
win_http_proxy:
bypass: abc
register: fail_bypass
failed_when: 'fail_bypass.msg != "missing parameter(s) required by ''bypass'': proxy"'
- name: ensure we fail when proxy and source is set
win_http_proxy:
proxy: proxy
source: ie
register: fail_source
failed_when: 'fail_source.msg != "parameters are mutually exclusive: proxy, source"'
- name: ensure we fail if an invalid protocol is specified
win_http_proxy:
proxy:
fail1: fail
fail2: fail
register: fail_protocol
failed_when: 'fail_protocol.msg != "Invalid keys found in proxy: fail1, fail2. Valid keys are http, https, ftp, socks."'
# WinHTTP does not validate on set, this ensures the module checks and revert any failed attempts at setting the proxy
# FIXME: Only certain hosts seem to have a strict winhttp definition, we can't run this in CI for now
#- name: ensure we fail if invalid value is set
# win_http_proxy:
# proxy: fake=proxy
# register: fail_invalid
# failed_when: fail_invalid.msg != "Unknown error when trying to set proxy 'fake=proxy' or bypass ''"
#
#- name: check proxy is still set to Direct access
# win_command: netsh winhttp show proxy
# register: fail_invalid_actual
# failed_when: fail_invalid_actual.stdout_lines[3]|trim != "Direct access (no proxy server)."
- name: set a proxy using a string (check)
win_http_proxy:
proxy: proxyhost
register: proxy_str_check
check_mode: True
- name: get result of set a proxy using a string (check)
win_command: netsh winhttp show proxy
register: proxy_str_actual_check
- name: assert set a proxy using a string (check)
assert:
that:
- proxy_str_check is changed
- proxy_str_actual_check.stdout_lines[3]|trim == "Direct access (no proxy server)."
- name: set a proxy using a string
win_http_proxy:
proxy: proxyhost
register: proxy_str
- name: get result of set a proxy using a string
win_command: netsh winhttp show proxy
register: proxy_str_actual
- name: assert set a proxy using a string
assert:
that:
- proxy_str is changed
- 'proxy_str_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost"'
- 'proxy_str_actual.stdout_lines[4]|trim == "Bypass List : (none)"'
- name: set a proxy using a string (idempotent)
win_http_proxy:
proxy: proxyhost
register: proxy_str_again
- name: assert set a proxy using a string (idempotent)
assert:
that:
- not proxy_str_again is changed
- name: change a proxy and set bypass (check)
win_http_proxy:
proxy: proxyhost:8080
bypass:
- abc
- def
- <local>
register: change_proxy_check
check_mode: True
- name: get result of change a proxy and set bypass (check)
win_command: netsh winhttp show proxy
register: change_proxy_actual_check
- name: assert change a proxy and set bypass (check)
assert:
that:
- change_proxy_check is changed
- 'change_proxy_actual_check.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost"'
- 'change_proxy_actual_check.stdout_lines[4]|trim == "Bypass List : (none)"'
- name: change a proxy and set bypass
win_http_proxy:
proxy: proxyhost:8080
bypass:
- abc
- def
- <local>
register: change_proxy
- name: get result of change a proxy and set bypass
win_command: netsh winhttp show proxy
register: change_proxy_actual
- name: assert change a proxy and set bypass
assert:
that:
- change_proxy is changed
- 'change_proxy_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"'
- 'change_proxy_actual.stdout_lines[4]|trim == "Bypass List : abc;def;<local>"'
- name: change a proxy and set bypass (idempotent)
win_http_proxy:
proxy: proxyhost:8080
bypass: abc,def,<local>
register: change_proxy_again
- name: assert change a proxy and set bypass (idempotent)
assert:
that:
- not change_proxy_again is changed
- name: change bypass list
win_http_proxy:
proxy: proxyhost:8080
bypass:
- abc
- <-loopback>
register: change_bypass
- name: get result of change bypass list
win_command: netsh winhttp show proxy
register: change_bypass_actual
- name: assert change bypass list
assert:
that:
- change_bypass is changed
- 'change_bypass_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"'
- 'change_bypass_actual.stdout_lines[4]|trim == "Bypass List : abc;<-loopback>"'
- name: remove proxy without options (check)
win_http_proxy:
register: remove_proxy_check
check_mode: yes
- name: get result of remove proxy without options (check)
win_command: netsh winhttp show proxy
register: remove_proxy_actual_check
- name: assert remove proxy without options (check)
assert:
that:
- remove_proxy_check is changed
- 'remove_proxy_actual_check.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"'
- 'remove_proxy_actual_check.stdout_lines[4]|trim == "Bypass List : abc;<-loopback>"'
- name: remove proxy without options
win_http_proxy:
register: remove_proxy
- name: get result of remove proxy without options
win_command: netsh winhttp show proxy
register: remove_proxy_actual
- name: assert remove proxy without options
assert:
that:
- remove_proxy is changed
- remove_proxy_actual.stdout_lines[3]|trim == "Direct access (no proxy server)."
- name: remove proxy without options (idempotent)
win_http_proxy:
register: remove_proxy_again
- name: assert remove proxy without options (idempotent)
assert:
that:
- not remove_proxy_again is changed
- name: set proxy with dictionary
win_http_proxy:
proxy:
http: proxy:8080
https: proxy:8443
ftp: proxy:821
socks: proxy:888
register: set_dict
- name: get result of set proxy with dictionary
win_command: netsh winhttp show proxy
register: set_dict_actual
- name: assert set proxy with dictionary
assert:
that:
- set_dict is changed
- 'set_dict_actual.stdout_lines[3]|trim == "Proxy Server(s) : http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888"'
- 'set_dict_actual.stdout_lines[4]|trim == "Bypass List : (none)"'
- name: set proxy protocol with str
win_http_proxy:
proxy: http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888
register: set_str_protocol
- name: assert set proxy protocol with str
assert:
that:
- not set_str_protocol is changed
- name: remove proxy with empty string
win_http_proxy:
proxy: ''
register: remove_empty_str
- name: get result of remove proxy with empty string
win_command: netsh winhttp show proxy
register: remove_empty_str_actual
- name: assert remove proxy with empty string
assert:
that:
- remove_empty_str is changed
- remove_empty_str_actual.stdout_lines[3]|trim == "Direct access (no proxy server)."
- name: set explicit proxy for WinINet
win_inet_proxy:
proxy: proxyhost:8080
bypass:
- abc
- def
- <local>
- name: import proxy from IE
win_http_proxy:
source: ie
register: import_ie
- name: get result of import proxy from IE
win_command: netsh winhttp show proxy
register: import_ie_actual
- name: assert import proxy from IE
assert:
that:
- import_ie is changed
- 'import_ie_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"'
- 'import_ie_actual.stdout_lines[4]|trim == "Bypass List : abc;def;<local>"'
- name: import proxy from IE (idempotent)
win_http_proxy:
source: ie
register: import_ie_again
- name: assert import proxy from IE (idempotent)
assert:
that:
- not import_ie_again is changed

View file

@ -0,0 +1 @@
shippable/windows/group4

View file

@ -0,0 +1,275 @@
#!powershell
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
$spec = @{
options = @{
connection = @{ type = "str" }
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$connection = $module.Params.connection
$win_inet_invoke = @'
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace Ansible.WinINetProxyInfo
{
internal class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class INTERNET_PER_CONN_OPTION_LISTW : IDisposable
{
public UInt32 dwSize;
public IntPtr pszConnection;
public UInt32 dwOptionCount;
public UInt32 dwOptionError;
public IntPtr pOptions;
public INTERNET_PER_CONN_OPTION_LISTW()
{
dwSize = (UInt32)Marshal.SizeOf(this);
}
public void Dispose()
{
if (pszConnection != IntPtr.Zero)
Marshal.FreeHGlobal(pszConnection);
if (pOptions != IntPtr.Zero)
Marshal.FreeHGlobal(pOptions);
GC.SuppressFinalize(this);
}
~INTERNET_PER_CONN_OPTION_LISTW() { this.Dispose(); }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class INTERNET_PER_CONN_OPTIONW : IDisposable
{
public INTERNET_PER_CONN_OPTION dwOption;
public ValueUnion Value;
[StructLayout(LayoutKind.Explicit)]
public class ValueUnion
{
[FieldOffset(0)]
public UInt32 dwValue;
[FieldOffset(0)]
public IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
public void Dispose()
{
// We can't just check if Value.pszValue is not IntPtr.Zero as the union means it could be set even
// when the value is a UInt32 or FILETIME. We check against a known string option type and only free
// the value in those cases.
List<INTERNET_PER_CONN_OPTION> stringOptions = new List<INTERNET_PER_CONN_OPTION>
{
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL },
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS },
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER }
};
if (Value != null && Value.pszValue != IntPtr.Zero && stringOptions.Contains(dwOption))
Marshal.FreeHGlobal(Value.pszValue);
GC.SuppressFinalize(this);
}
~INTERNET_PER_CONN_OPTIONW() { this.Dispose(); }
}
public enum INTERNET_OPTION : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
}
public enum INTERNET_PER_CONN_OPTION : uint
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_FLAGS_UI = 10, // IE8+ - Included with Windows 7 and Server 2008 R2
}
[Flags]
public enum PER_CONN_FLAGS : uint
{
PROXY_TYPE_DIRECT = 0x00000001,
PROXY_TYPE_PROXY = 0x00000002,
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004,
PROXY_TYPE_AUTO_DETECT = 0x00000008,
}
}
internal class NativeMethods
{
[DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool InternetQueryOptionW(
IntPtr hInternet,
NativeHelpers.INTERNET_OPTION dwOption,
SafeMemoryBuffer lpBuffer,
ref UInt32 lpdwBufferLength);
}
internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeMemoryBuffer() : base(true) { }
public SafeMemoryBuffer(int cb) : base(true)
{
base.SetHandle(Marshal.AllocHGlobal(cb));
}
public SafeMemoryBuffer(IntPtr handle) : base(true)
{
base.SetHandle(handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
}
public class WinINetProxy
{
private string Connection;
public string AutoConfigUrl;
public bool AutoDetect;
public string Proxy;
public string ProxyBypass;
public WinINetProxy(string connection)
{
Connection = connection;
Refresh();
}
public void Refresh()
{
using (var connFlags = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS_UI))
using (var autoConfigUrl = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL))
using (var server = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER))
using (var bypass = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS))
{
NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options = new NativeHelpers.INTERNET_PER_CONN_OPTIONW[]
{
connFlags, autoConfigUrl, server, bypass
};
try
{
QueryOption(options, Connection);
}
catch (Win32Exception e)
{
if (e.NativeErrorCode == 87) // ERROR_INVALID_PARAMETER
{
// INTERNET_PER_CONN_FLAGS_UI only works for IE8+, try the fallback in case we are still working
// with an ancient version.
connFlags.dwOption = NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS;
QueryOption(options, Connection);
}
else
throw;
}
NativeHelpers.PER_CONN_FLAGS flags = (NativeHelpers.PER_CONN_FLAGS)connFlags.Value.dwValue;
AutoConfigUrl = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL)
? Marshal.PtrToStringUni(autoConfigUrl.Value.pszValue) : null;
AutoDetect = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT);
if (flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_PROXY))
{
Proxy = Marshal.PtrToStringUni(server.Value.pszValue);
ProxyBypass = Marshal.PtrToStringUni(bypass.Value.pszValue);
}
else
{
Proxy = null;
ProxyBypass = null;
}
}
}
internal static NativeHelpers.INTERNET_PER_CONN_OPTIONW CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION option)
{
return new NativeHelpers.INTERNET_PER_CONN_OPTIONW
{
dwOption = option,
Value = new NativeHelpers.INTERNET_PER_CONN_OPTIONW.ValueUnion(),
};
}
internal static void QueryOption(NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection = null)
{
using (NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList = new NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW())
using (SafeMemoryBuffer optionListPtr = MarshalOptionList(optionList, options, connection))
{
UInt32 bufferSize = optionList.dwSize;
if (!NativeMethods.InternetQueryOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
optionListPtr,
ref bufferSize))
{
throw new Win32Exception();
}
for (int i = 0; i < options.Length; i++)
{
IntPtr opt = IntPtr.Add(optionList.pOptions, i * Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW)));
NativeHelpers.INTERNET_PER_CONN_OPTIONW option = (NativeHelpers.INTERNET_PER_CONN_OPTIONW)Marshal.PtrToStructure(opt,
typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW));
options[i].Value = option.Value;
option.Value = null; // Stops the GC from freeing the same memory twice
}
}
}
internal static SafeMemoryBuffer MarshalOptionList(NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList,
NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection)
{
optionList.pszConnection = Marshal.StringToHGlobalUni(connection);
optionList.dwOptionCount = (UInt32)options.Length;
int optionSize = Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW));
optionList.pOptions = Marshal.AllocHGlobal(optionSize * options.Length);
for (int i = 0; i < options.Length; i++)
{
IntPtr option = IntPtr.Add(optionList.pOptions, i * optionSize);
Marshal.StructureToPtr(options[i], option, false);
}
SafeMemoryBuffer optionListPtr = new SafeMemoryBuffer((int)optionList.dwSize);
Marshal.StructureToPtr(optionList, optionListPtr.DangerousGetHandle(), false);
return optionListPtr;
}
}
}
'@
Add-CSharpType -References $win_inet_invoke -AnsibleModule $module
$proxy = New-Object -TypeName Ansible.WinINetProxyInfo.WinINetProxy -ArgumentList @(,$connection)
$module.Result.auto_config_url = $proxy.AutoConfigUrl
$module.Result.auto_detect = $proxy.AutoDetect
$module.Result.proxy = $proxy.Proxy
$module.Result.bypass = $proxy.ProxyBypass
$module.ExitJson()

View file

@ -0,0 +1,521 @@
#!powershell
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
# This is a very basic skeleton of a possible Windows module for managing RAS connections. It is mostly barebones
# to enable testing for win_inet_proxy but I've done a bit of extra work in the PInvoke space to possible expand
# sometime in the future.
$spec = @{
options = @{
device_type = @{
type = "str"
choices = @("atm", "framerelay", "generic", "rda", "isdn", "modem", "pad",
"parallel", "pppoe", "vpn", "serial", "sonet", "sw56", "x25")
}
device_name = @{ type = "str" }
framing_protocol = @{ type = "str"; choices = @("ppp", "ras", "slip") }
name = @{ type = "str"; required = $true }
options = @{ type = "list" }
state = @{ type = "str"; choices = @("absent", "present"); default = "present" }
type = @{ type = "str"; choices = @("broadband", "direct", "phone", "vpn")}
}
required_if = @(
,@("state", "present", @("type", "device_name", "device_type", "framing_protocol"))
)
supports_check_mode = $false
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$device_type = $module.Params.device_type
$device_name = $module.Params.device_name
$framing_protocol = $module.Params.framing_protocol
$name = $module.Params.name
$options = $module.Params.options
$state = $module.Params.state
$type = $module.Params.type
$module.Result.guid = [System.Guid]::Empty
$win_ras_invoke = @'
using System;
using System.Runtime.InteropServices;
namespace Ansible.WinPhonebookEntry
{
public class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class RASENTRYW
{
public UInt32 dwSize;
public RasEntryOptions dwfOptions;
public UInt32 dwCountryId;
public UInt32 dwCountryCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] public string szAreaCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] public string szLocalPhoneNumber;
public UInt32 dwAlternateOffset;
public RASIPADDR ipaddr;
public RASIPADDR ipaddrDns;
public RASIPADDR ipaddrDnsAlt;
public RASIPADDR ipaddrWins;
public RASIPADDR ipaddrWinsAlt;
public UInt32 dwFrameSize;
public RasNetProtocols dwfNetProtocols;
public RasFramingProtocol dwFramingProtocol;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szScript;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szAutodialDll;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szAutodialFunc;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)] public string szDeviceType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] public string szDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string szX25PadType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string szX25Address;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string szX25Facilities;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string szX25UserData;
public UInt32 dwChannels;
public UInt32 dwReserved1;
public UInt32 dwReserved2;
public UInt32 dwSubEntries;
public RasDialMode dwDialMode;
public UInt32 dwDialExtraPercent;
public UInt32 dwDialExtraSampleSeconds;
public UInt32 dwHangUpExtraPercent;
public UInt32 dwHangUpExtraSampleSeconds;
public UInt32 dwIdleDisconnectSeconds;
public RasEntryTypes dwType;
public RasEntryEncryption dwEntryptionType;
public UInt32 dwCustomAuthKey;
public Guid guidId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szCustomDialDll;
public RasVpnStrategy dwVpnStrategy;
public RasEntryOptions2 dwfOptions2;
public UInt32 dwfOptions3;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szDnsSuffix;
public UInt32 dwTcpWindowSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szPrerequisitePbk;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szPrerequisiteEntry;
public UInt32 dwRedialCount;
public UInt32 dwRedialPause;
public RASIPV6ADDR ipv6addrDns;
public RASIPV6ADDR ipv6addrDnsAlt;
public UInt32 dwIPv4InterfaceMatrix;
public UInt32 dwIPv6InterfaceMatrix;
// Server 2008 R2 / Windows 7+
// We cannot include these fields when running in Server 2008 as it will break the SizeOf calc of the struct
#if !LONGHORN
public RASIPV6ADDR ipv6addr;
public UInt32 dwIPv6PrefixLength;
public UInt32 dwNetworkOutageTime;
#endif
public RASENTRYW()
{
this.dwSize = (UInt32)Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RASIPADDR
{
public byte a;
public byte b;
public byte c;
public byte d;
}
[StructLayout(LayoutKind.Sequential)]
public struct RASIPV6ADDR
{
byte a;
byte b;
byte c;
byte d;
byte e;
byte f;
byte g;
byte h;
byte i;
byte j;
byte k;
byte l;
byte m;
byte n;
byte o;
byte p;
}
public enum RasDialMode : uint
{
RASEDM_DialAll = 1,
RASEDM_DialAsNeeded = 2,
}
public enum RasEntryEncryption : uint
{
ET_None = 0,
ET_Require = 1,
ET_RequireMax = 2,
ET_Optional = 3
}
[Flags]
public enum RasEntryOptions : uint
{
RASEO_UseCountryAndAreaCodes = 0x00000001,
RASEO_SpecificIpAddr = 0x00000002,
RASEO_SpecificNameServers = 0x00000004,
RASEO_IpHeaderCompression = 0x00000008,
RASEO_RemoteDefaultGateway = 0x00000010,
RASEO_DisableLcpExtensions = 0x00000020,
RASEO_TerminalBeforeDial = 0x00000040,
RASEO_TerminalAfterDial = 0x00000080,
RASEO_ModemLights = 0x00000100,
RASEO_SwCompression = 0x00000200,
RASEO_RequireEncrptedPw = 0x00000400,
RASEO_RequireMsEncrptedPw = 0x00000800,
RASEO_RequireDataEncrption = 0x00001000,
RASEO_NetworkLogon = 0x00002000,
RASEO_UseLogonCredentials = 0x00004000,
RASEO_PromoteAlternates = 0x00008000,
RASEO_SecureLocalFiles = 0x00010000,
RASEO_RequireEAP = 0x00020000,
RASEO_RequirePAP = 0x00040000,
RASEO_RequireSPAP = 0x00080000,
RASEO_Custom = 0x00100000,
RASEO_PreviewPhoneNumber = 0x00200000,
RASEO_SharedPhoneNumbers = 0x00800000,
RASEO_PreviewUserPw = 0x01000000,
RASEO_PreviewDomain = 0x02000000,
RASEO_ShowDialingProgress = 0x04000000,
RASEO_RequireCHAP = 0x08000000,
RASEO_RequireMsCHAP = 0x10000000,
RASEO_RequireMsCHAP2 = 0x20000000,
RASEO_RequireW95MSCHAP = 0x40000000,
RASEO_CustomScript = 0x80000000,
}
[Flags]
public enum RasEntryOptions2 : uint
{
RASEO2_None = 0x00000000,
RASEO2_SecureFileAndPrint = 0x00000001,
RASEO2_SecureClientForMSNet = 0x00000002,
RASEO2_DontNegotiateMultilink = 0x00000004,
RASEO2_DontUseRasCredentials = 0x00000008,
RASEO2_UsePreSharedKey = 0x00000010,
RASEO2_Internet = 0x00000020,
RASEO2_DisableNbtOverIP = 0x00000040,
RASEO2_UseGlobalDeviceSettings = 0x00000080,
RASEO2_ReconnectIfDropped = 0x00000100,
RASEO2_SharePhoneNumbers = 0x00000200,
RASEO2_SecureRoutingCompartment = 0x00000400,
RASEO2_UseTypicalSettings = 0x00000800,
RASEO2_IPv6SpecificNameServers = 0x00001000,
RASEO2_IPv6RemoteDefaultGateway = 0x00002000,
RASEO2_RegisterIpWithDNS = 0x00004000,
RASEO2_UseDNSSuffixForRegistration = 0x00008000,
RASEO2_IPv4ExplicitMetric = 0x00010000,
RASEO2_IPv6ExplicitMetric = 0x00020000,
RASEO2_DisableIKENameEkuCheck = 0x00040000,
// Server 2008 R2 / Windows 7+
RASEO2_DisableClassBasedStaticRoute = 0x00800000,
RASEO2_SpecificIPv6Addr = 0x01000000,
RASEO2_DisableMobility = 0x02000000,
RASEO2_RequireMachineCertificates = 0x04000000,
// Server 2012 / Windows 8+
RASEO2_UsePreSharedKeyForIkev2Initiator = 0x00800000,
RASEO2_UsePreSharedKeyForIkev2Responder = 0x01000000,
RASEO2_CacheCredentials = 0x02000000,
// Server 2012 R2 / Windows 8.1+
RASEO2_AutoTriggerCapable = 0x04000000,
RASEO2_IsThirdPartyProfile = 0x08000000,
RASEO2_AuthTypeIsOtp = 0x10000000,
// Server 2016 / Windows 10+
RASEO2_IsAlwaysOn = 0x20000000,
RASEO2_IsPrivateNetwork = 0x40000000,
}
public enum RasEntryTypes : uint
{
RASET_Phone = 1,
RASET_Vpn = 2,
RASET_Direct = 3,
RASET_Internet = 4,
RASET_Broadband = 5,
}
public enum RasFramingProtocol : uint
{
RASFP_Ppp = 0x00000001,
RASFP_Slip = 0x00000002,
RASFP_Ras = 0x00000004
}
[Flags]
public enum RasNetProtocols : uint
{
RASNP_NetBEUI = 0x00000001,
RASNP_Ipx = 0x00000002,
RASNP_Ip = 0x00000004,
RASNP_Ipv6 = 0x00000008
}
public enum RasVpnStrategy : uint
{
VS_Default = 0,
VS_PptpOnly = 1,
VS_PptpFirst = 2,
VS_L2tpOnly = 3,
VS_L2tpFirst = 4,
VS_SstpOnly = 5,
VS_SstpFirst = 6,
VS_Ikev2Only = 7,
VS_Ikev2First = 8,
VS_GREOnly = 9,
VS_PptpSstp = 12,
VS_L2tpSstp = 13,
VS_Ikev2Sstp = 14,
}
}
internal class NativeMethods
{
[DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 RasDeleteEntryW(
string lpszPhonebook,
string lpszEntry);
[DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 RasGetEntryPropertiesW(
string lpszPhonebook,
string lpszEntry,
[In, Out] NativeHelpers.RASENTRYW lpRasEntry,
ref UInt32 dwEntryInfoSize,
IntPtr lpbDeviceInfo,
ref UInt32 dwDeviceInfoSize);
[DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 RasSetEntryPropertiesW(
string lpszPhonebook,
string lpszEntry,
NativeHelpers.RASENTRYW lpRasEntry,
UInt32 dwEntryInfoSize,
IntPtr lpbDeviceInfo,
UInt32 dwDeviceInfoSize);
[DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 RasValidateEntryNameW(
string lpszPhonebook,
string lpszEntry);
}
public class Phonebook
{
public static void CreateEntry(string entry, NativeHelpers.RASENTRYW details)
{
UInt32 res = NativeMethods.RasSetEntryPropertiesW(null, entry, details,
details.dwSize, IntPtr.Zero, 0);
if (res != 0)
throw new Exception(String.Format("RasSetEntryPropertiesW({0}) failed {1}", entry, res));
}
public static void DeleteEntry(string entry)
{
UInt32 res = NativeMethods.RasDeleteEntryW(null, entry);
if (res != 0)
throw new Exception(String.Format("RasDeleteEntryW({0}) failed {1}", entry, res));
}
public static NativeHelpers.RASENTRYW GetEntry(string entry)
{
NativeHelpers.RASENTRYW details = new NativeHelpers.RASENTRYW();
UInt32 dwEntryInfoSize = details.dwSize;
UInt32 dwDeviceInfoSize = 0;
UInt32 res = NativeMethods.RasGetEntryPropertiesW(null, entry, details, ref dwEntryInfoSize,
IntPtr.Zero, ref dwDeviceInfoSize);
if (res != 0)
throw new Exception(String.Format("RasGetEntryPropertiesW({0}) failed {1}", entry, res));
return details;
}
public static bool IsValidEntry(string entry)
{
// 183 == ENTRY_ALREADY_EXISTS
return NativeMethods.RasValidateEntryNameW(null, entry) == 183;
}
}
}
'@
$add_type_params = @{
Reference = $win_ras_invoke
AnsibleModule = $module
}
# We need to set a custom compile option when running on Server 2008 due to the change in the RASENTRYW structure
$os_version = [Version](Get-Item -LiteralPath $env:SystemRoot\System32\kernel32.dll).VersionInfo.ProductVersion
if ($os_version -lt [Version]"6.1") {
$add_type_params.CompileSymbols = @("LONGHORN")
}
Add-CSharpType @add_type_params
$exists = [Ansible.WinPhonebookEntry.Phonebook]::IsValidEntry($name)
if ($exists) {
$entry = [Ansible.WinPhonebookEntry.Phonebook]::GetEntry($name)
$module.Result.guid = $entry.guidId
}
if ($state -eq "present") {
# Convert the input values to enum values
$expected_type = switch ($type) {
"broadband" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Broadband }
"direct" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Direct }
"phone" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Phone }
"vpn" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Vpn }
}
$expected_framing_protocol = switch ($framing_protocol) {
"ppp" { [Ansible.WinPhonebookEntry.NativeHelpers+RasFramingProtocol]::RASFP_Ppp }
"ras" { [Ansible.WinPhonebookEntry.NativeHelpers+RasFramingProtocol]::RASFP_Ras }
"slip" { [Ansible.WinPhonebookEntry.NativeHelpers+RasFramingProtocol]::RASFP_Slip }
}
$expected_options1 = [System.Collections.Generic.List`1[String]]@()
$expected_options2 = [System.Collections.Generic.List`1[String]]@()
$invalid_options = [System.Collections.Generic.List`1[String]]@()
foreach ($option in $options) {
# See https://msdn.microsoft.com/en-us/25c46850-4fb7-47a9-9645-139f0e869559 for more info on the options
# TODO: some of these options are set to indicate entries in RASENTRYW, we should automatically add them
# based on the input values.
switch ($option) {
# dwfOptions
"use_country_and_area_codes" { $expected_options1.Add("RASEO_UseCountryAndAreaCode") }
"specific_ip_addr" { $expected_options1.Add("RASEO_SpecificIpAddr") }
"specific_name_servers" { $expected_options1.Add("RASEO_SpecificNameServers") }
"ip_header_compression" { $expected_options1.Add("RASEO_IpHeaderCompression") }
"remote_default_gateway" { $expected_options1.Add("RASEO_RemoteDefaultGateway") }
"disable_lcp_extensions" { $expected_options1.Add("RASEO_DisableLcpExtensions") }
"terminal_before_dial" { $expected_options1.Add("RASEO_TerminalBeforeDial") }
"terminal_after_dial" { $expected_options1.Add("RASEO_TerminalAfterDial") }
"modem_lights" { $expected_options1.Add("RASEO_ModemLights") }
"sw_compression" { $expected_options1.Add("RASEO_SwCompression") }
"require_encrypted_password" { $expected_options1.Add("RASEO_RequireEncrptedPw") }
"require_ms_encrypted_password" { $expected_options1.Add("RASEO_RequireMsEncrptedPw") }
"require_data_encryption" { $expected_options1.Add("RASEO_RequireDataEncrption") }
"network_logon" { $expected_options1.Add("RASEO_NetworkLogon") }
"use_logon_credentials" { $expected_options1.Add("RASEO_UseLogonCredentials") }
"promote_alternates" { $expected_options1.Add("RASEO_PromoteAlternates") }
"secure_local_files" { $expected_options1.Add("RASEO_SecureLocalFiles") }
"require_eap" { $expected_options1.Add("RASEO_RequireEAP") }
"require_pap" { $expected_options1.Add("RASEO_RequirePAP") }
"require_spap" { $expected_options1.Add("RASEO_RequireSPAP") }
"custom" { $expected_options1.Add("RASEO_Custom") }
"preview_phone_number" { $expected_options1.Add("RASEO_PreviewPhoneNumber") }
"shared_phone_numbers" { $expected_options1.Add("RASEO_SharedPhoneNumbers") }
"preview_user_password" { $expected_options1.Add("RASEO_PreviewUserPw") }
"preview_domain" { $expected_options1.Add("RASEO_PreviewDomain") }
"show_dialing_progress" { $expected_options1.Add("RASEO_ShowDialingProgress") }
"require_chap" { $expected_options1.Add("RASEO_RequireCHAP") }
"require_ms_chap" { $expected_options1.Add("RASEO_RequireMsCHAP") }
"require_ms_chap2" { $expected_options1.Add("RASEO_RequireMsCHAP2") }
"require_w95_ms_chap" { $expected_options1.Add("RASEO_RequireW95MSCHAP") }
"custom_script" { $expected_options1.Add("RASEO_CustomScript") }
# dwfOptions2
"secure_file_and_print" { $expected_options2.Add("RASEO2_SecureFileAndPrint") }
"secure_client_for_ms_net" { $expected_options2.Add("RASEO2_SecureClientForMSNet") }
"dont_negotiate_multilink" { $expected_options2.Add("RASEO2_DontNegotiateMultilink") }
"dont_use_ras_credential" { $expected_options2.Add("RASEO2_DontUseRasCredentials") }
"use_pre_shared_key" { $expected_options2.Add("RASEO2_UsePreSharedKey") }
"internet" { $expected_options2.Add("RASEO2_Internet") }
"disable_nbt_over_ip" { $expected_options2.Add("RASEO2_DisableNbtOverIP") }
"use_global_device_settings" { $expected_options2.Add("RASEO2_UseGlobalDeviceSettings") }
"reconnect_if_dropped" { $expected_options2.Add("RASEO2_ReconnectIfDropped") }
"share_phone_numbers" { $expected_options2.Add("RASEO2_SharePhoneNumbers") }
"secure_routing_compartment" { $expected_options2.Add("RASEO2_SecureRoutingCompartment") }
"use_typical_settings" { $expected_options2.Add("RASEO2_UseTypicalSettings") }
"ipv6_specific_name_servers" { $expected_options2.Add("RASEO2_IPv6SpecificNameServers") }
"ipv6_remote_default_gateway" { $expected_options2.Add("RASEO2_IPv6RemoteDefaultGateway") }
"register_ip_with_dns" { $expected_options2.Add("RASEO2_RegisterIpWithDNS") }
"use_dns_suffix_for_registration" { $expected_options2.Add("RASEO2_UseDNSSuffixForRegistration") }
"ipv4_explicit_metric" { $expected_options2.Add("RASEO2_IPv4ExplicitMetric") }
"ipv6_explicit_metric" { $expected_options2.Add("RASEO2_IPv6ExplicitMetric") }
"disable_ike_name_eku_check" { $expected_options2.Add("RASEO2_DisableIKENameEkuCheck") }
# TODO: Version check for the below, OS Version >= 6.1
"disable_class_based_static_route" { $expected_options2.Add("RASEO2_DisableClassBasedStaticRoute") }
"specific_ipv6_addr" { $expected_options2.Add("RASEO2_SpecificIPv6Addr") }
"disable_mobility" { $expected_options2.Add("RASEO2_DisableMobility") }
"require_machine_certificates" { $expected_options2.Add("RASEO2_RequireMachineCertificates") }
# TODO: Version check for the below, OS Version >= 6.2
"use_pre_shared_key_for_ikev2_initiator" { $expected_options2.Add("RASEO2_UsePreSharedKeyForIkev2Initiator") }
"use_pre_shared_key_for_ikev2_responder" { $expected_options2.Add("RASEO2_UsePreSharedKeyForIkev2Responder") }
"cache_credentials" { $expected_options2.Add("RASEO2_CacheCredentials") }
# TODO: Version check for the below, OS Version >= 6.3
"auto_trigger_capable" { $expected_options2.Add("RASEO2_AutoTriggerCapable") }
"is_third_party_profile" { $expected_options2.Add("RASEO2_IsThirdPartyProfile") }
"auth_type_is_otp" { $expected_options2.Add("RASEO2_AuthTypeIsOtp") }
# TODO: Version check for the below, OS Version >= 10.0
"is_always_on" { $expected_options2.Add("RASEO2_IsAlwaysOn") }
"is_private_network" { $expected_options2.Add("RASEO2_IsPrivateNetwork") }
default { $invalid_options.Add($option) }
}
}
if ($invalid_options.Count -gt 0) {
$module.FailJson("Encountered invalid options: $($invalid_options -join ", ")")
}
$expected_options1 = [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryOptions]($expected_options1 -join ", ")
$expected_options2 = [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryOptions2]($expected_options2 -join ", ")
$property_map = @{
szDeviceName = $device_name
szDeviceType = $device_type
dwFramingProtocol = $expected_framing_protocol
dwfOptions = $expected_options1
dwfOptions2 = $expected_options2
dwType = $expected_type
}
if (-not $exists) {
$entry = New-Object -TypeName Ansible.WinPhonebookEntry.NativeHelpers+RASENTRYW
foreach ($kvp in $property_map.GetEnumerator()) {
$entry."$($kvp.Key)" = $kvp.Value
}
[Ansible.WinPhonebookEntry.Phonebook]::CreateEntry($name, $entry)
$module.Result.changed = $true
# Once created we then get the entry object again to retrieve the unique GUID ID to return
$entry = [Ansible.WinPhonebookEntry.Phonebook]::GetEntry($name)
$module.Result.guid = $entry.guidId
} else {
$entry = [Ansible.WinPhonebookEntry.Phonebook]::GetEntry($name)
$changed = $false
foreach ($kvp in $property_map.GetEnumerator()) {
$key = $kvp.Key
$actual_value = $entry.$key
if ($actual_value -ne $kvp.Value) {
$entry.$key = $kvp.Value
$changed = $true
}
}
if ($changed) {
[Ansible.WinPhonebookEntry.Phonebook]::CreateEntry($name, $entry)
$module.Result.changed = $true
}
}
} else {
if ($exists) {
[Ansible.WinPhonebookEntry.Phonebook]::DeleteEntry($name)
$module.Result.changed = $true
}
}
$module.ExitJson()

View file

@ -0,0 +1,16 @@
---
- name: make sure we start the tests with the defaults
win_inet_proxy:
- block:
- name: run tests
include_tasks: tests.yml
always:
- name: reset proxy back to defaults
win_inet_proxy:
- name: remove phonebook entry
win_phonebook_entry:
name: Ansible Test Dialup
state: absent

View file

@ -0,0 +1,308 @@
---
- name: ensure we fail when proxy is not set with bypass
win_inet_proxy:
bypass: abc
register: fail_bypass
failed_when: 'fail_bypass.msg != "missing parameter(s) required by ''bypass'': proxy"'
- name: ensure we fail if an invalid protocol is specified
win_inet_proxy:
proxy:
fail1: fail
fail2: fail
register: fail_proxy
failed_when: 'fail_proxy.msg != "Invalid keys found in proxy: fail1, fail2. Valid keys are http, https, ftp, socks."'
- name: ensure we fail if invalid value is set
win_inet_proxy:
proxy: fake=proxy
register: fail_invalid
failed_when: fail_invalid.msg != "Unknown error when trying to set auto_config_url '', proxy 'fake=proxy', or bypass ''"
- name: ensure we fail if an invalid connection is set
win_inet_proxy:
connection: Fake Connection
register: fail_connection
failed_when: fail_connection.msg != "The connection 'Fake Connection' does not exist."
- name: check proxy is still set to Direct access
win_inet_proxy_info:
register: fail_invalid_actual
failed_when: fail_invalid_actual.proxy == 'fake=proxy'
- name: disable auto detect (check)
win_inet_proxy:
auto_detect: no
register: disable_auto_detect_check
check_mode: yes
- name: get result of disable auto detect (check)
win_inet_proxy_info:
register: disable_auto_detect_actual_check
- name: assert disable auto detect (check)
assert:
that:
- disable_auto_detect_check is changed
- disable_auto_detect_actual_check.auto_detect
- name: disable auto detect
win_inet_proxy:
auto_detect: no
register: disable_auto_detect
- name: get result of disable auto detect
win_inet_proxy_info:
register: disable_auto_detect_actual
- name: assert disable auto detect
assert:
that:
- disable_auto_detect is changed
- not disable_auto_detect_actual.auto_detect
- name: disable auto detect (idempotent)
win_inet_proxy:
auto_detect: no
register: disable_auto_detect_again
- name: assert disable auto detect (idempotent)
assert:
that:
- not disable_auto_detect_again is changed
- name: set auto config url
win_inet_proxy:
auto_config_url: http://ansible.com/proxy.pac
register: set_auto_url
- name: get result of set auto config url
win_inet_proxy_info:
register: set_auto_url_actual
- name: assert set auto config url
assert:
that:
- set_auto_url is changed
- set_auto_url_actual.auto_detect
- set_auto_url_actual.auto_config_url == 'http://ansible.com/proxy.pac'
- name: set auto config url (idempotent)
win_inet_proxy:
auto_config_url: http://ansible.com/proxy.pac
register: set_auto_url_again
- name: set auto config url (idempotent)
assert:
that:
- not set_auto_url_again is changed
- name: set a proxy using a string
win_inet_proxy:
proxy: proxyhost
register: proxy_str
- name: get result of set a proxy using a string
win_inet_proxy_info:
register: proxy_str_actual
- name: assert set a proxy using a string
assert:
that:
- proxy_str is changed
- proxy_str_actual.auto_detect
- proxy_str_actual.auto_config_url == None
- proxy_str_actual.proxy == 'proxyhost'
- name: set a proxy using a string (idempotent)
win_inet_proxy:
proxy: proxyhost
register: proxy_str_again
- name: assert set a proxy using a string (idempotent)
assert:
that:
- not proxy_str_again is changed
- name: change a proxy and set bypass
win_inet_proxy:
proxy: proxyhost:8080
bypass:
- abc
- def
- <local>
register: change_proxy
- name: get result of change a proxy and set bypass
win_inet_proxy_info:
register: change_proxy_actual
- name: assert change a proxy and set bypass
assert:
that:
- change_proxy is changed
- change_proxy_actual.proxy == 'proxyhost:8080'
- change_proxy_actual.bypass == 'abc;def;<local>'
- name: change a proxy and set bypass (idempotent)
win_inet_proxy:
proxy: proxyhost:8080
bypass: abc,def,<local>
register: change_proxy_again
- name: assert change a proxy and set bypass (idempotent)
assert:
that:
- not change_proxy_again is changed
- name: change bypass list
win_inet_proxy:
proxy: proxyhost:8080
bypass:
- abc
- <-loopback>
register: change_bypass
- name: get reuslt of change bypass list
win_inet_proxy_info:
register: change_bypass_actual
- name: assert change bypass list
assert:
that:
- change_bypass is changed
- change_bypass_actual.proxy == 'proxyhost:8080'
- change_bypass_actual.bypass == 'abc;<-loopback>'
- name: remove proxy without options
win_inet_proxy:
register: remove_proxy
- name: get result of remove proxy without options
win_inet_proxy_info:
register: remove_proxy_actual
- name: assert remove proxy without options
assert:
that:
- remove_proxy is changed
- remove_proxy_actual.auto_detect == True
- remove_proxy_actual.auto_config_url == None
- remove_proxy_actual.proxy == None
- remove_proxy_actual.bypass == None
- name: remove proxy without options (idempotent)
win_inet_proxy:
register: remove_proxy_again
- name: assert remove proxy without options (idempotent)
assert:
that:
- not remove_proxy_again is changed
- name: set proxy with dictionary
win_inet_proxy:
proxy:
http: proxy:8080
https: proxy:8443
ftp: proxy:821
socks: proxy:888
register: set_dict
- name: get result of set proxy with dictionary
win_inet_proxy_info:
register: set_dict_actual
- name: assert set proxy with dictionary
assert:
that:
- set_dict is changed
- set_dict_actual.proxy == 'http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888'
- name: set proxy protocol with str
win_inet_proxy:
proxy: http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888
register: set_str_protocol
- name: assert set proxy protocol with str
assert:
that:
- not set_str_protocol is changed
- name: remove proxy with empty string
win_inet_proxy:
proxy: ''
register: remove_empty_str
- name: get result of remove proxy with empty string
win_inet_proxy_info:
register: remove_empty_str_actual
- name: assert remove proxy with empty string
assert:
that:
- remove_empty_str is changed
- remove_empty_str_actual.proxy == None
- name: create test phonebook entry
win_phonebook_entry:
name: Ansible Test Dialup
device_type: pppoe
device_name: WAN Miniport (PPPOE)
framing_protocol: ppp
options:
- remote_default_gateway
- require_pap
- internet
type: broadband
state: present
- name: set proxy for specific connection
win_inet_proxy:
connection: Ansible Test Dialup
auto_detect: no
auto_config_url: proxy.com
proxy: proxyhost:8080
bypass: proxyhost
register: set_connection
- name: get result for set proxy for specific connection
win_inet_proxy_info:
connection: Ansible Test Dialup
register: set_connection_actual
- name: get result for LAN connection proxy
win_inet_proxy_info:
register: set_connection_lan_actual
- name: assert set proxy for specific connection
assert:
that:
- set_connection is changed
- set_connection_actual.auto_detect == False
- set_connection_actual.auto_config_url == 'proxy.com'
- set_connection_actual.proxy == 'proxyhost:8080'
- set_connection_actual.bypass == 'proxyhost'
- set_connection_lan_actual.auto_detect == True
- set_connection_lan_actual.auto_config_url == None
- set_connection_lan_actual.proxy == None
- set_connection_lan_actual.bypass == None
- name: remove proxy for specific connection
win_inet_proxy:
connection: Ansible Test Dialup
register: remove_connection
- name: get result of remove proxy for specific connection
win_inet_proxy_info:
connection: Ansible Test Dialup
register: remove_connection_actual
- name: assert remove proxy for specific connection
assert:
that:
- remove_connection is changed
- remove_connection_actual.auto_detect == True
- remove_connection_actual.auto_config_url == None
- remove_connection_actual.proxy == None
- remove_connection_actual.bypass == None