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:
parent
a39051067b
commit
10a9cf59dd
12 changed files with 2439 additions and 0 deletions
267
lib/ansible/modules/windows/win_http_proxy.ps1
Normal file
267
lib/ansible/modules/windows/win_http_proxy.ps1
Normal 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()
|
||||
|
103
lib/ansible/modules/windows/win_http_proxy.py
Normal file
103
lib/ansible/modules/windows/win_http_proxy.py
Normal 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'''
|
||||
#
|
||||
'''
|
495
lib/ansible/modules/windows/win_inet_proxy.ps1
Normal file
495
lib/ansible/modules/windows/win_inet_proxy.ps1
Normal 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()
|
173
lib/ansible/modules/windows/win_inet_proxy.py
Normal file
173
lib/ansible/modules/windows/win_inet_proxy.py
Normal 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'''
|
||||
#
|
||||
'''
|
1
test/integration/targets/win_http_proxy/aliases
Normal file
1
test/integration/targets/win_http_proxy/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
shippable/windows/group4
|
14
test/integration/targets/win_http_proxy/tasks/main.yml
Normal file
14
test/integration/targets/win_http_proxy/tasks/main.yml
Normal 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:
|
265
test/integration/targets/win_http_proxy/tasks/tests.yml
Normal file
265
test/integration/targets/win_http_proxy/tasks/tests.yml
Normal 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
|
1
test/integration/targets/win_inet_proxy/aliases
Normal file
1
test/integration/targets/win_inet_proxy/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
shippable/windows/group4
|
|
@ -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()
|
|
@ -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()
|
16
test/integration/targets/win_inet_proxy/tasks/main.yml
Normal file
16
test/integration/targets/win_inet_proxy/tasks/main.yml
Normal 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
|
308
test/integration/targets/win_inet_proxy/tasks/tests.yml
Normal file
308
test/integration/targets/win_inet_proxy/tasks/tests.yml
Normal 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
|
Loading…
Reference in a new issue