diff options
author | Ansible Core Team <info@ansible.com> | 2020-03-09 09:40:31 +0000 |
---|---|---|
committer | Matt Martz <matt@sivel.net> | 2020-03-23 11:14:21 -0500 |
commit | bb3494356aecbf0b37183da04eaf322996273771 (patch) | |
tree | 97aeae3b636084a601991495f73bc2edd80189db /lib/ansible/modules | |
parent | 40218535ee1bc40feb76ff1044eaafcb11ee7ba9 (diff) | |
download | ansible-bb3494356aecbf0b37183da04eaf322996273771.tar.gz |
Migrated to community.windows
Diffstat (limited to 'lib/ansible/modules')
151 files changed, 0 insertions, 28254 deletions
diff --git a/lib/ansible/modules/commands/psexec.py b/lib/ansible/modules/commands/psexec.py deleted file mode 100644 index 5751a64235..0000000000 --- a/lib/ansible/modules/commands/psexec.py +++ /dev/null @@ -1,520 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Jordan Borean <jborean93@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - - -DOCUMENTATION = r''' ---- -module: psexec -short_description: Runs commands on a remote Windows host based on the PsExec - model -version_added: "2.6" -description: -- Runs a remote command from a Linux host to a Windows host without WinRM being - set up. -- Can be run on the Ansible controller to bootstrap Windows hosts to get them - ready for WinRM. -options: - hostname: - description: - - The remote Windows host to connect to, can be either an IP address or a - hostname. - type: str - required: yes - connection_username: - description: - - The username to use when connecting to the remote Windows host. - - This user must be a member of the C(Administrators) group of the Windows - host. - - Required if the Kerberos requirements are not installed or the username - is a local account to the Windows host. - - Can be omitted to use the default Kerberos principal ticket in the - local credential cache if the Kerberos library is installed. - - If I(process_username) is not specified, then the remote process will run - under a Network Logon under this account. - type: str - connection_password: - description: - - The password for I(connection_user). - - Required if the Kerberos requirements are not installed or the username - is a local account to the Windows host. - - Can be omitted to use a Kerberos principal ticket for the principal set - by I(connection_user) if the Kerberos library is installed and the - ticket has already been retrieved with the C(kinit) command before. - type: str - port: - description: - - The port that the remote SMB service is listening on. - type: int - default: 445 - encrypt: - description: - - Will use SMB encryption to encrypt the SMB messages sent to and from the - host. - - This requires the SMB 3 protocol which is only supported from Windows - Server 2012 or Windows 8, older versions like Windows 7 or Windows Server - 2008 (R2) must set this to C(no) and use no encryption. - - When setting to C(no), the packets are in plaintext and can be seen by - anyone sniffing the network, any process options are included in this. - type: bool - default: yes - connection_timeout: - description: - - The timeout in seconds to wait when receiving the initial SMB negotiate - response from the server. - type: int - default: 60 - executable: - description: - - The executable to run on the Windows host. - type: str - required: yes - arguments: - description: - - Any arguments as a single string to use when running the executable. - type: str - working_directory: - description: - - Changes the working directory set when starting the process. - type: str - default: C:\Windows\System32 - asynchronous: - description: - - Will run the command as a detached process and the module returns - immediately after starting the process while the process continues to - run in the background. - - The I(stdout) and I(stderr) return values will be null when this is set - to C(yes). - - The I(stdin) option does not work with this type of process. - - The I(rc) return value is not set when this is C(yes) - type: bool - default: no - load_profile: - description: - - Runs the remote command with the user's profile loaded. - type: bool - default: yes - process_username: - description: - - The user to run the process as. - - This can be set to run the process under an Interactive logon of the - specified account which bypasses limitations of a Network logon used when - this isn't specified. - - If omitted then the process is run under the same account as - I(connection_username) with a Network logon. - - Set to C(System) to run as the builtin SYSTEM account, no password is - required with this account. - - If I(encrypt) is C(no), the username and password are sent as a simple - XOR scrambled byte string that is not encrypted. No special tools are - required to get the username and password just knowledge of the protocol. - type: str - process_password: - description: - - The password for I(process_username). - - Required if I(process_username) is defined and not C(System). - type: str - integrity_level: - description: - - The integrity level of the process when I(process_username) is defined - and is not equal to C(System). - - When C(default), the default integrity level based on the system setup. - - When C(elevated), the command will be run with Administrative rights. - - When C(limited), the command will be forced to run with - non-Administrative rights. - type: str - choices: - - limited - - default - - elevated - default: default - interactive: - description: - - Will run the process as an interactive process that shows a process - Window of the Windows session specified by I(interactive_session). - - The I(stdout) and I(stderr) return values will be null when this is set - to C(yes). - - The I(stdin) option does not work with this type of process. - type: bool - default: no - interactive_session: - description: - - The Windows session ID to use when displaying the interactive process on - the remote Windows host. - - This is only valid when I(interactive) is C(yes). - - The default is C(0) which is the console session of the Windows host. - type: int - default: 0 - priority: - description: - - Set the command's priority on the Windows host. - - See U(https://msdn.microsoft.com/en-us/library/windows/desktop/ms683211.aspx) - for more details. - type: str - choices: - - above_normal - - below_normal - - high - - idle - - normal - - realtime - default: normal - show_ui_on_logon_screen: - description: - - Shows the process UI on the Winlogon secure desktop when - I(process_username) is C(System). - type: bool - default: no - process_timeout: - description: - - The timeout in seconds that is placed upon the running process. - - A value of C(0) means no timeout. - type: int - default: 0 - stdin: - description: - - Data to send on the stdin pipe once the process has started. - - This option has no effect when I(interactive) or I(asynchronous) is - C(yes). - type: str -requirements: -- pypsexec -- smbprotocol[kerberos] for optional Kerberos authentication -notes: -- This module requires the Windows host to have SMB configured and enabled, - and port 445 opened on the firewall. -- This module will wait until the process is finished unless I(asynchronous) - is C(yes), ensure the process is run as a non-interactive command to avoid - infinite hangs waiting for input. -- The I(connection_username) must be a member of the local Administrator group - of the Windows host. For non-domain joined hosts, the - C(LocalAccountTokenFilterPolicy) should be set to C(1) to ensure this works, - see U(https://support.microsoft.com/en-us/help/951016/description-of-user-account-control-and-remote-restrictions-in-windows). -- For more information on this module and the various host requirements, see - U(https://github.com/jborean93/pypsexec). -seealso: -- module: raw -- module: win_command -- module: win_psexec -- module: win_shell -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Run a cmd.exe command - psexec: - hostname: server - connection_username: username - connection_password: password - executable: cmd.exe - arguments: /c echo Hello World - -- name: Run a PowerShell command - psexec: - hostname: server.domain.local - connection_username: username@DOMAIN.LOCAL - connection_password: password - executable: powershell.exe - arguments: Write-Host Hello World - -- name: Send data through stdin - psexec: - hostname: 192.168.1.2 - connection_username: username - connection_password: password - executable: powershell.exe - arguments: '-' - stdin: | - Write-Host Hello World - Write-Error Error Message - exit 0 - -- name: Run the process as a different user - psexec: - hostname: server - connection_user: username - connection_password: password - executable: whoami.exe - arguments: /all - process_username: anotheruser - process_password: anotherpassword - -- name: Run the process asynchronously - psexec: - hostname: server - connection_username: username - connection_password: password - executable: cmd.exe - arguments: /c rmdir C:\temp - asynchronous: yes - -- name: Use Kerberos authentication for the connection (requires smbprotocol[kerberos]) - psexec: - hostname: host.domain.local - connection_username: user@DOMAIN.LOCAL - executable: C:\some\path\to\executable.exe - arguments: /s - -- name: Disable encryption to work with WIndows 7/Server 2008 (R2) - psexec: - hostanme: windows-pc - connection_username: Administrator - connection_password: Password01 - encrypt: no - integrity_level: elevated - process_username: Administrator - process_password: Password01 - executable: powershell.exe - arguments: (New-Object -ComObject Microsoft.Update.Session).CreateUpdateInstaller().IsBusy - -- name: Download and run ConfigureRemotingForAnsible.ps1 to setup WinRM - psexec: - hostname: '{{ hostvars[inventory_hostname]["ansible_host"] | default(inventory_hostname) }}' - connection_username: '{{ ansible_user }}' - connection_password: '{{ ansible_password }}' - encrypt: yes - executable: powershell.exe - arguments: '-' - stdin: | - $ErrorActionPreference = "Stop" - $sec_protocols = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::SystemDefault - $sec_protocols = $sec_protocols -bor [Net.SecurityProtocolType]::Tls12 - [Net.ServicePointManager]::SecurityProtocol = $sec_protocols - $url = "https://github.com/ansible/ansible/raw/devel/examples/scripts/ConfigureRemotingForAnsible.ps1" - Invoke-Expression ((New-Object Net.WebClient).DownloadString($url)) - exit - delegate_to: localhost -''' - -RETURN = r''' -msg: - description: Any exception details when trying to run the process - returned: module failed - type: str - sample: 'Received exception from remote PAExec service: Failed to start "invalid.exe". The system cannot find the file specified. [Err=0x2, 2]' -stdout: - description: The stdout from the remote process - returned: success and interactive or asynchronous is 'no' - type: str - sample: Hello World -stderr: - description: The stderr from the remote process - returned: success and interactive or asynchronous is 'no' - type: str - sample: Error [10] running process -pid: - description: The process ID of the asynchronous process that was created - returned: success and asynchronous is 'yes' - type: int - sample: 719 -rc: - description: The return code of the remote process - returned: success and asynchronous is 'no' - type: int - sample: 0 -''' - -import traceback - -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -from ansible.module_utils._text import to_bytes, to_text - -PYPSEXEC_IMP_ERR = None -try: - from pypsexec import client - from pypsexec.exceptions import PypsexecException, PAExecException, \ - PDUException, SCMRException - from pypsexec.paexec import ProcessPriority - from smbprotocol.exceptions import SMBException, SMBAuthenticationError, \ - SMBResponseException - import socket - HAS_PYPSEXEC = True -except ImportError: - PYPSEXEC_IMP_ERR = traceback.format_exc() - HAS_PYPSEXEC = False - -KERBEROS_IMP_ERR = None -try: - import gssapi - # GSSAPI extension required for Kerberos Auth in SMB - from gssapi.raw import inquire_sec_context_by_oid - HAS_KERBEROS = True -except ImportError: - KERBEROS_IMP_ERR = traceback.format_exc() - HAS_KERBEROS = False - - -def remove_artifacts(module, client): - try: - client.remove_service() - except (SMBException, PypsexecException) as exc: - module.warn("Failed to cleanup PAExec service and executable: %s" - % to_text(exc)) - - -def main(): - module_args = dict( - hostname=dict(type='str', required=True), - connection_username=dict(type='str'), - connection_password=dict(type='str', no_log=True), - port=dict(type='int', required=False, default=445), - encrypt=dict(type='bool', default=True), - connection_timeout=dict(type='int', default=60), - executable=dict(type='str', required=True), - arguments=dict(type='str'), - working_directory=dict(type='str', default=r'C:\Windows\System32'), - asynchronous=dict(type='bool', default=False), - load_profile=dict(type='bool', default=True), - process_username=dict(type='str'), - process_password=dict(type='str', no_log=True), - integrity_level=dict(type='str', default='default', - choices=['default', 'elevated', 'limited']), - interactive=dict(type='bool', default=False), - interactive_session=dict(type='int', default=0), - priority=dict(type='str', default='normal', - choices=['above_normal', 'below_normal', 'high', - 'idle', 'normal', 'realtime']), - show_ui_on_logon_screen=dict(type='bool', default=False), - process_timeout=dict(type='int', default=0), - stdin=dict(type='str') - ) - result = dict( - changed=False, - ) - module = AnsibleModule( - argument_spec=module_args, - supports_check_mode=False, - ) - - process_username = module.params['process_username'] - process_password = module.params['process_password'] - use_system = False - if process_username is not None and process_username.lower() == "system": - use_system = True - process_username = None - process_password = None - - if process_username is not None and process_password is None: - module.fail_json(msg='parameters are required together when not ' - 'running as System: process_username, ' - 'process_password') - if not HAS_PYPSEXEC: - module.fail_json(msg=missing_required_lib("pypsexec"), - exception=PYPSEXEC_IMP_ERR) - - hostname = module.params['hostname'] - connection_username = module.params['connection_username'] - connection_password = module.params['connection_password'] - port = module.params['port'] - encrypt = module.params['encrypt'] - connection_timeout = module.params['connection_timeout'] - executable = module.params['executable'] - arguments = module.params['arguments'] - working_directory = module.params['working_directory'] - asynchronous = module.params['asynchronous'] - load_profile = module.params['load_profile'] - elevated = module.params['integrity_level'] == "elevated" - limited = module.params['integrity_level'] == "limited" - interactive = module.params['interactive'] - interactive_session = module.params['interactive_session'] - - priority = { - "above_normal": ProcessPriority.ABOVE_NORMAL_PRIORITY_CLASS, - "below_normal": ProcessPriority.BELOW_NORMAL_PRIORITY_CLASS, - "high": ProcessPriority.HIGH_PRIORITY_CLASS, - "idle": ProcessPriority.IDLE_PRIORITY_CLASS, - "normal": ProcessPriority.NORMAL_PRIORITY_CLASS, - "realtime": ProcessPriority.REALTIME_PRIORITY_CLASS - }[module.params['priority']] - show_ui_on_logon_screen = module.params['show_ui_on_logon_screen'] - - process_timeout = module.params['process_timeout'] - stdin = module.params['stdin'] - - if (connection_username is None or connection_password is None) and \ - not HAS_KERBEROS: - module.fail_json(msg=missing_required_lib("gssapi"), - execption=KERBEROS_IMP_ERR) - - win_client = client.Client(server=hostname, username=connection_username, - password=connection_password, port=port, - encrypt=encrypt) - - try: - win_client.connect(timeout=connection_timeout) - except SMBAuthenticationError as exc: - module.fail_json(msg='Failed to authenticate over SMB: %s' - % to_text(exc)) - except SMBResponseException as exc: - module.fail_json(msg='Received unexpected SMB response when opening ' - 'the connection: %s' % to_text(exc)) - except PDUException as exc: - module.fail_json(msg='Received an exception with RPC PDU message: %s' - % to_text(exc)) - except SCMRException as exc: - module.fail_json(msg='Received an exception when dealing with SCMR on ' - 'the Windows host: %s' % to_text(exc)) - except (SMBException, PypsexecException) as exc: - module.fail_json(msg=to_text(exc)) - except socket.error as exc: - module.fail_json(msg=to_text(exc)) - - # create PAExec service and run the process - result['changed'] = True - b_stdin = to_bytes(stdin, encoding='utf-8') if stdin else None - run_args = dict( - executable=executable, arguments=arguments, asynchronous=asynchronous, - load_profile=load_profile, interactive=interactive, - interactive_session=interactive_session, - run_elevated=elevated, run_limited=limited, - username=process_username, password=process_password, - use_system_account=use_system, working_dir=working_directory, - priority=priority, show_ui_on_win_logon=show_ui_on_logon_screen, - timeout_seconds=process_timeout, stdin=b_stdin - ) - try: - win_client.create_service() - except (SMBException, PypsexecException) as exc: - module.fail_json(msg='Failed to create PAExec service: %s' - % to_text(exc)) - - try: - proc_result = win_client.run_executable(**run_args) - except (SMBException, PypsexecException) as exc: - module.fail_json(msg='Received error when running remote process: %s' - % to_text(exc)) - finally: - remove_artifacts(module, win_client) - - if asynchronous: - result['pid'] = proc_result[2] - elif interactive: - result['rc'] = proc_result[2] - else: - result['stdout'] = proc_result[0] - result['stderr'] = proc_result[1] - result['rc'] = proc_result[2] - - # close the SMB connection - try: - win_client.disconnect() - except (SMBException, PypsexecException) as exc: - module.warn("Failed to close the SMB connection: %s" % to_text(exc)) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/windows/win_audit_policy_system.ps1 b/lib/ansible/modules/windows/win_audit_policy_system.ps1 deleted file mode 100644 index f21e63e779..0000000000 --- a/lib/ansible/modules/windows/win_audit_policy_system.ps1 +++ /dev/null @@ -1,143 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.CommandUtil - -$ErrorActionPreference = 'Stop' - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$results = @{ - changed = $false -} - -###################################### -### populate sets for -validateset ### -###################################### -$categories_rc = run-command -command 'auditpol /list /category /r' -$subcategories_rc = run-command -command 'auditpol /list /subcategory:* /r' - -If ($categories_rc.item('rc') -eq 0) -{ - $categories = ConvertFrom-Csv $categories_rc.item('stdout') | Select-Object -expand Category* -} -Else -{ - Fail-Json -obj $results -message "Failed to retrive audit policy categories. Please make sure the auditpol command is functional on - the system and that the account ansible is running under is able to retrieve them. $($_.Exception.Message)" -} - -If ($subcategories_rc.item('rc') -eq 0) -{ - $subcategories = ConvertFrom-Csv $subcategories_rc.item('stdout') | Select-Object -expand Category* | - Where-Object {$_ -notin $categories} -} -Else -{ - Fail-Json -obj $results -message "Failed to retrive audit policy subcategories. Please make sure the auditpol command is functional on - the system and that the account ansible is running under is able to retrieve them. $($_.Exception.Message)" -} - -###################### -### ansible params ### -###################### -$category = Get-AnsibleParam -obj $params -name "category" -type "str" -ValidateSet $categories -$subcategory = Get-AnsibleParam -obj $params -name "subcategory" -type "str" -ValidateSet $subcategories -$audit_type = Get-AnsibleParam -obj $params -name "audit_type" -type "list" -failifempty - - -######################## -### Start Processing ### -######################## -Function Get-AuditPolicy ($GetString) { - $auditpolcsv = Run-Command -command $GetString - If ($auditpolcsv.item('rc') -eq 0) - { - $Obj = ConvertFrom-CSV $auditpolcsv.item('stdout') | Select-Object @{n='subcategory';e={$_.Subcategory.ToLower()}}, - @{n='audit_type';e={$_."Inclusion Setting".ToLower()}} - } - Else { - return $auditpolcsv.item('stderr') - } - - $HT = @{} - Foreach ( $Item in $Obj ) - { - $HT.Add($Item.subcategory,$Item.audit_type) - } - $HT -} - -################ -### Validate ### -################ - -#make sure category and subcategory are valid -If (-Not $category -and -Not $subcategory) {Fail-Json -obj $results -message "You must provide either a Category or Subcategory parameter"} -If ($category -and $subcategory) {Fail-Json -obj $results -message "Must pick either a specific subcategory or category. You cannot define both"} - - -$possible_audit_types = 'success','failure','none' -$audit_type | ForEach-Object { - If ($_ -notin $possible_audit_types) - { - Fail-Json -obj $result -message "$_ is not a valid audit_type. Please choose from $($possible_audit_types -join ',')" - } -} - -############################################################# -### build lists for setting, getting, and comparing rules ### -############################################################# -$audit_type_string = $audit_type -join ' and ' - -$SetString = 'auditpol /set' -$GetString = 'auditpol /get /r' - -If ($category) {$SetString = "$SetString /category:`"$category`""; $GetString = "$GetString /category:`"$category`""} -If ($subcategory) {$SetString= "$SetString /subcategory:`"$subcategory`""; $GetString = "$GetString /subcategory:`"$subcategory`""} - - -Switch ($audit_type_string) -{ - 'success and failure' {$SetString = "$SetString /success:enable /failure:enable"; $audit_type_check = $audit_type_string} - 'failure' {$SetString = "$SetString /success:disable /failure:enable"; $audit_type_check = $audit_type_string} - 'success' {$SetString = "$SetString /success:enable /failure:disable"; $audit_type_check = $audit_type_string} - 'none' {$SetString = "$SetString /success:disable /failure:disable"; $audit_type_check = 'No Auditing'} - default {Fail-Json -obj $result -message "It seems you have specified an invalid combination of items for audit_type. Please review documentation"} -} - -######################### -### check Idempotence ### -######################### - -$CurrentRule = Get-AuditPolicy $GetString - -#exit if the audit_type is already set properly for the category -If (-not ($CurrentRule.Values | Where-Object {$_ -ne $audit_type_check}) ) -{ - $results.current_audit_policy = Get-AuditPolicy $GetString - Exit-Json -obj $results -} - -#################### -### Apply Change ### -#################### - -If (-not $check_mode) -{ - $ApplyPolicy = Run-Command -command $SetString - - If ($ApplyPolicy.Item('rc') -ne 0) - { - $results.current_audit_policy = Get-AuditPolicy $GetString - Fail-Json $results "Failed to set audit policy - $($_.Exception.Message)" - } -} - -$results.changed = $true -$results.current_audit_policy = Get-AuditPolicy $GetString -Exit-Json $results diff --git a/lib/ansible/modules/windows/win_audit_policy_system.py b/lib/ansible/modules/windows/win_audit_policy_system.py deleted file mode 100644 index bbcff6aad7..0000000000 --- a/lib/ansible/modules/windows/win_audit_policy_system.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> -# Copyright: (c) 2017, 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_audit_policy_system -short_description: Used to make changes to the system wide Audit Policy -description: - - Used to make changes to the system wide Audit Policy. -version_added: "2.5" -options: - category: - description: - - Single string value for the category you would like to adjust the policy on. - - Cannot be used with I(subcategory). You must define one or the other. - - Changing this setting causes all subcategories to be adjusted to the defined I(audit_type). - type: str - subcategory: - description: - - Single string value for the subcategory you would like to adjust the policy on. - - Cannot be used with I(category). You must define one or the other. - type: str - audit_type: - description: - - The type of event you would like to audit for. - - Accepts a list. See examples. - type: list - required: yes - choices: [ failure, none, success ] -notes: - - It is recommended to take a backup of the policies before adjusting them for the first time. - - See this page for in depth information U(https://technet.microsoft.com/en-us/library/cc766468.aspx). -seealso: -- module: win_audit_rule -author: - - Noah Sparks (@nwsparks) -''' - -EXAMPLES = r''' -- name: Enable failure auditing for the subcategory "File System" - win_audit_policy_system: - subcategory: File System - audit_type: failure - -- name: Enable all auditing types for the category "Account logon events" - win_audit_policy_system: - category: Account logon events - audit_type: success, failure - -- name: Disable auditing for the subcategory "File System" - win_audit_policy_system: - subcategory: File System - audit_type: none -''' - -RETURN = r''' -current_audit_policy: - description: details on the policy being targetted - returned: always - type: dict - sample: |- - { - "File Share":"failure" - } -''' diff --git a/lib/ansible/modules/windows/win_audit_rule.ps1 b/lib/ansible/modules/windows/win_audit_rule.ps1 deleted file mode 100644 index 0d3ebf57b3..0000000000 --- a/lib/ansible/modules/windows/win_audit_rule.ps1 +++ /dev/null @@ -1,193 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.SID - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -# module parameters -$path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -aliases "destination","dest" -$user = Get-AnsibleParam -obj $params -name "user" -type "str" -failifempty $true -$rights = Get-AnsibleParam -obj $params -name "rights" -type "list" -$inheritance_flags = Get-AnsibleParam -obj $params -name "inheritance_flags" -type "list" -default 'ContainerInherit','ObjectInherit' -$propagation_flags = Get-AnsibleParam -obj $params -name "propagation_flags" -type "str" -default "none" -ValidateSet 'InheritOnly','None','NoPropagateInherit' -$audit_flags = Get-AnsibleParam -obj $params -name "audit_flags" -type "list" -default 'success' -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset 'present','absent' - -#Make sure target path is valid -If (-not (Test-Path -Path $path) ) -{ - Fail-Json -obj $result -message "defined path ($path) is not found/invalid" -} - -#function get current audit rules and convert to hashtable -Function Get-CurrentAuditRules ($path) { - Try { - $ACL = Get-Acl $path -Audit - } - Catch { - Return "Unable to retrieve the ACL on $Path" - } - - $HT = Foreach ($Obj in $ACL.Audit) - { - @{ - user = $Obj.IdentityReference.ToString() - rights = ($Obj | Select-Object -expand "*rights").ToString() - audit_flags = $Obj.AuditFlags.ToString() - is_inherited = $Obj.IsInherited.ToString() - inheritance_flags = $Obj.InheritanceFlags.ToString() - propagation_flags = $Obj.PropagationFlags.ToString() - } - } - - If (-Not $HT) - { - "No audit rules defined on $path" - } - Else {$HT} -} - -$result = @{ - changed = $false - current_audit_rules = Get-CurrentAuditRules $path -} - -#Make sure identity is valid and can be looked up -Try { - $SID = Convert-ToSid $user -} -Catch { - Fail-Json -obj $result -message "Failed to lookup the identity ($user) - $($_.exception.message)" -} - -#get the path type -$ItemType = (Get-Item $path).GetType() -switch ($ItemType) -{ - ([Microsoft.Win32.RegistryKey]) {$registry = $true; $result.path_type = 'registry'} - ([System.IO.FileInfo]) {$file = $true; $result.path_type = 'file'} - ([System.IO.DirectoryInfo]) {$result.path_type = 'directory'} -} - -#Get current acl/audit rules on the target -Try { - $ACL = Get-Acl $path -Audit -} -Catch { - Fail-Json -obj $result -message "Unable to retrieve the ACL on $Path - $($_.Exception.Message)" -} - -#configure acl object to remove the specified user -If ($state -eq 'absent') -{ - #Try and find an identity on the object that matches user - #We skip inherited items since we can't remove those - $ToRemove = ($ACL.Audit | Where-Object {$_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $SID -and - $_.IsInherited -eq $false}).IdentityReference - - #Exit with changed false if no identity is found - If (-Not $ToRemove) - { - $result.current_audit_rules = Get-CurrentAuditRules $path - Exit-Json -obj $result - } - - #update the ACL object if identity found - Try - { - $ToRemove | ForEach-Object { $ACL.PurgeAuditRules($_) } - } - Catch - { - $result.current_audit_rules = Get-CurrentAuditRules $path - Fail-Json -obj $result -message "Failed to remove audit rule: $($_.Exception.Message)" - } -} - -Else -{ - If ($registry) - { - $PossibleRights = [System.Enum]::GetNames([System.Security.AccessControl.RegistryRights]) - - Foreach ($right in $rights) - { - if ($right -notin $PossibleRights) - { - Fail-Json -obj $result -message "$right does not seem to be a valid REGISTRY right" - } - } - - $NewAccessRule = New-Object System.Security.AccessControl.RegistryAuditRule($user,$rights,$inheritance_flags,$propagation_flags,$audit_flags) - } - Else - { - $PossibleRights = [System.Enum]::GetNames([System.Security.AccessControl.FileSystemRights]) - - Foreach ($right in $rights) - { - if ($right -notin $PossibleRights) - { - Fail-Json -obj $result -message "$right does not seem to be a valid FILE SYSTEM right" - } - } - - If ($file -and $inheritance_flags -ne 'none') - { - Fail-Json -obj $result -message "The target type is a file. inheritance_flags must be changed to 'none'" - } - - $NewAccessRule = New-Object System.Security.AccessControl.FileSystemAuditRule($user,$rights,$inheritance_flags,$propagation_flags,$audit_flags) - } - - #exit here if any existing rule matches defined rule since no change is needed - #if we need to ignore inherited rules in the future, this would be where to do it - #Just filter out inherited rules from $ACL.Audit - Foreach ($group in $ACL.Audit | Where-Object {$_.IsInherited -eq $false}) - { - If ( - ($group | Select-Object -expand "*Rights") -eq ($NewAccessRule | Select-Object -expand "*Rights") -and - $group.AuditFlags -eq $NewAccessRule.AuditFlags -and - $group.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $SID -and - $group.InheritanceFlags -eq $NewAccessRule.InheritanceFlags -and - $group.PropagationFlags -eq $NewAccessRule.PropagationFlags - ) - { - $result.current_audit_rules = Get-CurrentAuditRules $path - Exit-Json -obj $result - } - } - - #try and set the acl object. AddAuditRule allows for multiple entries to exist under the same - #identity...so if someone wanted success: write and failure: delete for example, that setup would be - #possible. The alternative is SetAuditRule which would instead modify an existing rule and not allow - #for setting the above example. - Try - { - $ACL.AddAuditRule($NewAccessRule) - } - Catch - { - Fail-Json -obj $result -message "Failed to set the audit rule: $($_.Exception.Message)" - } -} - - -#finally set the permissions -Try { - Set-Acl -Path $path -ACLObject $ACL -WhatIf:$check_mode -} -Catch { - $result.current_audit_rules = Get-CurrentAuditRules $path - Fail-Json -obj $result -message "Failed to apply audit change: $($_.Exception.Message)" -} - -#exit here after a change is applied -$result.current_audit_rules = Get-CurrentAuditRules $path -$result.changed = $true -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_audit_rule.py b/lib/ansible/modules/windows/win_audit_rule.py deleted file mode 100644 index d5687c120e..0000000000 --- a/lib/ansible/modules/windows/win_audit_rule.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> -# 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_audit_rule -short_description: Adds an audit rule to files, folders, or registry keys -description: - - Used to apply audit rules to files, folders or registry keys. - - Once applied, it will begin recording the user who performed the operation defined into the Security - Log in the Event viewer. - - The behavior is designed to ignore inherited rules since those cannot be adjusted without first disabling - the inheritance behavior. It will still print inherited rules in the output though for debugging purposes. -version_added: "2.5" -options: - path: - description: - - Path to the file, folder, or registry key. - - Registry paths should be in Powershell format, beginning with an abbreviation for the root - such as, C(HKLM:\Software). - type: path - required: yes - aliases: [ dest, destination ] - user: - description: - - The user or group to adjust rules for. - type: str - required: yes - rights: - description: - - Comma separated list of the rights desired. Only required for adding a rule. - - If I(path) is a file or directory, rights can be any right under MSDN - FileSystemRights U(https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx). - - If I(path) is a registry key, rights can be any right under MSDN - RegistryRights U(https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.registryrights.aspx). - type: list - required: yes - inheritance_flags: - description: - - Defines what objects inside of a folder or registry key will inherit the settings. - - If you are setting a rule on a file, this value has to be changed to C(none). - - For more information on the choices see MSDN PropagationFlags enumeration - at U(https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.inheritanceflags.aspx). - type: list - choices: [ ContainerInherit, ObjectInherit ] - default: ContainerInherit,ObjectInherit - propagation_flags: - description: - - Propagation flag on the audit rules. - - This value is ignored when the path type is a file. - - For more information on the choices see MSDN PropagationFlags enumeration - at U(https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.propagationflags.aspx). - choices: [ None, InherityOnly, NoPropagateInherit ] - default: "None" - audit_flags: - description: - - Defines whether to log on failure, success, or both. - - To log both define as comma separated list "Success, Failure". - type: list - required: yes - choices: [ Failure, Success ] - state: - description: - - Whether the rule should be C(present) or C(absent). - - For absent, only I(path), I(user), and I(state) are required. - - Specifying C(absent) will remove all rules matching the defined I(user). - type: str - choices: [ absent, present ] - default: present -seealso: -- module: win_audit_policy_system -author: - - Noah Sparks (@nwsparks) -''' - -EXAMPLES = r''' -- name: Add filesystem audit rule for a folder - win_audit_rule: - path: C:\inetpub\wwwroot\website - user: BUILTIN\Users - rights: write,delete,changepermissions - audit_flags: success,failure - inheritance_flags: ContainerInherit,ObjectInherit - -- name: Add filesystem audit rule for a file - win_audit_rule: - path: C:\inetpub\wwwroot\website\web.config - user: BUILTIN\Users - rights: write,delete,changepermissions - audit_flags: success,failure - inheritance_flags: None - -- name: Add registry audit rule - win_audit_rule: - path: HKLM:\software - user: BUILTIN\Users - rights: delete - audit_flags: 'success' - -- name: Remove filesystem audit rule - win_audit_rule: - path: C:\inetpub\wwwroot\website - user: BUILTIN\Users - state: absent - -- name: Remove registry audit rule - win_audit_rule: - path: HKLM:\software - user: BUILTIN\Users - state: absent -''' - -RETURN = r''' -current_audit_rules: - description: - - The current rules on the defined I(path) - - Will return "No audit rules defined on I(path)" - returned: always - type: dict - sample: | - { - "audit_flags": "Success", - "user": "Everyone", - "inheritance_flags": "False", - "is_inherited": "False", - "propagation_flags": "None", - "rights": "Delete" - } -path_type: - description: - - The type of I(path) being targetted. - - Will be one of file, directory, registry. - returned: always - type: str -''' diff --git a/lib/ansible/modules/windows/win_auto_logon.ps1 b/lib/ansible/modules/windows/win_auto_logon.ps1 deleted file mode 100644 index 61b0d67f5a..0000000000 --- a/lib/ansible/modules/windows/win_auto_logon.ps1 +++ /dev/null @@ -1,403 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Prasoon Karunan V (@prasoonkarunan) <kvprasoon@Live.in> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# All helper methods are written in a binary module and has to be loaded for consuming them. -#AnsibleRequires -CSharpUtil Ansible.Basic -#Requires -Module Ansible.ModuleUtils.AddType - -Set-StrictMode -Version 2.0 - -$spec = @{ - options = @{ - logon_count = @{type = "int"} - password = @{type = "str"; no_log = $true} - state = @{type = "str"; choices = "absent", "present"; default = "present"} - username = @{type = "str"} - } - required_if = @( - ,@("state", "present", @("username", "password")) - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$logonCount = $module.Params.logon_count -$password = $module.Params.password -$state = $module.Params.state -$username = $module.Params.username -$domain = $null - -if ($username) { - # Try and get the Netlogon form of the username specified. Translating to and from a SID gives us an NTAccount - # in the Netlogon form that we desire. - $ntAccount = New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList $username - try { - $accountSid = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier]) - } catch [System.Security.Principal.IdentityNotMappedException] { - $module.FailJson("Failed to find a local or domain user with the name '$username'", $_) - } - $ntAccount = $accountSid.Translate([System.Security.Principal.NTAccount]) - - $domain, $username = $ntAccount.Value -split '\\' -} - -# Make sure $null regardless of any input value if state: absent -if ($state -eq 'absent') { - $password = $null -} - -Add-CSharpType -AnsibleModule $module -References @' -using Microsoft.Win32.SafeHandles; -using System; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; -using System.Text; - -namespace Ansible.WinAutoLogon -{ - internal class NativeHelpers - { - [StructLayout(LayoutKind.Sequential)] - public class LSA_OBJECT_ATTRIBUTES - { - public UInt32 Length = 0; - public IntPtr RootDirectory = IntPtr.Zero; - public IntPtr ObjectName = IntPtr.Zero; - public UInt32 Attributes = 0; - public IntPtr SecurityDescriptor = IntPtr.Zero; - public IntPtr SecurityQualityOfService = IntPtr.Zero; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct LSA_UNICODE_STRING - { - public UInt16 Length; - public UInt16 MaximumLength; - public IntPtr Buffer; - - public static explicit operator string(LSA_UNICODE_STRING s) - { - byte[] strBytes = new byte[s.Length]; - Marshal.Copy(s.Buffer, strBytes, 0, s.Length); - return Encoding.Unicode.GetString(strBytes); - } - - public static SafeMemoryBuffer CreateSafeBuffer(string s) - { - if (s == null) - return new SafeMemoryBuffer(IntPtr.Zero); - - byte[] stringBytes = Encoding.Unicode.GetBytes(s); - int structSize = Marshal.SizeOf(typeof(LSA_UNICODE_STRING)); - IntPtr buffer = Marshal.AllocHGlobal(structSize + stringBytes.Length); - try - { - LSA_UNICODE_STRING lsaString = new LSA_UNICODE_STRING() - { - Length = (UInt16)(stringBytes.Length), - MaximumLength = (UInt16)(stringBytes.Length), - Buffer = IntPtr.Add(buffer, structSize), - }; - Marshal.StructureToPtr(lsaString, buffer, false); - Marshal.Copy(stringBytes, 0, lsaString.Buffer, stringBytes.Length); - return new SafeMemoryBuffer(buffer); - } - catch - { - // Make sure we free the pointer before raising the exception. - Marshal.FreeHGlobal(buffer); - throw; - } - } - } - } - - internal class NativeMethods - { - [DllImport("Advapi32.dll")] - public static extern UInt32 LsaClose( - IntPtr ObjectHandle); - - [DllImport("Advapi32.dll")] - public static extern UInt32 LsaFreeMemory( - IntPtr Buffer); - - [DllImport("Advapi32.dll")] - internal static extern Int32 LsaNtStatusToWinError( - UInt32 Status); - - [DllImport("Advapi32.dll")] - public static extern UInt32 LsaOpenPolicy( - IntPtr SystemName, - NativeHelpers.LSA_OBJECT_ATTRIBUTES ObjectAttributes, - LsaPolicyAccessMask AccessMask, - out SafeLsaHandle PolicyHandle); - - [DllImport("Advapi32.dll")] - public static extern UInt32 LsaRetrievePrivateData( - SafeLsaHandle PolicyHandle, - SafeMemoryBuffer KeyName, - out SafeLsaMemory PrivateData); - - [DllImport("Advapi32.dll")] - public static extern UInt32 LsaStorePrivateData( - SafeLsaHandle PolicyHandle, - SafeMemoryBuffer KeyName, - SafeMemoryBuffer PrivateData); - } - - internal class SafeLsaMemory : SafeBuffer - { - internal SafeLsaMemory() : base(true) { } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - - protected override bool ReleaseHandle() - { - return NativeMethods.LsaFreeMemory(handle) == 0; - } - } - - internal class SafeMemoryBuffer : SafeBuffer - { - internal SafeMemoryBuffer() : base(true) { } - - internal SafeMemoryBuffer(IntPtr ptr) : base(true) - { - base.SetHandle(ptr); - } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - - protected override bool ReleaseHandle() - { - if (handle != IntPtr.Zero) - Marshal.FreeHGlobal(handle); - return true; - } - } - - public class SafeLsaHandle : SafeHandleZeroOrMinusOneIsInvalid - { - internal SafeLsaHandle() : base(true) { } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - - protected override bool ReleaseHandle() - { - return NativeMethods.LsaClose(handle) == 0; - } - } - - public class Win32Exception : System.ComponentModel.Win32Exception - { - private string _exception_msg; - public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { } - public Win32Exception(int errorCode, string message) : base(errorCode) - { - _exception_msg = String.Format("{0} - {1} (Win32 Error Code {2}: 0x{3})", message, base.Message, errorCode, errorCode.ToString("X8")); - } - public override string Message { get { return _exception_msg; } } - public static explicit operator Win32Exception(string message) { return new Win32Exception(message); } - } - - [Flags] - public enum LsaPolicyAccessMask : uint - { - ViewLocalInformation = 0x00000001, - ViewAuditInformation = 0x00000002, - GetPrivateInformation = 0x00000004, - TrustAdmin = 0x00000008, - CreateAccount = 0x00000010, - CreateSecret = 0x00000020, - CreatePrivilege = 0x00000040, - SetDefaultQuotaLimits = 0x00000080, - SetAuditRequirements = 0x00000100, - AuditLogAdmin = 0x00000200, - ServerAdmin = 0x00000400, - LookupNames = 0x00000800, - Read = 0x00020006, - Write = 0x000207F8, - Execute = 0x00020801, - AllAccess = 0x000F0FFF, - } - - public class LsaUtil - { - public static SafeLsaHandle OpenPolicy(LsaPolicyAccessMask access) - { - NativeHelpers.LSA_OBJECT_ATTRIBUTES oa = new NativeHelpers.LSA_OBJECT_ATTRIBUTES(); - SafeLsaHandle lsaHandle; - UInt32 res = NativeMethods.LsaOpenPolicy(IntPtr.Zero, oa, access, out lsaHandle); - if (res != 0) - throw new Win32Exception(NativeMethods.LsaNtStatusToWinError(res), - String.Format("LsaOpenPolicy({0}) failed", access.ToString())); - return lsaHandle; - } - - public static string RetrievePrivateData(SafeLsaHandle handle, string key) - { - using (SafeMemoryBuffer keyBuffer = NativeHelpers.LSA_UNICODE_STRING.CreateSafeBuffer(key)) - { - SafeLsaMemory buffer; - UInt32 res = NativeMethods.LsaRetrievePrivateData(handle, keyBuffer, out buffer); - using (buffer) - { - if (res != 0) - { - // If the data object was not found we return null to indicate it isn't set. - if (res == 0xC0000034) // STATUS_OBJECT_NAME_NOT_FOUND - return null; - - throw new Win32Exception(NativeMethods.LsaNtStatusToWinError(res), - String.Format("LsaRetrievePrivateData({0}) failed", key)); - } - - NativeHelpers.LSA_UNICODE_STRING lsaString = (NativeHelpers.LSA_UNICODE_STRING) - Marshal.PtrToStructure(buffer.DangerousGetHandle(), - typeof(NativeHelpers.LSA_UNICODE_STRING)); - return (string)lsaString; - } - } - } - - public static void StorePrivateData(SafeLsaHandle handle, string key, string data) - { - using (SafeMemoryBuffer keyBuffer = NativeHelpers.LSA_UNICODE_STRING.CreateSafeBuffer(key)) - using (SafeMemoryBuffer dataBuffer = NativeHelpers.LSA_UNICODE_STRING.CreateSafeBuffer(data)) - { - UInt32 res = NativeMethods.LsaStorePrivateData(handle, keyBuffer, dataBuffer); - if (res != 0) - { - // When clearing the private data with null it may return this error which we can ignore. - if (data == null && res == 0xC0000034) // STATUS_OBJECT_NAME_NOT_FOUND - return; - - throw new Win32Exception(NativeMethods.LsaNtStatusToWinError(res), - String.Format("LsaStorePrivateData({0}) failed", key)); - } - } - } - } -} -'@ - -$autoLogonRegPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -$logonDetails = Get-ItemProperty -LiteralPath $autoLogonRegPath - -$before = @{ - state = 'absent' -} -if ('AutoAdminLogon' -in $logonDetails.PSObject.Properties.Name -and $logonDetails.AutoAdminLogon -eq 1) { - $before.state = 'present' -} - -$mapping = @{ - DefaultUserName = 'username' - DefaultDomainName = 'domain' - AutoLogonCount = 'logon_count' -} -foreach ($map_detail in $mapping.GetEnumerator()) { - if ($map_detail.Key -in $logonDetails.PSObject.Properties.Name) { - $before."$($map_detail.Value)" = $logonDetails."$($map_detail.Key)" - } -} - -$module.Diff.before = $before - -$propParams = @{ - LiteralPath = $autoLogonRegPath - WhatIf = $module.CheckMode - Force = $true -} - -# First set the registry information -# The DefaultPassword reg key should never be set, we use LSA to store the password in a more secure way. -if ('DefaultPassword' -in (Get-Item -LiteralPath $autoLogonRegPath).Property) { - # Bug on older Windows hosts where -WhatIf causes it fail to find the property - if (-not $module.CheckMode) { - Remove-ItemProperty -Name 'DefaultPassword' @propParams - } - $module.Result.changed = $true -} - -$autoLogonKeyList = @{ - DefaultUserName = @{ - before = if ($before.ContainsKey('username')) { $before.username } else { $null } - after = $username - } - DefaultDomainName = @{ - before = if ($before.ContainsKey('domain')) { $before.domain } else { $null } - after = $domain - } - AutoLogonCount = @{ - before = if ($before.ContainsKey('logon_count')) { $before.logon_count } else { $null } - after = $logonCount - } -} - -# Check AutoAdminLogon separately as it has different logic (key must exist) -if ($state -ne $before.state) { - $newValue = if ($state -eq 'present') { 1 } else { 0 } - $null = New-ItemProperty -Name 'AutoAdminLogon' -Value $newValue -PropertyType DWord @propParams - $module.Result.changed = $true -} - -foreach ($key in $autoLogonKeyList.GetEnumerator()) { - $beforeVal = $key.Value.before - $after = $key.Value.after - - if ($state -eq 'present' -and $beforeVal -cne $after) { - if ($null -ne $after) { - $null = New-ItemProperty -Name $key.Key -Value $after @propParams - } - elseif (-not $module.CheckMode) { - Remove-ItemProperty -Name $key.Key @propParams - } - $module.Result.changed = $true - } - elseif ($state -eq 'absent' -and $null -ne $beforeVal) { - if (-not $module.CheckMode) { - Remove-ItemProperty -Name $key.Key @propParams - } - $module.Result.changed = $true - } -} - -# Finally update the password in the LSA private store. -$lsaHandle = [Ansible.WinAutoLogon.LsaUtil]::OpenPolicy('CreateSecret, GetPrivateInformation') -try { - $beforePass = [Ansible.WinAutoLogon.LsaUtil]::RetrievePrivateData($lsaHandle, 'DefaultPassword') - - if ($beforePass -cne $password) { - # Due to .NET marshaling we need to pass in $null as NullString.Value so it's truly a null value. - if ($null -eq $password) { - $password = [NullString]::Value - } - if (-not $module.CheckMode) { - [Ansible.WinAutoLogon.LsaUtil]::StorePrivateData($lsaHandle, 'DefaultPassword', $password) - } - $module.Result.changed = $true - } -} -finally { - $lsaHandle.Dispose() -} - -# Need to manually craft the after diff in case we are running in check mode -$module.Diff.after = @{ - state = $state -} -if ($state -eq 'present') { - $module.Diff.after.username = $username - $module.Diff.after.domain = $domain - if ($null -ne $logonCount) { - $module.Diff.after.logon_count = $logonCount - } -} - -$module.ExitJson() - diff --git a/lib/ansible/modules/windows/win_auto_logon.py b/lib/ansible/modules/windows/win_auto_logon.py deleted file mode 100644 index c869361df9..0000000000 --- a/lib/ansible/modules/windows/win_auto_logon.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Prasoon Karunan V (@prasoonkarunan) -# 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_auto_logon -short_description: Adds or Sets auto logon registry keys. -description: - - Used to apply auto logon registry setting. -version_added: "2.10" -options: - logon_count: - description: - - The number of times to do an automatic logon. - - This count is deremented by Windows everytime an automatic logon is - performed. - - Once the count reaches C(0) then the automatic logon process is - disabled. - type: int - username: - description: - - Username to login automatically. - - Must be set when C(state=present). - - This can be the Netlogon or UPN of a domain account and is - automatically parsed to the C(DefaultUserName) and C(DefaultDomainName) - registry properties. - type: str - password: - description: - - Password to be used for automatic login. - - Must be set when C(state=present). - - Value of this input will be used as password for I(username). - - While this value is encrypted by LSA it is decryptable to any user who - is an Administrator on the remote host. - type: str - state: - description: - - Whether the registry key should be C(present) or C(absent). - type: str - choices: [ absent, present ] - default: present -author: - - Prasoon Karunan V (@prasoonkarunan) -''' - -EXAMPLES = r''' -- name: Set autologon for user1 - win_auto_logon: - username: User1 - password: str0ngp@ssword - -- name: Set autologon for abc.com\user1 - win_auto_logon: - username: abc.com\User1 - password: str0ngp@ssword - -- name: Remove autologon for user1 - win_auto_logon: - state: absent - -- name: Set autologon for user1 with a limited logon count - win_auto_logon: - username: User1 - password: str0ngp@ssword - logon_count: 5 -''' - -RETURN = r''' -# -''' diff --git a/lib/ansible/modules/windows/win_certificate_info.ps1 b/lib/ansible/modules/windows/win_certificate_info.ps1 deleted file mode 100644 index b1ff876479..0000000000 --- a/lib/ansible/modules/windows/win_certificate_info.ps1 +++ /dev/null @@ -1,132 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Micah Hunsberger -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -function ConvertTo-Timestamp($start_date, $end_date) -{ - if ($start_date -and $end_date) - { - return (New-TimeSpan -Start $start_date -End $end_date).TotalSeconds - } -} - -function Format-Date([DateTime]$date) -{ - return $date.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssK') -} - -function Get-CertificateInfo ($cert) -{ - $epoch_date = Get-Date -Date "01/01/1970" - - $cert_info = @{ extensions = @() } - $cert_info.friendly_name = $cert.FriendlyName - $cert_info.thumbprint = $cert.Thumbprint - $cert_info.subject = $cert.Subject - $cert_info.issuer = $cert.Issuer - $cert_info.valid_from = (ConvertTo-Timestamp -start_date $epoch_date -end_date $cert.NotBefore.ToUniversalTime()) - $cert_info.valid_from_iso8601 = Format-Date -date $cert.NotBefore - $cert_info.valid_to = (ConvertTo-Timestamp -start_date $epoch_date -end_date $cert.NotAfter.ToUniversalTime()) - $cert_info.valid_to_iso8601 = Format-Date -date $cert.NotAfter - $cert_info.serial_number = $cert.SerialNumber - $cert_info.archived = $cert.Archived - $cert_info.version = $cert.Version - $cert_info.has_private_key = $cert.HasPrivateKey - $cert_info.issued_by = $cert.GetNameInfo('SimpleName', $true) - $cert_info.issued_to = $cert.GetNameInfo('SimpleName', $false) - $cert_info.signature_algorithm = $cert.SignatureAlgorithm.FriendlyName - $cert_info.dns_names = [System.Collections.Generic.List`1[String]]@($cert_info.issued_to) - $cert_info.raw = [System.Convert]::ToBase64String($cert.GetRawCertData()) - $cert_info.public_key = [System.Convert]::ToBase64String($cert.GetPublicKey()) - if ($cert.Extensions.Count -gt 0) - { - [array]$cert_info.extensions = foreach ($extension in $cert.Extensions) - { - $extension_info = @{ - critical = $extension.Critical - field = $extension.Oid.FriendlyName - value = $extension.Format($false) - } - if ($extension -is [System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]) - { - $cert_info.is_ca = $extension.CertificateAuthority - $cert_info.path_length_constraint = $extension.PathLengthConstraint - } - elseif ($extension -is [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]) - { - $cert_info.intended_purposes = $extension.EnhancedKeyUsages.FriendlyName -as [string[]] - } - elseif ($extension -is [System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]) - { - $cert_info.key_usages = $extension.KeyUsages.ToString().Split(',').Trim() -as [string[]] - } - elseif ($extension -is [System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension]) - { - $cert_info.ski = $extension.SubjectKeyIdentifier - } - elseif ($extension.Oid.value -eq '2.5.29.17') - { - $sans = $extension.Format($true).Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries) - foreach ($san in $sans) - { - $san_parts = $san.Split("=") - if ($san_parts.Length -ge 2 -and $san_parts[0].Trim() -eq 'DNS Name') - { - $cert_info.dns_names.Add($san_parts[1].Trim()) - } - } - } - $extension_info - } - } - return $cert_info -} - -$store_location_values = ([System.Security.Cryptography.X509Certificates.StoreLocation]).GetEnumValues() | ForEach-Object { $_.ToString() } - -$spec = @{ - options = @{ - thumbprint = @{ type = "str"; required = $false } - store_name = @{ type = "str"; default = "My"; } - store_location = @{ type = "str"; default = "LocalMachine"; choices = $store_location_values; } - } -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$thumbprint = $module.Params.thumbprint -$store_name = $module.Params.store_name -$store_location = [System.Security.Cryptography.X509Certificates.Storelocation]"$($module.Params.store_location)" - -$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location -$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly) - -$module.Result.exists = $false -$module.Result.certificates = @() - -try -{ - if ($null -ne $thumbprint) - { - $found_certs = $store.Certificates.Find([System.Security.Cryptography.X509Certificates.X509FindType]::FindByThumbprint, $thumbprint, $false) - } - else - { - $found_certs = $store.Certificates - } - - if ($found_certs.Count -gt 0) - { - $module.Result.exists = $true - [array]$module.Result.certificates = $found_certs | ForEach-Object { Get-CertificateInfo -cert $_ } | Sort-Object -Property { $_.thumbprint } - } -} -finally -{ - $store.Close() -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_certificate_info.py b/lib/ansible/modules/windows/win_certificate_info.py deleted file mode 100644 index c8a75731ca..0000000000 --- a/lib/ansible/modules/windows/win_certificate_info.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Ansible, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_certificate_info -version_added: "2.10" -short_description: Get information on certificates from a Windows Certificate Store -description: -- Returns information about certificates in a Windows Certificate Store. -options: - thumbprint: - description: - - The thumbprint as a hex string of a certificate to find. - - When specified, filters the I(certificates) return value to a single certificate - - See the examples for how to format the thumbprint. - type: str - required: no - store_name: - description: - - The name of the store to search. - - See U(https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.storename) - for a list of built-in store names. - type: str - default: My - store_location: - description: - - The location of the store to search. - type: str - choices: [ CurrentUser, LocalMachine ] - default: LocalMachine -seealso: -- module: win_certificate_store -author: -- Micah Hunsberger (@mhunsber) -''' - -EXAMPLES = r''' -- name: Obtain information about a particular certificate in the computer's personal store - win_certificate_info: - thumbprint: BD7AF104CF1872BDB518D95C9534EA941665FD27 - register: mycert - -# thumbprint can also be lower case -- name: Obtain information about a particular certificate in the computer's personal store - win_certificate_info: - thumbprint: bd7af104cf1872bdb518d95c9534ea941665fd27 - register: mycert - -- name: Obtain information about all certificates in the root store - win_certificate_info: - store_name: Root - register: ca - -# Import a pfx and then get information on the certificates -- name: Import pfx certificate that is password protected - win_certificate_store: - path: C:\Temp\cert.pfx - state: present - password: VeryStrongPasswordHere! - become: yes - become_method: runas - register: mycert - -- name: Obtain information on each certificate that was touched - win_certificate_info: - thumbprint: "{{ item }}" - register: mycert_stats - loop: "{{ mycert.thumbprints }}" -''' - -RETURN = r''' -exists: - description: - - Whether any certificates were found in the store. - - When I(thumbprint) is specified, returns true only if the certificate mathing the thumbprint exists. - returned: success - type: bool - sample: true -certificates: - description: - - A list of information about certificates found in the store, sorted by thumbprint. - returned: success - type: list - elements: dict - contains: - archived: - description: Indicates that the certificate is archived. - type: bool - sample: false - dns_names: - description: Lists the registered dns names for the certificate. - type: list - elements: str - sample: [ '*.m.wikiquote.org', '*.wikipedia.org' ] - extensions: - description: The collection of the certificates extensions. - type: list - elements: dict - sample: [ - { - "critical": false, - "field": "Subject Key Identifier", - "value": "88 27 17 09 a9 b6 18 60 8b ec eb ba f6 47 59 c5 52 54 a3 b7" - }, - { - "critical": true, - "field": "Basic Constraints", - "value": "Subject Type=CA, Path Length Constraint=None" - }, - { - "critical": false, - "field": "Authority Key Identifier", - "value": "KeyID=2b d0 69 47 94 76 09 fe f4 6b 8d 2e 40 a6 f7 47 4d 7f 08 5e" - }, - { - "critical": false, - "field": "CRL Distribution Points", - "value": "[1]CRL Distribution Point: Distribution Point Name:Full Name:URL=http://crl.apple.com/root.crl" - }, - { - "critical": true, - "field": "Key Usage", - "value": "Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86)" - }, - { - "critical": false, - "field": null, - "value": "05 00" - } - ] - friendly_name: - description: The associated alias for the certificate. - type: str - sample: Microsoft Root Authority - has_private_key: - description: Indicates that the certificate contains a private key. - type: bool - sample: false - intended_purposes: - description: lists the intended applications for the certificate. - returned: enhanced key usages extension exists. - type: list - sample: [ "Server Authentication" ] - is_ca: - description: Indicates that the certificate is a certificate authority (CA) certificate. - returned: basic constraints extension exists. - type: bool - sample: true - issued_by: - description: The certificate issuer's common name. - type: str - sample: Apple Root CA - issued_to: - description: The certificate's common name. - type: str - sample: Apple Worldwide Developer Relations Certification Authority - issuer: - description: The certificate issuer's distinguished name. - type: str - sample: 'CN=Apple Root CA, OU=Apple Certification Authority, O=Apple Inc., C=US' - key_usages: - description: - - Defines how the certificate key can be used. - - If this value is not defined, the key can be used for any purpose. - returned: key usages extension exists. - type: list - elements: str - sample: [ "CrlSign", "KeyCertSign", "DigitalSignature" ] - path_length_constraint: - description: - - The number of levels allowed in a certificates path. - - If this value is 0, the certificate does not have a restriction. - returned: basic constraints extension exists - type: int - sample: 0 - public_key: - description: The base64 encoded public key of the certificate. - type: str - cert_data: - description: The base64 encoded data of the entire certificate. - type: str - serial_number: - description: The serial number of the certificate represented as a hexadecimal string - type: str - sample: 01DEBCC4396DA010 - signature_algorithm: - description: The algorithm used to create the certificate's signature - type: str - sample: sha1RSA - ski: - description: The certificate's subject key identifier - returned: subject key identifier extension exists. - type: str - sample: 88271709A9B618608BECEBBAF64759C55254A3B7 - subject: - description: The certificate's distinguished name. - type: str - sample: 'CN=Apple Worldwide Developer Relations Certification Authority, OU=Apple Worldwide Developer Relations, O=Apple Inc., C=US' - thumbprint: - description: - - The thumbprint as a hex string of the certificate. - - The return format will always be upper case. - type: str - sample: FF6797793A3CD798DC5B2ABEF56F73EDC9F83A64 - valid_from: - description: The start date of the certificate represented in seconds since epoch. - type: float - sample: 1360255727 - valid_from_iso8601: - description: The start date of the certificate represented as an iso8601 formatted date. - type: str - sample: '2017-12-15T08:39:32Z' - valid_to: - description: The expiry date of the certificate represented in seconds since epoch. - type: float - sample: 1675788527 - valid_to_iso8601: - description: The expiry date of the certificate represented as an iso8601 formatted date. - type: str - sample: '2086-01-02T08:39:32Z' - version: - description: The x509 format version of the certificate - type: int - sample: 3 -''' diff --git a/lib/ansible/modules/windows/win_chocolatey.ps1 b/lib/ansible/modules/windows/win_chocolatey.ps1 deleted file mode 100644 index d5cb87f7c0..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey.ps1 +++ /dev/null @@ -1,803 +0,0 @@ -#!powershell - -# Copyright: (c) 2014, Trond Hindenes <trond@hindenes.com> -# Copyright: (c) 2017, Dag Wieers <dag@wieers.com> -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil -#AnsibleRequires -CSharpUtil Ansible.Basic - -# As of chocolatey 0.9.10, non-zero success exit codes can be returned -# See https://github.com/chocolatey/choco/issues/512#issuecomment-214284461 -$successexitcodes = (0, 1605, 1614, 1641, 3010) - -$spec = @{ - options = @{ - allow_empty_checksums = @{ type = "bool"; default = $false } - allow_multiple = @{ type = "bool"; default = $false } - allow_prerelease = @{ type = "bool"; default = $false } - architecture = @{ type = "str"; default = "default"; choices = "default", "x86" } - force = @{ type = "bool"; default = $false } - ignore_checksums = @{ type = "bool"; default = $false } - ignore_dependencies = @{ type = "bool"; default = $false } - install_args = @{ type = "str" } - name = @{ type = "list"; elements = "str"; required = $true } - override_args = @{ type = "bool"; default = $false } - package_params = @{ type = "str"; aliases = @("params") } - pinned = @{ type = "bool" } - proxy_url = @{ type = "str" } - proxy_username = @{ type = "str" } - proxy_password = @{ type = "str"; no_log = $true } - skip_scripts = @{ type = "bool"; default = $false } - source = @{ type = "str" } - source_username = @{ type = "str" } - source_password = @{ type = "str"; no_log = $true } - state = @{ type = "str"; default = "present"; choices = "absent", "downgrade", "latest", "present", "reinstalled" } - timeout = @{ type = "int"; default = 2700; aliases = @("execution_timeout") } - validate_certs = @{ type = "bool"; default = $true } - version = @{ type = "str" } - } - supports_check_mode = $true -} -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$allow_empty_checksums = $module.Params.allow_empty_checksums -$allow_multiple = $module.Params.allow_multiple -$allow_prerelease = $module.Params.allow_prerelease -$architecture = $module.Params.architecture -$force = $module.Params.force -$ignore_checksums = $module.Params.ignore_checksums -$ignore_dependencies = $module.Params.ignore_dependencies -$install_args = $module.Params.install_args -$name = $module.Params.name -$override_args = $module.Params.override_args -$package_params = $module.Params.package_params -$pinned = $module.Params.pinned -$proxy_url = $module.Params.proxy_url -$proxy_username = $module.Params.proxy_username -$proxy_password = $module.Params.proxy_password -$skip_scripts = $module.Params.skip_scripts -$source = $module.Params.source -$source_username = $module.Params.source_username -$source_password = $module.Params.source_password -$state = $module.Params.state -$timeout = $module.Params.timeout -$validate_certs = $module.Params.validate_certs -$version = $module.Params.version - -$module.Result.rc = 0 - -if (-not $validate_certs) { - [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } -} - -Function Get-CommonChocolateyArguments { - # uses global vars like check_mode and verbosity to control the common args - # run with Chocolatey - $arguments = [System.Collections.ArrayList]@("--yes", "--no-progress") - # global vars that control the arguments - if ($module.CheckMode) { - $arguments.Add("--what-if") > $null - } - if ($module.Verbosity -gt 4) { - $arguments.Add("--debug") > $null - $arguments.Add("--verbose") > $null - } elseif ($module.Verbosity -gt 3) { - $arguments.Add("--verbose") > $null - } else { - $arguments.Add("--limit-output") > $null - } - - return ,$arguments -} - -Function Get-InstallChocolateyArguments { - param( - [bool]$allow_downgrade, - [bool]$allow_empty_checksums, - [bool]$allow_multiple, - [bool]$allow_prerelease, - [String]$architecture, - [bool]$force, - [bool]$ignore_dependencies, - [String]$install_args, - [bool]$override_args, - [String]$package_params, - [String]$proxy_url, - [String]$proxy_username, - [String]$proxy_password, - [bool]$skip_scripts, - [String]$source, - [String]$source_usename, - [String]$source_password, - [int]$timeout, - [String]$version - ) - # returns an ArrayList of common arguments for install/updated a Chocolatey - # package - $arguments = [System.Collections.ArrayList]@("--fail-on-unfound") - $common_args = Get-CommonChocolateyArguments - $arguments.AddRange($common_args) - - if ($allow_downgrade) { - $arguments.Add("--allow-downgrade") > $null - } - if ($allow_empty_checksums) { - $arguments.Add("--allow-empty-checksums") > $null - } - if ($allow_multiple) { - $arguments.Add("--allow-multiple") > $null - } - if ($allow_prerelease) { - $arguments.Add("--prerelease") > $null - } - if ($architecture -eq "x86") { - $arguments.Add("--x86") > $null - } - if ($force) { - $arguments.Add("--force") > $null - } - if ($ignore_checksums) { - $arguments.Add("--ignore-checksums") > $null - } - if ($ignore_dependencies) { - $arguments.Add("--ignore-dependencies") > $null - } - if ($install_args) { - $arguments.Add("--install-arguments") > $null - $arguments.add($install_args) > $null - } - if ($override_args) { - $arguments.Add("--override-arguments") > $null - } - if ($package_params) { - $arguments.Add("--package-parameters") > $null - $arguments.Add($package_params) > $null - } - if ($proxy_url) { - $arguments.Add("--proxy") > $null - $arguments.Add($proxy_url) > $null - } - if ($proxy_username) { - $arguments.Add("--proxy-user") > $null - $arguments.Add($proxy_username) > $null - } - if ($proxy_password) { - $arguments.Add("--proxy-password") > $null - $arguments.Add($proxy_password) > $null - } - if ($skip_scripts) { - $arguments.Add("--skip-scripts") > $null - } - if ($source) { - $arguments.Add("--source") > $null - $arguments.Add($source) > $null - } - if ($source_username) { - $arguments.Add("--user") > $null - $arguments.Add($source_username) > $null - $arguments.Add("--password") > $null - $arguments.Add($source_password) > $null - } - if ($null -ne $timeout) { - $arguments.Add("--timeout") > $null - $arguments.Add($timeout) > $null - } - if ($version) { - $arguments.Add("--version") > $null - $arguments.Add($version) > $null - } - - return ,$arguments -} - -Function Install-Chocolatey { - param( - [String]$proxy_url, - [String]$proxy_username, - [String]$proxy_password, - [String]$source, - [String]$source_username, - [String]$source_password, - [String]$version - ) - - $choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue - if ($null -eq $choco_app) { - # We need to install chocolatey - # Enable TLS1.1/TLS1.2 if they're available but disabled (eg. .NET 4.5) - $security_protocols = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::SystemDefault - if ([Net.SecurityProtocolType].GetMember("Tls11").Count -gt 0) { - $security_protocols = $security_protcols -bor [Net.SecurityProtocolType]::Tls11 - } - if ([Net.SecurityProtocolType].GetMember("Tls12").Count -gt 0) { - $security_protocols = $security_protcols -bor [Net.SecurityProtocolType]::Tls12 - } - [Net.ServicePointManager]::SecurityProtocol = $security_protocols - - $client = New-Object -TypeName System.Net.WebClient - $new_environment = @{} - if ($proxy_url) { - # the env values are used in the install.ps1 script when getting - # external dependencies - $new_environment.chocolateyProxyLocation = $proxy_url - $web_proxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $proxy_url, $true - $client.Proxy = $web_proxy - if ($proxy_username -and $proxy_password) { - $new_environment.chocolateyProxyUser = $proxy_username - $new_environment.chocolateyProxyPassword = $proxy_password - $sec_proxy_password = ConvertTo-SecureString -String $proxy_password -AsPlainText -Force - $web_proxy.Credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $proxy_username, $sec_proxy_password - } - } - if ($version) { - # Set the chocolateyVersion environment variable when bootstrapping Chocolatey to install that specific - # version. - $new_environment.chocolateyVersion = $version - } - - $environment = @{} - if ($new_environment.Count -gt 0) { - $environment = [Environment]::GetEnvironmentVariables() - $environment += $new_environment - } - - if ($source) { - # check if the URL already contains the path to PS script - if ($source.EndsWith(".ps1")) { - $script_url = $source - } else { - # chocolatey server automatically serves a script at - # http://host/install.ps1, we rely on this behaviour when a - # user specifies the choco source URL. If a custom URL or file - # path is desired, they should use win_get_url/win_shell - # manually - # we need to strip the path off the URL and append install.ps1 - $uri_info = [System.Uri]$source - $script_url = "$($uri_info.Scheme)://$($uri_info.Authority)/install.ps1" - } - if ($source_username) { - # while the choco-server does not require creds on install.ps1, - # Net.WebClient will only send the credentials if the initial - # req fails so we will add the creds in case the source URL - # is not choco-server and requires authentication - $sec_source_password = ConvertTo-SecureString -String $source_password -AsPlainText -Force - $client.Credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $source_username, $sec_source_password - } - } else { - $script_url = "https://chocolatey.org/install.ps1" - } - - try { - $install_script = $client.DownloadString($script_url) - } catch { - $module.FailJson("Failed to download Chocolatey script from '$script_url'; $($_.Exception.Message)", $_) - } - if (-not $module.CheckMode) { - $res = Run-Command -command "powershell.exe -" -stdin $install_script -environment $environment - if ($res.rc -ne 0) { - $module.Result.rc = $res.rc - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson("Chocolatey bootstrap installation failed.") - } - $module.Warn("Chocolatey was missing from this system, so it was installed during this task run.") - } - $module.Result.changed = $true - - # locate the newly installed choco.exe - $choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue - if ($null -eq $choco_app) { - $choco_dir = $env:ChocolateyInstall - if ($null -eq $choco_dir) { - $choco_dir = "$env:SYSTEMDRIVE\ProgramData\Chocolatey" - } - $choco_app = Get-Command -Name "$choco_dir\bin\choco.exe" -CommandType Application -ErrorAction SilentlyContinue - } - } - - if ($module.CheckMode -and $null -eq $choco_app) { - $module.Result.skipped = $true - $module.Result.msg = "Skipped check mode run on win_chocolatey as choco.exe cannot be found on the system" - $module.ExitJson() - } - - if ($null -eq $choco_app -or -not (Test-Path -LiteralPath $choco_app.Path)) { - $module.FailJson("Failed to find choco.exe, make sure it is added to the PATH or the env var 'ChocolateyInstall' is set") - } - - $actual_version = (Get-ChocolateyPackageVersion -choco_path $choco_app.Path -name chocolatey)[0] - try { - # The Chocolatey version may not be in the strict form of major.minor.build and will fail to cast to - # System.Version. We want to warn if this is the case saying module behaviour may be incorrect. - $actual_version = [Version]$actual_version - } catch { - $module.Warn("Failed to parse Chocolatey version '$actual_version' for checking module requirements, module may not work correctly: $($_.Exception.Message)") - $actual_version = $null - } - if ($null -ne $actual_version -and $actual_version -lt [Version]"0.10.5") { - if ($module.CheckMode) { - $module.Result.skipped = $true - $module.Result.msg = "Skipped check mode run on win_chocolatey as choco.exe is too old, a real run would have upgraded the executable. Actual: '$actual_version', Minimum Version: '0.10.5'" - $module.ExitJson() - } - $module.Warn("Chocolatey was older than v0.10.5 so it was upgraded during this task run.") - Update-ChocolateyPackage -choco_path $choco_app.Path -packages @("chocolatey") ` - - -proxy_url $proxy_url -proxy_username $proxy_username ` - -proxy_password $proxy_password -source $source ` - -source_username $source_username -source_password $source_password - } - - return $choco_app.Path -} - -Function Get-ChocolateyPackageVersion { - Param ( - [Parameter(Mandatory=$true)] - [System.String] - $choco_path, - - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] - [System.String] - $name - ) - - Begin { - # Due to https://github.com/chocolatey/choco/issues/1843, we get a list of all the installed packages and - # filter it ourselves. This has the added benefit of being quicker when dealing with multiple packages as we - # only call choco.exe once. - $command = Argv-ToString -arguments @($choco_path, 'list', '--local-only', '--limit-output', '--all-versions') - $res = Run-Command -command $command - - # Chocolatey v0.10.12 introduced enhanced exit codes, 2 means no results, e.g. no package - if ($res.rc -notin @(0, 2)) { - $module.Result.command = $command - $module.Result.rc = $res.rc - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson('Error checking installation status for chocolatey packages') - } - - # Parse the stdout to get a list of all packages installed and their versions. - $installed_packages = $res.stdout.Trim().Split([System.Environment]::NewLine) | ForEach-Object -Process { - if ($_.Contains('|')) { # Sanity in case further output is added in the future. - $package_split = $_.Split('|', 2) - @{ Name = $package_split[0]; Version = $package_split[1] } - } - } - - # Create a hashtable that will store our package version info. - $installed_info = @{} - } - - Process { - if ($name -eq 'all') { - # All is a special package name that means all installed packages, we set a dummy version so absent, latest - # and downgrade will run with all. - $installed_info.'all' = @('0.0.0') - } else { - $package_info = $installed_packages | Where-Object { $_.Name -eq $name } - if ($null -eq $package_info) { - $installed_info.$name = $null - } else { - $installed_info.$name = @($package_info.Version) - } - } - } - - End { - return $installed_info - } -} - -Function Get-ChocolateyPin { - param( - [Parameter(Mandatory=$true)][String]$choco_path - ) - - $command = Argv-ToString -arguments @($choco_path, "pin", "list", "--limit-output") - $res = Run-Command -command $command - if ($res.rc -ne 0) { - $module.Result.command = $command - $module.Result.rc = $res.rc - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson("Error getting list of pinned packages") - } - - $stdout = $res.stdout.Trim() - $pins = @{} - - $stdout.Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { - $package = $_.Substring(0, $_.LastIndexOf("|")) - $version = $_.Substring($_.LastIndexOf("|") + 1) - - if ($pins.ContainsKey($package)) { - $pinned_versions = $pins.$package - } else { - $pinned_versions = [System.Collections.Generic.List`1[String]]@() - } - $pinned_versions.Add($version) - $pins.$package = $pinned_versions - } - return ,$pins -} - -Function Set-ChocolateyPin { - param( - [Parameter(Mandatory=$true)][String]$choco_path, - [Parameter(Mandatory=$true)][String]$name, - [Switch]$pin, - [String]$version - ) - if ($pin) { - $action = "add" - $err_msg = "Error pinning package '$name'" - } else { - $action = "remove" - $err_msg = "Error unpinning package '$name'" - } - - $arguments = [System.Collections.ArrayList]@($choco_path, "pin", $action, "--name", $name) - if ($version) { - $err_msg += " at '$version'" - $arguments.Add("--version") > $null - $arguments.Add($version) > $null - } - $common_args = Get-CommonChocolateyArguments - $arguments.AddRange($common_args) - - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - if ($res.rc -ne 0) { - $module.Result.command = $command - $module.Result.rc = $res.rc - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson($err_msg) - } - $module.result.changed = $true -} - -Function Update-ChocolateyPackage { - param( - [Parameter(Mandatory=$true)][String]$choco_path, - [Parameter(Mandatory=$true)][String[]]$packages, - [bool]$allow_downgrade, - [bool]$allow_empty_checksums, - [bool]$allow_multiple, - [bool]$allow_prerelease, - [String]$architecture, - [bool]$force, - [bool]$ignore_checksums, - [bool]$ignore_dependencies, - [String]$install_args, - [bool]$override_args, - [String]$package_params, - [String]$proxy_url, - [String]$proxy_username, - [String]$proxy_password, - [bool]$skip_scripts, - [String]$source, - [String]$source_username, - [String]$source_password, - [int]$timeout, - [String]$version - ) - - $arguments = [System.Collections.ArrayList]@($choco_path, "upgrade") - $arguments.AddRange($packages) - - $common_params = @{ - allow_downgrade = $allow_downgrade - allow_empty_checksums = $allow_empty_checksums - allow_multiple = $allow_multiple - allow_prerelease = $allow_prerelease - architecture = $architecture - force = $force - ignore_checksums = $ignore_checksums - ignore_dependencies = $ignore_dependencies - install_args = $install_args - override_args = $override_args - package_params = $package_params - proxy_url = $proxy_url - proxy_username = $proxy_username - proxy_password = $proxy_password - skip_scripts = $skip_scripts - source = $source - source_username = $source_username - source_password = $source_password - timeout = $timeout - version = $version - } - $common_args = Get-InstallChocolateyArguments @common_params - $arguments.AddRange($common_args) - - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - $module.Result.rc = $res.rc - if ($res.rc -notin $successexitcodes) { - $module.Result.command = $command - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson("Error updating package(s) '$($packages -join ", ")'") - } - - if ($module.Verbosity -gt 1) { - $module.Result.stdout = $res.stdout - } - - if ($res.stdout -match ' upgraded (\d+)/\d+ package') { - if ($Matches[1] -gt 0) { - $module.Result.changed = $true - } - } - # need to set to false in case the rc is not 0 and a failure didn't actually occur - $module.Result.failed = $false -} - -Function Install-ChocolateyPackage { - param( - [Parameter(Mandatory=$true)][String]$choco_path, - [Parameter(Mandatory=$true)][String[]]$packages, - [bool]$allow_downgrade, - [bool]$allow_empty_checksums, - [bool]$allow_multiple, - [bool]$allow_prerelease, - [String]$architecture, - [bool]$force, - [bool]$ignore_checksums, - [bool]$ignore_dependencies, - [String]$install_args, - [bool]$override_args, - [String]$package_params, - [String]$proxy_url, - [String]$proxy_username, - [String]$proxy_password, - [bool]$skip_scripts, - [String]$source, - [String]$source_username, - [String]$source_password, - [int]$timeout, - [String]$version - ) - - $arguments = [System.Collections.ArrayList]@($choco_path, "install") - $arguments.AddRange($packages) - $common_params = @{ - allow_downgrade = $allow_downgrade - allow_empty_checksums = $allow_empty_checksums - allow_multiple = $allow_multiple - allow_prerelease = $allow_prerelease - architecture = $architecture - force = $force - ignore_checksums = $ignore_checksums - ignore_dependencies = $ignore_dependencies - install_args = $install_args - override_args = $override_args - package_params = $package_params - proxy_url = $proxy_url - proxy_username = $proxy_username - proxy_password = $proxy_password - skip_scripts = $skip_scripts - source = $source - source_username = $source_username - source_password = $source_password - timeout = $timeout - version = $version - } - $common_args = Get-InstallChocolateyArguments @common_params - $arguments.AddRange($common_args) - - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - $module.Result.rc = $res.rc - if ($res.rc -notin $successexitcodes) { - $module.Result.command = $command - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson("Error installing package(s) '$($packages -join ', ')'") - } - - if ($module.Verbosity -gt 1) { - $module.Result.stdout = $res.stdout - } - - $module.Result.changed = $true - # need to set to false in case the rc is not 0 and a failure didn't actually occur - $module.Result.failed = $false -} - -Function Uninstall-ChocolateyPackage { - param( - [Parameter(Mandatory=$true)][String]$choco_path, - [Parameter(Mandatory=$true)][String[]]$packages, - [bool]$force, - [String]$package_params, - [bool]$skip_scripts, - [int]$timeout, - [String]$version - ) - - $arguments = [System.Collections.ArrayList]@($choco_path, "uninstall") - $arguments.AddRange($packages) - $common_args = Get-CommonChocolateyArguments - $arguments.AddRange($common_args) - - if ($force) { - $arguments.Add("--force") > $null - } - if ($package_params) { - $arguments.Add("--package-params") > $null - $arguments.Add($package_params) > $null - } - if ($skip_scripts) { - $arguments.Add("--skip-scripts") > $null - } - if ($null -ne $timeout) { - $arguments.Add("--timeout") > $null - $arguments.Add($timeout) > $null - } - if ($version) { - # Need to set allow-multiple to make sure choco doesn't uninstall all versions - $arguments.Add("--allow-multiple") > $null - $arguments.Add("--version") > $null - $arguments.Add($version) > $null - } else { - $arguments.Add("--all-versions") > $null - } - - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - $module.Result.rc = $res.rc - if ($res.rc -notin $successexitcodes) { - $module.Result.command = $command - $module.Result.stdout = $res.stdout - $module.Result.stderr = $res.stderr - $module.FailJson("Error uninstalling package(s) '$($packages -join ", ")'") - } - - if ($module.Verbosity -gt 1) { - $module.Result.stdout = $res.stdout - } - $module.Result.changed = $true - # need to set to false in case the rc is not 0 and a failure didn't actually occur - $module.Result.failed = $false -} - -# get the full path to choco.exe, otherwise install/upgrade to at least 0.10.5 -$install_params = @{ - proxy_url = $proxy_url - proxy_username = $proxy_username - proxy_password = $proxy_password - source = $source - source_username = $source_username - source_password = $source_password -} -if ($version -and "chocolatey" -in $name) { - # If a version is set and chocolatey is in the package list, pass the chocolatey version to the bootstrapping - # process. - $install_params.version = $version -} -$choco_path = Install-Chocolatey @install_params - -if ('all' -in $name -and $state -in @('present', 'reinstalled')) { - $module.FailJson("Cannot specify the package name as 'all' when state=$state") -} - -# get the version of all specified packages -$package_info = $name | Get-ChocolateyPackageVersion -choco_path $choco_path - -if ($state -in "absent", "reinstalled") { - $installed_packages = ($package_info.GetEnumerator() | Where-Object { $null -ne $_.Value }).Key - if ($null -ne $installed_packages) { - Uninstall-ChocolateyPackage -choco_path $choco_path -packages $installed_packages ` - -force $force -package_params $package_params -skip_scripts $skip_scripts ` - -timeout $timeout -version $version - } - - # ensure the package info for the uninstalled versions has been removed - # so state=reinstall will install them in the next step - foreach ($package in $installed_packages) { - $package_info.$package = $null - } -} - -if ($state -in @("downgrade", "latest", "present", "reinstalled")) { - if ($state -eq "present" -and $force) { - # when present and force, we just run the install step with the packages specified - $missing_packages = $name - } else { - # otherwise only install the packages that are not installed - $missing_packages = [System.Collections.ArrayList]@() - foreach ($package in $package_info.GetEnumerator()) { - if ($null -eq $package.Value) { - $missing_packages.Add($package.Key) > $null - } - } - } - - # if version is specified and installed version does not match or not - # allow_multiple, throw error ignore this if force is set - if ($state -eq "present" -and $null -ne $version -and -not $force) { - foreach ($package in $name) { - $package_versions = [System.Collections.ArrayList]$package_info.$package - if ($package_versions.Count -gt 0) { - if (-not $package_versions.Contains($version) -and -not $allow_multiple) { - $module.FailJson("Chocolatey package '$package' is already installed with version(s) '$($package_versions -join "', '")' but was expecting '$version'. Either change the expected version, set state=latest, set allow_multiple=yes, or set force=yes to continue") - } elseif ($version -notin $package_versions -and $allow_multiple) { - # add the package back into the list of missing packages if installing multiple - $missing_packages.Add($package) > $null - } - } - } - } - $common_args = @{ - choco_path = $choco_path - allow_downgrade = ($state -eq "downgrade") - allow_empty_checksums = $allow_empty_checksums - allow_multiple = $allow_multiple - allow_prerelease = $allow_prerelease - architecture = $architecture - force = $force - ignore_checksums = $ignore_checksums - ignore_dependencies = $ignore_dependencies - install_args = $install_args - override_args = $override_args - package_params = $package_params - proxy_url = $proxy_url - proxy_username = $proxy_username - proxy_password = $proxy_password - skip_scripts = $skip_scripts - source = $source - source_username = $source_username - source_password = $source_password - timeout = $timeout - version = $version - } - - if ($missing_packages) { - Install-ChocolateyPackage -packages $missing_packages @common_args - } - - if ($state -eq "latest" -or ($state -eq "downgrade" -and $null -ne $version)) { - # when in a downgrade/latest situation, we want to run choco upgrade on - # the remaining packages that were already installed, don't run this if - # state=downgrade and a version isn't specified (this will actually - # upgrade a package) - $installed_packages = ($package_info.GetEnumerator() | Where-Object { $null -ne $_.Value }).Key - if ($null -ne $installed_packages) { - Update-ChocolateyPackage -packages $installed_packages @common_args - } - } - - # Now we want to pin/unpin any packages now that it has been installed/upgraded - if ($null -ne $pinned) { - $pins = Get-ChocolateyPin -choco_path $choco_path - - foreach ($package in $name) { - if ($pins.ContainsKey($package)) { - if (-not $pinned -and $null -eq $version) { - # No version is set and pinned=no, we want to remove all pins on the package. There is a bug in - # 'choco pin remove' with multiple versions where an older version might be pinned but - # 'choco pin remove' will still fail without an explicit version. Instead we take the literal - # interpretation that pinned=no and no version means the package has no pins at all - foreach ($v in $pins.$package) { - Set-ChocolateyPin -choco_path $choco_path -name $package -version $v - } - } elseif ($null -ne $version -and $pins.$package.Contains($version) -ne $pinned) { - Set-ChocolateyPin -choco_path $choco_path -name $package -pin:$pinned -version $version - } - } elseif ($pinned) { - # Package had no pins but pinned=yes is set. - Set-ChocolateyPin -choco_path $choco_path -name $package -pin -version $version - } - } - } -} - -$module.ExitJson() - diff --git a/lib/ansible/modules/windows/win_chocolatey.py b/lib/ansible/modules/windows/win_chocolatey.py deleted file mode 100644 index 6792f59a93..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey.py +++ /dev/null @@ -1,385 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2014, Trond Hindenes <trond@hindenes.com> -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_chocolatey -version_added: '1.9' -short_description: Manage packages using chocolatey -description: -- Manage packages using Chocolatey. -- If Chocolatey is missing from the system, the module will install it. -requirements: -- chocolatey >= 0.10.5 (will be upgraded if older) -options: - allow_empty_checksums: - description: - - Allow empty checksums to be used for downloaded resource from non-secure - locations. - - Use M(win_chocolatey_feature) with the name C(allowEmptyChecksums) to - control this option globally. - type: bool - default: no - version_added: '2.2' - allow_multiple: - description: - - Allow the installation of multiple packages when I(version) is specified. - - Having multiple packages at different versions can cause issues if the - package doesn't support this. Use at your own risk. - type: bool - default: no - version_added: '2.8' - allow_prerelease: - description: - - Allow the installation of pre-release packages. - - If I(state) is C(latest), the latest pre-release package will be - installed. - type: bool - default: no - version_added: '2.6' - architecture: - description: - - Force Chocolatey to install the package of a specific process - architecture. - - When setting C(x86), will ensure Chocolatey installs the x86 package - even when on an x64 bit OS. - type: str - choices: [ default, x86 ] - default: default - version_added: '2.7' - force: - description: - - Forces the install of a package, even if it already is installed. - - Using I(force) will cause Ansible to always report that a change was - made. - type: bool - default: no - ignore_checksums: - description: - - Ignore the checksums provided by the package. - - Use M(win_chocolatey_feature) with the name C(checksumFiles) to control - this option globally. - type: bool - default: no - version_added: '2.2' - ignore_dependencies: - description: - - Ignore dependencies, only install/upgrade the package itself. - type: bool - default: no - version_added: '2.1' - install_args: - description: - - Arguments to pass to the native installer. - - These are arguments that are passed directly to the installer the - Chocolatey package runs, this is generally an advanced option. - type: str - version_added: '2.1' - name: - description: - - Name of the package(s) to be installed. - - Set to C(all) to run the action on all the installed packages. - type: list - required: yes - override_args: - description: - - Override arguments of native installer with arguments provided by user. - - Should install arguments be used exclusively without appending - to current package passed arguments. - type: bool - version_added: '2.10' - package_params: - description: - - Parameters to pass to the package. - - These are parameters specific to the Chocolatey package and are generally - documented by the package itself. - - Before Ansible 2.7, this option was just I(params). - type: str - version_added: '2.1' - aliases: [ params ] - pinned: - description: - - Whether to pin the Chocolatey package or not. - - If omitted then no checks on package pins are done. - - Will pin/unpin the specific version if I(version) is set. - - Will pin the latest version of a package if C(yes), I(version) is not set - and and no pin already exists. - - Will unpin all versions of a package if C(no) and I(version) is not set. - - This is ignored when C(state=absent). - type: bool - version_added: '2.8' - proxy_url: - description: - - Proxy URL used to install chocolatey and the package. - - Use M(win_chocolatey_config) with the name C(proxy) to control this - option globally. - type: str - version_added: '2.4' - proxy_username: - description: - - Proxy username used to install Chocolatey and the package. - - Before Ansible 2.7, users with double quote characters C(") would need to - be escaped with C(\) beforehand. This is no longer necessary. - - Use M(win_chocolatey_config) with the name C(proxyUser) to control this - option globally. - type: str - version_added: '2.4' - proxy_password: - description: - - Proxy password used to install Chocolatey and the package. - - This value is exposed as a command argument and any privileged account - can see this value when the module is running Chocolatey, define the - password on the global config level with M(win_chocolatey_config) with - name C(proxyPassword) to avoid this. - type: str - version_added: '2.4' - skip_scripts: - description: - - Do not run I(chocolateyInstall.ps1) or I(chocolateyUninstall.ps1) scripts - when installing a package. - type: bool - default: no - version_added: '2.4' - source: - description: - - Specify the source to retrieve the package from. - - Use M(win_chocolatey_source) to manage global sources. - - This value can either be the URL to a Chocolatey feed, a path to a folder - containing C(.nupkg) packages or the name of a source defined by - M(win_chocolatey_source). - - This value is also used when Chocolatey is not installed as the location - of the install.ps1 script and only supports URLs for this case. - type: str - source_username: - description: - - A username to use with I(source) when accessing a feed that requires - authentication. - - It is recommended you define the credentials on a source with - M(win_chocolatey_source) instead of passing it per task. - type: str - version_added: '2.7' - source_password: - description: - - The password for I(source_username). - - This value is exposed as a command argument and any privileged account - can see this value when the module is running Chocolatey, define the - credentials with a source with M(win_chocolatey_source) to avoid this. - type: str - version_added: '2.7' - state: - description: - - State of the package on the system. - - When C(absent), will ensure the package is not installed. - - When C(present), will ensure the package is installed. - - When C(downgrade), will allow Chocolatey to downgrade a package if - I(version) is older than the installed version. - - When C(latest), will ensure the package is installed to the latest - available version. - - When C(reinstalled), will uninstall and reinstall the package. - type: str - choices: [ absent, downgrade, latest, present, reinstalled ] - default: present - timeout: - description: - - The time to allow chocolatey to finish before timing out. - type: int - default: 2700 - version_added: '2.3' - aliases: [ execution_timeout ] - validate_certs: - description: - - Used when downloading the Chocolatey install script if Chocolatey is not - already installed, this does not affect the Chocolatey package install - process. - - When C(no), no SSL certificates will be validated. - - This should only be used on personally controlled sites using self-signed - certificate. - type: bool - default: yes - version_added: '2.7' - version: - description: - - Specific version of the package to be installed. - - When I(state) is set to C(absent), will uninstall the specific version - otherwise all versions of that package will be removed. - - If a different version of package is installed, I(state) must be C(latest) - or I(force) set to C(yes) to install the desired version. - - Provide as a string (e.g. C('6.1')), otherwise it is considered to be - a floating-point number and depending on the locale could become C(6,1), - which will cause a failure. - - If I(name) is set to C(chocolatey) and Chocolatey is not installed on the - host, this will be the version of Chocolatey that is installed. You can - also set the C(chocolateyVersion) environment var. - type: str -notes: -- This module will install or upgrade Chocolatey when needed. -- When using verbosity 2 or less (C(-vv)) the C(stdout) output will be restricted. - When using verbosity 4 (C(-vvvv)) the C(stdout) output will be more verbose. - When using verbosity 5 (C(-vvvvv)) the C(stdout) output will include debug output. -- Some packages, like hotfixes or updates need an interactive user logon in - order to install. You can use C(become) to achieve this, see - :ref:`become_windows`. - Even if you are connecting as local Administrator, using C(become) to - become Administrator will give you an interactive user logon, see examples - below. -- If C(become) is unavailable, use M(win_hotfix) to install hotfixes instead - of M(win_chocolatey) as M(win_hotfix) avoids using C(wusa.exe) which cannot - be run without C(become). -seealso: -- module: win_chocolatey_config -- module: win_chocolatey_facts -- module: win_chocolatey_feature -- module: win_chocolatey_source -- module: win_feature -- module: win_hotfix - description: Use when C(become) is unavailable, to avoid using C(wusa.exe). -- module: win_package -- module: win_updates -- name: Chocolatey website - description: More information about the Chocolatey tool. - link: http://chocolatey.org/ -- name: Chocolatey packages - description: An overview of the available Chocolatey packages. - link: http://chocolatey.org/packages -- ref: become_windows - description: Some packages, like hotfixes or updates need an interactive user logon - in order to install. You can use C(become) to achieve this. -author: -- Trond Hindenes (@trondhindenes) -- Peter Mounce (@petemounce) -- Pepe Barbe (@elventear) -- Adam Keech (@smadam813) -- Pierre Templier (@ptemplier) -- Jordan Borean (@jborean93) -''' - -# TODO: -# * Better parsing when a package has dependencies - currently fails -# * Time each item that is run -# * Support 'changed' with gems - would require shelling out to `gem list` first and parsing, kinda defeating the point of using chocolatey. -# * Version provided not as string might be translated to 6,6 depending on Locale (results in errors) - -EXAMPLES = r''' -- name: Install git - win_chocolatey: - name: git - state: present - -- name: Upgrade installed packages - win_chocolatey: - name: all - state: latest - -- name: Install notepadplusplus version 6.6 - win_chocolatey: - name: notepadplusplus - version: '6.6' - -- name: Install notepadplusplus 32 bit version - win_chocolatey: - name: notepadplusplus - architecture: x86 - -- name: Install git from specified repository - win_chocolatey: - name: git - source: https://someserver/api/v2/ - -- name: Install git from a pre configured source (win_chocolatey_source) - win_chocolatey: - name: git - source: internal_repo - -- name: Ensure Chocolatey itself is installed and use internal repo as source - win_chocolatey: - name: chocolatey - source: http://someserver/chocolatey - -- name: Uninstall git - win_chocolatey: - name: git - state: absent - -- name: Install multiple packages - win_chocolatey: - name: - - procexp - - putty - - windirstat - state: present - -- name: Install multiple packages sequentially - win_chocolatey: - name: '{{ item }}' - state: present - loop: - - procexp - - putty - - windirstat - -- name: Uninstall multiple packages - win_chocolatey: - name: - - procexp - - putty - - windirstat - state: absent - -- name: Install curl using proxy - win_chocolatey: - name: curl - proxy_url: http://proxy-server:8080/ - proxy_username: joe - proxy_password: p@ssw0rd - -- name: Install a package that requires 'become' - win_chocolatey: - name: officepro2013 - become: yes - become_user: Administrator - become_method: runas - -- name: install and pin Notepad++ at 7.6.3 - win_chocolatey: - name: notepadplusplus - version: 7.6.3 - pinned: yes - state: present - -- name: remove all pins for Notepad++ on all versions - win_chocolatey: - name: notepadplusplus - pinned: no - state: present -''' - -RETURN = r''' -command: - description: The full command used in the chocolatey task. - returned: changed - type: str - sample: choco.exe install -r --no-progress -y sysinternals --timeout 2700 --failonunfound -rc: - description: The return code from the chocolatey task. - returned: always - type: int - sample: 0 -stdout: - description: The stdout from the chocolatey task. The verbosity level of the - messages are affected by Ansible verbosity setting, see notes for more - details. - returned: changed - type: str - sample: Chocolatey upgraded 1/1 packages. -''' diff --git a/lib/ansible/modules/windows/win_chocolatey_config.ps1 b/lib/ansible/modules/windows/win_chocolatey_config.ps1 deleted file mode 100644 index 119b12648d..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_config.ps1 +++ /dev/null @@ -1,122 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent", "present" -$value = Get-AnsibleParam -obj $params -name "value" -type "str" -failifempty ($state -eq "present") - -$result = @{ - changed = $false -} -if ($diff) { - $result.diff = @{ - before = $null - after = $null - } -} - -if ($state -eq "present") { - if ($value -eq "") { - Fail-Json -obj $result -message "Cannot set Chocolatey config as an empty string when state=present, use state=absent instead" - } - # make sure bool values are lower case - if ($value -ceq "True" -or $value -ceq "False") { - $value = $value.ToLower() - } -} - -Function Get-ChocolateyConfig { - param($choco_app) - - # 'choco config list -r' does not display an easily parsable config entries - # It contains config/sources/feature in the one command, and is in the - # structure 'configKey = configValue | description', if the key or value - # contains a = or |, it will make it quite hard to easily parse it, - # compared to reading an XML file that already delimits these values - $choco_config_path = "$(Split-Path -Path (Split-Path -Path $choco_app.Path))\config\chocolatey.config" - if (-not (Test-Path -Path $choco_config_path)) { - Fail-Json -obj $result -message "Expecting Chocolatey config file to exist at '$choco_config_path'" - } - - try { - [xml]$choco_config = Get-Content -Path $choco_config_path - } catch { - Fail-Json -obj $result -message "Failed to parse Chocolatey config file at '$choco_config_path': $($_.Exception.Message)" - } - - $config_info = @{} - foreach ($config in $choco_config.chocolatey.config.GetEnumerator()) { - $config_info."$($config.key)" = $config.value - } - - return ,$config_info -} - -Function Remove-ChocolateyConfig { - param( - $choco_app, - $name - ) - $command = Argv-ToString -arguments @($choco_app.Path, "config", "unset", "--name", $name) - $res = Run-Command -command $command - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to unset Chocolatey config for '$name': $($res.stderr)" - } -} - -Function Set-ChocolateyConfig { - param( - $choco_app, - $name, - $value - ) - $command = Argv-ToString -arguments @($choco_app.Path, "config", "set", "--name", $name, "--value", $value) - $res = Run-Command -command $command - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to set Chocolatey config for '$name' to '$value': $($res.stderr)" - } -} - -$choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue -if (-not $choco_app) { - Fail-Json -obj $result -message "Failed to find Chocolatey installation, make sure choco.exe is in the PATH env value" -} - -$config_info = Get-ChocolateyConfig -choco_app $choco_app -if ($name -notin $config_info.Keys) { - Fail-Json -obj $result -message "The Chocolatey config '$name' is not an existing config value, check the spelling. Valid config names: $($config_info.Keys -join ', ')" -} -if ($diff) { - $result.diff.before = $config_info.$name -} - -if ($state -eq "absent" -and $config_info.$name -ne "") { - if (-not $check_mode) { - Remove-ChocolateyConfig -choco_app $choco_app -name $name - } - $result.changed = $true -# choco.exe config set is not case sensitive, it won't make a change if the -# value is the same but doesn't match -} elseif ($state -eq "present" -and $config_info.$name -ne $value) { - if (-not $check_mode) { - Set-ChocolateyConfig -choco_app $choco_app -name $name -value $value - } - $result.changed = $true - if ($diff) { - $result.diff.after = $value - } -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_chocolatey_config.py b/lib/ansible/modules/windows/win_chocolatey_config.py deleted file mode 100644 index 1d20bab311..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_config.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, 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_chocolatey_config -version_added: '2.7' -short_description: Manages Chocolatey config settings -description: -- Used to manage Chocolatey config settings as well as unset the values. -options: - name: - description: - - The name of the config setting to manage. - - See U(https://chocolatey.org/docs/chocolatey-configuration) for a list of - valid configuration settings that can be changed. - - Any config values that contain encrypted values like a password are not - idempotent as the plaintext value cannot be read. - type: str - required: yes - state: - description: - - When C(absent), it will ensure the setting is unset or blank. - - When C(present), it will ensure the setting is set to the value of - I(value). - type: str - choices: [ absent, present ] - default: present - value: - description: - - Used when C(state=present) that contains the value to set for the config - setting. - - Cannot be null or an empty string, use C(state=absent) to unset a config - value instead. - type: str -seealso: -- module: win_chocolatey -- module: win_chocolatey_facts -- module: win_chocolatey_feature -- module: win_chocolatey_source -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Set the cache location - win_chocolatey_config: - name: cacheLocation - state: present - value: D:\chocolatey_temp - -- name: Unset the cache location - win_chocolatey_config: - name: cacheLocation - state: absent -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_chocolatey_facts.ps1 b/lib/ansible/modules/windows/win_chocolatey_facts.ps1 deleted file mode 100644 index fdbdc96725..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_facts.ps1 +++ /dev/null @@ -1,182 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Ansible Project -# Copyright: (c) 2018, Simon Baerlocher <s.baerlocher@sbaerlocher.ch> -# Copyright: (c) 2018, ITIGO AG <opensource@itigo.ch> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version 2.0 - -# Create a new result object -$result = @{ - changed = $false - ansible_facts = @{ - ansible_chocolatey = @{ - config = @{} - feature = @{} - sources = @() - packages = @() - } - } -} - -$choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue -if (-not $choco_app) { - Fail-Json -obj $result -message "Failed to find Chocolatey installation, make sure choco.exe is in the PATH env value" -} - -Function Get-ChocolateyFeature { - - param($choco_app) - - $command = Argv-ToString -arguments $choco_app.Path, "feature", "list", "-r" - $res = Run-Command -command $command - if ($res.rc -ne 0) { - $result.stdout = $res.stdout - $result.stderr = $res.stderr - $result.rc = $res.rc - Fail-Json -obj $result -message "Failed to list Chocolatey features, see stderr" - } - - $feature_info = @{} - $res.stdout -split "`r`n" | Where-Object { $_ -ne "" } | ForEach-Object { - $feature_split = $_ -split "\|" - $feature_info."$($feature_split[0])" = $feature_split[1] -eq "Enabled" - } - $result.ansible_facts.ansible_chocolatey.feature = $feature_info -} - -Function Get-ChocolateyConfig { - - param($choco_app) - - $choco_config_path = "$(Split-Path -Path (Split-Path -Path $choco_app.Path))\config\chocolatey.config" - if (-not (Test-Path -Path $choco_config_path)) { - Fail-Json -obj $result -message "Expecting Chocolatey config file to exist at '$choco_config_path'" - } - - try { - [xml]$choco_config = Get-Content -Path $choco_config_path - } catch { - Fail-Json -obj $result -message "Failed to parse Chocolatey config file at '$choco_config_path': $($_.Exception.Message)" - } - - $config_info = @{} - foreach ($config in $choco_config.chocolatey.config.GetEnumerator()) { - # try and parse as a boot, then an int, fallback to string - try { - $value = [System.Boolean]::Parse($config.value) - } catch { - try { - $value = [System.Int32]::Parse($config.value) - } catch { - $value = $config.value - } - } - $config_info."$($config.key)" = $value - } - $result.ansible_facts.ansible_chocolatey.config = $config_info -} - -Function Get-ChocolateyPackages { - - param($choco_app) - - $command = Argv-ToString -arguments $choco_app.Path, "list", "--local-only", "--limit-output", "--all-versions" - $res = Run-Command -command $command - if ($res.rc -ne 0) { - $result.stdout = $res.stdout - $result.stderr = $res.stderr - $result.rc = $res.rc - Fail-Json -obj $result -message "Failed to list Chocolatey Packages, see stderr" - } - - $packages_info = [System.Collections.ArrayList]@() - $res.stdout.Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { - $packages_split = $_ -split "\|" - $package_info = @{ - package = $packages_split[0] - version = $packages_split[1] - } - $packages_info.Add($package_info) > $null - } - $result.ansible_facts.ansible_chocolatey.packages = $packages_info -} - -Function Get-ChocolateySources { - param($choco_app) - - $choco_config_path = "$(Split-Path -Path (Split-Path -Path $choco_app.Path))\config\chocolatey.config" - if (-not (Test-Path -LiteralPath $choco_config_path)) { - Fail-Json -obj $result -message "Expecting Chocolatey config file to exist at '$choco_config_path'" - } - - try { - [xml]$choco_config = Get-Content -Path $choco_config_path - } catch { - Fail-Json -obj $result -message "Failed to parse Chocolatey config file at '$choco_config_path': $($_.Exception.Message)" - } - - $sources = [System.Collections.ArrayList]@() - foreach ($xml_source in $choco_config.chocolatey.sources.GetEnumerator()) { - $source_username = $xml_source.Attributes.GetNamedItem("user") - if ($null -ne $source_username) { - $source_username = $source_username.Value - } - - # 0.9.9.9+ - $priority = $xml_source.Attributes.GetNamedItem("priority") - if ($null -ne $priority) { - $priority = [int]$priority.Value - } - - # 0.9.10+ - $certificate = $xml_source.Attributes.GetNamedItem("certificate") - if ($null -ne $certificate) { - $certificate = $certificate.Value - } - - # 0.10.4+ - $bypass_proxy = $xml_source.Attributes.GetNamedItem("bypassProxy") - if ($null -ne $bypass_proxy) { - $bypass_proxy = [System.Convert]::ToBoolean($bypass_proxy.Value) - } - $allow_self_service = $xml_source.Attributes.GetNamedItem("selfService") - if ($null -ne $allow_self_service) { - $allow_self_service = [System.Convert]::ToBoolean($allow_self_service.Value) - } - - # 0.10.8+ - $admin_only = $xml_source.Attributes.GetNamedItem("adminOnly") - if ($null -ne $admin_only) { - $admin_only = [System.Convert]::ToBoolean($admin_only.Value) - } - - $source_info = @{ - name = $xml_source.id - source = $xml_source.value - disabled = [System.Convert]::ToBoolean($xml_source.disabled) - source_username = $source_username - priority = $priority - certificate = $certificate - bypass_proxy = $bypass_proxy - allow_self_service = $allow_self_service - admin_only = $admin_only - } - $sources.Add($source_info) > $null - } - $result.ansible_facts.ansible_chocolatey.sources = $sources -} - -Get-ChocolateyConfig -choco_app $choco_app -Get-ChocolateyFeature -choco_app $choco_app -Get-ChocolateyPackages -choco_app $choco_app -Get-ChocolateySources -choco_app $choco_app - -# Return result -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_chocolatey_facts.py b/lib/ansible/modules/windows/win_chocolatey_facts.py deleted file mode 100644 index d35f14d012..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_facts.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Ansible Project -# Copyright: (c) 2018, Simon Baerlocher <s.baerlocher@sbaerlocher.ch> -# Copyright: (c) 2018, ITIGO AG <opensource@itigo.ch> -# 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_chocolatey_facts -version_added: '2.8' -short_description: Create a facts collection for Chocolatey -description: -- This module shows information from Chocolatey, such as installed packages, configuration, feature and sources. -notes: -- Chocolatey must be installed beforehand, use M(win_chocolatey) to do this. -seealso: -- module: win_chocolatey -- module: win_chocolatey_config -- module: win_chocolatey_feature -- module: win_chocolatey_source -author: -- Simon Bärlocher (@sbaerlocher) -- ITIGO AG (@itigoag) -''' - -EXAMPLES = r''' -- name: Gather facts from chocolatey - win_chocolatey_facts: - -- name: Displays the Configuration - debug: - var: ansible_chocolatey.config - -- name: Displays the Feature - debug: - var: ansible_chocolatey.feature - -- name: Displays the Sources - debug: - var: ansible_chocolatey.sources - -- name: Displays the Packages - debug: - var: ansible_chocolatey.packages -''' - -RETURN = r''' -ansible_facts: - description: Detailed information about the Chocolatey installation - returned: always - type: complex - contains: - ansible_chocolatey: - description: Detailed information about the Chocolatey installation - returned: always - type: complex - contains: - config: - description: Detailed information about stored the configurations - returned: always - type: dict - sample: - commandExecutionTimeoutSeconds: 2700 - containsLegacyPackageInstalls: true - feature: - description: Detailed information about enabled and disabled features - returned: always - type: dict - sample: - allowEmptyCheckums: false - autoUninstaller: true - failOnAutoUninstaller: false - sources: - description: List of Chocolatey sources - returned: always - type: complex - contains: - admin_only: - description: Is the source visible to Administrators only - returned: always - type: bool - sample: false - allow_self_service: - description: Is the source allowed to be used with self-service - returned: always - type: bool - sample: false - bypass_proxy: - description: Can the source explicitly bypass configured proxies - returned: always - type: bool - sample: true - certificate: - description: Path to a PFX certificate for X509 authenticated feeds - returned: always - type: str - sample: C:\chocolatey\cert.pfx - disabled: - description: Is the source disabled - returned: always - type: bool - sample: false - name: - description: Name of the source - returned: always - type: str - sample: chocolatey - priority: - description: The priority order of this source, lower is better, 0 is no priority - returned: always - type: int - sample: 0 - source: - description: The source, can be a folder/file or an url - returned: always - type: str - sample: https://chocolatey.org/api/v2/ - source_username: - description: Username used to access authenticated feeds - returned: always - type: str - sample: username - packages: - description: List of installed Packages - returned: always - type: complex - contains: - package: - description: Name of the package - returned: always - type: str - sample: vscode - version: - description: Version of the package - returned: always - type: str - sample: '1.27.2' -''' diff --git a/lib/ansible/modules/windows/win_chocolatey_feature.ps1 b/lib/ansible/modules/windows/win_chocolatey_feature.ps1 deleted file mode 100644 index a33378cd6a..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_feature.ps1 +++ /dev/null @@ -1,74 +0,0 @@ -#!powershell - -# Copyright: (c), 2018 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.CommandUtil -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "enabled" -validateset "disabled", "enabled" - -$result = @{ - changed = $false -} - -Function Get-ChocolateyFeatures { - param($choco_app) - - $res = Run-Command -command "`"$($choco_app.Path)`" feature list -r" - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to list Chocolatey features: $($res.stderr)" - } - $feature_info = @{} - $res.stdout -split "`r`n" | Where-Object { $_ -ne "" } | ForEach-Object { - $feature_split = $_ -split "\|" - $feature_info."$($feature_split[0])" = $feature_split[1] -eq "Enabled" - } - - return ,$feature_info -} - -Function Set-ChocolateyFeature { - param( - $choco_app, - $name, - $enabled - ) - - if ($enabled) { - $state_string = "enable" - } else { - $state_string = "disable" - } - $res = Run-Command -command "`"$($choco_app.Path)`" feature $state_string --name `"$name`"" - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to set Chocolatey feature $name to $($state_string): $($res.stderr)" - } -} - -$choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue -if (-not $choco_app) { - Fail-Json -obj $result -message "Failed to find Chocolatey installation, make sure choco.exe is in the PATH env value" -} - -$feature_info = Get-ChocolateyFeatures -choco_app $choco_app -if ($name -notin $feature_info.keys) { - Fail-Json -obj $result -message "Invalid feature name '$name' specified, valid features are: $($feature_info.keys -join ', ')" -} - -$expected_status = $state -eq "enabled" -$feature_status = $feature_info.$name -if ($feature_status -ne $expected_status) { - if (-not $check_mode) { - Set-ChocolateyFeature -choco_app $choco_app -name $name -enabled $expected_status - } - $result.changed = $true -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_chocolatey_feature.py b/lib/ansible/modules/windows/win_chocolatey_feature.py deleted file mode 100644 index 13358f4e1e..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_feature.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, 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_chocolatey_feature -version_added: '2.7' -short_description: Manages Chocolatey features -description: -- Used to enable or disable features in Chocolatey. -options: - name: - description: - - The name of the feature to manage. - - Run C(choco.exe feature list) to get a list of features that can be - managed. - type: str - required: yes - state: - description: - - When C(disabled) then the feature will be disabled. - - When C(enabled) then the feature will be enabled. - type: str - choices: [ disabled, enabled ] - default: enabled -seealso: -- module: win_chocolatey -- module: win_chocolatey_config -- module: win_chocolatey_facts -- module: win_chocolatey_source -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Disable file checksum matching - win_chocolatey_feature: - name: checksumFiles - state: disabled - -- name: Stop Chocolatey on the first package failure - win_chocolatey_feature: - name: stopOnFirstPackageFailure - state: enabled -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_chocolatey_source.ps1 b/lib/ansible/modules/windows/win_chocolatey_source.ps1 deleted file mode 100644 index c2360f153a..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_source.ps1 +++ /dev/null @@ -1,306 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent", "disabled", "present" - -$admin_only = Get-AnsibleParam -obj $params -name "admin_only" -type "bool" -$allow_self_service = Get-AnsibleParam -obj $params -name "allow_self_service" -type "bool" -$bypass_proxy = Get-AnsibleParam -obj $params -name "bypass_proxy" -type "bool" -$certificate = Get-AnsibleParam -obj $params -name "certificate" -type "str" -$certificate_password = Get-AnsibleParam -obj $params -name "certificate_password" -type "str" -$priority = Get-AnsibleParam -obj $params -name "priority" -type "int" -$source = Get-AnsibleParam -obj $params -name "source" -type "str" -$source_username = Get-AnsibleParam -obj $params -name "source_username" -type "str" -$source_password = Get-AnsibleParam -obj $params -name "source_password" -type "str" -failifempty ($null -ne $source_username) -$update_password = Get-AnsibleParam -obj $params -name "update_password" -type "str" -default "always" -validateset "always", "on_create" - -$result = @{ - changed = $false -} -if ($diff) { - $result.diff = @{ - before = @{} - after = @{} - } -} - -Function Get-ChocolateySources { - param($choco_app) - - $choco_config_path = "$(Split-Path -Path (Split-Path -Path $choco_app.Path))\config\chocolatey.config" - if (-not (Test-Path -LiteralPath $choco_config_path)) { - Fail-Json -obj $result -message "Expecting Chocolatey config file to exist at '$choco_config_path'" - } - - # would prefer to enumerate the existing sources with an actual API but the - # only stable interface is choco.exe source list and that does not output - # the sources in an easily parsable list. Using -r will split each entry by - # | like a psv but does not quote values that have a | already in it making - # it inadequete for our tasks. Instead we will parse the chocolatey.config - # file and get the values from there - try { - [xml]$choco_config = Get-Content -Path $choco_config_path - } catch { - Fail-Json -obj $result -message "Failed to parse Chocolatey config file at '$choco_config_path': $($_.Exception.Message)" - } - - $sources = [System.Collections.ArrayList]@() - foreach ($xml_source in $choco_config.chocolatey.sources.GetEnumerator()) { - $source_username = $xml_source.Attributes.GetNamedItem("user") - if ($null -ne $source_username) { - $source_username = $source_username.Value - } - - # 0.9.9.9+ - $priority = $xml_source.Attributes.GetNamedItem("priority") - if ($null -ne $priority) { - $priority = [int]$priority.Value - } - - # 0.9.10+ - $certificate = $xml_source.Attributes.GetNamedItem("certificate") - if ($null -ne $certificate) { - $certificate = $certificate.Value - } - - # 0.10.4+ - $bypass_proxy = $xml_source.Attributes.GetNamedItem("bypassProxy") - if ($null -ne $bypass_proxy) { - $bypass_proxy = [System.Convert]::ToBoolean($bypass_proxy.Value) - } - $allow_self_service = $xml_source.Attributes.GetNamedItem("selfService") - if ($null -ne $allow_self_service) { - $allow_self_service = [System.Convert]::ToBoolean($allow_self_service.Value) - } - - # 0.10.8+ - $admin_only = $xml_source.Attributes.GetNamedItem("adminOnly") - if ($null -ne $admin_only) { - $admin_only = [System.Convert]::ToBoolean($admin_only.Value) - } - - $source_info = @{ - name = $xml_source.id - source = $xml_source.value - disabled = [System.Convert]::ToBoolean($xml_source.disabled) - source_username = $source_username - priority = $priority - certificate = $certificate - bypass_proxy = $bypass_proxy - allow_self_service = $allow_self_service - admin_only = $admin_only - } - $sources.Add($source_info) > $null - } - return ,$sources -} - -Function New-ChocolateySource { - param( - $choco_app, - $name, - $source, - $source_username, - $source_password, - $certificate, - $certificate_password, - $priority, - $bypass_proxy, - $allow_self_service, - $admin_only - ) - # build the base arguments - $arguments = [System.Collections.ArrayList]@($choco_app.Path, - "source", "add", "--name", $name, "--source", $source - ) - - # add optional arguments from user input - if ($null -ne $source_username) { - $arguments.Add("--user") > $null - $arguments.Add($source_username) > $null - $arguments.Add("--password") > $null - $arguments.Add($source_password) > $null - } - if ($null -ne $certificate) { - $arguments.Add("--cert") > $null - $arguments.Add($certificate) > $null - } - if ($null -ne $certificate_password) { - $arguments.Add("--certpassword") > $null - $arguments.Add($certificate_password) > $null - } - if ($null -ne $priority) { - $arguments.Add("--priority") > $null - $arguments.Add($priority) > $null - } else { - $priority = 0 - } - if ($bypass_proxy -eq $true) { - $arguments.Add("--bypass-proxy") > $null - } else { - $bypass_proxy = $false - } - if ($allow_self_service -eq $true) { - $arguments.Add("--allow-self-service") > $null - } else { - $allow_self_service = $false - } - if ($admin_only -eq $true) { - $arguments.Add("--admin-only") > $null - } else { - $admin_only = $false - } - - if ($check_mode) { - $arguments.Add("--what-if") > $null - } - - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to add Chocolatey source '$name': $($res.stderr)" - } - - $source_info = @{ - name = $name - source = $source - disabled = $false - source_username = $source_username - priority = $priority - certificate = $certificate - bypass_proxy = $bypass_proxy - allow_self_service = $allow_self_service - admin_only = $admin_only - } - return ,$source_info -} - -Function Remove-ChocolateySource { - param( - $choco_app, - $name - ) - $arguments = [System.Collections.ArrayList]@($choco_app.Path, "source", "remove", "--name", $name) - if ($check_mode) { - $arguments.Add("--what-if") > $null - } - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to remove Chocolatey source '$name': $($_.res.stderr)" - } -} - -$choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue -if (-not $choco_app) { - Fail-Json -obj $result -message "Failed to find Chocolatey installation, make sure choco.exe is in the PATH env value" -} -$actual_sources = Get-ChocolateySources -choco_app $choco_app -$actual_source = $actual_sources | Where-Object { $_.name -eq $name } -if ($diff) { - if ($null -ne $actual_source) { - $before = $actual_source.Clone() - } else { - $before = @{} - } - $result.diff.before = $before -} - -if ($state -eq "absent" -and $null -ne $actual_source) { - Remove-ChocolateySource -choco_app $choco_app -name $name - $result.changed = $true -} elseif ($state -in ("disabled", "present")) { - $change = $false - if ($null -eq $actual_source) { - if ($null -eq $source) { - Fail-Json -obj $result -message "The source option must be set when creating a new source" - } - $change = $true - } else { - if ($null -ne $source -and $source -ne $actual_source.source) { - $change = $true - } - if ($null -ne $source_username -and $source_username -ne $actual_source.source_username) { - $change = $true - } - if ($null -ne $source_password -and $update_password -eq "always") { - $change = $true - } - if ($null -ne $certificate -and $certificate -ne $actual_source.certificate) { - $change = $true - } - if ($null -ne $certificate_password -and $update_password -eq "always") { - $change = $true - } - if ($null -ne $priority -and $priority -ne $actual_source.priority) { - $change = $true - } - if ($null -ne $bypass_proxy -and $bypass_proxy -ne $actual_source.bypass_proxy) { - $change = $true - } - if ($null -ne $allow_self_service -and $allow_self_service -ne $actual_source.allow_self_service) { - $change = $true - } - if ($null -ne $admin_only -and $admin_only -ne $actual_source.admin_only) { - $change = $true - } - - if ($change) { - Remove-ChocolateySource -choco_app $choco_app -name $name - $result.changed = $true - } - } - - if ($change) { - $actual_source = New-ChocolateySource -choco_app $choco_app -name $name -source $source ` - -source_username $source_username -source_password $source_password ` - -certificate $certificate -certificate_password $certificate_password ` - -priority $priority -bypass_proxy $bypass_proxy -allow_self_service $allow_self_service ` - -admin_only $admin_only - $result.changed = $true - } - - # enable/disable the source if necessary - $status_action = $null - if ($state -ne "disabled" -and $actual_source.disabled) { - $status_action = "enable" - } elseif ($state -eq "disabled" -and (-not $actual_source.disabled)) { - $status_action = "disable" - } - if ($null -ne $status_action) { - $arguments = [System.Collections.ArrayList]@($choco_app.Path, "source", $status_action, "--name", $name) - if ($check_mode) { - $arguments.Add("--what-if") > $null - } - $command = Argv-ToString -arguments $arguments - $res = Run-Command -command $command - if ($res.rc -ne 0) { - Fail-Json -obj $result -message "Failed to $status_action Chocolatey source '$name': $($res.stderr)" - } - $actual_source.disabled = ($status_action -eq "disable") - $result.changed = $true - } - - if ($diff) { - $after = $actual_source - $result.diff.after = $after - } -} - -# finally remove the diff if there was no change -if (-not $result.changed -and $diff) { - $result.diff = @{} -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_chocolatey_source.py b/lib/ansible/modules/windows/win_chocolatey_source.py deleted file mode 100644 index 0e1fc0f588..0000000000 --- a/lib/ansible/modules/windows/win_chocolatey_source.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, 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_chocolatey_source -version_added: '2.7' -short_description: Manages Chocolatey sources -description: -- Used to managed Chocolatey sources configured on the client. -- Requires Chocolatey to be already installed on the remote host. -options: - admin_only: - description: - - Makes the source visible to Administrators only. - - Requires Chocolatey >= 0.10.8. - - When creating a new source, this defaults to C(no). - type: bool - allow_self_service: - description: - - Allow the source to be used with self-service - - Requires Chocolatey >= 0.10.4. - - When creating a new source, this defaults to C(no). - type: bool - bypass_proxy: - description: - - Bypass the proxy when using this source. - - Requires Chocolatey >= 0.10.4. - - When creating a new source, this defaults to C(no). - type: bool - certificate: - description: - - The path to a .pfx file to use for X509 authenticated feeds. - - Requires Chocolatey >= 0.9.10. - type: str - certificate_password: - description: - - The password for I(certificate) if required. - - Requires Chocolatey >= 0.9.10. - name: - description: - - The name of the source to configure. - required: yes - priority: - description: - - The priority order of this source compared to other sources, lower is - better. - - All priorities above C(0) will be evaluated first, then zero-based values - will be evaluated in config file order. - - Requires Chocolatey >= 0.9.9.9. - - When creating a new source, this defaults to C(0). - type: int - source: - description: - - The file/folder/url of the source. - - Required when I(state) is C(present) or C(disabled) and the source does - not already exist. - source_username: - description: - - The username used to access I(source). - source_password: - description: - - The password for I(source_username). - - Required if I(source_username) is set. - state: - description: - - When C(absent), will remove the source. - - When C(disabled), will ensure the source exists but is disabled. - - When C(present), will ensure the source exists and is enabled. - choices: - - absent - - disabled - - present - default: present - update_password: - description: - - When C(always), the module will always set the password and report a - change if I(certificate_password) or I(source_password) is set. - - When C(on_create), the module will only set the password if the source - is being created. - choices: - - always - - on_create - default: always -seealso: -- module: win_chocolatey -- module: win_chocolatey_config -- module: win_chocolatey_facts -- module: win_chocolatey_feature -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Remove the default public source - win_chocolatey_source: - name: chocolatey - state: absent - -- name: Add new internal source - win_chocolatey_source: - name: internal repo - state: present - source: http://chocolatey-server/chocolatey - -- name: Create HTTP source with credentials - win_chocolatey_source: - name: internal repo - state: present - source: https://chocolatey-server/chocolatey - source_username: username - source_password: password - -- name: Disable Chocolatey source - win_chocolatey_source: - name: chocolatey - state: disabled -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_computer_description.ps1 b/lib/ansible/modules/windows/win_computer_description.ps1 deleted file mode 100644 index f1b75d1ab0..0000000000 --- a/lib/ansible/modules/windows/win_computer_description.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, RusoSova -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic -#AnsibleRequires -OSVersion 6.1 - -$spec = @{ - options = @{ - owner = @{ type="str" } - organization = @{ type="str" } - description = @{ type="str" } - } - required_one_of = @( - ,@('owner', 'organization', 'description') - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$owner = $module.Params.owner -$organization = $module.Params.organization -$description = $module.Params.description -$regPath="HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\" - -#Change description -if ($description -or $description -eq "") { - $descriptionObject=Get-CimInstance -class "Win32_OperatingSystem" - if ($description -cne $descriptionObject.description) { - Set-CimInstance -InputObject $descriptionObject -Property @{"Description"="$description"} -WhatIf:$module.CheckMode - $module.Result.changed = $true - } -} - -#Change owner -if ($owner -or $owner -eq "") { - $curentOwner=(Get-ItemProperty -LiteralPath $regPath -Name RegisteredOwner).RegisteredOwner - if ($curentOwner -cne $owner) { - Set-ItemProperty -LiteralPath $regPath -Name "RegisteredOwner" -Value $owner -WhatIf:$module.CheckMode - $module.Result.changed = $true - } -} - -#Change organization -if ($organization -or $organization -eq "") { - $curentOrganization=(Get-ItemProperty -LiteralPath $regPath -Name RegisteredOrganization).RegisteredOrganization - if ($curentOrganization -cne $organization) { - Set-ItemProperty -LiteralPath $regPath -Name "RegisteredOrganization" -Value $organization -WhatIf:$module.CheckMode - $module.Result.changed = $true - } -} -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_computer_description.py b/lib/ansible/modules/windows/win_computer_description.py deleted file mode 100644 index b8aa276185..0000000000 --- a/lib/ansible/modules/windows/win_computer_description.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, RusoSova -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = r''' ---- -module: win_computer_description -short_description: Set windows description, owner and organization -description: - - This module sets Windows description that is shown under My Computer properties. Module also sets - Windows license owner and organization. License information can be viewed by running winver commad. -options: - description: - description: - - String value to apply to Windows descripton. Specify value of "" to clear the value. - required: false - type: str - organization: - description: - - String value of organization that the Windows is licensed to. Specify value of "" to clear the value. - required: false - type: str - owner: - description: - - String value of the persona that the Windows is licensed to. Specify value of "" to clear the value. - required: false - type: str -version_added: '2.10' -author: - - RusoSova (@RusoSova) -''' - -EXAMPLES = r''' -- name: Set Windows description, owner and organization - win_computer_description: - description: Best Box - owner: RusoSova - organization: MyOrg - register: result - -- name: Set Windows description only - win_computer_description: - description: This is my Windows machine - register: result - -- name: Set organization and clear owner field - win_computer_description: - owner: '' - organization: Black Mesa - -- name: Clear organization, description and owner - win_computer_description: - organization: "" - owner: "" - description: "" - register: result -''' - -RETURN = r''' -# -''' diff --git a/lib/ansible/modules/windows/win_credential.ps1 b/lib/ansible/modules/windows/win_credential.ps1 deleted file mode 100644 index fdc83584f0..0000000000 --- a/lib/ansible/modules/windows/win_credential.ps1 +++ /dev/null @@ -1,714 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, 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 = @{ - alias = @{ type = "str" } - attributes = @{ - type = "list" - elements = "dict" - options = @{ - name = @{ type = "str"; required = $true } - data = @{ type = "str" } - data_format = @{ type = "str"; default = "text"; choices = @("base64", "text") } - } - } - comment = @{ type = "str" } - name = @{ type = "str"; required = $true } - persistence = @{ type = "str"; default = "local"; choices = @("enterprise", "local") } - secret = @{ type = "str"; no_log = $true } - secret_format = @{ type = "str"; default = "text"; choices = @("base64", "text") } - state = @{ type = "str"; default = "present"; choices = @("absent", "present") } - type = @{ - type = "str" - required = $true - choices = @("domain_password", "domain_certificate", "generic_password", "generic_certificate") - } - update_secret = @{ type = "str"; default = "always"; choices = @("always", "on_create") } - username = @{ type = "str" } - } - required_if = @( - ,@("state", "present", @("username")) - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$alias = $module.Params.alias -$attributes = $module.Params.attributes -$comment = $module.Params.comment -$name = $module.Params.name -$persistence = $module.Params.persistence -$secret = $module.Params.secret -$secret_format = $module.Params.secret_format -$state = $module.Params.state -$type = $module.Params.type -$update_secret = $module.Params.update_secret -$username = $module.Params.username - -$module.Diff.before = "" -$module.Diff.after = "" - -Add-CSharpType -AnsibleModule $module -References @' -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; -using System.Text; - -namespace Ansible.CredentialManager -{ - internal class NativeHelpers - { - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public class CREDENTIAL - { - public CredentialFlags Flags; - public CredentialType Type; - [MarshalAs(UnmanagedType.LPWStr)] public string TargetName; - [MarshalAs(UnmanagedType.LPWStr)] public string Comment; - public FILETIME LastWritten; - public UInt32 CredentialBlobSize; - public IntPtr CredentialBlob; - public CredentialPersist Persist; - public UInt32 AttributeCount; - public IntPtr Attributes; - [MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias; - [MarshalAs(UnmanagedType.LPWStr)] public string UserName; - - public static explicit operator Credential(CREDENTIAL v) - { - byte[] secret = new byte[(int)v.CredentialBlobSize]; - if (v.CredentialBlob != IntPtr.Zero) - Marshal.Copy(v.CredentialBlob, secret, 0, secret.Length); - - List<CredentialAttribute> attributes = new List<CredentialAttribute>(); - if (v.AttributeCount > 0) - { - CREDENTIAL_ATTRIBUTE[] rawAttributes = new CREDENTIAL_ATTRIBUTE[v.AttributeCount]; - Credential.PtrToStructureArray(rawAttributes, v.Attributes); - attributes = rawAttributes.Select(x => (CredentialAttribute)x).ToList(); - } - - string userName = v.UserName; - if (v.Type == CredentialType.DomainCertificate || v.Type == CredentialType.GenericCertificate) - userName = Credential.UnmarshalCertificateCredential(userName); - - return new Credential - { - Type = v.Type, - TargetName = v.TargetName, - Comment = v.Comment, - LastWritten = (DateTimeOffset)v.LastWritten, - Secret = secret, - Persist = v.Persist, - Attributes = attributes, - TargetAlias = v.TargetAlias, - UserName = userName, - Loaded = true, - }; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct CREDENTIAL_ATTRIBUTE - { - [MarshalAs(UnmanagedType.LPWStr)] public string Keyword; - public UInt32 Flags; // Set to 0 and is reserved - public UInt32 ValueSize; - public IntPtr Value; - - public static explicit operator CredentialAttribute(CREDENTIAL_ATTRIBUTE v) - { - byte[] value = new byte[v.ValueSize]; - Marshal.Copy(v.Value, value, 0, (int)v.ValueSize); - - return new CredentialAttribute - { - Keyword = v.Keyword, - Flags = v.Flags, - Value = value, - }; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct FILETIME - { - internal UInt32 dwLowDateTime; - internal UInt32 dwHighDateTime; - - public static implicit operator long(FILETIME v) { return ((long)v.dwHighDateTime << 32) + v.dwLowDateTime; } - public static explicit operator DateTimeOffset(FILETIME v) { return DateTimeOffset.FromFileTime(v); } - public static explicit operator FILETIME(DateTimeOffset v) - { - return new FILETIME() - { - dwLowDateTime = (UInt32)v.ToFileTime(), - dwHighDateTime = ((UInt32)v.ToFileTime() >> 32), - }; - } - } - - [Flags] - public enum CredentialCreateFlags : uint - { - PreserveCredentialBlob = 1, - } - - [Flags] - public enum CredentialFlags - { - None = 0, - PromptNow = 2, - UsernameTarget = 4, - } - - public enum CredMarshalType : uint - { - CertCredential = 1, - UsernameTargetCredential, - BinaryBlobCredential, - UsernameForPackedCredential, - BinaryBlobForSystem, - } - } - - internal class NativeMethods - { - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CredDeleteW( - [MarshalAs(UnmanagedType.LPWStr)] string TargetName, - CredentialType Type, - UInt32 Flags); - - [DllImport("advapi32.dll")] - public static extern void CredFree( - IntPtr Buffer); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CredMarshalCredentialW( - NativeHelpers.CredMarshalType CredType, - SafeMemoryBuffer Credential, - out SafeCredentialBuffer MarshaledCredential); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CredReadW( - [MarshalAs(UnmanagedType.LPWStr)] string TargetName, - CredentialType Type, - UInt32 Flags, - out SafeCredentialBuffer Credential); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CredUnmarshalCredentialW( - [MarshalAs(UnmanagedType.LPWStr)] string MarshaledCredential, - out NativeHelpers.CredMarshalType CredType, - out SafeCredentialBuffer Credential); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CredWriteW( - NativeHelpers.CREDENTIAL Credential, - NativeHelpers.CredentialCreateFlags Flags); - } - - internal class SafeCredentialBuffer : SafeHandleZeroOrMinusOneIsInvalid - { - public SafeCredentialBuffer() : base(true) { } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - protected override bool ReleaseHandle() - { - NativeMethods.CredFree(handle); - return true; - } - } - - 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 _exception_msg; - public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { } - public Win32Exception(int errorCode, string message) : base(errorCode) - { - _exception_msg = String.Format("{0} - {1} (Win32 Error Code {2}: 0x{3})", message, base.Message, errorCode, errorCode.ToString("X8")); - } - public override string Message { get { return _exception_msg; } } - public static explicit operator Win32Exception(string message) { return new Win32Exception(message); } - } - - public enum CredentialPersist - { - Session = 1, - LocalMachine = 2, - Enterprise = 3, - } - - public enum CredentialType - { - Generic = 1, - DomainPassword = 2, - DomainCertificate = 3, - DomainVisiblePassword = 4, - GenericCertificate = 5, - DomainExtended = 6, - Maximum = 7, - MaximumEx = 1007, - } - - public class CredentialAttribute - { - public string Keyword; - public UInt32 Flags; - public byte[] Value; - } - - public class Credential - { - public CredentialType Type; - public string TargetName; - public string Comment; - public DateTimeOffset LastWritten; - public byte[] Secret; - public CredentialPersist Persist; - public List<CredentialAttribute> Attributes = new List<CredentialAttribute>(); - public string TargetAlias; - public string UserName; - - // Used to track whether the credential has been loaded into the store or not - public bool Loaded { get; internal set; } - - public void Delete() - { - if (!Loaded) - return; - - if (!NativeMethods.CredDeleteW(TargetName, Type, 0)) - throw new Win32Exception(String.Format("CredDeleteW({0}) failed", TargetName)); - Loaded = false; - } - - public void Write(bool preserveExisting) - { - string userName = UserName; - // Convert the certificate thumbprint to the string expected - if (Type == CredentialType.DomainCertificate || Type == CredentialType.GenericCertificate) - userName = Credential.MarshalCertificateCredential(userName); - - NativeHelpers.CREDENTIAL credential = new NativeHelpers.CREDENTIAL - { - Flags = NativeHelpers.CredentialFlags.None, - Type = Type, - TargetName = TargetName, - Comment = Comment, - LastWritten = new NativeHelpers.FILETIME(), - CredentialBlobSize = (UInt32)(Secret == null ? 0 : Secret.Length), - CredentialBlob = IntPtr.Zero, // Must be allocated and freed outside of this to ensure no memory leaks - Persist = Persist, - AttributeCount = (UInt32)(Attributes.Count), - Attributes = IntPtr.Zero, // Attributes must be allocated and freed outside of this to ensure no memory leaks - TargetAlias = TargetAlias, - UserName = userName, - }; - - using (SafeMemoryBuffer credentialBlob = new SafeMemoryBuffer((int)credential.CredentialBlobSize)) - { - if (Secret != null) - Marshal.Copy(Secret, 0, credentialBlob.DangerousGetHandle(), Secret.Length); - credential.CredentialBlob = credentialBlob.DangerousGetHandle(); - - // Store the CREDENTIAL_ATTRIBUTE value in a safe memory buffer and make sure we dispose in all cases - List<SafeMemoryBuffer> attributeBuffers = new List<SafeMemoryBuffer>(); - try - { - int attributeLength = Attributes.Sum(a => Marshal.SizeOf(typeof(NativeHelpers.CREDENTIAL_ATTRIBUTE))); - byte[] attributeBytes = new byte[attributeLength]; - int offset = 0; - foreach (CredentialAttribute attribute in Attributes) - { - SafeMemoryBuffer attributeBuffer = new SafeMemoryBuffer(attribute.Value.Length); - attributeBuffers.Add(attributeBuffer); - if (attribute.Value != null) - Marshal.Copy(attribute.Value, 0, attributeBuffer.DangerousGetHandle(), attribute.Value.Length); - - NativeHelpers.CREDENTIAL_ATTRIBUTE credentialAttribute = new NativeHelpers.CREDENTIAL_ATTRIBUTE - { - Keyword = attribute.Keyword, - Flags = attribute.Flags, - ValueSize = (UInt32)(attribute.Value == null ? 0 : attribute.Value.Length), - Value = attributeBuffer.DangerousGetHandle(), - }; - int attributeStructLength = Marshal.SizeOf(typeof(NativeHelpers.CREDENTIAL_ATTRIBUTE)); - - byte[] attrBytes = new byte[attributeStructLength]; - using (SafeMemoryBuffer tempBuffer = new SafeMemoryBuffer(attributeStructLength)) - { - Marshal.StructureToPtr(credentialAttribute, tempBuffer.DangerousGetHandle(), false); - Marshal.Copy(tempBuffer.DangerousGetHandle(), attrBytes, 0, attributeStructLength); - } - Buffer.BlockCopy(attrBytes, 0, attributeBytes, offset, attributeStructLength); - offset += attributeStructLength; - } - - using (SafeMemoryBuffer attributes = new SafeMemoryBuffer(attributeBytes.Length)) - { - if (attributeBytes.Length != 0) - { - Marshal.Copy(attributeBytes, 0, attributes.DangerousGetHandle(), attributeBytes.Length); - credential.Attributes = attributes.DangerousGetHandle(); - } - - NativeHelpers.CredentialCreateFlags createFlags = 0; - if (preserveExisting) - createFlags |= NativeHelpers.CredentialCreateFlags.PreserveCredentialBlob; - - if (!NativeMethods.CredWriteW(credential, createFlags)) - throw new Win32Exception(String.Format("CredWriteW({0}) failed", TargetName)); - } - } - finally - { - foreach (SafeMemoryBuffer attributeBuffer in attributeBuffers) - attributeBuffer.Dispose(); - } - } - Loaded = true; - } - - public static Credential GetCredential(string target, CredentialType type) - { - SafeCredentialBuffer buffer; - if (!NativeMethods.CredReadW(target, type, 0, out buffer)) - { - int lastErr = Marshal.GetLastWin32Error(); - - // Not running with Become so cannot manage the user's credentials - if (lastErr == 0x00000520) // ERROR_NO_SUCH_LOGON_SESSION - throw new InvalidOperationException("Failed to access the user's credential store, run the module with become"); - else if (lastErr == 0x00000490) // ERROR_NOT_FOUND - return null; - throw new Win32Exception(lastErr, "CredEnumerateW() failed"); - } - - using (buffer) - { - NativeHelpers.CREDENTIAL credential = (NativeHelpers.CREDENTIAL)Marshal.PtrToStructure( - buffer.DangerousGetHandle(), typeof(NativeHelpers.CREDENTIAL)); - return (Credential)credential; - } - } - - public static string MarshalCertificateCredential(string thumbprint) - { - // CredWriteW requires the UserName field to be the value of CredMarshalCredentialW() when writting a - // certificate auth. This converts the UserName property to the format required. - - // While CERT_CREDENTIAL_INFO is the correct structure, we manually marshal the data in order to - // support different cert hash lengths in the future. - // https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_cert_credential_info - int hexLength = thumbprint.Length; - byte[] credInfo = new byte[sizeof(UInt32) + (hexLength / 2)]; - - // First field is cbSize which is a UInt32 value denoting the size of the total structure - Array.Copy(BitConverter.GetBytes((UInt32)credInfo.Length), credInfo, sizeof(UInt32)); - - // Now copy the byte representation of the thumbprint to the rest of the struct bytes - for (int i = 0; i < hexLength; i += 2) - credInfo[sizeof(UInt32) + (i / 2)] = Convert.ToByte(thumbprint.Substring(i, 2), 16); - - IntPtr pCredInfo = Marshal.AllocHGlobal(credInfo.Length); - Marshal.Copy(credInfo, 0, pCredInfo, credInfo.Length); - SafeMemoryBuffer pCredential = new SafeMemoryBuffer(pCredInfo); - - NativeHelpers.CredMarshalType marshalType = NativeHelpers.CredMarshalType.CertCredential; - using (pCredential) - { - SafeCredentialBuffer marshaledCredential; - if (!NativeMethods.CredMarshalCredentialW(marshalType, pCredential, out marshaledCredential)) - throw new Win32Exception("CredMarshalCredentialW() failed"); - using (marshaledCredential) - return Marshal.PtrToStringUni(marshaledCredential.DangerousGetHandle()); - } - } - - public static string UnmarshalCertificateCredential(string value) - { - NativeHelpers.CredMarshalType credType; - SafeCredentialBuffer pCredInfo; - if (!NativeMethods.CredUnmarshalCredentialW(value, out credType, out pCredInfo)) - throw new Win32Exception("CredUnmarshalCredentialW() failed"); - - using (pCredInfo) - { - if (credType != NativeHelpers.CredMarshalType.CertCredential) - throw new InvalidOperationException(String.Format("Expected unmarshalled cred type of CertCredential, received {0}", credType)); - - byte[] structSizeBytes = new byte[sizeof(UInt32)]; - Marshal.Copy(pCredInfo.DangerousGetHandle(), structSizeBytes, 0, sizeof(UInt32)); - UInt32 structSize = BitConverter.ToUInt32(structSizeBytes, 0); - - byte[] certInfoBytes = new byte[structSize]; - Marshal.Copy(pCredInfo.DangerousGetHandle(), certInfoBytes, 0, certInfoBytes.Length); - - StringBuilder hex = new StringBuilder((certInfoBytes.Length - sizeof(UInt32)) * 2); - for (int i = 4; i < certInfoBytes.Length; i++) - hex.AppendFormat("{0:x2}", certInfoBytes[i]); - - return hex.ToString().ToUpperInvariant(); - } - } - - internal static void PtrToStructureArray<T>(T[] array, IntPtr ptr) - { - IntPtr ptrOffset = ptr; - for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T)))) - array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T)); - } - } -} -'@ - -Function ConvertTo-CredentialAttribute { - param($Attributes) - - $converted_attributes = [System.Collections.Generic.List`1[Ansible.CredentialManager.CredentialAttribute]]@() - foreach ($attribute in $Attributes) { - $new_attribute = New-Object -TypeName Ansible.CredentialManager.CredentialAttribute - $new_attribute.Keyword = $attribute.name - - if ($null -ne $attribute.data) { - if ($attribute.data_format -eq "base64") { - $new_attribute.Value = [System.Convert]::FromBase64String($attribute.data) - } else { - $new_attribute.Value = [System.Text.Encoding]::UTF8.GetBytes($attribute.data) - } - } - $converted_attributes.Add($new_attribute) > $null - } - - return ,$converted_attributes -} - -Function Get-DiffInfo { - param($AnsibleCredential) - - $diff = @{ - alias = $AnsibleCredential.TargetAlias - attributes = [System.Collections.ArrayList]@() - comment = $AnsibleCredential.Comment - name = $AnsibleCredential.TargetName - persistence = $AnsibleCredential.Persist.ToString() - type = $AnsibleCredential.Type.ToString() - username = $AnsibleCredential.UserName - } - - foreach ($attribute in $AnsibleCredential.Attributes) { - $attribute_info = @{ - name = $attribute.Keyword - data = $null - } - if ($null -ne $attribute.Value) { - $attribute_info.data = [System.Convert]::ToBase64String($attribute.Value) - } - $diff.attributes.Add($attribute_info) > $null - } - - return ,$diff -} - -# If the username is a certificate thumbprint, verify it's a valid cert in the CurrentUser/Personal store -if ($null -ne $username -and $type -in @("domain_certificate", "generic_certificate")) { - # Ensure the thumbprint is upper case with no spaces or hyphens - $username = $username.ToUpperInvariant().Replace(" ", "").Replace("-", "") - - $certificate = Get-Item -Path Cert:\CurrentUser\My\$username -ErrorAction SilentlyContinue - if ($null -eq $certificate) { - $module.FailJson("Failed to find certificate with the thumbprint $username in the CurrentUser\My store") - } -} - -# Convert the input secret to a byte array -if ($null -ne $secret) { - if ($secret_format -eq "base64") { - $secret = [System.Convert]::FromBase64String($secret) - } else { - $secret = [System.Text.Encoding]::Unicode.GetBytes($secret) - } -} - -$persistence = switch ($persistence) { - "local" { [Ansible.CredentialManager.CredentialPersist]::LocalMachine } - "enterprise" { [Ansible.CredentialManager.CredentialPersist]::Enterprise } -} - -$type = switch ($type) { - "domain_password" { [Ansible.CredentialManager.CredentialType]::DomainPassword } - "domain_certificate" { [Ansible.CredentialManager.CredentialType]::DomainCertificate } - "generic_password" { [Ansible.CredentialManager.CredentialType]::Generic } - "generic_certificate" { [Ansible.CredentialManager.CredentialType]::GenericCertificate } -} - -$existing_credential = [Ansible.CredentialManager.Credential]::GetCredential($name, $type) -if ($null -ne $existing_credential) { - $module.Diff.before = Get-DiffInfo -AnsibleCredential $existing_credential -} - -if ($state -eq "absent") { - if ($null -ne $existing_credential) { - if (-not $module.CheckMode) { - $existing_credential.Delete() - } - $module.Result.changed = $true - } -} else { - if ($null -eq $existing_credential) { - $new_credential = New-Object -TypeName Ansible.CredentialManager.Credential - $new_credential.Type = $type - $new_credential.TargetName = $name - $new_credential.Comment = if ($comment) { $comment } else { [NullString]::Value } - $new_credential.Secret = $secret - $new_credential.Persist = $persistence - $new_credential.TargetAlias = if ($alias) { $alias } else { [NullString]::Value } - $new_credential.UserName = $username - - if ($null -ne $attributes) { - $new_credential.Attributes = ConvertTo-CredentialAttribute -Attributes $attributes - } - - if (-not $module.CheckMode) { - $new_credential.Write($false) - } - $module.Result.changed = $true - } else { - $changed = $false - $preserve_blob = $false - - # make sure we do case comparison for the comment - if ($existing_credential.Comment -cne $comment) { - $existing_credential.Comment = $comment - $changed = $true - } - - if ($existing_credential.Persist -ne $persistence) { - $existing_credential.Persist = $persistence - $changed = $true - } - - if ($existing_credential.TargetAlias -ne $alias) { - $existing_credential.TargetAlias = $alias - $changed = $true - } - - if ($existing_credential.UserName -ne $username) { - $existing_credential.UserName = $username - $changed = $true - } - - if ($null -ne $attributes) { - $attribute_changed = $false - - $new_attributes = ConvertTo-CredentialAttribute -Attributes $attributes - if ($new_attributes.Count -ne $existing_credential.Attributes.Count) { - $attribute_changed = $true - } else { - for ($i = 0; $i -lt $new_attributes.Count; $i++) { - $new_keyword = $new_attributes[$i].Keyword - $new_value = $new_attributes[$i].Value - if ($null -eq $new_value) { - $new_value = "" - } else { - $new_value = [System.Convert]::ToBase64String($new_value) - } - - $existing_keyword = $existing_credential.Attributes[$i].Keyword - $existing_value = $existing_credential.Attributes[$i].Value - if ($null -eq $existing_value) { - $existing_value = "" - } else { - $existing_value = [System.Convert]::ToBase64String($existing_value) - } - - if (($new_keyword -cne $existing_keyword) -or ($new_value -ne $existing_value)) { - $attribute_changed = $true - break - } - } - } - - if ($attribute_changed) { - $existing_credential.Attributes = $new_attributes - $changed = $true - } - } - - if ($null -eq $secret) { - # If we haven't explicitly set a secret, tell Windows to preserve the existing blob - $preserve_blob = $true - $existing_credential.Secret = $null - } elseif ($update_secret -eq "always") { - # We should only set the password if we can't read the existing one or it doesn't match our secret - if ($existing_credential.Secret.Length -eq 0) { - # We cannot read the secret so don't know if its the configured secret - $existing_credential.Secret = $secret - $changed = $true - } else { - # We can read the secret so compare with our input - $input_secret_b64 = [System.Convert]::ToBase64String($secret) - $actual_secret_b64 = [System.Convert]::ToBase64String($existing_credential.Secret) - if ($input_secret_b64 -ne $actual_secret_b64) { - $existing_credential.Secret = $secret - $changed = $true - } - } - } - - if ($changed -and -not $module.CheckMode) { - $existing_credential.Write($preserve_blob) - } - $module.Result.changed = $changed - } - - if ($module.CheckMode) { - # We cannot reliably get the credential in check mode, set it based on the input - $module.Diff.after = @{ - alias = $alias - attributes = $attributes - comment = $comment - name = $name - persistence = $persistence.ToString() - type = $type.ToString() - username = $username - } - } else { - # Get a new copy of the credential and use that to set the after diff - $new_credential = [Ansible.CredentialManager.Credential]::GetCredential($name, $type) - $module.Diff.after = Get-DiffInfo -AnsibleCredential $new_credential - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_credential.py b/lib/ansible/modules/windows/win_credential.py deleted file mode 100644 index 57cbb483af..0000000000 --- a/lib/ansible/modules/windows/win_credential.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, 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_credential -version_added: '2.8' -short_description: Manages Windows Credentials in the Credential Manager -description: -- Used to create and remove Windows Credentials in the Credential Manager. -- This module can manage both standard username/password credentials as well as - certificate credentials. -options: - alias: - description: - - Adds an alias for the credential. - - Typically this is the NetBIOS name of a host if I(name) is set to the DNS - name. - type: str - attributes: - description: - - A list of dicts that set application specific attributes for a - credential. - - When set, existing attributes will be compared to the list as a whole, - any differences means all attributes will be replaced. - suboptions: - name: - description: - - The key for the attribute. - - This is not a unique identifier as multiple attributes can have the - same key. - type: str - required: true - data: - description: - - The value for the attribute. - type: str - data_format: - description: - - Controls the input type for I(data). - - If C(text), I(data) is a text string that is UTF-16LE encoded to - bytes. - - If C(base64), I(data) is a base64 string that is base64 decoded to - bytes. - type: str - choices: [ base64, text ] - default: text - comment: - description: - - A user defined comment for the credential. - type: str - name: - description: - - The target that identifies the server or servers that the credential is - to be used for. - - If the value can be a NetBIOS name, DNS server name, DNS host name suffix - with a wildcard character (C(*)), a NetBIOS of DNS domain name that - contains a wildcard character sequence, or an asterisk. - - See C(TargetName) in U(https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentiala) - for more details on what this value can be. - - This is used with I(type) to produce a unique credential. - type: str - required: true - persistence: - description: - - Defines the persistence of the credential. - - If C(local), the credential will persist for all logons of the same user - on the same host. - - C(enterprise) is the same as C(local) but the credential is visible to - the same domain user when running on other hosts and not just localhost. - type: str - choices: [ enterprise, local ] - default: local - secret: - description: - - The secret for the credential. - - When omitted, then no secret is used for the credential if a new - credentials is created. - - When I(type) is a password type, this is the password for I(username). - - When I(type) is a certificate type, this is the pin for the certificate. - type: str - secret_format: - description: - - Controls the input type for I(secret). - - If C(text), I(secret) is a text string that is UTF-16LE encoded to bytes. - - If C(base64), I(secret) is a base64 string that is base64 decoded to - bytes. - type: str - choices: [ base64, text ] - default: text - state: - description: - - When C(absent), the credential specified by I(name) and I(type) is - removed. - - When C(present), the credential specified by I(name) and I(type) is - removed. - type: str - choices: [ absent, present ] - default: present - type: - description: - - The type of credential to store. - - This is used with I(name) to produce a unique credential. - - When the type is a C(domain) type, the credential is used by Microsoft - authentication packages like Negotiate. - - When the type is a C(generic) type, the credential is not used by any - particular authentication package. - - It is recommended to use a C(domain) type as only authentication - providers can access the secret. - type: str - required: true - choices: [ domain_certificate, domain_password, generic_certificate, generic_password ] - update_secret: - description: - - When C(always), the secret will always be updated if they differ. - - When C(on_create), the secret will only be checked/updated when it is - first created. - - If the secret cannot be retrieved and this is set to C(always), the - module will always result in a change. - type: str - choices: [ always, on_create ] - default: always - username: - description: - - When I(type) is a password type, then this is the username to store for - the credential. - - When I(type) is a credential type, then this is the thumbprint as a hex - string of the certificate to use. - - When C(type=domain_password), this should be in the form of a Netlogon - (DOMAIN\Username) or a UPN (username@DOMAIN). - - If using a certificate thumbprint, the certificate must exist in the - C(CurrentUser\My) certificate store for the executing user. - type: str -notes: -- This module requires to be run with C(become) so it can access the - user's credential store. -- There can only be one credential per host and type. if a second credential is - defined that uses the same host and type, then the original credential is - overwritten. -seealso: -- module: win_user_right -- module: win_whoami -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Create a local only credential - win_credential: - name: server.domain.com - type: domain_password - username: DOMAIN\username - secret: Password01 - state: present - -- name: Remove a credential - win_credential: - name: server.domain.com - type: domain_password - state: absent - -- name: Create a credential with full values - win_credential: - name: server.domain.com - type: domain_password - alias: server - username: username@DOMAIN.COM - secret: Password01 - comment: Credential for server.domain.com - persistence: enterprise - attributes: - - name: Source - data: Ansible - - name: Unique Identifier - data: Y3VzdG9tIGF0dHJpYnV0ZQ== - data_format: base64 - -- name: Create a certificate credential - win_credential: - name: '*.domain.com' - type: domain_certificate - username: 0074CC4F200D27DC3877C24A92BA8EA21E6C7AF4 - state: present - -- name: Create a generic credential - win_credential: - name: smbhost - type: generic_password - username: smbuser - secret: smbuser - state: present - -- name: Remove a generic credential - win_credential: - name: smbhost - type: generic_password - state: absent -''' - -RETURN = r''' -# -''' diff --git a/lib/ansible/modules/windows/win_data_deduplication.ps1 b/lib/ansible/modules/windows/win_data_deduplication.ps1 deleted file mode 100644 index 593ee76381..0000000000 --- a/lib/ansible/modules/windows/win_data_deduplication.ps1 +++ /dev/null @@ -1,129 +0,0 @@ -#!powershell - -# Copyright: 2019, rnsc(@rnsc) <github@rnsc.be> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt - -#AnsibleRequires -CSharpUtil Ansible.Basic -#AnsibleRequires -OSVersion 6.3 - -$spec = @{ - options = @{ - drive_letter = @{ type = "str"; required = $true } - state = @{ type = "str"; choices = "absent", "present"; default = "present"; } - settings = @{ - type = "dict" - required = $false - options = @{ - minimum_file_size = @{ type = "int"; default = 32768 } - minimum_file_age_days = @{ type = "int"; default = 2 } - no_compress = @{ type = "bool"; required = $false; default = $false } - optimize_in_use_files = @{ type = "bool"; required = $false; default = $false } - verify = @{ type = "bool"; required = $false; default = $false } - } - } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$drive_letter = $module.Params.drive_letter -$state = $module.Params.state -$settings = $module.Params.settings - -$module.Result.changed = $false -$module.Result.reboot_required = $false -$module.Result.msg = "" - -function Set-DataDeduplication($volume, $state, $settings, $dedup_job) { - - $current_state = 'absent' - - try { - $dedup_info = Get-DedupVolume -Volume "$($volume.DriveLetter):" - } catch { - $dedup_info = $null - } - - if ($dedup_info.Enabled) { - $current_state = 'present' - } - - if ( $state -ne $current_state ) { - if( -not $module.CheckMode) { - if($state -eq 'present') { - # Enable-DedupVolume -Volume <String> - Enable-DedupVolume -Volume "$($volume.DriveLetter):" - } elseif ($state -eq 'absent') { - Disable-DedupVolume -Volume "$($volume.DriveLetter):" - } - } - $module.Result.changed = $true - } - - if ($state -eq 'present') { - if ($null -ne $settings) { - Set-DataDedupJobSettings -volume $volume -settings $settings - } - } -} - -function Set-DataDedupJobSettings ($volume, $settings) { - - try { - $dedup_info = Get-DedupVolume -Volume "$($volume.DriveLetter):" - } catch { - $dedup_info = $null - } - - ForEach ($key in $settings.keys) { - - # See Microsoft documentation: - # https://docs.microsoft.com/en-us/powershell/module/deduplication/set-dedupvolume?view=win10-ps - - $update_key = $key - $update_value = $settings.$($key) - # Transform Ansible style options to Powershell params - $update_key = $update_key -replace('_', '') - - if ($update_key -eq "MinimumFileSize" -and $update_value -lt 32768) { - $update_value = 32768 - } - - $current_value = ($dedup_info | Select-Object -ExpandProperty $update_key) - - if ($update_value -ne $current_value) { - $command_param = @{ - $($update_key) = $update_value - } - - # Set-DedupVolume -Volume <String>` - # -NoCompress <bool> ` - # -MinimumFileAgeDays <UInt32> ` - # -MinimumFileSize <UInt32> (minimum 32768) - if( -not $module.CheckMode ) { - Set-DedupVolume -Volume "$($volume.DriveLetter):" @command_param - } - - $module.Result.changed = $true - } - } - -} - -# Install required feature -$feature_name = "FS-Data-Deduplication" -if( -not $module.CheckMode) { - $feature = Install-WindowsFeature -Name $feature_name - - if ($feature.RestartNeeded -eq 'Yes') { - $module.Result.reboot_required = $true - $module.FailJson("$feature_name was installed but requires Windows to be rebooted to work.") - } -} - -$volume = Get-Volume -DriveLetter $drive_letter - -Set-DataDeduplication -volume $volume -state $state -settings $settings -dedup_job $dedup_job - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_data_deduplication.py b/lib/ansible/modules/windows/win_data_deduplication.py deleted file mode 100644 index d320b9f7c2..0000000000 --- a/lib/ansible/modules/windows/win_data_deduplication.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: 2019, rnsc(@rnsc) <github@rnsc.be> -# 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_data_deduplication -version_added: "2.10" -short_description: Module to enable Data Deduplication on a volume. -description: -- This module can be used to enable Data Deduplication on a Windows volume. -- The module will install the FS-Data-Deduplication feature (a reboot will be necessary). -options: - drive_letter: - description: - - Windows drive letter on which to enable data deduplication. - required: yes - type: str - state: - description: - - Wether to enable or disable data deduplication on the selected volume. - default: present - type: str - choices: [ present, absent ] - settings: - description: - - Dictionary of settings to pass to the Set-DedupVolume powershell command. - type: dict - suboptions: - minimum_file_size: - description: - - Minimum file size you want to target for deduplication. - - It will default to 32768 if not defined or if the value is less than 32768. - type: int - default: 32768 - minimum_file_age_days: - description: - - Minimum file age you want to target for deduplication. - type: int - default: 2 - no_compress: - description: - - Wether you want to enabled filesystem compression or not. - type: bool - default: no - optimize_in_use_files: - description: - - Indicates that the server attempts to optimize currently open files. - type: bool - default: no - verify: - description: - - Indicates whether the deduplication engine performs a byte-for-byte verification for each duplicate chunk - that optimization creates, rather than relying on a cryptographically strong hash. - - This option is not recommend. - - Setting this parameter to True can degrade optimization performance. - type: bool - default: no -author: -- rnsc (@rnsc) -''' - -EXAMPLES = r''' -- name: Enable Data Deduplication on D - win_data_deduplication: - drive_letter: 'D' - state: present - -- name: Enable Data Deduplication on D - win_data_deduplication: - drive_letter: 'D' - state: present - settings: - no_compress: true - minimum_file_age_days: 1 - minimum_file_size: 0 -''' - -RETURN = r''' -# -''' diff --git a/lib/ansible/modules/windows/win_defrag.ps1 b/lib/ansible/modules/windows/win_defrag.ps1 deleted file mode 100644 index b88b4ef252..0000000000 --- a/lib/ansible/modules/windows/win_defrag.ps1 +++ /dev/null @@ -1,97 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil - -$spec = @{ - options = @{ - include_volumes = @{ type='list' } - exclude_volumes = @{ type='list' } - freespace_consolidation = @{ type='bool'; default=$false } - priority = @{ type='str'; default='low'; choices=@( 'low', 'normal') } - parallel = @{ type='bool'; default=$false } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$include_volumes = $module.Params.include_volumes -$exclude_volumes = $module.Params.exclude_volumes -$freespace_consolidation = $module.Params.freespace_consolidation -$priority = $module.Params.priority -$parallel = $module.Params.parallel - -$module.Result.changed = $false - -$executable = "defrag.exe" - -if (-not (Get-Command -Name $executable -ErrorAction SilentlyContinue)) { - $module.FailJson("Command '$executable' not found in $env:PATH.") -} - -$arguments = @() - -if ($include_volumes) { - foreach ($volume in $include_volumes) { - if ($volume.Length -eq 1) { - $arguments += "$($volume):" - } else { - $arguments += $volume - } - } -} else { - $arguments += "/C" -} - -if ($exclude_volumes) { - $arguments += "/E" - foreach ($volume in $exclude_volumes) { - if ($volume.Length -eq 1) { - $arguments += "$($volume):" - } else { - $arguments += $volume - } - } -} - -if ($module.CheckMode) { - $arguments += "/A" -} elseif ($freespace_consolidation) { - $arguments += "/X" -} - -if ($priority -eq "normal") { - $arguments += "/H" -} - -if ($parallel) { - $arguments += "/M" -} - -$arguments += "/V" - -$argument_string = Argv-ToString -arguments $arguments - -$start_datetime = [DateTime]::UtcNow -$module.Result.cmd = "$executable $argument_string" - -$command_result = Run-Command -command "$executable $argument_string" - -$end_datetime = [DateTime]::UtcNow - -$module.Result.stdout = $command_result.stdout -$module.Result.stderr = $command_result.stderr -$module.Result.rc = $command_result.rc - -$module.Result.start = $start_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") -$module.Result.end = $end_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") -$module.Result.delta = $($end_datetime - $start_datetime).ToString("h\:mm\:ss\.ffffff") - -$module.Result.changed = $true - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_defrag.py b/lib/ansible/modules/windows/win_defrag.py deleted file mode 100644 index 6889dfb7dc..0000000000 --- a/lib/ansible/modules/windows/win_defrag.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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_defrag -version_added: '2.4' -short_description: Consolidate fragmented files on local volumes -description: -- Locates and consolidates fragmented files on local volumes to improve system performance. -- 'More information regarding C(win_defrag) is available from: U(https://technet.microsoft.com/en-us/library/cc731650(v=ws.11).aspx)' -requirements: -- defrag.exe -options: - include_volumes: - description: - - A list of drive letters or mount point paths of the volumes to be defragmented. - - If this parameter is omitted, all volumes (not excluded) will be fragmented. - type: list - exclude_volumes: - description: - - A list of drive letters or mount point paths to exclude from defragmentation. - type: list - freespace_consolidation: - description: - - Perform free space consolidation on the specified volumes. - type: bool - default: no - priority: - description: - - Run the operation at low or normal priority. - type: str - choices: [ low, normal ] - default: low - parallel: - description: - - Run the operation on each volume in parallel in the background. - type: bool - default: no -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- name: Defragment all local volumes (in parallel) - win_defrag: - parallel: yes - -- name: 'Defragment all local volumes, except C: and D:' - win_defrag: - exclude_volumes: [ C, D ] - -- name: 'Defragment volume D: with normal priority' - win_defrag: - include_volumes: D - priority: normal - -- name: Consolidate free space (useful when reducing volumes) - win_defrag: - freespace_consolidation: yes -''' - -RETURN = r''' -cmd: - description: The complete command line used by the module. - returned: always - type: str - sample: defrag.exe /C /V -rc: - description: The return code for the command. - returned: always - type: int - sample: 0 -stdout: - description: The standard output from the command. - returned: always - type: str - sample: Success. -stderr: - description: The error output from the command. - returned: always - type: str - sample: -msg: - description: Possible error message on failure. - returned: failed - type: str - sample: Command 'defrag.exe' not found in $env:PATH. -changed: - description: Whether or not any changes were made. - returned: always - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_disk_facts.ps1 b/lib/ansible/modules/windows/win_disk_facts.ps1 deleted file mode 100644 index 30bff60c14..0000000000 --- a/lib/ansible/modules/windows/win_disk_facts.ps1 +++ /dev/null @@ -1,251 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Marc Tschapek <marc.tschapek@itelligence.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#AnsibleRequires -OSVersion 6.2 - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version 2.0 - -# Functions -function Test-Admin { - $CurrentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) - $IsAdmin = $CurrentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) - - return $IsAdmin -} - -# Check admin rights -if (-not (Test-Admin)) { - Fail-Json -obj @{} -message "Module was not started with elevated rights" -} - -# Create a new result object -$result = @{ - changed = $false - ansible_facts = @{ - ansible_disks = @() - } -} - -# Search disks -try { - $disks = Get-Disk -} catch { - Fail-Json -obj $result -message "Failed to search the disks on the target: $($_.Exception.Message)" -} -foreach ($disk in $disks) { - $disk_info = @{} - $pdisk = Get-PhysicalDisk -ErrorAction SilentlyContinue | Where-Object { - $_.DeviceId -eq $disk.Number - } - if ($pdisk) { - $disk_info["physical_disk"] += @{ - size = $pdisk.Size - allocated_size = $pdisk.AllocatedSize - device_id = $pdisk.DeviceId - friendly_name = $pdisk.FriendlyName - operational_status = $pdisk.OperationalStatus - health_status = $pdisk.HealthStatus - bus_type = $pdisk.BusType - usage_type = $pdisk.Usage - supported_usages = $pdisk.SupportedUsages - spindle_speed = $pdisk.SpindleSpeed - firmware_version = $pdisk.FirmwareVersion - physical_location = $pdisk.PhysicalLocation - manufacturer = $pdisk.Manufacturer - model = $pdisk.Model - can_pool = $pdisk.CanPool - indication_enabled = $pdisk.IsIndicationEnabled - partial = $pdisk.IsPartial - serial_number = $pdisk.SerialNumber - object_id = $pdisk.ObjectId - unique_id = $pdisk.UniqueId - } - if ([single]"$([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor)" -ge 6.3) { - $disk_info.physical_disk.media_type = $pdisk.MediaType - } - if (-not $pdisk.CanPool) { - $disk_info.physical_disk.cannot_pool_reason = $pdisk.CannotPoolReason - } - $vdisk = Get-VirtualDisk -PhysicalDisk $pdisk -ErrorAction SilentlyContinue - if ($vdisk) { - $disk_info["virtual_disk"] += @{ - size = $vdisk.Size - allocated_size = $vdisk.AllocatedSize - footprint_on_pool = $vdisk.FootprintOnPool - name = $vdisk.name - friendly_name = $vdisk.FriendlyName - operational_status = $vdisk.OperationalStatus - health_status = $vdisk.HealthStatus - provisioning_type = $vdisk.ProvisioningType - allocation_unit_size = $vdisk.AllocationUnitSize - media_type = $vdisk.MediaType - parity_layout = $vdisk.ParityLayout - access = $vdisk.Access - detached_reason = $vdisk.DetachedReason - write_cache_size = $vdisk.WriteCacheSize - fault_domain_awareness = $vdisk.FaultDomainAwareness - inter_leave = $vdisk.InterLeave - deduplication_enabled = $vdisk.IsDeduplicationEnabled - enclosure_aware = $vdisk.IsEnclosureAware - manual_attach = $vdisk.IsManualAttach - snapshot = $vdisk.IsSnapshot - tiered = $vdisk.IsTiered - physical_sector_size = $vdisk.PhysicalSectorSize - logical_sector_size = $vdisk.LogicalSectorSize - available_copies = $vdisk.NumberOfAvailableCopies - columns = $vdisk.NumberOfColumns - groups = $vdisk.NumberOfGroups - physical_disk_redundancy = $vdisk.PhysicalDiskRedundancy - read_cache_size = $vdisk.ReadCacheSize - request_no_spof = $vdisk.RequestNoSinglePointOfFailure - resiliency_setting_name = $vdisk.ResiliencySettingName - object_id = $vdisk.ObjectId - unique_id_format = $vdisk.UniqueIdFormat - unique_id = $vdisk.UniqueId - } - } - } - $win32_disk_drive = Get-CimInstance -ClassName Win32_DiskDrive -ErrorAction SilentlyContinue | Where-Object { - if ($_.SerialNumber) { - $_.SerialNumber -eq $disk.SerialNumber - } elseif ($disk.UniqueIdFormat -eq 'Vendor Specific') { - $_.PNPDeviceID -eq $disk.UniqueId.split(':')[0] - } - } - if ($win32_disk_drive) { - $disk_info["win32_disk_drive"] += @{ - availability=$win32_disk_drive.Availability - bytes_per_sector=$win32_disk_drive.BytesPerSector - capabilities=$win32_disk_drive.Capabilities - capability_descriptions=$win32_disk_drive.CapabilityDescriptions - caption=$win32_disk_drive.Caption - compression_method=$win32_disk_drive.CompressionMethod - config_manager_error_code=$win32_disk_drive.ConfigManagerErrorCode - config_manager_user_config=$win32_disk_drive.ConfigManagerUserConfig - creation_class_name=$win32_disk_drive.CreationClassName - default_block_size=$win32_disk_drive.DefaultBlockSize - description=$win32_disk_drive.Description - device_id=$win32_disk_drive.DeviceID - error_cleared=$win32_disk_drive.ErrorCleared - error_description=$win32_disk_drive.ErrorDescription - error_methodology=$win32_disk_drive.ErrorMethodology - firmware_revision=$win32_disk_drive.FirmwareRevision - index=$win32_disk_drive.Index - install_date=$win32_disk_drive.InstallDate - interface_type=$win32_disk_drive.InterfaceType - last_error_code=$win32_disk_drive.LastErrorCode - manufacturer=$win32_disk_drive.Manufacturer - max_block_size=$win32_disk_drive.MaxBlockSize - max_media_size=$win32_disk_drive.MaxMediaSize - media_loaded=$win32_disk_drive.MediaLoaded - media_type=$win32_disk_drive.MediaType - min_block_size=$win32_disk_drive.MinBlockSize - model=$win32_disk_drive.Model - name=$win32_disk_drive.Name - needs_cleaning=$win32_disk_drive.NeedsCleaning - number_of_media_supported=$win32_disk_drive.NumberOfMediaSupported - partitions=$win32_disk_drive.Partitions - pnp_device_id=$win32_disk_drive.PNPDeviceID - power_management_capabilities=$win32_disk_drive.PowerManagementCapabilities - power_management_supported=$win32_disk_drive.PowerManagementSupported - scsi_bus=$win32_disk_drive.SCSIBus - scsi_logical_unit=$win32_disk_drive.SCSILogicalUnit - scsi_port=$win32_disk_drive.SCSIPort - scsi_target_id=$win32_disk_drive.SCSITargetId - sectors_per_track=$win32_disk_drive.SectorsPerTrack - serial_number=$win32_disk_drive.SerialNumber - signature=$win32_disk_drive.Signature - size=$win32_disk_drive.Size - status=$win32_disk_drive.status - status_info=$win32_disk_drive.StatusInfo - system_creation_class_name=$win32_disk_drive.SystemCreationClassName - system_name=$win32_disk_drive.SystemName - total_cylinders=$win32_disk_drive.TotalCylinders - total_heads=$win32_disk_drive.TotalHeads - total_sectors=$win32_disk_drive.TotalSectors - total_tracks=$win32_disk_drive.TotalTracks - tracks_per_cylinder=$win32_disk_drive.TracksPerCylinder - } - } - $disk_info.number = $disk.Number - $disk_info.size = $disk.Size - $disk_info.bus_type = $disk.BusType - $disk_info.friendly_name = $disk.FriendlyName - $disk_info.partition_style = $disk.PartitionStyle - $disk_info.partition_count = $disk.NumberOfPartitions - $disk_info.operational_status = $disk.OperationalStatus - $disk_info.sector_size = $disk.PhysicalSectorSize - $disk_info.read_only = $disk.IsReadOnly - $disk_info.bootable = $disk.IsBoot - $disk_info.system_disk = $disk.IsSystem - $disk_info.clustered = $disk.IsClustered - $disk_info.manufacturer = $disk.Manufacturer - $disk_info.model = $disk.Model - $disk_info.firmware_version = $disk.FirmwareVersion - $disk_info.location = $disk.Location - $disk_info.serial_number = $disk.SerialNumber - $disk_info.unique_id = $disk.UniqueId - $disk_info.guid = $disk.Guid - $disk_info.path = $disk.Path - $parts = Get-Partition -DiskNumber $($disk.Number) -ErrorAction SilentlyContinue - if ($parts) { - $disk_info["partitions"] += @() - foreach ($part in $parts) { - $partition_info = @{ - number = $part.PartitionNumber - size = $part.Size - type = $part.Type - drive_letter = $part.DriveLetter - transition_state = $part.TransitionState - offset = $part.Offset - hidden = $part.IsHidden - shadow_copy = $part.IsShadowCopy - guid = $part.Guid - access_paths = $part.AccessPaths - } - if ($disks.PartitionStyle -eq "GPT") { - $partition_info.gpt_type = $part.GptType - $partition_info.no_default_driveletter = $part.NoDefaultDriveLetter - } elseif ($disks.PartitionStyle -eq "MBR") { - $partition_info.mbr_type = $part.MbrType - $partition_info.active = $part.IsActive - } - $vols = Get-Volume -Partition $part -ErrorAction SilentlyContinue - if ($vols) { - $partition_info["volumes"] += @() - foreach ($vol in $vols) { - $volume_info = @{ - size = $vol.Size - size_remaining = $vol.SizeRemaining - type = $vol.FileSystem - label = $vol.FileSystemLabel - health_status = $vol.HealthStatus - drive_type = $vol.DriveType - object_id = $vol.ObjectId - path = $vol.Path - } - if ([System.Environment]::OSVersion.Version.Major -ge 10) { - $volume_info.allocation_unit_size = $vol.AllocationUnitSize - } else { - $volPath = ($vol.Path.TrimStart("\\?\")).TrimEnd("\") - $BlockSize = (Get-CimInstance -Query "SELECT BlockSize FROM Win32_Volume WHERE DeviceID like '%$volPath%'" -ErrorAction SilentlyContinue | Select-Object BlockSize).BlockSize - $volume_info.allocation_unit_size = $BlockSize - } - $partition_info.volumes += $volume_info - } - } - $disk_info.partitions += $partition_info - } - } - $result.ansible_facts.ansible_disks += $disk_info -} - -# Sort by disk number property -$result.ansible_facts.ansible_disks = @() + ($result.ansible_facts.ansible_disks | Sort-Object -Property {$_.Number}) - -# Return result -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_disk_facts.py b/lib/ansible/modules/windows/win_disk_facts.py deleted file mode 100644 index b203d79002..0000000000 --- a/lib/ansible/modules/windows/win_disk_facts.py +++ /dev/null @@ -1,891 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Marc Tschapek <marc.tschapek@itelligence.de> -# 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_disk_facts -version_added: '2.5' -short_description: Show the attached disks and disk information of the target host -description: - - With the module you can retrieve and output detailed information about the attached disks of the target and - its volumes and partitions if existent. -requirements: - - Windows 8.1 / Windows 2012 (NT 6.2) -notes: - - In order to understand all the returned properties and values please visit the following site and open the respective MSFT class - U(https://msdn.microsoft.com/en-us/library/windows/desktop/hh830612.aspx) -author: - - Marc Tschapek (@marqelme) -''' - -EXAMPLES = r''' -- name: Get disk facts - win_disk_facts: - -- name: Output first disk size - debug: - var: ansible_facts.disks[0].size - -- name: Convert first system disk into various formats - debug: - msg: '{{ disksize_gib }} vs {{ disksize_gib_human }}' - vars: - # Get first system disk - disk: '{{ ansible_facts.disks|selectattr("system_disk")|first }}' - - # Show disk size in Gibibytes - disksize_gib_human: '{{ disk.size|filesizeformat(true) }}' # returns "223.6 GiB" (human readable) - disksize_gib: '{{ (disk.size/1024|pow(3))|round|int }} GiB' # returns "224 GiB" (value in GiB) - - # Show disk size in Gigabytes - disksize_gb_human: '{{ disk.size|filesizeformat }}' # returns "240.1 GB" (human readable) - disksize_gb: '{{ (disk.size/1000|pow(3))|round|int }} GB' # returns "240 GB" (value in GB) - -- name: Output second disk serial number - debug: - var: ansible_facts.disks[1].serial_number -''' - -RETURN = r''' -ansible_facts: - description: Dictionary containing all the detailed information about the disks of the target. - returned: always - type: complex - contains: - ansible_disks: - description: Detailed information about one particular disk. - returned: if disks were found - type: list - contains: - number: - description: Disk number of the particular disk. - returned: always - type: int - sample: 0 - size: - description: Size in bytes of the particular disk. - returned: always - type: int - sample: 227727638528 - bus_type: - description: Bus type of the particular disk. - returned: always - type: str - sample: "SCSI" - friendly_name: - description: Friendly name of the particular disk. - returned: always - type: str - sample: "Red Hat VirtIO SCSI Disk Device" - partition_style: - description: Partition style of the particular disk. - returned: always - type: str - sample: "MBR" - partition_count: - description: Number of partitions on the particular disk. - returned: always - type: int - sample: 4 - operational_status: - description: Operational status of the particular disk. - returned: always - type: str - sample: "Online" - sector_size: - description: Sector size in bytes of the particular disk. - returned: always - type: int - sample: 4096 - read_only: - description: Read only status of the particular disk. - returned: always - type: bool - sample: true - bootable: - description: Information whether the particular disk is a bootable disk. - returned: always - type: bool - sample: false - system_disk: - description: Information whether the particular disk is a system disk. - returned: always - type: bool - sample: true - clustered: - description: Information whether the particular disk is clustered (part of a failover cluster). - returned: always - type: bool - sample: false - manufacturer: - description: Manufacturer of the particular disk. - returned: always - type: str - sample: "Red Hat" - model: - description: Model specification of the particular disk. - returned: always - type: str - sample: "VirtIO" - firmware_version: - description: Firmware version of the particular disk. - returned: always - type: str - sample: "0001" - location: - description: Location of the particular disk on the target. - returned: always - type: str - sample: "PCIROOT(0)#PCI(0400)#SCSI(P00T00L00)" - serial_number: - description: Serial number of the particular disk on the target. - returned: always - type: str - sample: "b62beac80c3645e5877f" - unique_id: - description: Unique ID of the particular disk on the target. - returned: always - type: str - sample: "3141463431303031" - guid: - description: GUID of the particular disk on the target. - returned: if existent - type: str - sample: "{efa5f928-57b9-47fc-ae3e-902e85fbe77f}" - path: - description: Path of the particular disk on the target. - returned: always - type: str - sample: "\\\\?\\scsi#disk&ven_red_hat&prod_virtio#4&23208fd0&1&000000#{<id>}" - partitions: - description: Detailed information about one particular partition on the specified disk. - returned: if existent - type: list - contains: - number: - description: Number of the particular partition. - returned: always - type: int - sample: 1 - size: - description: - - Size in bytes of the particular partition. - returned: always - type: int - sample: 838860800 - type: - description: Type of the particular partition. - returned: always - type: str - sample: "IFS" - gpt_type: - description: gpt type of the particular partition. - returned: if partition_style property of the particular disk has value "GPT" - type: str - sample: "{e3c9e316-0b5c-4db8-817d-f92df00215ae}" - no_default_driveletter: - description: Information whether the particular partition has a default drive letter or not. - returned: if partition_style property of the particular disk has value "GPT" - type: bool - sample: true - mbr_type: - description: mbr type of the particular partition. - returned: if partition_style property of the particular disk has value "MBR" - type: int - sample: 7 - active: - description: Information whether the particular partition is an active partition or not. - returned: if partition_style property of the particular disk has value "MBR" - type: bool - sample: true - drive_letter: - description: Drive letter of the particular partition. - returned: if existent - type: str - sample: "C" - transition_state: - description: Transition state of the particular partition. - returned: always - type: int - sample: 1 - offset: - description: Offset of the particular partition. - returned: always - type: int - sample: 368050176 - hidden: - description: Information whether the particular partition is hidden or not. - returned: always - type: bool - sample: true - shadow_copy: - description: Information whether the particular partition is a shadow copy of another partition. - returned: always - type: bool - sample: false - guid: - description: GUID of the particular partition. - returned: if existent - type: str - sample: "{302e475c-6e64-4674-a8e2-2f1c7018bf97}" - access_paths: - description: Access paths of the particular partition. - returned: if existent - type: str - sample: "\\\\?\\Volume{85bdc4a8-f8eb-11e6-80fa-806e6f6e6963}\\" - volumes: - description: Detailed information about one particular volume on the specified partition. - returned: if existent - type: list - contains: - size: - description: - - Size in bytes of the particular volume. - returned: always - type: int - sample: 838856704 - size_remaining: - description: - - Remaining size in bytes of the particular volume. - returned: always - type: int - sample: 395620352 - type: - description: File system type of the particular volume. - returned: always - type: str - sample: "NTFS" - label: - description: File system label of the particular volume. - returned: always - type: str - sample: "System Reserved" - health_status: - description: Health status of the particular volume. - returned: always - type: str - sample: "Healthy" - drive_type: - description: Drive type of the particular volume. - returned: always - type: str - sample: "Fixed" - allocation_unit_size: - description: Allocation unit size in bytes of the particular volume. - returned: always - type: int - sample: 4096 - object_id: - description: Object ID of the particular volume. - returned: always - type: str - sample: "\\\\?\\Volume{85bdc4a9-f8eb-11e6-80fa-806e6f6e6963}\\" - path: - description: Path of the particular volume. - returned: always - type: str - sample: "\\\\?\\Volume{85bdc4a9-f8eb-11e6-80fa-806e6f6e6963}\\" - physical_disk: - description: Detailed information about physical disk properties of the particular disk. - returned: if existent - type: complex - contains: - media_type: - description: Media type of the particular physical disk. - returned: always - type: str - sample: "UnSpecified" - size: - description: - - Size in bytes of the particular physical disk. - returned: always - type: int - sample: 240057409536 - allocated_size: - description: - - Allocated size in bytes of the particular physical disk. - returned: always - type: int - sample: 240057409536 - device_id: - description: Device ID of the particular physical disk. - returned: always - type: str - sample: "0" - friendly_name: - description: Friendly name of the particular physical disk. - returned: always - type: str - sample: "PhysicalDisk0" - operational_status: - description: Operational status of the particular physical disk. - returned: always - type: str - sample: "OK" - health_status: - description: Health status of the particular physical disk. - returned: always - type: str - sample: "Healthy" - bus_type: - description: Bus type of the particular physical disk. - returned: always - type: str - sample: "SCSI" - usage_type: - description: Usage type of the particular physical disk. - returned: always - type: str - sample: "Auto-Select" - supported_usages: - description: Supported usage types of the particular physical disk. - returned: always - type: complex - contains: - Count: - description: Count of supported usage types. - returned: always - type: int - sample: 5 - value: - description: List of supported usage types. - returned: always - type: str - sample: "Auto-Select, Hot Spare" - spindle_speed: - description: Spindle speed in rpm of the particular physical disk. - returned: always - type: int - sample: 4294967295 - physical_location: - description: Physical location of the particular physical disk. - returned: always - type: str - sample: "Integrated : Adapter 3 : Port 0 : Target 0 : LUN 0" - manufacturer: - description: Manufacturer of the particular physical disk. - returned: always - type: str - sample: "SUSE" - model: - description: Model of the particular physical disk. - returned: always - type: str - sample: "Xen Block" - can_pool: - description: Information whether the particular physical disk can be added to a storage pool. - returned: always - type: bool - sample: false - cannot_pool_reason: - description: Information why the particular physical disk can not be added to a storage pool. - returned: if can_pool property has value false - type: str - sample: "Insufficient Capacity" - indication_enabled: - description: Information whether indication is enabled for the particular physical disk. - returned: always - type: bool - sample: true - partial: - description: Information whether the particular physical disk is partial. - returned: always - type: bool - sample: false - serial_number: - description: Serial number of the particular physical disk. - returned: always - type: str - sample: "b62beac80c3645e5877f" - object_id: - description: Object ID of the particular physical disk. - returned: always - type: str - sample: '{1}\\\\HOST\\root/Microsoft/Windows/Storage/Providers_v2\\SPACES_PhysicalDisk.ObjectId=\"{<object_id>}:PD:{<pd>}\"' - unique_id: - description: Unique ID of the particular physical disk. - returned: always - type: str - sample: "3141463431303031" - virtual_disk: - description: Detailed information about virtual disk properties of the particular disk. - returned: if existent - type: complex - contains: - size: - description: - - Size in bytes of the particular virtual disk. - returned: always - type: int - sample: 240057409536 - allocated_size: - description: - - Allocated size in bytes of the particular virtual disk. - returned: always - type: int - sample: 240057409536 - footprint_on_pool: - description: - - Footprint on pool in bytes of the particular virtual disk. - returned: always - type: int - sample: 240057409536 - name: - description: Name of the particular virtual disk. - returned: always - type: str - sample: "vDisk1" - friendly_name: - description: Friendly name of the particular virtual disk. - returned: always - type: str - sample: "Prod2 Virtual Disk" - operational_status: - description: Operational status of the particular virtual disk. - returned: always - type: str - sample: "OK" - health_status: - description: Health status of the particular virtual disk. - returned: always - type: str - sample: "Healthy" - provisioning_type: - description: Provisioning type of the particular virtual disk. - returned: always - type: str - sample: "Thin" - allocation_unit_size: - description: Allocation unit size in bytes of the particular virtual disk. - returned: always - type: int - sample: 4096 - media_type: - description: Media type of the particular virtual disk. - returned: always - type: str - sample: "Unspecified" - parity_layout: - description: Parity layout of the particular virtual disk. - returned: if existent - type: int - sample: 1 - access: - description: Access of the particular virtual disk. - returned: always - type: str - sample: "Read/Write" - detached_reason: - description: Detached reason of the particular virtual disk. - returned: always - type: str - sample: "None" - write_cache_size: - description: Write cache size in byte of the particular virtual disk. - returned: always - type: int - sample: 100 - fault_domain_awareness: - description: Fault domain awareness of the particular virtual disk. - returned: always - type: str - sample: "PhysicalDisk" - inter_leave: - description: - - Inter leave in bytes of the particular virtual disk. - returned: always - type: int - sample: 102400 - deduplication_enabled: - description: Information whether deduplication is enabled for the particular virtual disk. - returned: always - type: bool - sample: true - enclosure_aware: - description: Information whether the particular virtual disk is enclosure aware. - returned: always - type: bool - sample: false - manual_attach: - description: Information whether the particular virtual disk is manual attached. - returned: always - type: bool - sample: true - snapshot: - description: Information whether the particular virtual disk is a snapshot. - returned: always - type: bool - sample: false - tiered: - description: Information whether the particular virtual disk is tiered. - returned: always - type: bool - sample: true - physical_sector_size: - description: Physical sector size in bytes of the particular virtual disk. - returned: always - type: int - sample: 4096 - logical_sector_size: - description: Logical sector size in byte of the particular virtual disk. - returned: always - type: int - sample: 512 - available_copies: - description: Number of the available copies of the particular virtual disk. - returned: if existent - type: int - sample: 1 - columns: - description: Number of the columns of the particular virtual disk. - returned: always - type: int - sample: 2 - groups: - description: Number of the groups of the particular virtual disk. - returned: always - type: int - sample: 1 - physical_disk_redundancy: - description: Type of the physical disk redundancy of the particular virtual disk. - returned: always - type: int - sample: 1 - read_cache_size: - description: Read cache size in byte of the particular virtual disk. - returned: always - type: int - sample: 0 - request_no_spof: - description: Information whether the particular virtual disk requests no single point of failure. - returned: always - type: bool - sample: true - resiliency_setting_name: - description: Type of the physical disk redundancy of the particular virtual disk. - returned: always - type: int - sample: 1 - object_id: - description: Object ID of the particular virtual disk. - returned: always - type: str - sample: '{1}\\\\HOST\\root/Microsoft/Windows/Storage/Providers_v2\\SPACES_VirtualDisk.ObjectId=\"{<object_id>}:VD:{<vd>}\"' - unique_id: - description: Unique ID of the particular virtual disk. - returned: always - type: str - sample: "260542E4C6B01D47A8FA7630FD90FFDE" - unique_id_format: - description: Unique ID format of the particular virtual disk. - returned: always - type: str - sample: "Vendor Specific" - win32_disk_drive: - description: Representation of the Win32_DiskDrive class. - returned: if existent - type: complex - contains: - availability: - description: Availability and status of the device. - returned: always - type: int - bytes_per_sector: - description: Number of bytes in each sector for the physical disk drive. - returned: always - type: int - sample: 512 - capabilities: - description: - - Array of capabilities of the media access device. - - For example, the device may support random access (3), removable media (7), and automatic cleaning (9). - returned: always - type: list - sample: - - 3 - - 4 - capability_descriptions: - description: - - List of more detailed explanations for any of the access device features indicated in the Capabilities array. - - Note, each entry of this array is related to the entry in the Capabilities array that is located at the same index. - returned: always - type: list - sample: - - Random Access - - Supports Writing - caption: - description: Short description of the object. - returned: always - type: str - sample: VMware Virtual disk SCSI Disk Device - compression_method: - description: Algorithm or tool used by the device to support compression. - returned: always - type: str - sample: Compressed - config_manager_error_code: - description: Windows Configuration Manager error code. - returned: always - type: int - sample: 0 - config_manager_user_config: - description: If True, the device is using a user-defined configuration. - returned: always - type: bool - sample: true - creation_class_name: - description: - - Name of the first concrete class to appear in the inheritance chain used in the creation of an instance. - - When used with the other key properties of the class, the property allows all instances of this class - - and its subclasses to be uniquely identified. - returned: always - type: str - sample: Win32_DiskDrive - default_block_size: - description: Default block size, in bytes, for this device. - returned: always - type: int - sample: 512 - description: - description: Description of the object. - returned: always - type: str - sample: Disk drive - device_id: - description: Unique identifier of the disk drive with other devices on the system. - returned: always - type: str - sample: "\\\\.\\PHYSICALDRIVE0" - error_cleared: - description: If True, the error reported in LastErrorCode is now cleared. - returned: always - type: bool - sample: true - error_description: - description: - - More information about the error recorded in LastErrorCode, - - and information on any corrective actions that may be taken. - returned: always - type: str - error_methodology: - description: Type of error detection and correction supported by this device. - returned: always - type: str - firmware_revision: - description: Revision for the disk drive firmware that is assigned by the manufacturer. - returned: always - type: str - sample: 1.0 - index: - description: - - Physical drive number of the given drive. - - This property is filled by the STORAGE_DEVICE_NUMBER structure returned from the IOCTL_STORAGE_GET_DEVICE_NUMBER control code - - A value of 0xffffffff indicates that the given drive does not map to a physical drive. - returned: always - type: int - sample: 0 - install_date: - description: Date and time the object was installed. This property does not need a value to indicate that the object is installed. - returned: always - type: str - interface_type: - description: Interface type of physical disk drive. - returned: always - type: str - sample: SCSI - last_error_code: - description: Last error code reported by the logical device. - returned: always - type: int - manufacturer: - description: Name of the disk drive manufacturer. - returned: always - type: str - sample: Seagate - max_block_size: - description: Maximum block size, in bytes, for media accessed by this device. - returned: always - type: int - max_media_size: - description: Maximum media size, in kilobytes, of media supported by this device. - returned: always - type: int - media_loaded: - description: - - If True, the media for a disk drive is loaded, which means that the device has a readable file system and is accessible. - - For fixed disk drives, this property will always be TRUE. - returned: always - type: bool - sample: true - media_type: - description: Type of media used or accessed by this device. - returned: always - type: str - sample: Fixed hard disk media - min_block_size: - description: Minimum block size, in bytes, for media accessed by this device. - returned: always - type: int - model: - description: Manufacturer's model number of the disk drive. - returned: always - type: str - sample: ST32171W - name: - description: Label by which the object is known. When subclassed, the property can be overridden to be a key property. - returned: always - type: str - sample: \\\\.\\PHYSICALDRIVE0 - needs_cleaning: - description: - - If True, the media access device needs cleaning. - - Whether manual or automatic cleaning is possible is indicated in the Capabilities property. - returned: always - type: bool - number_of_media_supported: - description: - - Maximum number of media which can be supported or inserted - - (when the media access device supports multiple individual media). - returned: always - type: int - partitions: - description: Number of partitions on this physical disk drive that are recognized by the operating system. - returned: always - type: int - sample: 3 - pnp_device_id: - description: Windows Plug and Play device identifier of the logical device. - returned: always - type: str - sample: "SCSI\\DISK&VEN_VMWARE&PROD_VIRTUAL_DISK\\5&1982005&0&000000" - power_management_capabilities: - description: Array of the specific power-related capabilities of a logical device. - returned: always - type: list - power_management_supported: - description: - - If True, the device can be power-managed (can be put into suspend mode, and so on). - - The property does not indicate that power management features are currently enabled, - - only that the logical device is capable of power management. - returned: always - type: bool - scsi_bus: - description: SCSI bus number of the disk drive. - returned: always - type: int - sample: 0 - scsi_logical_unit: - description: SCSI logical unit number (LUN) of the disk drive. - returned: always - type: int - sample: 0 - scsi_port: - description: SCSI port number of the disk drive. - returned: always - type: int - sample: 0 - scsi_target_id: - description: SCSI identifier number of the disk drive. - returned: always - type: int - sample: 0 - sectors_per_track: - description: Number of sectors in each track for this physical disk drive. - returned: always - type: int - sample: 63 - serial_number: - description: Number allocated by the manufacturer to identify the physical media. - returned: always - type: str - sample: 6000c298f34101b38cb2b2508926b9de - signature: - description: Disk identification. This property can be used to identify a shared resource. - returned: always - type: int - size: - description: - - Size of the disk drive. It is calculated by multiplying the total number of cylinders, tracks in each cylinder, - - sectors in each track, and bytes in each sector. - returned: always - type: int - sample: 53686402560 - status: - description: - - Current status of the object. Various operational and nonoperational statuses can be defined. - - 'Operational statuses include: "OK", "Degraded", and "Pred Fail"' - - (an element, such as a SMART-enabled hard disk drive, may be functioning properly but predicting a failure in the near future). - - 'Nonoperational statuses include: "Error", "Starting", "Stopping", and "Service".' - - '"Service", could apply during mirror-resilvering of a disk, reload of a user permissions list, or other administrative work.' - - Not all such work is online, yet the managed element is neither "OK" nor in one of the other states. - returned: always - type: str - sample: OK - status_info: - description: - - State of the logical device. If this property does not apply to the logical device, the value 5 (Not Applicable) should be used. - returned: always - type: int - system_creation_class_name: - description: Value of the scoping computer's CreationClassName property. - returned: always - type: str - sample: Win32_ComputerSystem - system_name: - description: Name of the scoping system. - returned: always - type: str - sample: WILMAR-TEST-123 - total_cylinders: - description: - - Total number of cylinders on the physical disk drive. - - 'Note: the value for this property is obtained through extended functions of BIOS interrupt 13h.' - - The value may be inaccurate if the drive uses a translation scheme to support high-capacity disk sizes. - - Consult the manufacturer for accurate drive specifications. - returned: always - type: int - sample: 6527 - total_heads: - description: - - Total number of heads on the disk drive. - - 'Note: the value for this property is obtained through extended functions of BIOS interrupt 13h.' - - The value may be inaccurate if the drive uses a translation scheme to support high-capacity disk sizes. - - Consult the manufacturer for accurate drive specifications. - returned: always - type: int - sample: 255 - total_sectors: - description: - - Total number of sectors on the physical disk drive. - - 'Note: the value for this property is obtained through extended functions of BIOS interrupt 13h.' - - The value may be inaccurate if the drive uses a translation scheme to support high-capacity disk sizes. - - Consult the manufacturer for accurate drive specifications. - returned: always - type: int - sample: 104856255 - total_tracks: - description: - - Total number of tracks on the physical disk drive. - - 'Note: the value for this property is obtained through extended functions of BIOS interrupt 13h.' - - The value may be inaccurate if the drive uses a translation scheme to support high-capacity disk sizes. - - Consult the manufacturer for accurate drive specifications. - returned: always - type: int - sample: 1664385 - tracks_per_cylinder: - description: - - Number of tracks in each cylinder on the physical disk drive. - - 'Note: the value for this property is obtained through extended functions of BIOS interrupt 13h.' - - The value may be inaccurate if the drive uses a translation scheme to support high-capacity disk sizes. - - Consult the manufacturer for accurate drive specifications. - returned: always - type: int - sample: 255 -''' diff --git a/lib/ansible/modules/windows/win_disk_image.ps1 b/lib/ansible/modules/windows/win_disk_image.ps1 deleted file mode 100644 index a5627fde53..0000000000 --- a/lib/ansible/modules/windows/win_disk_image.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Red Hat, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version 2 - -If(-not (Get-Command Get-DiskImage -ErrorAction SilentlyContinue)) { - Fail-Json -message "win_disk_image requires Windows 8+ or Windows Server 2012+" -} - -$parsed_args = Parse-Args $args -supports_check_mode $true - -$result = @{changed=$false} - -$image_path = Get-AnsibleParam $parsed_args "image_path" -failifempty $result -$state = Get-AnsibleParam $parsed_args "state" -default "present" -validateset "present","absent" -$check_mode = Get-AnsibleParam $parsed_args "_ansible_check_mode" -default $false - -$di = Get-DiskImage $image_path - -If($state -eq "present") { - If(-not $di.Attached) { - $result.changed = $true - - If(-not $check_mode) { - $di = Mount-DiskImage $image_path -PassThru - - # the actual mount is async, so the CIMInstance result may not immediately contain the data we need - $retry_count = 0 - While(-not $di.Attached -and $retry_count -lt 5) { - Start-Sleep -Seconds 1 > $null - $di = $di | Get-DiskImage - $retry_count++ - } - - If(-not $di.Attached) { - Fail-Json $result -message "Timed out waiting for disk to attach" - } - } - } - - # FUTURE: detect/handle "ejected" ISOs - # FUTURE: support explicit drive letter and NTFS in-volume mountpoints. - # VHDs don't always auto-assign, and other system settings can prevent automatic assignment - - If($di.Attached) { # only try to get the mount_path if the disk is attached ( - If($di.StorageType -eq 1) { # ISO, we can get the mountpoint directly from Get-Volume - $drive_letters = ($di | Get-Volume).DriveLetter - } - ElseIf($di.StorageType -in @(2,3)) { # VHD/VHDX, need Get-Disk + Get-Partition to discover mountpoint - $drive_letters = ($di | Get-Disk | Get-Partition).DriveLetter - } - # remove any null entries (no drive letter) - $drive_letters = $drive_letters | Where-Object { $_ } - - If(-not $drive_letters) { - Fail-Json -message "Unable to retrieve drive letter from mounted image" - } - - # mount_path is deprecated and will be removed in 2.11, use mount_paths which contains all the partitions instead - $result.mount_path = $drive_letters[0] + ":\" - $result.mount_paths = @($drive_letters | ForEach-Object { "$($_):\" }) - } -} -ElseIf($state -eq "absent") { - If($di.Attached) { - $result.changed = $true - If(-not $check_mode) { - Dismount-DiskImage $image_path > $null - } - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_disk_image.py b/lib/ansible/modules/windows/win_disk_image.py deleted file mode 100644 index 9a83e49693..0000000000 --- a/lib/ansible/modules/windows/win_disk_image.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Red Hat, Inc. -# 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': 'core'} - -DOCUMENTATION = r''' -module: win_disk_image -short_description: Manage ISO/VHD/VHDX mounts on Windows hosts -version_added: '2.3' -description: - - Manages mount behavior for a specified ISO, VHD, or VHDX image on a Windows host. When C(state) is C(present), - the image will be mounted under a system-assigned drive letter, which will be returned in the C(mount_path) value - of the module result. - - Requires Windows 8+ or Windows Server 2012+. -options: - image_path: - description: - - Path to an ISO, VHD, or VHDX image on the target Windows host (the file cannot reside on a network share) - type: str - required: yes - state: - description: - - Whether the image should be present as a drive-letter mount or not. - type: str - choices: [ absent, present ] - default: present -author: - - Matt Davis (@nitzmahone) -''' - -EXAMPLES = r''' -# Run installer from mounted ISO, then unmount -- name: Ensure an ISO is mounted - win_disk_image: - image_path: C:\install.iso - state: present - register: disk_image_out - -- name: Run installer from mounted ISO - win_package: - path: '{{ disk_image_out.mount_paths[0] }}setup\setup.exe' - product_id: 35a4e767-0161-46b0-979f-e61f282fee21 - state: present - -- name: Unmount ISO - win_disk_image: - image_path: C:\install.iso - state: absent -''' - -RETURN = r''' -mount_path: - description: Filesystem path where the target image is mounted, this has been deprecated in favour of C(mount_paths). - returned: when C(state) is C(present) - type: str - sample: F:\ -mount_paths: - description: A list of filesystem paths mounted from the target image. - returned: when C(state) is C(present) - type: list - sample: [ 'E:\', 'F:\' ] -''' diff --git a/lib/ansible/modules/windows/win_dns_record.ps1 b/lib/ansible/modules/windows/win_dns_record.ps1 deleted file mode 100644 index e3937dbad8..0000000000 --- a/lib/ansible/modules/windows/win_dns_record.ps1 +++ /dev/null @@ -1,149 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Hitachi ID Systems, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -$spec = @{ - options = @{ - name = @{ type = "str"; required = $true } - state = @{ type = "str"; choices = "absent", "present"; default = "present" } - ttl = @{ type = "int"; default = "3600" } - type = @{ type = "str"; choices = "A","AAAA","CNAME","PTR"; required = $true } - value = @{ type = "list"; elements = "str"; default = @() ; aliases=@( 'values' )} - zone = @{ type = "str"; required = $true } - computer_name = @{ type = "str" } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$name = $module.Params.name -$state = $module.Params.state -$ttl = $module.Params.ttl -$type = $module.Params.type -$values = $module.Params.value -$zone = $module.Params.zone -$dns_computer_name = $module.Params.computer_name - - -$extra_args = @{} -if ($null -ne $dns_computer_name) { - $extra_args.ComputerName = $dns_computer_name -} - -if ($state -eq 'present') { - if ($values.Count -eq 0) { - $module.FailJson("Parameter 'values' must be non-empty when state='present'") - } -} else { - if ($values.Count -ne 0) { - $module.FailJson("Parameter 'values' must be undefined or empty when state='absent'") - } -} - - -# TODO: add warning for forest minTTL override -- see https://docs.microsoft.com/en-us/windows/desktop/ad/configuration-of-ttl-limits -if ($ttl -lt 1 -or $ttl -gt 31557600) { - $module.FailJson("Parameter 'ttl' must be between 1 and 31557600") -} -$ttl = New-TimeSpan -Seconds $ttl - - -if (($type -eq 'CNAME' -or $type -eq 'PTR') -and $null -ne $values -and $values.Count -gt 0 -and $zone[-1] -ne '.') { - # CNAMEs and PTRs should be '.'-terminated, or record matching will fail - $values = $values | ForEach-Object { - if ($_ -Like "*.") { $_ } else { "$_." } - } -} - - -$record_argument_name = @{ - A = "IPv4Address"; - AAAA = "IPv6Address"; - CNAME = "HostNameAlias"; - # MX = "MailExchange"; - # NS = "NameServer"; - PTR = "PtrDomainName"; - # TXT = "DescriptiveText" -}[$type] - - -$changes = @{ - before = ""; - after = "" -} - - -$records = Get-DnsServerResourceRecord -ZoneName $zone -Name $name -RRType $type -Node -ErrorAction:Ignore @extra_args | Sort-Object -if ($null -ne $records) { - # We use [Hashtable]$required_values below as a set rather than a map. - # It provides quick lookup to test existing DNS record against. By removing - # items as each is processed, whatever remains at the end is missing - # content (that needs to be added). - $required_values = @{} - foreach ($value in $values) { - $required_values[$value.ToString()] = $null - } - - foreach ($record in $records) { - $record_value = $record.RecordData.$record_argument_name.ToString() - - if ($required_values.ContainsKey($record_value)) { - # This record matches one of the values; but does it match the TTL? - if ($record.TimeToLive -ne $ttl) { - $new_record = $record.Clone() - $new_record.TimeToLive = $ttl - Set-DnsServerResourceRecord -ZoneName $zone -OldInputObject $record -NewInputObject $new_record -WhatIf:$module.CheckMode @extra_args - - $changes.before += "[$zone] $($record.HostName) $($record.TimeToLive.TotalSeconds) IN $type $record_value`n" - $changes.after += "[$zone] $($record.HostName) $($ttl.TotalSeconds) IN $type $record_value`n" - $module.Result.changed = $true - } - - # Cross this one off the list, so we don't try adding it later - $required_values.Remove($record_value) - } else { - # This record doesn't match any of the values, and must be removed - $record | Remove-DnsServerResourceRecord -ZoneName $zone -Force -WhatIf:$module.CheckMode @extra_args - - $changes.before += "[$zone] $($record.HostName) $($record.TimeToLive.TotalSeconds) IN $type $record_value`n" - $module.Result.changed = $true - } - } - - # Whatever is left in $required_values needs to be added - $values = $required_values.Keys -} - - -if ($null -ne $values -and $values.Count -gt 0) { - foreach ($value in $values) { - $splat_args = @{ $type = $true; $record_argument_name = $value } - $module.Result.debug_splat_args = $splat_args - try { - Add-DnsServerResourceRecord -ZoneName $zone -Name $name -AllowUpdateAny -TimeToLive $ttl @splat_args -WhatIf:$module.CheckMode @extra_args - } catch { - $module.FailJson("Error adding DNS $type resource $name in zone $zone with value $value", $_) - } - $changes.after += "[$zone] $name $($ttl.TotalSeconds) IN $type $value`n" - } - - $module.Result.changed = $true -} - -if ($module.CheckMode) { - # Simulated changes - $module.Diff.before = $changes.before - $module.Diff.after = $changes.after -} else { - # Real changes - $records_end = Get-DnsServerResourceRecord -ZoneName $zone -Name $name -RRType $type -Node -ErrorAction:Ignore @extra_args | Sort-Object - - $module.Diff.before = @($records | ForEach-Object { "[$zone] $($_.HostName) $($_.TimeToLive.TotalSeconds) IN $type $($_.RecordData.$record_argument_name.ToString())`n" }) -join '' - $module.Diff.after = @($records_end | ForEach-Object { "[$zone] $($_.HostName) $($_.TimeToLive.TotalSeconds) IN $type $($_.RecordData.$record_argument_name.ToString())`n" }) -join '' -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_dns_record.py b/lib/ansible/modules/windows/win_dns_record.py deleted file mode 100644 index 0d5e631072..0000000000 --- a/lib/ansible/modules/windows/win_dns_record.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hitachi ID Systems, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# This is a windows documentation stub. The actual code lives in the .ps1 -# file of the same name. - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_dns_record -version_added: "2.8" -short_description: Manage Windows Server DNS records -description: -- Manage DNS records within an existing Windows Server DNS zone. -author: John Nelson (@johnboy2) -requirements: - - This module requires Windows 8, Server 2012, or newer. -options: - name: - description: - - The name of the record. - required: yes - type: str - state: - description: - - Whether the record should exist or not. - choices: [ absent, present ] - default: present - type: str - ttl: - description: - - The "time to live" of the record, in seconds. - - Ignored when C(state=absent). - - Valid range is 1 - 31557600. - - Note that an Active Directory forest can specify a minimum TTL, and will - dynamically "round up" other values to that minimum. - default: 3600 - type: int - type: - description: - - The type of DNS record to manage. - choices: [ A, AAAA, CNAME, PTR ] - required: yes - type: str - value: - description: - - The value(s) to specify. Required when C(state=present). - - When C(type=PTR) only the partial part of the IP should be given. - aliases: [ values ] - type: list - zone: - description: - - The name of the zone to manage (eg C(example.com)). - - The zone must already exist. - required: yes - type: str - computer_name: - description: - - Specifies a DNS server. - - You can specify an IP address or any value that resolves to an IP - address, such as a fully qualified domain name (FQDN), host name, or - NETBIOS name. - type: str -''' - -EXAMPLES = r''' -# Demonstrate creating a matching A and PTR record. - -- name: Create database server record - win_dns_record: - name: "cgyl1404p.amer.example.com" - type: "A" - value: "10.1.1.1" - zone: "amer.example.com" - -- name: Create matching PTR record - win_dns_record: - name: "1.1.1" - type: "PTR" - value: "db1" - zone: "10.in-addr.arpa" - -# Demonstrate replacing an A record with a CNAME - -- name: Remove static record - win_dns_record: - name: "db1" - type: "A" - state: absent - zone: "amer.example.com" - -- name: Create database server alias - win_dns_record: - name: "db1" - type: "CNAME" - value: "cgyl1404p.amer.example.com" - zone: "amer.example.com" - -# Demonstrate creating multiple A records for the same name - -- name: Create multiple A record values for www - win_dns_record: - name: "www" - type: "A" - values: - - 10.0.42.5 - - 10.0.42.6 - - 10.0.42.7 - zone: "example.com" - -# Demonstrates a partial update (replace some existing values with new ones) -# for a pre-existing name - -- name: Update www host with new addresses - win_dns_record: - name: "www" - type: "A" - values: - - 10.0.42.5 # this old value was kept (others removed) - - 10.0.42.12 # this new value was added - zone: "example.com" -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_domain_computer.ps1 b/lib/ansible/modules/windows/win_domain_computer.ps1 deleted file mode 100644 index 3da4a6f785..0000000000 --- a/lib/ansible/modules/windows/win_domain_computer.ps1 +++ /dev/null @@ -1,208 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, AMTEGA - Xunta de Galicia -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - - -# ------------------------------------------------------------------------------ -$ErrorActionPreference = "Stop" - -# Preparing result -$result = @{} -$result.changed = $false - -# Parameter ingestion -$params = Parse-Args $args -supports_check_mode $true - -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -failifempty $true -resultobj $result -$sam_account_name = Get-AnsibleParam -obj $params -name "sam_account_name" -default "$name$" -If (-not $sam_account_name.EndsWith("$")) { - Fail-Json -obj $result -message "sam_account_name must end in $" -} -$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -default $true -$description = Get-AnsibleParam -obj $params -name "description" -default $null -$domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str" -$domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($null -ne $domain_username) -$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str" -$state = Get-AnsibleParam -obj $params -name "state" -ValidateSet "present","absent" -default "present" - -$extra_args = @{} -if ($null -ne $domain_username) { - $domain_password = ConvertTo-SecureString $domain_password -AsPlainText -Force - $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $domain_password - $extra_args.Credential = $credential -} -if ($null -ne $domain_server) { - $extra_args.Server = $domain_server -} - -If ($state -eq "present") { - $dns_hostname = Get-AnsibleParam -obj $params -name "dns_hostname" -failifempty $true -resultobj $result - $ou = Get-AnsibleParam -obj $params -name "ou" -failifempty $true -resultobj $result - $distinguished_name = "CN=$name,$ou" - - $desired_state = [ordered]@{ - name = $name - sam_account_name = $sam_account_name - dns_hostname = $dns_hostname - ou = $ou - distinguished_name = $distinguished_name - description = $description - enabled = $enabled - state = $state - } -} Else { - $desired_state = [ordered]@{ - name = $name - sam_account_name = $sam_account_name - state = $state - } -} - -# ------------------------------------------------------------------------------ -Function Get-InitialState($desired_state) { - # Test computer exists - $computer = Try { - Get-ADComputer ` - -Identity $desired_state.sam_account_name ` - -Properties DistinguishedName,DNSHostName,Enabled,Name,SamAccountName,Description,ObjectClass ` - @extra_args - } Catch { $null } - If ($computer) { - $initial_state = [ordered]@{ - name = $computer.Name - sam_account_name = $computer.SamAccountName - dns_hostname = $computer.DNSHostName - # Get OU from regexp that removes all characters to the first "," - ou = $computer.DistinguishedName -creplace "^[^,]*,","" - distinguished_name = $computer.DistinguishedName - description = $computer.Description - enabled = $computer.Enabled - state = "present" - } - } Else { - $initial_state = [ordered]@{ - name = $desired_state.name - sam_account_name = $desired_state.sam_account_name - state = "absent" - } - } - - return $initial_state -} - -# ------------------------------------------------------------------------------ -Function Set-ConstructedState($initial_state, $desired_state) { - Try { - Set-ADComputer ` - -Identity $desired_state.name ` - -SamAccountName $desired_state.name ` - -DNSHostName $desired_state.dns_hostname ` - -Enabled $desired_state.enabled ` - -Description $desired_state.description ` - -WhatIf:$check_mode ` - @extra_args - } Catch { - Fail-Json -obj $result -message "Failed to set the AD object $($desired_state.name): $($_.Exception.Message)" - } - - If ($initial_state.distinguished_name -cne $desired_state.distinguished_name) { - # Move computer to OU - Try { - Get-ADComputer -Identity $desired_state.sam_account_name @extra_args | - Move-ADObject ` - -TargetPath $desired_state.ou ` - -Confirm:$False ` - -WhatIf:$check_mode ` - @extra_args - } Catch { - Fail-Json -obj $result -message "Failed to move the AD object $($initial_state.distinguished_name) to $($desired_state.distinguished_name): $($_.Exception.Message)" - } - } - $result.changed = $true -} - -# ------------------------------------------------------------------------------ -Function Add-ConstructedState($desired_state) { - Try { - New-ADComputer ` - -Name $desired_state.name ` - -SamAccountName $desired_state.sam_account_name ` - -DNSHostName $desired_state.dns_hostname ` - -Path $desired_state.ou ` - -Enabled $desired_state.enabled ` - -Description $desired_state.description ` - -WhatIf:$check_mode ` - @extra_args - } Catch { - Fail-Json -obj $result -message "Failed to create the AD object $($desired_state.name): $($_.Exception.Message)" - } - - $result.changed = $true -} - -# ------------------------------------------------------------------------------ -Function Remove-ConstructedState($initial_state) { - Try { - Get-ADComputer -Identity $initial_state.sam_account_name @extra_args | - Remove-ADObject ` - -Recursive ` - -Confirm:$False ` - -WhatIf:$check_mode ` - @extra_args - } Catch { - Fail-Json -obj $result -message "Failed to remove the AD object $($desired_state.name): $($_.Exception.Message)" - } - - $result.changed = $true -} - -# ------------------------------------------------------------------------------ -Function are_hashtables_equal($x, $y) { - # Compare not nested HashTables - Foreach ($key in $x.Keys) { - If (($y.Keys -notcontains $key) -or ($x[$key] -cne $y[$key])) { - Return $false - } - } - foreach ($key in $y.Keys) { - if (($x.Keys -notcontains $key) -or ($x[$key] -cne $y[$key])) { - Return $false - } - } - Return $true -} - -# ------------------------------------------------------------------------------ -$initial_state = Get-InitialState($desired_state) - -If ($desired_state.state -eq "present") { - If ($initial_state.state -eq "present") { - $in_desired_state = are_hashtables_equal $initial_state $desired_state - - If (-not $in_desired_state) { - Set-ConstructedState $initial_state $desired_state - } - } Else { # $desired_state.state = "Present" & $initial_state.state = "Absent" - Add-ConstructedState($desired_state) - } - } Else { # $desired_state.state = "Absent" - If ($initial_state.state -eq "present") { - Remove-ConstructedState($initial_state) - } - } - -If ($diff_support) { - $diff = @{ - before = $initial_state - after = $desired_state - } - $result.diff = $diff -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_domain_computer.py b/lib/ansible/modules/windows/win_domain_computer.py deleted file mode 100644 index f95cc5cd72..0000000000 --- a/lib/ansible/modules/windows/win_domain_computer.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, AMTEGA - Xunta de Galicia -# 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_domain_computer -short_description: Manage computers in Active Directory -description: - - Create, read, update and delete computers in Active Directory using a - windows bridge computer to launch New-ADComputer, Get-ADComputer, - Set-ADComputer, Remove-ADComputer and Move-ADObject powershell commands. -version_added: '2.6' -options: - name: - description: - - Specifies the name of the object. - - This parameter sets the Name property of the Active Directory object. - - The LDAP display name (ldapDisplayName) of this property is name. - type: str - required: true - sam_account_name: - description: - - Specifies the Security Account Manager (SAM) account name of the - computer. - - It maximum is 256 characters, 15 is advised for older - operating systems compatibility. - - The LDAP display name (ldapDisplayName) for this property is sAMAccountName. - - If ommitted the value is the same as C(name). - - Note that all computer SAMAccountNames need to end with a $. - type: str - enabled: - description: - - Specifies if an account is enabled. - - An enabled account requires a password. - - This parameter sets the Enabled property for an account object. - - This parameter also sets the ADS_UF_ACCOUNTDISABLE flag of the - Active Directory User Account Control (UAC) attribute. - type: bool - default: yes - ou: - description: - - Specifies the X.500 path of the Organizational Unit (OU) or container - where the new object is created. Required when I(state=present). - type: str - description: - description: - - Specifies a description of the object. - - This parameter sets the value of the Description property for the object. - - The LDAP display name (ldapDisplayName) for this property is description. - type: str - default: '' - dns_hostname: - description: - - Specifies the fully qualified domain name (FQDN) of the computer. - - This parameter sets the DNSHostName property for a computer object. - - The LDAP display name for this property is dNSHostName. - - Required when I(state=present). - type: str - domain_username: - description: - - The username to use when interacting with AD. - - If this is not set then the user Ansible used to log in with will be - used instead when using CredSSP or Kerberos with credential delegation. - type: str - version_added: '2.8' - domain_password: - description: - - The password for I(username). - type: str - version_added: '2.8' - domain_server: - description: - - Specifies the Active Directory Domain Services instance to connect to. - - Can be in the form of an FQDN or NetBIOS name. - - If not specified then the value is based on the domain of the computer - running PowerShell. - type: str - version_added: '2.8' - state: - description: - - Specified whether the computer should be C(present) or C(absent) in - Active Directory. - type: str - choices: [ absent, present ] - default: present -seealso: -- module: win_domain -- module: win_domain_controller -- module: win_domain_group -- module: win_domain_membership -- module: win_domain_user -author: -- Daniel Sánchez Fábregas (@Daniel-Sanchez-Fabregas) -''' - -EXAMPLES = r''' - - name: Add linux computer to Active Directory OU using a windows machine - win_domain_computer: - name: one_linux_server.my_org.local - sam_account_name: linux_server$ - dns_hostname: one_linux_server.my_org.local - ou: "OU=servers,DC=my_org,DC=local" - description: Example of linux server - enabled: yes - state: present - delegate_to: my_windows_bridge.my_org.local - - - name: Remove linux computer from Active Directory using a windows machine - win_domain_computer: - name: one_linux_server.my_org.local - state: absent - delegate_to: my_windows_bridge.my_org.local -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_domain_group.ps1 b/lib/ansible/modules/windows/win_domain_group.ps1 deleted file mode 100644 index b392c52c49..0000000000 --- a/lib/ansible/modules/windows/win_domain_group.ps1 +++ /dev/null @@ -1,344 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Jordan Borean <jborean93@gmail.com>, and others -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$display_name = Get-AnsibleParam -obj $params -name "display_name" -type "str" -$domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str" -$domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($null -ne $domain_username) -$description = Get-AnsibleParam -obj $params -name "description" -type "str" -$category = Get-AnsibleParam -obj $params -name "category" -type "str" -validateset "distribution","security" -$scope = Get-AnsibleParam -obj $params -name "scope" -type "str" -validateset "domainlocal","global","universal" -$managed_by = Get-AnsibleParam -obj $params -name "managed_by" -type "str" -$attributes = Get-AnsibleParam -obj $params -name "attributes" -$organizational_unit = Get-AnsibleParam -obj $params -name "organizational_unit" -type "str" -aliases "ou","path" -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent" -$protect = Get-AnsibleParam -obj $params -name "protect" -type "bool" -$ignore_protection = Get-AnsibleParam -obj $params -name "ignore_protection" -type "bool" -default $false -$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str" - -$result = @{ - changed = $false - created = $false -} - -if ($diff_mode) { - $result.diff = @{} -} - -if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) { - Fail-Json $result "win_domain_group requires the ActiveDirectory PS module to be installed" -} -Import-Module ActiveDirectory - -$extra_args = @{} -if ($null -ne $domain_username) { - $domain_password = ConvertTo-SecureString $domain_password -AsPlainText -Force - $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $domain_password - $extra_args.Credential = $credential -} -if ($null -ne $domain_server) { - $extra_args.Server = $domain_server -} - -try { - $group = Get-ADGroup -Identity $name -Properties * @extra_args -} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { - $group = $null -} catch { - Fail-Json $result "failed to retrieve initial details for group $($name): $($_.Exception.Message)" -} -if ($state -eq "absent") { - if ($null -ne $group) { - if ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $true) { - $group = $group | Set-ADObject -ProtectedFromAccidentalDeletion $false -WhatIf:$check_mode -PassThru @extra_args - } elseif ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $false) { - Fail-Json $result "cannot delete group $name when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this" - } - - try { - $group | Remove-ADGroup -Confirm:$false -WhatIf:$check_mode @extra_args - } catch { - Fail-Json $result "failed to remove group $($name): $($_.Exception.Message)" - } - - $result.changed = $true - if ($diff_mode) { - $result.diff.prepared = "-[$name]" - } - } -} else { - # validate that path is an actual path - if ($null -ne $organizational_unit) { - try { - Get-ADObject -Identity $organizational_unit @extra_args | Out-Null - } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { - Fail-Json $result "the group path $organizational_unit does not exist, please specify a valid LDAP path" - } - } - - $diff_text = $null - if ($null -ne $group) { - # will be overridden later if no change actually occurs - $diff_text += "[$name]`n" - - # change the path of the group - if ($null -ne $organizational_unit) { - $group_cn = $group.CN - $existing_path = $group.DistinguishedName -replace "^CN=$group_cn,",'' - if ($existing_path -ne $organizational_unit) { - $protection_disabled = $false - if ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $true) { - $group | Set-ADObject -ProtectedFromAccidentalDeletion $false -WhatIf:$check_mode -PassThru @extra_args | Out-Null - $protection_disabled = $true - } elseif ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $false) { - Fail-Json $result "cannot move group $name when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this" - } - - try { - $group = $group | Move-ADObject -Targetpath $organizational_unit -WhatIf:$check_mode -PassThru @extra_args - } catch { - Fail-Json $result "failed to move group from $existing_path to $($organizational_unit): $($_.Exception.Message)" - } finally { - if ($protection_disabled -eq $true) { - $group | Set-ADObject -ProtectedFromAccidentalDeletion $true -WhatIf:$check_mode -PassThru @extra_args | Out-Null - } - } - - $result.changed = $true - $diff_text += "-DistinguishedName = CN=$group_cn,$existing_path`n+DistinguishedName = CN=$group_cn,$organizational_unit`n" - - if ($protection_disabled -eq $true) { - $group | Set-ADObject -ProtectedFromAccidentalDeletion $true -WhatIf:$check_mode @extra_args | Out-Null - } - # get the group again once we have moved it - $group = Get-ADGroup -Identity $name -Properties * @extra_args - } - } - - # change attributes of group - $extra_scope_change = $null - $run_change = $false - $set_args = $extra_args.Clone() - - if ($null -ne $scope) { - if ($group.GroupScope -ne $scope) { - # you cannot from from Global to DomainLocal and vice-versa, we - # need to change it to Universal and then finally to the target - # scope - if ($group.GroupScope -eq "global" -and $scope -eq "domainlocal") { - $set_args.GroupScope = "Universal" - $extra_scope_change = $scope - } elseif ($group.GroupScope -eq "domainlocal" -and $scope -eq "global") { - $set_args.GroupScope = "Universal" - $extra_scope_change = $scope - } else { - $set_args.GroupScope = $scope - } - $run_change = $true - $diff_text += "-GroupScope = $($group.GroupScope)`n+GroupScope = $scope`n" - } - } - - if ($null -ne $description -and $group.Description -cne $description) { - $set_args.Description = $description - $run_change = $true - $diff_text += "-Description = $($group.Description)`n+Description = $description`n" - } - - if ($null -ne $display_name -and $group.DisplayName -cne $display_name) { - $set_args.DisplayName = $display_name - $run_change = $true - $diff_text += "-DisplayName = $($group.DisplayName)`n+DisplayName = $display_name`n" - } - - if ($null -ne $category -and $group.GroupCategory -ne $category) { - $set_args.GroupCategory = $category - $run_change = $true - $diff_text += "-GroupCategory = $($group.GroupCategory)`n+GroupCategory = $category`n" - } - - if ($null -ne $managed_by) { - if ($null -eq $group.ManagedBy) { - $set_args.ManagedBy = $managed_by - $run_change = $true - $diff_text += "+ManagedBy = $managed_by`n" - } else { - try { - $managed_by_object = Get-ADGroup -Identity $managed_by @extra_args - } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { - try { - $managed_by_object = Get-ADUser -Identity $managed_by @extra_args - } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { - Fail-Json $result "failed to find managed_by user or group $managed_by to be used for comparison" - } - } - - if ($group.ManagedBy -ne $managed_by_object.DistinguishedName) { - $set_args.ManagedBy = $managed_by - $run_change = $true - $diff_text += "-ManagedBy = $($group.ManagedBy)`n+ManagedBy = $($managed_by_object.DistinguishedName)`n" - } - } - } - - if ($null -ne $attributes) { - $add_attributes = @{} - $replace_attributes = @{} - foreach ($attribute in $attributes.GetEnumerator()) { - $attribute_name = $attribute.Name - $attribute_value = $attribute.Value - - $valid_property = [bool]($group.PSobject.Properties.name -eq $attribute_name) - if ($valid_property) { - $existing_value = $group.$attribute_name - if ($existing_value -cne $attribute_value) { - $replace_attributes.$attribute_name = $attribute_value - $diff_text += "-$attribute_name = $existing_value`n+$attribute_name = $attribute_value`n" - } - } else { - $add_attributes.$attribute_name = $attribute_value - $diff_text += "+$attribute_name = $attribute_value`n" - } - } - if ($add_attributes.Count -gt 0) { - $set_args.Add = $add_attributes - $run_change = $true - } - if ($replace_attributes.Count -gt 0) { - $set_args.Replace = $replace_attributes - $run_change = $true - } - } - - if ($run_change) { - try { - $group = $group | Set-ADGroup -WhatIf:$check_mode -PassThru @set_args - } catch { - Fail-Json $result "failed to change group $($name): $($_.Exception.Message)" - } - $result.changed = $true - - if ($null -ne $extra_scope_change) { - try { - $group = $group | Set-ADGroup -GroupScope $extra_scope_change -WhatIf:$check_mode -PassThru @extra_args - } catch { - Fail-Json $result "failed to change scope of group $name to $($scope): $($_.Exception.Message)" - } - } - } - - # make sure our diff text is null if no change occurred - if ($result.changed -eq $false) { - $diff_text = $null - } - } else { - # validate if scope is set - if ($null -eq $scope) { - Fail-Json $result "scope must be set when state=present and the group doesn't exist" - } - - $diff_text += "+[$name]`n+Scope = $scope`n" - $add_args = $extra_args.Clone() - $add_args.Name = $name - $add_args.GroupScope = $scope - - if ($null -ne $description) { - $add_args.Description = $description - $diff_text += "+Description = $description`n" - } - - if ($null -ne $display_name) { - $add_args.DisplayName = $display_name - $diff_text += "+DisplayName = $display_name`n" - } - - if ($null -ne $category) { - $add_args.GroupCategory = $category - $diff_text += "+GroupCategory = $category`n" - } - - if ($null -ne $managed_by) { - $add_args.ManagedBy = $managed_by - $diff_text += "+ManagedBy = $managed_by`n" - } - - if ($null -ne $attributes) { - $add_args.OtherAttributes = $attributes - foreach ($attribute in $attributes.GetEnumerator()) { - $diff_text += "+$($attribute.Name) = $($attribute.Value)`n" - } - } - - if ($null -ne $organizational_unit) { - $add_args.Path = $organizational_unit - $diff_text += "+Path = $organizational_unit`n" - } - - try { - $group = New-AdGroup -WhatIf:$check_mode -PassThru @add_args - } catch { - Fail-Json $result "failed to create group $($name): $($_.Exception.Message)" - } - $result.changed = $true - $result.created = $true - } - - # set the protection value - if ($null -ne $protect) { - if (-not $check_mode) { - $group = Get-ADGroup -Identity $name -Properties * @extra_args - } - $existing_protection_value = $group.ProtectedFromAccidentalDeletion - if ($null -eq $existing_protection_value) { - $existing_protection_value = $false - } - if ($existing_protection_value -ne $protect) { - $diff_text += @" --ProtectedFromAccidentalDeletion = $existing_protection_value -+ProtectedFromAccidentalDeletion = $protect -"@ - - $group | Set-ADObject -ProtectedFromAccidentalDeletion $protect -WhatIf:$check_mode -PassThru @extra_args - $result.changed = $true - } - } - - if ($diff_mode -and $null -ne $diff_text) { - $result.diff.prepared = $diff_text - } - - if (-not $check_mode) { - $group = Get-ADGroup -Identity $name -Properties * @extra_args - $result.sid = $group.SID.Value - $result.description = $group.Description - $result.distinguished_name = $group.DistinguishedName - $result.display_name = $group.DisplayName - $result.name = $group.Name - $result.canonical_name = $group.CanonicalName - $result.guid = $group.ObjectGUID - $result.protected_from_accidental_deletion = $group.ProtectedFromAccidentalDeletion - $result.managed_by = $group.ManagedBy - $result.group_scope = ($group.GroupScope).ToString() - $result.category = ($group.GroupCategory).ToString() - - if ($null -ne $attributes) { - $result.attributes = @{} - foreach ($attribute in $attributes.GetEnumerator()) { - $attribute_name = $attribute.Name - $result.attributes.$attribute_name = $group.$attribute_name - } - } - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_domain_group.py b/lib/ansible/modules/windows/win_domain_group.py deleted file mode 100644 index 7a8792c1ad..0000000000 --- a/lib/ansible/modules/windows/win_domain_group.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, 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_domain_group -version_added: '2.4' -short_description: Creates, modifies or removes domain groups -description: -- Creates, modifies or removes groups in Active Directory. -- For local groups, use the M(win_group) module instead. -options: - attributes: - description: - - A dict of custom LDAP attributes to set on the group. - - This can be used to set custom attributes that are not exposed as module - parameters, e.g. C(mail). - - See the examples on how to format this parameter. - type: dict - category: - description: - - The category of the group, this is the value to assign to the LDAP - C(groupType) attribute. - - If a new group is created then C(security) will be used by default. - type: str - choices: [ distribution, security ] - description: - description: - - The value to be assigned to the LDAP C(description) attribute. - type: str - display_name: - description: - - The value to assign to the LDAP C(displayName) attribute. - type: str - domain_username: - description: - - The username to use when interacting with AD. - - If this is not set then the user Ansible used to log in with will be - used instead. - type: str - domain_password: - description: - - The password for C(username). - type: str - domain_server: - description: - - Specifies the Active Directory Domain Services instance to connect to. - - Can be in the form of an FQDN or NetBIOS name. - - If not specified then the value is based on the domain of the computer - running PowerShell. - type: str - version_added: '2.5' - ignore_protection: - description: - - Will ignore the C(ProtectedFromAccidentalDeletion) flag when deleting or - moving a group. - - The module will fail if one of these actions need to occur and this value - is set to C(no). - type: bool - default: no - managed_by: - description: - - The value to be assigned to the LDAP C(managedBy) attribute. - - This value can be in the forms C(Distinguished Name), C(objectGUID), - C(objectSid) or C(sAMAccountName), see examples for more details. - type: str - name: - description: - - The name of the group to create, modify or remove. - - This value can be in the forms C(Distinguished Name), C(objectGUID), - C(objectSid) or C(sAMAccountName), see examples for more details. - type: str - required: yes - organizational_unit: - description: - - The full LDAP path to create or move the group to. - - This should be the path to the parent object to create or move the group to. - - See examples for details of how this path is formed. - type: str - aliases: [ ou, path ] - protect: - description: - - Will set the C(ProtectedFromAccidentalDeletion) flag based on this value. - - This flag stops a user from deleting or moving a group to a different - path. - type: bool - scope: - description: - - The scope of the group. - - If C(state=present) and the group doesn't exist then this must be set. - type: str - choices: [domainlocal, global, universal] - state: - description: - - If C(state=present) this module will ensure the group is created and is - configured accordingly. - - If C(state=absent) this module will delete the group if it exists - type: str - choices: [ absent, present ] - default: present -notes: -- This must be run on a host that has the ActiveDirectory powershell module installed. -seealso: -- module: win_domain -- module: win_domain_controller -- module: win_domain_computer -- module: win_domain_membership -- module: win_domain_user -- module: win_group -- module: win_group_membership -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Ensure the group Cow exists using sAMAccountName - win_domain_group: - name: Cow - scope: global - path: OU=groups,DC=ansible,DC=local - -- name: Ensure the group Cow doesn't exist using the Distinguished Name - win_domain_group: - name: CN=Cow,OU=groups,DC=ansible,DC=local - state: absent - -- name: Delete group ignoring the protection flag - win_domain_group: - name: Cow - state: absent - ignore_protection: yes - -- name: Create group with delete protection enabled and custom attributes - win_domain_group: - name: Ansible Users - scope: domainlocal - category: security - attributes: - mail: helpdesk@ansible.com - wWWHomePage: www.ansible.com - ignore_protection: yes - -- name: Change the OU of a group using the SID and ignore the protection flag - win_domain_group: - name: S-1-5-21-2171456218-3732823212-122182344-1189 - scope: global - organizational_unit: OU=groups,DC=ansible,DC=local - ignore_protection: yes - -- name: Add managed_by user - win_domain_group: - name: Group Name Here - managed_by: Domain Admins - -- name: Add group and specify the AD domain services to use for the create - win_domain_group: - name: Test Group - domain_username: user@CORP.ANSIBLE.COM - domain_password: Password01! - domain_server: corp-DC12.corp.ansible.com - scope: domainlocal -''' - -RETURN = r''' -attributes: - description: Custom attributes that were set by the module. This does not - show all the custom attributes rather just the ones that were set by the - module. - returned: group exists and attributes are set on the module invocation - type: dict - sample: - mail: 'helpdesk@ansible.com' - wWWHomePage: 'www.ansible.com' -canonical_name: - description: The canonical name of the group. - returned: group exists - type: str - sample: ansible.local/groups/Cow -category: - description: The Group type value of the group, i.e. Security or Distribution. - returned: group exists - type: str - sample: Security -description: - description: The Description of the group. - returned: group exists - type: str - sample: Group Description -display_name: - description: The Display name of the group. - returned: group exists - type: str - sample: Users who connect through RDP -distinguished_name: - description: The full Distinguished Name of the group. - returned: group exists - type: str - sample: CN=Cow,OU=groups,DC=ansible,DC=local -group_scope: - description: The Group scope value of the group. - returned: group exists - type: str - sample: Universal -guid: - description: The guid of the group. - returned: group exists - type: str - sample: 512a9adb-3fc0-4a26-9df0-e6ea1740cf45 -managed_by: - description: The full Distinguished Name of the AD object that is set on the - managedBy attribute. - returned: group exists - type: str - sample: CN=Domain Admins,CN=Users,DC=ansible,DC=local -name: - description: The name of the group. - returned: group exists - type: str - sample: Cow -protected_from_accidental_deletion: - description: Whether the group is protected from accidental deletion. - returned: group exists - type: bool - sample: true -sid: - description: The Security ID of the group. - returned: group exists - type: str - sample: S-1-5-21-2171456218-3732823212-122182344-1189 -created: - description: Whether a group was created - returned: always - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_domain_group_membership.ps1 b/lib/ansible/modules/windows/win_domain_group_membership.ps1 deleted file mode 100644 index 878b9fc669..0000000000 --- a/lib/ansible/modules/windows/win_domain_group_membership.ps1 +++ /dev/null @@ -1,131 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Marius Rieder <marius.rieder@scs.ch> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -try { - Import-Module ActiveDirectory -} -catch { - Fail-Json -obj @{} -message "win_domain_group_membership requires the ActiveDirectory PS module to be installed" -} - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -# Module control parameters -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","pure" -$domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str" -$domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($null -ne $domain_username) -$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str" - -# Group Membership parameters -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$members = Get-AnsibleParam -obj $params -name "members" -type "list" -failifempty $true - -# Filter ADObjects by ObjectClass -$ad_object_class_filter = "(ObjectClass -eq 'user' -or ObjectClass -eq 'group' -or ObjectClass -eq 'computer' -or ObjectClass -eq 'msDS-ManagedServiceAccount')" - -$extra_args = @{} -if ($null -ne $domain_username) { - $domain_password = ConvertTo-SecureString $domain_password -AsPlainText -Force - $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $domain_password - $extra_args.Credential = $credential -} -if ($null -ne $domain_server) { - $extra_args.Server = $domain_server -} - -$ADGroup = Get-ADGroup -Identity $name @extra_args - -$result = @{ - changed = $false - added = [System.Collections.Generic.List`1[String]]@() - removed = [System.Collections.Generic.List`1[String]]@() -} -if ($diff_mode) { - $result.diff = @{} -} - -$members_before = Get-AdGroupMember -Identity $ADGroup @extra_args -$pure_members = [System.Collections.Generic.List`1[String]]@() - -foreach ($member in $members) { - $extra_member_args = $extra_args.Clone() - if ($member -match "\\"){ - $extra_member_args.Server = $member.Split("\")[0] - $member = $member.Split("\")[1] - } - $group_member = Get-ADObject -Filter "SamAccountName -eq '$member' -and $ad_object_class_filter" -Properties objectSid, sAMAccountName @extra_member_args - if (!$group_member) { - Fail-Json -obj $result "Could not find domain user, group, service account or computer named $member" - } - - if ($state -eq "pure") { - $pure_members.Add($group_member.objectSid) - } - - $user_in_group = $false - foreach ($current_member in $members_before) { - if ($current_member.sid -eq $group_member.objectSid) { - $user_in_group = $true - break - } - } - - if ($state -in @("present", "pure") -and !$user_in_group) { - Add-ADPrincipalGroupMembership -Identity $group_member -MemberOf $ADGroup -WhatIf:$check_mode @extra_member_args - $result.added.Add($group_member.SamAccountName) - $result.changed = $true - } elseif ($state -eq "absent" -and $user_in_group) { - Remove-ADPrincipalGroupMembership -Identity $group_member -MemberOf $ADGroup -WhatIf:$check_mode -Confirm:$False @extra_member_args - $result.removed.Add($group_member.SamAccountName) - $result.changed = $true - } -} - -if ($state -eq "pure") { - # Perform removals for existing group members not defined in $members - $current_members = Get-AdGroupMember -Identity $ADGroup @extra_args - - foreach ($current_member in $current_members) { - $user_to_remove = $true - foreach ($pure_member in $pure_members) { - if ($pure_member -eq $current_member.sid) { - $user_to_remove = $false - break - } - } - - if ($user_to_remove) { - Remove-ADPrincipalGroupMembership -Identity $current_member -MemberOf $ADGroup -WhatIf:$check_mode -Confirm:$False - $result.removed.Add($current_member.SamAccountName) - $result.changed = $true - } - } -} - -$final_members = Get-AdGroupMember -Identity $ADGroup @extra_args - -if ($final_members) { - $result.members = [Array]$final_members.SamAccountName -} else { - $result.members = @() -} - -if ($diff_mode -and $result.changed) { - $result.diff.before = $members_before.SamAccountName | Out-String - if (!$check_mode) { - $result.diff.after = [Array]$final_members.SamAccountName | Out-String - } else { - $after = [System.Collections.Generic.List`1[String]]$result.members - $result.removed | ForEach-Object { $after.Remove($_) > $null } - $after.AddRange($result.added) - $result.diff.after = $after | Out-String - } -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_domain_group_membership.py b/lib/ansible/modules/windows/win_domain_group_membership.py deleted file mode 100644 index be1b7f04af..0000000000 --- a/lib/ansible/modules/windows/win_domain_group_membership.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> -# 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_domain_group_membership -version_added: "2.8" -short_description: Manage Windows domain group membership -description: - - Allows the addition and removal of domain users - and domain groups from/to a domain group. -options: - name: - description: - - Name of the domain group to manage membership on. - type: str - required: yes - members: - description: - - A list of members to ensure are present/absent from the group. - - The given names must be a SamAccountName of a user, group, service account, or computer. - - For computers, you must add "$" after the name; for example, to add "Mycomputer" to a group, use "Mycomputer$" as the member. - - If the member object is part of another domain in a multi-domain forest, you must add the domain and "\" in front of the name. - type: list - required: yes - state: - description: - - Desired state of the members in the group. - - When C(state) is C(pure), only the members specified will exist, - and all other existing members not specified are removed. - type: str - choices: [ absent, present, pure ] - default: present - domain_username: - description: - - The username to use when interacting with AD. - - If this is not set then the user Ansible used to log in with will be - used instead when using CredSSP or Kerberos with credential delegation. - type: str - domain_password: - description: - - The password for I(username). - type: str - domain_server: - description: - - Specifies the Active Directory Domain Services instance to connect to. - - Can be in the form of an FQDN or NetBIOS name. - - If not specified then the value is based on the domain of the computer - running PowerShell. - type: str -notes: -- This must be run on a host that has the ActiveDirectory powershell module installed. -seealso: -- module: win_domain_user -- module: win_domain_group -author: - - Marius Rieder (@jiuka) -''' - -EXAMPLES = r''' -- name: Add a domain user/group to a domain group - win_domain_group_membership: - name: Foo - members: - - Bar - state: present - -- name: Remove a domain user/group from a domain group - win_domain_group_membership: - name: Foo - members: - - Bar - state: absent - -- name: Ensure only a domain user/group exists in a domain group - win_domain_group_membership: - name: Foo - members: - - Bar - state: pure - -- name: Add a computer to a domain group - win_domain_group_membership: - name: Foo - members: - - DESKTOP$ - state: present - -- name: Add a domain user/group from another Domain in the multi-domain forest to a domain group - win_domain_group_membership: - domain_server: DomainAAA.cloud - name: GroupinDomainAAA - members: - - DomainBBB.cloud\UserInDomainBBB - state: Present - -''' - -RETURN = r''' -name: - description: The name of the target domain group. - returned: always - type: str - sample: Domain-Admins -added: - description: A list of members added when C(state) is C(present) or - C(pure); this is empty if no members are added. - returned: success and C(state) is C(present) or C(pure) - type: list - sample: ["UserName", "GroupName"] -removed: - description: A list of members removed when C(state) is C(absent) or - C(pure); this is empty if no members are removed. - returned: success and C(state) is C(absent) or C(pure) - type: list - sample: ["UserName", "GroupName"] -members: - description: A list of all domain group members at completion; this is empty - if the group contains no members. - returned: success - type: list - sample: ["UserName", "GroupName"] -''' diff --git a/lib/ansible/modules/windows/win_domain_object_info.ps1 b/lib/ansible/modules/windows/win_domain_object_info.ps1 deleted file mode 100644 index f8f6f6457b..0000000000 --- a/lib/ansible/modules/windows/win_domain_object_info.ps1 +++ /dev/null @@ -1,271 +0,0 @@ -#!powershell - -# Copyright: (c) 2020, 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 = @{ - domain_password = @{ type = 'str'; no_log = $true } - domain_server = @{ type = 'str' } - domain_username = @{ type = 'str' } - filter = @{ type = 'str' } - identity = @{ type = 'str' } - include_deleted = @{ type = 'bool'; default = $false } - ldap_filter = @{ type = 'str' } - properties = @{ type = 'list'; elements = 'str' } - search_base = @{ type = 'str' } - search_scope = @{ type = 'str'; choices = @('base', 'one_level', 'subtree') } - } - supports_check_mode = $true - mutually_exclusive = @( - @('filter', 'identity', 'ldap_filter'), - @('identity', 'search_base'), - @('identity', 'search_scope') - ) - required_one_of = @( - ,@('filter', 'identity', 'ldap_filter') - ) - required_together = @(,@('domain_username', 'domain_password')) -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$module.Result.objects = @() # Always ensure this is returned even in a failure. - -$domainServer = $module.Params.domain_server -$domainPassword = $module.Params.domain_password -$domainUsername = $module.Params.domain_username -$filter = $module.Params.filter -$identity = $module.Params.identity -$includeDeleted = $module.Params.include_deleted -$ldapFilter = $module.Params.ldap_filter -$properties = $module.Params.properties -$searchBase = $module.Params.search_base -$searchScope = $module.Params.search_scope - -$credential = $null -if ($domainUsername) { - $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList @( - $domainUsername, - (ConvertTo-SecureString -AsPlainText -Force -String $domainPassword) - ) -} - -Add-CSharpType -References @' -using System; - -namespace Ansible.WinDomainObjectInfo -{ - [Flags] - public enum UserAccountControl : int - { - ADS_UF_SCRIPT = 0x00000001, - ADS_UF_ACCOUNTDISABLE = 0x00000002, - ADS_UF_HOMEDIR_REQUIRED = 0x00000008, - ADS_UF_LOCKOUT = 0x00000010, - ADS_UF_PASSWD_NOTREQD = 0x00000020, - ADS_UF_PASSWD_CANT_CHANGE = 0x00000040, - ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x00000080, - ADS_UF_TEMP_DUPLICATE_ACCOUNT = 0x00000100, - ADS_UF_NORMAL_ACCOUNT = 0x00000200, - ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 0x00000800, - ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x00001000, - ADS_UF_SERVER_TRUST_ACCOUNT = 0x00002000, - ADS_UF_DONT_EXPIRE_PASSWD = 0x00010000, - ADS_UF_MNS_LOGON_ACCOUNT = 0x00020000, - ADS_UF_SMARTCARD_REQUIRED = 0x00040000, - ADS_UF_TRUSTED_FOR_DELEGATION = 0x00080000, - ADS_UF_NOT_DELEGATED = 0x00100000, - ADS_UF_USE_DES_KEY_ONLY = 0x00200000, - ADS_UF_DONT_REQUIRE_PREAUTH = 0x00400000, - ADS_UF_PASSWORD_EXPIRED = 0x00800000, - ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x01000000, - } - - public enum sAMAccountType : int - { - SAM_DOMAIN_OBJECT = 0x00000000, - SAM_GROUP_OBJECT = 0x10000000, - SAM_NON_SECURITY_GROUP_OBJECT = 0x10000001, - SAM_ALIAS_OBJECT = 0x20000000, - SAM_NON_SECURITY_ALIAS_OBJECT = 0x20000001, - SAM_USER_OBJECT = 0x30000000, - SAM_NORMAL_USER_ACCOUNT = 0x30000000, - SAM_MACHINE_ACCOUNT = 0x30000001, - SAM_TRUST_ACCOUNT = 0x30000002, - SAM_APP_BASIC_GROUP = 0x40000000, - SAM_APP_QUERY_GROUP = 0x40000001, - SAM_ACCOUNT_TYPE_MAX = 0x7fffffff, - } -} -'@ - -Function ConvertTo-OutputValue { - [CmdletBinding()] - Param ( - [Parameter(Mandatory=$true)] - [AllowNull()] - [Object] - $InputObject - ) - - if ($InputObject -is [System.Security.Principal.SecurityIdentifier]) { - # Syntax: SID - Only serialize the SID as a string and not the other metadata properties. - $sidInfo = @{ - Sid = $InputObject.Value - } - - # Try and map the SID to the account name, this may fail if the SID is invalid or not mappable. - try { - $sidInfo.Name = $InputObject.Translate([System.Security.Principal.NTAccount]).Value - } catch [System.Security.Principal.IdentityNotMappedException] { - $sidInfo.Name = $null - } - - $sidInfo - } elseif ($InputObject -is [Byte[]]) { - # Syntax: Octet String - By default will serialize as a list of decimal values per byte, instead return a - # Base64 string as Ansible can easily parse that. - [System.Convert]::ToBase64String($InputObject) - } elseif ($InputObject -is [DateTime]) { - # Syntax: UTC Coded Time - .NET DateTimes serialized as in the form "Date(FILETIME)" which isn't easily - # parsable by Ansible, instead return as an ISO 8601 string in the UTC timezone. - [TimeZoneInfo]::ConvertTimeToUtc($InputObject).ToString("o") - } elseif ($InputObject -is [System.Security.AccessControl.ObjectSecurity]) { - # Complex object which isn't easily serializable. Instead we should just return the SDDL string. If a user - # needs to parse this then they really need to reprocess the SDDL string and process their results on another - # win_shell task. - $InputObject.GetSecurityDescriptorSddlForm(([System.Security.AccessControl.AccessControlSections]::All)) - } else { - # Syntax: (All Others) - The default serialization handling of other syntaxes are fine, don't do anything. - $InputObject - } -} - -<# -Calling Get-ADObject that returns multiple objects with -Properties * will only return the properties that were set on -the first found object. To counter this problem we will first call Get-ADObject to list all the objects that match the -filter specified then get the properties on each object. -#> - -$commonParams = @{ - IncludeDeletedObjects = $includeDeleted -} - -if ($credential) { - $commonParams.Credential = $credential -} - -if ($domainServer) { - $commonParams.Server = $domainServer -} - -# First get the IDs for all the AD objects that match the filter specified. -$getParams = @{ - Properties = @('DistinguishedName', 'ObjectGUID') -} - -if ($filter) { - $getParams.Filter = $filter -} elseif ($identity) { - $getParams.Identity = $identity -} elseif ($ldapFilter) { - $getParams.LDAPFilter = $ldapFilter -} - -# Explicit check on $null as an empty string is different from not being set. -if ($null -ne $searchBase) { - $getParams.SearchBase = $searchbase -} - -if ($searchScope) { - $getParams.SearchScope = switch($searchScope) { - base { 'Base' } - one_level { 'OneLevel' } - subtree { 'Subtree' } - } -} - -try { - # We run this in a custom PowerShell pipeline so that users of this module can't use any of the variables defined - # above in their filter. While the cmdlet won't execute sub expressions we don't want anyone implicitly relying on - # a defined variable in this module in case we ever change the name or remove it. - $ps = [PowerShell]::Create() - $null = $ps.AddCommand('Get-ADObject').AddParameters($commonParams).AddParameters($getParams) - $null = $ps.AddCommand('Select-Object').AddParameter('Property', @('DistinguishedName', 'ObjectGUID')) - - $foundGuids = @($ps.Invoke()) -} catch { - # Because we ran in a pipeline we can't catch ADIdentityNotFoundException. Instead just get the base exception and - # do the error checking on that. - if ($_.Exception.GetBaseException() -is [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]) { - $foundGuids = @() - } else { - # The exception is from the .Invoke() call, compare on the InnerException which was what was actually raised by - # the pipeline. - $innerException = $_.Exception.InnerException.InnerException - if ($innerException -is [Microsoft.ActiveDirectory.Management.ADServerDownException]) { - # Point users in the direction of the double hop problem as that is what is typically the cause of this. - $msg = "Failed to contact the AD server, this could be caused by the double hop problem over WinRM. " - $msg += "Try using the module with auth as Kerberos with credential delegation or CredSSP, become, or " - $msg += "defining the domain_username and domain_password module parameters." - $module.FailJson($msg, $innerException) - } else { - throw $innerException - } - } -} - -$getParams = @{} -if ($properties) { - $getParams.Properties = $properties -} -$module.Result.objects = @(foreach ($adId in $foundGuids) { - try { - $adObject = Get-ADObject @commonParams @getParams -Identity $adId.ObjectGUID - } catch { - $msg = "Failed to retrieve properties for AD Object '$($adId.DistinguishedName)': $($_.Exception.Message)" - $module.Warn($msg) - continue - } - - $propertyNames = $adObject.PropertyNames - $propertyNames += ($properties | Where-Object { $_ -ne '*' }) - - # Now process each property to an easy to represent string - $filteredObject = [Ordered]@{} - foreach ($name in ($propertyNames | Sort-Object)) { - # In the case of explicit properties that were asked for but weren't set, Get-ADObject won't actually return - # the property so this is a defensive check against that scenario. - if (-not $adObject.PSObject.Properties.Name.Contains($name)) { - $filteredObject.$name = $null - continue - } - - $value = $adObject.$name - if ($value -is [Microsoft.ActiveDirectory.Management.ADPropertyValueCollection]) { - $value = foreach ($v in $value) { - ConvertTo-OutputValue -InputObject $v - } - } else { - $value = ConvertTo-OutputValue -InputObject $value - } - $filteredObject.$name = $value - - # For these 2 properties, add an _AnsibleFlags attribute which contains the enum strings that are set. - if ($name -eq 'sAMAccountType') { - $enumValue = [Ansible.WinDomainObjectInfo.sAMAccountType]$value - $filteredObject.'sAMAccountType_AnsibleFlags' = $enumValue.ToString() -split ', ' - } elseif ($name -eq 'userAccountControl') { - $enumValue = [Ansible.WinDomainObjectInfo.UserAccountControl]$value - $filteredObject.'userAccountControl_AnsibleFlags' = $enumValue.ToString() -split ', ' - } - } - - $filteredObject -}) - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_domain_object_info.py b/lib/ansible/modules/windows/win_domain_object_info.py deleted file mode 100644 index 008b141b1b..0000000000 --- a/lib/ansible/modules/windows/win_domain_object_info.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, 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_domain_object_info -version_added: '2.10' -short_description: Gather information an Active Directory object -description: -- Gather information about multiple Active Directory object(s). -options: - domain_password: - description: - - The password for C(domain_username). - type: str - domain_server: - description: - - Specified the Active Directory Domain Services instance to connect to. - - Can be in the form of an FQDN or NetBIOS name. - - If not specified then the value is based on the default domain of the computer running PowerShell. - type: str - domain_username: - description: - - The username to use when interacting with AD. - - If this is not set then the user that is used for authentication will be the connection user. - - Ansible will be unable to use the connection user unless auth is Kerberos with credential delegation or CredSSP, - or become is used on the task. - type: str - filter: - description: - - Specifies a query string using the PowerShell Expression Language syntax. - - This follows the same rules and formatting as the C(-Filter) parameter for the PowerShell AD cmdlets exception - there is no variable substitutions. - - This is mutually exclusive with I(identity) and I(ldap_filter). - type: str - identity: - description: - - Specifies a single Active Directory object by its distinguished name or its object GUID. - - This is mutually exclusive with I(filter) and I(ldap_filter). - - This cannot be used with either the I(search_base) or I(search_scope) options. - type: str - include_deleted: - description: - - Also search for deleted Active Directory objects. - default: no - type: bool - ldap_filter: - description: - - Like I(filter) but this is a tradiitional LDAP query string to filter the objects to return. - - This is mutually exclusive with I(filter) and I(identity). - type: str - properties: - description: - - A list of properties to return. - - If a property is C(*), all properties that have a set value on the AD object will be returned. - - If a property is valid on the object but not set, it is only returned if defined explicitly in this option list. - - The properties C(DistinguishedName), C(Name), C(ObjectClass), and C(ObjectGUID) are always returned. - - Specifying multiple properties can have a performance impact, it is best to only return what is needed. - - If an invalid property is specified then the module will display a warning for each object it is invalid on. - type: list - elements: str - search_base: - description: - - Specify the Active Directory path to search for objects in. - - This cannot be set with I(identity). - - By default the search base is the default naming context of the target AD instance which is the DN returned by - "(Get-ADRootDSE).defaultNamingContext". - type: str - search_scope: - description: - - Specify the scope of when searching for an object in the C(search_base). - - C(base) will limit the search to the base object so the maximum number of objects returned is always one. This - will not search any objects inside a container.. - - C(one_level) will search the current path and any immediate objects in that path. - - C(subtree) will search the current path and all objects of that path recursively. - - This cannot be set with I(identity). - choices: - - base - - one_level - - subtree - type: str -notes: -- The C(sAMAccountType_AnsibleFlags) and C(userAccountControl_AnsibleFlags) return property is something set by the - module itself as an easy way to view what those flags represent. These properties cannot be used as part of the - I(filter) or I(ldap_filter) and are automatically added if those properties were requested. -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Get all properties for the specified account using its DistinguishedName - win_domain_object_info: - identity: CN=Username,CN=Users,DC=domain,DC=com - properties: '*' - -- name: Get the SID for all user accounts as a filter - win_domain_object_info: - filter: ObjectClass -eq 'user' -and objectCategory -eq 'Person' - properties: - - objectSid - -- name: Get the SID for all user accounts as a LDAP filter - win_domain_object_info: - ldap_filter: (&(objectClass=user)(objectCategory=Person)) - properties: - - objectSid - -- name: Search all computer accounts in a specific path that were added after February 1st - win_domain_object_info: - filter: objectClass -eq 'computer' -and whenCreated -gt '20200201000000.0Z' - properties: '*' - search_scope: one_level - search_base: CN=Computers,DC=domain,DC=com -''' - -RETURN = r''' -objects: - description: - - A list of dictionaries that are the Active Directory objects found and the properties requested. - - The dict's keys are the property name and the value is the value for the property. - - All date properties are return in the ISO 8601 format in the UTC timezone. - - All SID properties are returned as a dict with the keys C(Sid) as the SID string and C(Name) as the translated SID - account name. - - All byte properties are returned as a base64 string. - - All security descriptor properties are returned as the SDDL string of that descriptor. - - The properties C(DistinguishedName), C(Name), C(ObjectClass), and C(ObjectGUID) are always returned. - returned: always - type: list - elements: dict - sample: | - [{ - "accountExpires": 0, - "adminCount": 1, - "CanonicalName": "domain.com/Users/Administrator", - "CN": "Administrator", - "Created": "2020-01-13T09:03:22.0000000Z", - "Description": "Built-in account for administering computer/domain", - "DisplayName": null, - "DistinguishedName": "CN=Administrator,CN=Users,DC=domain,DC=com", - "memberOf": [ - "CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=com", - "CN=Domain Admins",CN=Users,DC=domain,DC=com" - ], - "Name": "Administrator", - "nTSecurityDescriptor": "O:DAG:DAD:PAI(A;;LCRPLORC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPLOCRSDRCWDWO;;;BA)", - "ObjectCategory": "CN=Person,CN=Schema,CN=Configuration,DC=domain,DC=com", - "ObjectClass": "user", - "ObjectGUID": "c8c6569e-4688-4f3c-8462-afc4ff60817b", - "objectSid": { - "Sid": "S-1-5-21-2959096244-3298113601-420842770-500", - "Name": "DOMAIN\Administrator" - }, - "sAMAccountName": "Administrator", - }] -''' diff --git a/lib/ansible/modules/windows/win_domain_user.ps1 b/lib/ansible/modules/windows/win_domain_user.ps1 deleted file mode 100644 index da690d7b16..0000000000 --- a/lib/ansible/modules/windows/win_domain_user.ps1 +++ /dev/null @@ -1,384 +0,0 @@ -#!powershell - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#AnsibleRequires -CSharpUtil Ansible.AccessToken - -Function Test-Credential { - param( - [String]$Username, - [String]$Password, - [String]$Domain = $null - ) - if (($Username.ToCharArray()) -contains [char]'@') { - # UserPrincipalName - $Domain = $null # force $Domain to be null, to prevent undefined behaviour, as a domain name is already included in the username - } elseif (($Username.ToCharArray()) -contains [char]'\') { - # Pre Win2k Account Name - $Username = ($Username -split '\')[0] - $Domain = ($Username -split '\')[1] - } else { - # No domain provided, so maybe local user, or domain specified separately. - } - - try { - $handle = [Ansible.AccessToken.TokenUtil]::LogonUser($Username, $Domain, $Password, "Network", "Default") - $handle.Dispose() - return $true - } catch [Ansible.AccessToken.Win32Exception] { - # following errors indicate the creds are correct but the user was - # unable to log on for other reasons, which we don't care about - $success_codes = @( - 0x0000052F, # ERROR_ACCOUNT_RESTRICTION - 0x00000530, # ERROR_INVALID_LOGON_HOURS - 0x00000531, # ERROR_INVALID_WORKSTATION - 0x00000569 # ERROR_LOGON_TYPE_GRANTED - ) - $failed_codes = @( - 0x0000052E, # ERROR_LOGON_FAILURE - 0x00000532 # ERROR_PASSWORD_EXPIRED - ) - - if ($_.Exception.NativeErrorCode -in $failed_codes) { - return $false - } elseif ($_.Exception.NativeErrorCode -in $success_codes) { - return $true - } else { - # an unknown failure, reraise exception - throw $_ - } - } -} - -try { - Import-Module ActiveDirectory - } - catch { - Fail-Json $result "Failed to import ActiveDirectory PowerShell module. This module should be run on a domain controller, and the ActiveDirectory module must be available." - } - -$result = @{ - changed = $false - created = $false - password_updated = $false -} - -$ErrorActionPreference = "Stop" - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false - -# Module control parameters -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","query" -$update_password = Get-AnsibleParam -obj $params -name "update_password" -type "str" -default "always" -validateset "always","on_create","when_changed" -$groups_action = Get-AnsibleParam -obj $params -name "groups_action" -type "str" -default "replace" -validateset "add","remove","replace" -$domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str" -$domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($null -ne $domain_username) -$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str" - -# User account parameters -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$identity = Get-AnsibleParam -obj $params -name "identity" -type "str" -default $name -$description = Get-AnsibleParam -obj $params -name "description" -type "str" -$password = Get-AnsibleParam -obj $params -name "password" -type "str" -$password_expired = Get-AnsibleParam -obj $params -name "password_expired" -type "bool" -$password_never_expires = Get-AnsibleParam -obj $params -name "password_never_expires" -type "bool" -$user_cannot_change_password = Get-AnsibleParam -obj $params -name "user_cannot_change_password" -type "bool" -$account_locked = Get-AnsibleParam -obj $params -name "account_locked" -type "bool" -$groups = Get-AnsibleParam -obj $params -name "groups" -type "list" -$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -default $true -$path = Get-AnsibleParam -obj $params -name "path" -type "str" -$upn = Get-AnsibleParam -obj $params -name "upn" -type "str" - -# User informational parameters -$user_info = @{ - GivenName = Get-AnsibleParam -obj $params -name "firstname" -type "str" - Surname = Get-AnsibleParam -obj $params -name "surname" -type "str" - Company = Get-AnsibleParam -obj $params -name "company" -type "str" - EmailAddress = Get-AnsibleParam -obj $params -name "email" -type "str" - StreetAddress = Get-AnsibleParam -obj $params -name "street" -type "str" - City = Get-AnsibleParam -obj $params -name "city" -type "str" - State = Get-AnsibleParam -obj $params -name "state_province" -type "str" - PostalCode = Get-AnsibleParam -obj $params -name "postal_code" -type "str" - Country = Get-AnsibleParam -obj $params -name "country" -type "str" -} - -# Additional attributes -$attributes = Get-AnsibleParam -obj $params -name "attributes" - -# Parameter validation -If ($null -ne $account_locked -and $account_locked) { - Fail-Json $result "account_locked must be set to 'no' if provided" -} -If (($null -ne $password_expired) -and ($null -ne $password_never_expires)) { - Fail-Json $result "password_expired and password_never_expires are mutually exclusive but have both been set" -} - -$extra_args = @{} -if ($null -ne $domain_username) { - $domain_password = ConvertTo-SecureString $domain_password -AsPlainText -Force - $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $domain_password - $extra_args.Credential = $credential -} -if ($null -ne $domain_server) { - $extra_args.Server = $domain_server -} - -Function Get-PrincipalGroups { - Param ($identity, $args_extra) - try{ - $groups = Get-ADPrincipalGroupMembership -Identity $identity @args_extra -ErrorAction Stop - } catch { - Add-Warning -obj $result -message "Failed to enumerate user groups but continuing on.: $($_.Exception.Message)" - return @() - } - - $result_groups = foreach ($group in $groups) { - $group.DistinguishedName - } - return $result_groups -} - -try { - $user_obj = Get-ADUser -Identity $identity -Properties * @extra_args - $user_guid = $user_obj.ObjectGUID -} -catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { - $user_obj = $null - $user_guid = $null -} - -If ($state -eq 'present') { - # Ensure user exists - $new_user = $false - - # If the account does not exist, create it - If (-not $user_obj) { - $create_args = @{} - $create_args.Name = $name - If ($null -ne $path){ - $create_args.Path = $path - } - If ($null -ne $upn){ - $create_args.UserPrincipalName = $upn - $create_args.SamAccountName = $upn.Split('@')[0] - } - $user_obj = New-ADUser @create_args -WhatIf:$check_mode -PassThru @extra_args - $user_guid = $user_obj.ObjectGUID - $new_user = $true - $result.created = $true - $result.changed = $true - If ($check_mode) { - Exit-Json $result - } - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - } - - If ($password) { - # Don't unnecessary check for working credentials. - # Set the password if we need to. - # For new_users there is also no difference between always and when_changed - # so we don't need to differentiate between this two states. - If ($new_user -or ($update_password -eq "always")) { - $set_new_credentials = $true - } elseif ($update_password -eq "when_changed") { - $set_new_credentials = -not (Test-Credential -Username $user_obj.UserPrincipalName -Password $password) - } else { - $set_new_credentials = $false - } - If ($set_new_credentials) { - $secure_password = ConvertTo-SecureString $password -AsPlainText -Force - Set-ADAccountPassword -Identity $user_guid -Reset:$true -Confirm:$false -NewPassword $secure_password -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.password_updated = $true - $result.changed = $true - } - } - - # Configure password policies - If (($null -ne $password_never_expires) -and ($password_never_expires -ne $user_obj.PasswordNeverExpires)) { - Set-ADUser -Identity $user_guid -PasswordNeverExpires $password_never_expires -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - If (($null -ne $password_expired) -and ($password_expired -ne $user_obj.PasswordExpired)) { - Set-ADUser -Identity $user_guid -ChangePasswordAtLogon $password_expired -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - If (($null -ne $user_cannot_change_password) -and ($user_cannot_change_password -ne $user_obj.CannotChangePassword)) { - Set-ADUser -Identity $user_guid -CannotChangePassword $user_cannot_change_password -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - - # Assign other account settings - If (($null -ne $upn) -and ($upn -ne $user_obj.UserPrincipalName)) { - Set-ADUser -Identity $user_guid -UserPrincipalName $upn -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - If (($null -ne $description) -and ($description -ne $user_obj.Description)) { - Set-ADUser -Identity $user_guid -description $description -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - If ($enabled -ne $user_obj.Enabled) { - Set-ADUser -Identity $user_guid -Enabled $enabled -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - If ((-not $account_locked) -and ($user_obj.LockedOut -eq $true)) { - Unlock-ADAccount -Identity $user_guid -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - - # Set user information - Foreach ($key in $user_info.Keys) { - If ($null -eq $user_info[$key]) { - continue - } - $value = $user_info[$key] - If ($value -ne $user_obj.$key) { - $set_args = $extra_args.Clone() - $set_args.$key = $value - Set-ADUser -Identity $user_guid -WhatIf:$check_mode @set_args - $result.changed = $true - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - } - } - - # Set additional attributes - $set_args = $extra_args.Clone() - $run_change = $false - if ($null -ne $attributes) { - $add_attributes = @{} - $replace_attributes = @{} - foreach ($attribute in $attributes.GetEnumerator()) { - $attribute_name = $attribute.Name - $attribute_value = $attribute.Value - - $valid_property = [bool]($user_obj.PSobject.Properties.name -eq $attribute_name) - if ($valid_property) { - $existing_value = $user_obj.$attribute_name - if ($existing_value -cne $attribute_value) { - $replace_attributes.$attribute_name = $attribute_value - } - } else { - $add_attributes.$attribute_name = $attribute_value - } - } - if ($add_attributes.Count -gt 0) { - $set_args.Add = $add_attributes - $run_change = $true - } - if ($replace_attributes.Count -gt 0) { - $set_args.Replace = $replace_attributes - $run_change = $true - } - } - - if ($run_change) { - try { - $user_obj = $user_obj | Set-ADUser -WhatIf:$check_mode -PassThru @set_args - } catch { - Fail-Json $result "failed to change user $($name): $($_.Exception.Message)" - } - $result.changed = $true - } - - - # Configure group assignment - If ($null -ne $groups) { - $group_list = $groups - - $groups = @() - Foreach ($group in $group_list) { - $groups += (Get-ADGroup -Identity $group @extra_args).DistinguishedName - } - - $assigned_groups = Get-PrincipalGroups $user_guid $extra_args - - switch ($groups_action) { - "add" { - Foreach ($group in $groups) { - If (-not ($assigned_groups -Contains $group)) { - Add-ADGroupMember -Identity $group -Members $user_guid -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - } - } - "remove" { - Foreach ($group in $groups) { - If ($assigned_groups -Contains $group) { - Remove-ADGroupMember -Identity $group -Members $user_guid -Confirm:$false -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - } - } - "replace" { - Foreach ($group in $assigned_groups) { - If (($group -ne $user_obj.PrimaryGroup) -and -not ($groups -Contains $group)) { - Remove-ADGroupMember -Identity $group -Members $user_guid -Confirm:$false -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - } - Foreach ($group in $groups) { - If (-not ($assigned_groups -Contains $group)) { - Add-ADGroupMember -Identity $group -Members $user_guid -WhatIf:$check_mode @extra_args - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.changed = $true - } - } - } - } - } -} ElseIf ($state -eq 'absent') { - # Ensure user does not exist - If ($user_obj) { - Remove-ADUser $user_obj -Confirm:$false -WhatIf:$check_mode @extra_args - $result.changed = $true - If ($check_mode) { - Exit-Json $result - } - $user_obj = $null - } -} - -If ($user_obj) { - $user_obj = Get-ADUser -Identity $user_guid -Properties * @extra_args - $result.name = $user_obj.Name - $result.firstname = $user_obj.GivenName - $result.surname = $user_obj.Surname - $result.enabled = $user_obj.Enabled - $result.company = $user_obj.Company - $result.street = $user_obj.StreetAddress - $result.email = $user_obj.EmailAddress - $result.city = $user_obj.City - $result.state_province = $user_obj.State - $result.country = $user_obj.Country - $result.postal_code = $user_obj.PostalCode - $result.distinguished_name = $user_obj.DistinguishedName - $result.description = $user_obj.Description - $result.password_expired = $user_obj.PasswordExpired - $result.password_never_expires = $user_obj.PasswordNeverExpires - $result.user_cannot_change_password = $user_obj.CannotChangePassword - $result.account_locked = $user_obj.LockedOut - $result.sid = [string]$user_obj.SID - $result.upn = $user_obj.UserPrincipalName - $result.groups = Get-PrincipalGroups $user_guid $extra_args - $result.msg = "User '$name' is present" - $result.state = "present" -} -Else { - $result.name = $name - $result.msg = "User '$name' is absent" - $result.state = "absent" -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_domain_user.py b/lib/ansible/modules/windows/win_domain_user.py deleted file mode 100644 index 1880ece176..0000000000 --- a/lib/ansible/modules/windows/win_domain_user.py +++ /dev/null @@ -1,376 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_domain_user -version_added: '2.4' -short_description: Manages Windows Active Directory user accounts -description: - - Manages Windows Active Directory user accounts. -options: - name: - description: - - Name of the user to create, remove or modify. - type: str - required: true - identity: - description: - - Identity parameter used to find the User in the Active Directory. - - This value can be in the forms C(Distinguished Name), C(objectGUID), - C(objectSid) or C(sAMAccountName). - - Default to C(name) if not set. - type: str - version_added: '2.10' - state: - description: - - When C(present), creates or updates the user account. - - When C(absent), removes the user account if it exists. - - When C(query), retrieves the user account details without making any changes. - type: str - choices: [ absent, present, query ] - default: present - enabled: - description: - - C(yes) will enable the user account. - - C(no) will disable the account. - type: bool - default: yes - account_locked: - description: - - C(no) will unlock the user account if locked. - - Note that there is not a way to lock an account as an administrator. - - Accounts are locked due to user actions; as an admin, you may only unlock a locked account. - - If you wish to administratively disable an account, set I(enabled) to C(no). - choices: [ no ] - description: - description: - - Description of the user - type: str - groups: - description: - - Adds or removes the user from this list of groups, - depending on the value of I(groups_action). - - To remove all but the Principal Group, set C(groups=<principal group name>) and - I(groups_action=replace). - - Note that users cannot be removed from their principal group (for example, "Domain Users"). - type: list - groups_action: - description: - - If C(add), the user is added to each group in I(groups) where not already a member. - - If C(remove), the user is removed from each group in I(groups). - - If C(replace), the user is added as a member of each group in - I(groups) and removed from any other groups. - type: str - choices: [ add, remove, replace ] - default: replace - password: - description: - - Optionally set the user's password to this (plain text) value. - - To enable an account - I(enabled) - a password must already be - configured on the account, or you must provide a password here. - type: str - update_password: - description: - - C(always) will always update passwords. - - C(on_create) will only set the password for newly created users. - - C(when_changed) will only set the password when changed (added in ansible 2.9). - type: str - choices: [ always, on_create, when_changed ] - default: always - password_expired: - description: - - C(yes) will require the user to change their password at next login. - - C(no) will clear the expired password flag. - - This is mutually exclusive with I(password_never_expires). - type: bool - password_never_expires: - description: - - C(yes) will set the password to never expire. - - C(no) will allow the password to expire. - - This is mutually exclusive with I(password_expired). - type: bool - user_cannot_change_password: - description: - - C(yes) will prevent the user from changing their password. - - C(no) will allow the user to change their password. - type: bool - firstname: - description: - - Configures the user's first name (given name). - type: str - surname: - description: - - Configures the user's last name (surname). - type: str - company: - description: - - Configures the user's company name. - type: str - upn: - description: - - Configures the User Principal Name (UPN) for the account. - - This is not required, but is best practice to configure for modern - versions of Active Directory. - - The format is C(<username>@<domain>). - type: str - email: - description: - - Configures the user's email address. - - This is a record in AD and does not do anything to configure any email - servers or systems. - type: str - street: - description: - - Configures the user's street address. - type: str - city: - description: - - Configures the user's city. - type: str - state_province: - description: - - Configures the user's state or province. - type: str - postal_code: - description: - - Configures the user's postal code / zip code. - type: str - country: - description: - - Configures the user's country code. - - Note that this is a two-character ISO 3166 code. - type: str - path: - description: - - Container or OU for the new user; if you do not specify this, the - user will be placed in the default container for users in the domain. - - Setting the path is only available when a new user is created; - if you specify a path on an existing user, the user's path will not - be updated - you must delete (e.g., C(state=absent)) the user and - then re-add the user with the appropriate path. - type: str - attributes: - description: - - A dict of custom LDAP attributes to set on the user. - - This can be used to set custom attributes that are not exposed as module - parameters, e.g. C(telephoneNumber). - - See the examples on how to format this parameter. - type: str - version_added: '2.5' - domain_username: - description: - - The username to use when interacting with AD. - - If this is not set then the user Ansible used to log in with will be - used instead when using CredSSP or Kerberos with credential delegation. - type: str - version_added: '2.5' - domain_password: - description: - - The password for I(username). - type: str - version_added: '2.5' - domain_server: - description: - - Specifies the Active Directory Domain Services instance to connect to. - - Can be in the form of an FQDN or NetBIOS name. - - If not specified then the value is based on the domain of the computer - running PowerShell. - type: str - version_added: '2.5' -notes: - - Works with Windows 2012R2 and newer. - - If running on a server that is not a Domain Controller, credential - delegation through CredSSP or Kerberos with delegation must be used or the - I(domain_username), I(domain_password) must be set. - - Note that some individuals have confirmed successful operation on Windows - 2008R2 servers with AD and AD Web Services enabled, but this has not - received the same degree of testing as Windows 2012R2. -seealso: -- module: win_domain -- module: win_domain_controller -- module: win_domain_computer -- module: win_domain_group -- module: win_domain_membership -- module: win_user -- module: win_user_profile -author: - - Nick Chandler (@nwchandler) -''' - -EXAMPLES = r''' -- name: Ensure user bob is present with address information - win_domain_user: - name: bob - firstname: Bob - surname: Smith - company: BobCo - password: B0bP4ssw0rd - state: present - groups: - - Domain Admins - street: 123 4th St. - city: Sometown - state_province: IN - postal_code: 12345 - country: US - attributes: - telephoneNumber: 555-123456 - -- name: Ensure user bob is created and use custom credentials to create the user - win_domain_user: - name: bob - firstname: Bob - surname: Smith - password: B0bP4ssw0rd - state: present - domain_username: DOMAIN\admin-account - domain_password: SomePas2w0rd - domain_server: domain@DOMAIN.COM - -- name: Ensure user bob is present in OU ou=test,dc=domain,dc=local - win_domain_user: - name: bob - password: B0bP4ssw0rd - state: present - path: ou=test,dc=domain,dc=local - groups: - - Domain Admins - -- name: Ensure user bob is absent - win_domain_user: - name: bob - state: absent -''' - -RETURN = r''' -account_locked: - description: true if the account is locked - returned: always - type: bool - sample: false -changed: - description: true if the account changed during execution - returned: always - type: bool - sample: false -city: - description: The user city - returned: always - type: str - sample: Indianapolis -company: - description: The user company - returned: always - type: str - sample: RedHat -country: - description: The user country - returned: always - type: str - sample: US -description: - description: A description of the account - returned: always - type: str - sample: Server Administrator -distinguished_name: - description: DN of the user account - returned: always - type: str - sample: CN=nick,OU=test,DC=domain,DC=local -email: - description: The user email address - returned: always - type: str - sample: nick@domain.local -enabled: - description: true if the account is enabled and false if disabled - returned: always - type: str - sample: true -firstname: - description: The user first name - returned: always - type: str - sample: Nick -groups: - description: AD Groups to which the account belongs - returned: always - type: list - sample: [ "Domain Admins", "Domain Users" ] -msg: - description: Summary message of whether the user is present or absent - returned: always - type: str - sample: User nick is present -name: - description: The username on the account - returned: always - type: str - sample: nick -password_expired: - description: true if the account password has expired - returned: always - type: bool - sample: false -password_updated: - description: true if the password changed during this execution - returned: always - type: bool - sample: true -postal_code: - description: The user postal code - returned: always - type: str - sample: 46033 -sid: - description: The SID of the account - returned: always - type: str - sample: S-1-5-21-2752426336-228313920-2202711348-1175 -state: - description: The state of the user account - returned: always - type: str - sample: present -state_province: - description: The user state or province - returned: always - type: str - sample: IN -street: - description: The user street address - returned: always - type: str - sample: 123 4th St. -surname: - description: The user last name - returned: always - type: str - sample: Doe -upn: - description: The User Principal Name of the account - returned: always - type: str - sample: nick@domain.local -user_cannot_change_password: - description: true if the user is not allowed to change password - returned: always - type: str - sample: false -created: - description: Whether a user was created - returned: always - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_dotnet_ngen.ps1 b/lib/ansible/modules/windows/win_dotnet_ngen.ps1 deleted file mode 100644 index 8dfca6b1ee..0000000000 --- a/lib/ansible/modules/windows/win_dotnet_ngen.ps1 +++ /dev/null @@ -1,61 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Peter Mounce <public@neverrunwithscissors.com> -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.CommandUtil - -$ErrorActionPreference = 'Stop' - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$result = @{ - changed = $false -} - -Function Invoke-Ngen($architecture="") { - $cmd = "$($env:windir)\Microsoft.NET\Framework$($architecture)\v4.0.30319\ngen.exe" - - if (Test-Path -Path $cmd) { - $arguments = "update /force" - if ($check_mode) { - $ngen_result = @{ - rc = 0 - stdout = "check mode output for $cmd $arguments" - } - } else { - try { - $ngen_result = Run-Command -command "$cmd $arguments" - } catch { - Fail-Json -obj $result -message "failed to execute '$cmd $arguments': $($_.Exception.Message)" - } - } - $result."dotnet_ngen$($architecture)_update_exit_code" = $ngen_result.rc - $result."dotnet_ngen$($architecture)_update_output" = $ngen_result.stdout - - $arguments = "executeQueuedItems" - if ($check_mode) { - $executed_queued_items = @{ - rc = 0 - stdout = "check mode output for $cmd $arguments" - } - } else { - try { - $executed_queued_items = Run-Command -command "$cmd $arguments" - } catch { - Fail-Json -obj $result -message "failed to execute '$cmd $arguments': $($_.Exception.Message)" - } - } - $result."dotnet_ngen$($architecture)_eqi_exit_code" = $executed_queued_items.rc - $result."dotnet_ngen$($architecture)_eqi_output" = $executed_queued_items.stdout - $result.changed = $true - } -} - -Invoke-Ngen -Invoke-Ngen -architecture "64" - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_dotnet_ngen.py b/lib/ansible/modules/windows/win_dotnet_ngen.py deleted file mode 100644 index db682c98ec..0000000000 --- a/lib/ansible/modules/windows/win_dotnet_ngen.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Peter Mounce <public@neverrunwithscissors.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_dotnet_ngen -version_added: "2.0" -short_description: Runs ngen to recompile DLLs after .NET updates -description: - - After .NET framework is installed/updated, Windows will probably want to recompile things to optimise for the host. - - This happens via scheduled task, usually at some inopportune time. - - This module allows you to run this task on your own schedule, so you incur the CPU hit at some more convenient and controlled time. - - U(https://docs.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator#native-image-service) - - U(http://blogs.msdn.com/b/dotnet/archive/2013/08/06/wondering-why-mscorsvw-exe-has-high-cpu-usage-you-can-speed-it-up.aspx) -options: {} -notes: - - There are in fact two scheduled tasks for ngen but they have no triggers so aren't a problem. - - There's no way to test if they've been completed. - - The stdout is quite likely to be several megabytes. -author: -- Peter Mounce (@petemounce) -''' - -EXAMPLES = r''' -- name: Run ngen tasks - win_dotnet_ngen: -''' - -RETURN = r''' -dotnet_ngen_update_exit_code: - description: The exit code after running the 32-bit ngen.exe update /force - command. - returned: 32-bit ngen executable exists - type: int - sample: 0 -dotnet_ngen_update_output: - description: The stdout after running the 32-bit ngen.exe update /force - command. - returned: 32-bit ngen executable exists - type: str - sample: sample output -dotnet_ngen_eqi_exit_code: - description: The exit code after running the 32-bit ngen.exe - executeQueuedItems command. - returned: 32-bit ngen executable exists - type: int - sample: 0 -dotnet_ngen_eqi_output: - description: The stdout after running the 32-bit ngen.exe executeQueuedItems - command. - returned: 32-bit ngen executable exists - type: str - sample: sample output -dotnet_ngen64_update_exit_code: - description: The exit code after running the 64-bit ngen.exe update /force - command. - returned: 64-bit ngen executable exists - type: int - sample: 0 -dotnet_ngen64_update_output: - description: The stdout after running the 64-bit ngen.exe update /force - command. - returned: 64-bit ngen executable exists - type: str - sample: sample output -dotnet_ngen64_eqi_exit_code: - description: The exit code after running the 64-bit ngen.exe - executeQueuedItems command. - returned: 64-bit ngen executable exists - type: int - sample: 0 -dotnet_ngen64_eqi_output: - description: The stdout after running the 64-bit ngen.exe executeQueuedItems - command. - returned: 64-bit ngen executable exists - type: str - sample: sample output -''' diff --git a/lib/ansible/modules/windows/win_eventlog.ps1 b/lib/ansible/modules/windows/win_eventlog.ps1 deleted file mode 100644 index c4f695ce22..0000000000 --- a/lib/ansible/modules/windows/win_eventlog.ps1 +++ /dev/null @@ -1,287 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -function Get-EventLogDetail { - <# - .SYNOPSIS - Get details of an event log, sources, and associated attributes. - Used for comparison against passed-in option values to ensure idempotency. - #> - param( - [String]$LogName - ) - - $log_details = @{} - $log_details.name = $LogName - $log_details.exists = $false - $log = Get-EventLog -List | Where-Object {$_.Log -eq $LogName} - - if ($log) { - $log_details.exists = $true - $log_details.maximum_size_kb = $log.MaximumKilobytes - $log_details.overflow_action = $log.OverflowAction.ToString() - $log_details.retention_days = $log.MinimumRetentionDays - $log_details.entries = $log.Entries.Count - $log_details.sources = [Ordered]@{} - - # Retrieve existing sources and category/message/parameter file locations - # Associating file locations and sources with logs can only be done from the registry - - $root_key = "HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\{0}" -f $LogName - $log_root = Get-ChildItem -Path $root_key - - foreach ($child in $log_root) { - $source_name = $child.PSChildName - $log_details.sources.$source_name = @{} - $hash_cursor = $log_details.sources.$source_name - - $source_root = "{0}\{1}" -f $root_key, $source_name - $resource_files = Get-ItemProperty -Path $source_root - - $hash_cursor.category_file = $resource_files.CategoryMessageFile - $hash_cursor.message_file = $resource_files.EventMessageFile - $hash_cursor.parameter_file = $resource_files.ParameterMessageFile - } - } - - return $log_details -} - -function Test-SourceExistence { - <# - .SYNOPSIS - Get information on a source's existence. - Examine existence regarding the parent log it belongs to and its expected state. - #> - param( - [String]$LogName, - [String]$SourceName, - [Switch]$NoLogShouldExist - ) - - $source_exists = [System.Diagnostics.EventLog]::SourceExists($SourceName) - - if ($source_exists -and $NoLogShouldExist) { - Fail-Json -obj $result -message "Source $SourceName already exists and cannot be created" - } - elseif ($source_exists) { - $source_log = [System.Diagnostics.EventLog]::LogNameFromSourceName($SourceName, ".") - if ($source_log -ne $LogName) { - Fail-Json -obj $result -message "Source $SourceName does not belong to log $LogName and cannot be modified" - } - } - - return $source_exists -} - -function ConvertTo-MaximumSize { - <# - .SYNOPSIS - Convert a string KB/MB/GB value to common bytes and KB representations. - .NOTES - Size must be between 64KB and 4GB and divisible by 64KB, as per the MaximumSize parameter of Limit-EventLog. - #> - param( - [String]$Size - ) - - $parsed_size = @{ - bytes = $null - KB = $null - } - - $size_regex = "^\d+(\.\d+)?(KB|MB|GB)$" - if ($Size -notmatch $size_regex) { - Fail-Json -obj $result -message "Maximum size $Size is not properly specified" - } - - $size_upper = $Size.ToUpper() - $size_numeric = [Double]$Size.Substring(0, $Size.Length -2) - - if ($size_upper.EndsWith("GB")) { - $size_bytes = $size_numeric * 1GB - } - elseif ($size_upper.EndsWith("MB")) { - $size_bytes = $size_numeric * 1MB - } - elseif ($size_upper.EndsWith("KB")) { - $size_bytes = $size_numeric * 1KB - } - - if (($size_bytes -lt 64KB) -or ($size_bytes -ge 4GB)) { - Fail-Json -obj $result -message "Maximum size must be between 64KB and 4GB" - } - elseif (($size_bytes % 64KB) -ne 0) { - Fail-Json -obj $result -message "Maximum size must be divisible by 64KB" - } - - $parsed_size.bytes = $size_bytes - $parsed_size.KB = $size_bytes / 1KB - return $parsed_size -} - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","clear","absent" -$sources = Get-AnsibleParam -obj $params -name "sources" -type "list" -$category_file = Get-AnsibleParam -obj $params -name "category_file" -type "path" -$message_file = Get-AnsibleParam -obj $params -name "message_file" -type "path" -$parameter_file = Get-AnsibleParam -obj $params -name "parameter_file" -type "path" -$maximum_size = Get-AnsibleParam -obj $params -name "maximum_size" -type "str" -$overflow_action = Get-AnsibleParam -obj $params -name "overflow_action" -type "str" -validateset "OverwriteOlder","OverwriteAsNeeded","DoNotOverwrite" -$retention_days = Get-AnsibleParam -obj $params -name "retention_days" -type "int" - -$result = @{ - changed = $false - name = $name - sources_changed = @() -} - -$log_details = Get-EventLogDetail -LogName $name - -# Handle common error cases up front -if ($state -eq "present" -and !$log_details.exists -and !$sources) { - # When creating a log, one or more sources must be passed - Fail-Json -obj $result -message "You must specify one or more sources when creating a log for the first time" -} -elseif ($state -eq "present" -and $log_details.exists -and $name -in $sources -and ($category_file -or $message_file -or $parameter_file)) { - # After a default source of the same name is created, it cannot be modified without removing the log - Fail-Json -obj $result -message "Cannot modify default source $name of log $name - you must remove the log" -} -elseif ($state -eq "clear" -and !$log_details.exists) { - Fail-Json -obj $result -message "Cannot clear log $name as it does not exist" -} -elseif ($state -eq "absent" -and $name -in $sources) { - # You also cannot remove a default source for the log - you must remove the log itself - Fail-Json -obj $result -message "Cannot remove default source $name from log $name - you must remove the log" -} - -try { - switch ($state) { - "present" { - foreach ($source in $sources) { - if ($log_details.exists) { - $source_exists = Test-SourceExistence -LogName $name -SourceName $source - } - else { - $source_exists = Test-SourceExistence -LogName $name -SourceName $source -NoLogShouldExist - } - - if ($source_exists) { - $category_change = $category_file -and $log_details.sources.$source.category_file -ne $category_file - $message_change = $message_file -and $log_details.sources.$source.message_file -ne $message_file - $parameter_change = $parameter_file -and $log_details.sources.$source.parameter_file -ne $parameter_file - # Remove source and recreate later if any of the above are true - if ($category_change -or $message_change -or $parameter_change) { - Remove-EventLog -Source $source -WhatIf:$check_mode - } - else { - continue - } - } - - $new_params = @{ - LogName = $name - Source = $source - } - if ($category_file) { - $new_params.CategoryResourceFile = $category_file - } - if ($message_file) { - $new_params.MessageResourceFile = $message_file - } - if ($parameter_file) { - $new_params.ParameterResourceFile = $parameter_file - } - - if (!$check_mode) { - New-EventLog @new_params - $result.sources_changed += $source - } - $result.changed = $true - } - - if ($maximum_size) { - $converted_size = ConvertTo-MaximumSize -Size $maximum_size - } - - $size_change = $maximum_size -and $log_details.maximum_size_kb -ne $converted_size.KB - $overflow_change = $overflow_action -and $log_details.overflow_action -ne $overflow_action - $retention_change = $retention_days -and $log_details.retention_days -ne $retention_days - - if ($size_change -or $overflow_change -or $retention_change) { - $limit_params = @{ - LogName = $name - WhatIf = $check_mode - } - if ($maximum_size) { - $limit_params.MaximumSize = $converted_size.bytes - } - if ($overflow_action) { - $limit_params.OverflowAction = $overflow_action - } - if ($retention_days) { - $limit_params.RetentionDays = $retention_days - } - - Limit-EventLog @limit_params - $result.changed = $true - } - - } - "clear" { - if ($log_details.entries -gt 0) { - Clear-EventLog -LogName $name -WhatIf:$check_mode - $result.changed = $true - } - } - "absent" { - if ($sources -and $log_details.exists) { - # Since sources were passed, remove sources tied to event log - foreach ($source in $sources) { - $source_exists = Test-SourceExistence -LogName $name -SourceName $source - if ($source_exists) { - Remove-EventLog -Source $source -WhatIf:$check_mode - if (!$check_mode) { - $result.sources_changed += $source - } - $result.changed = $true - } - } - } - elseif ($log_details.exists) { - # Only name passed, so remove event log itself (which also removes contained sources) - Remove-EventLog -LogName $name -WhatIf:$check_mode - if (!$check_mode) { - $log_details.sources.GetEnumerator() | ForEach-Object { $result.sources_changed += $_.Name } - } - $result.changed = $true - } - } - } -} -catch { - Fail-Json -obj $result -message $_.Exception.Message -} - -$final_log_details = Get-EventLogDetail -LogName $name -foreach ($final_log_detail in $final_log_details.GetEnumerator()) { - if ($final_log_detail.Name -eq "sources") { - $sources = @() - $final_log_detail.Value.GetEnumerator() | ForEach-Object { $sources += $_.Name } - $result.$($final_log_detail.Name) = [Array]$sources - } - else { - $result.$($final_log_detail.Name) = $final_log_detail.Value - } -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_eventlog.py b/lib/ansible/modules/windows/win_eventlog.py deleted file mode 100644 index 85cb948701..0000000000 --- a/lib/ansible/modules/windows/win_eventlog.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_eventlog -version_added: "2.4" -short_description: Manage Windows event logs -description: - - Allows the addition, clearing and removal of local Windows event logs, - and the creation and removal of sources from a given event log. Also - allows the specification of settings per log and source. -options: - name: - description: - - Name of the event log to manage. - type: str - required: yes - state: - description: - - Desired state of the log and/or sources. - - When C(sources) is populated, state is checked for sources. - - When C(sources) is not populated, state is checked for the specified log itself. - - If C(state) is C(clear), event log entries are cleared for the target log. - type: str - choices: [ absent, clear, present ] - default: present - sources: - description: - - A list of one or more sources to ensure are present/absent in the log. - - When C(category_file), C(message_file) and/or C(parameter_file) are specified, - these values are applied across all sources. - type: list - category_file: - description: - - For one or more sources specified, the path to a custom category resource file. - type: path - message_file: - description: - - For one or more sources specified, the path to a custom event message resource file. - type: path - parameter_file: - description: - - For one or more sources specified, the path to a custom parameter resource file. - type: path - maximum_size: - description: - - The maximum size of the event log. - - Value must be between 64KB and 4GB, and divisible by 64KB. - - Size can be specified in KB, MB or GB (e.g. 128KB, 16MB, 2.5GB). - type: str - overflow_action: - description: - - The action for the log to take once it reaches its maximum size. - - For C(DoNotOverwrite), all existing entries are kept and new entries are not retained. - - For C(OverwriteAsNeeded), each new entry overwrites the oldest entry. - - For C(OverwriteOlder), new log entries overwrite those older than the C(retention_days) value. - type: str - choices: [ DoNotOverwrite, OverwriteAsNeeded, OverwriteOlder ] - retention_days: - description: - - The minimum number of days event entries must remain in the log. - - This option is only used when C(overflow_action) is C(OverwriteOlder). - type: int -seealso: -- module: win_eventlog_entry -author: - - Andrew Saraceni (@andrewsaraceni) -''' - -EXAMPLES = r''' -- name: Add a new event log with two custom sources - win_eventlog: - name: MyNewLog - sources: - - NewLogSource1 - - NewLogSource2 - state: present - -- name: Change the category and message resource files used for NewLogSource1 - win_eventlog: - name: MyNewLog - sources: - - NewLogSource1 - category_file: C:\NewApp\CustomCategories.dll - message_file: C:\NewApp\CustomMessages.dll - state: present - -- name: Change the maximum size and overflow action for MyNewLog - win_eventlog: - name: MyNewLog - maximum_size: 16MB - overflow_action: DoNotOverwrite - state: present - -- name: Clear event entries for MyNewLog - win_eventlog: - name: MyNewLog - state: clear - -- name: Remove NewLogSource2 from MyNewLog - win_eventlog: - name: MyNewLog - sources: - - NewLogSource2 - state: absent - -- name: Remove MyNewLog and all remaining sources - win_eventlog: - name: MyNewLog - state: absent -''' - -RETURN = r''' -name: - description: The name of the event log. - returned: always - type: str - sample: MyNewLog -exists: - description: Whether the event log exists or not. - returned: success - type: bool - sample: true -entries: - description: The count of entries present in the event log. - returned: success - type: int - sample: 50 -maximum_size_kb: - description: Maximum size of the log in KB. - returned: success - type: int - sample: 512 -overflow_action: - description: The action the log takes once it reaches its maximum size. - returned: success - type: str - sample: OverwriteOlder -retention_days: - description: The minimum number of days entries are retained in the log. - returned: success - type: int - sample: 7 -sources: - description: A list of the current sources for the log. - returned: success - type: list - sample: ["MyNewLog", "NewLogSource1", "NewLogSource2"] -sources_changed: - description: A list of sources changed (e.g. re/created, removed) for the log; - this is empty if no sources are changed. - returned: always - type: list - sample: ["NewLogSource2"] -''' diff --git a/lib/ansible/modules/windows/win_eventlog_entry.ps1 b/lib/ansible/modules/windows/win_eventlog_entry.ps1 deleted file mode 100644 index ddc4d1e168..0000000000 --- a/lib/ansible/modules/windows/win_eventlog_entry.ps1 +++ /dev/null @@ -1,106 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -function Test-LogExistence { - <# - .SYNOPSIS - Get information on a log's existence. - #> - param( - [String]$LogName - ) - - $log_exists = $false - $log = Get-EventLog -List | Where-Object {$_.Log -eq $LogName} - if ($log) { - $log_exists = $true - } - return $log_exists -} - -function Test-SourceExistence { - <# - .SYNOPSIS - Get information on a source's existence. - #> - param( - [String]$LogName, - [String]$SourceName - ) - - $source_exists = [System.Diagnostics.EventLog]::SourceExists($SourceName) - - if ($source_exists) { - $source_log = [System.Diagnostics.EventLog]::LogNameFromSourceName($SourceName, ".") - if ($source_log -ne $LogName) { - Fail-Json -obj $result -message "Source $SourceName does not belong to log $LogName and cannot be written to" - } - } - - return $source_exists -} - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$log = Get-AnsibleParam -obj $params -name "log" -type "str" -failifempty $true -$source = Get-AnsibleParam -obj $params -name "source" -type "str" -failifempty $true -$event_id = Get-AnsibleParam -obj $params -name "event_id" -type "int" -failifempty $true -$message = Get-AnsibleParam -obj $params -name "message" -type "str" -failifempty $true -$entry_type = Get-AnsibleParam -obj $params -name "entry_type" -type "str" -validateset "Error","FailureAudit","Information","SuccessAudit","Warning" -$category = Get-AnsibleParam -obj $params -name "category" -type "int" -$raw_data = Get-AnsibleParam -obj $params -name "raw_data" -type "str" - -$result = @{ - changed = $false -} - -$log_exists = Test-LogExistence -LogName $log -if (!$log_exists) { - Fail-Json -obj $result -message "Log $log does not exist and cannot be written to" -} - -$source_exists = Test-SourceExistence -LogName $log -SourceName $source -if (!$source_exists) { - Fail-Json -obj $result -message "Source $source does not exist" -} - -if ($event_id -lt 0 -or $event_id -gt 65535) { - Fail-Json -obj $result -message "Event ID must be between 0 and 65535" -} - -$write_params = @{ - LogName = $log - Source = $source - EventId = $event_id - Message = $message -} - -try { - if ($entry_type) { - $write_params.EntryType = $entry_type - } - if ($category) { - $write_params.Category = $category - } - if ($raw_data) { - $write_params.RawData = [Byte[]]($raw_data -split ",") - } - - if (!$check_mode) { - Write-EventLog @write_params - } - $result.changed = $true - $result.msg = "Entry added to log $log from source $source" -} -catch { - Fail-Json -obj $result -message $_.Exception.Message -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_eventlog_entry.py b/lib/ansible/modules/windows/win_eventlog_entry.py deleted file mode 100644 index 05652e9df5..0000000000 --- a/lib/ansible/modules/windows/win_eventlog_entry.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> -# 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_eventlog_entry -version_added: "2.4" -short_description: Write entries to Windows event logs -description: - - Write log entries to a given event log from a specified source. -options: - log: - description: - - Name of the event log to write an entry to. - type: str - required: yes - source: - description: - - Name of the log source to indicate where the entry is from. - type: str - required: yes - event_id: - description: - - The numeric event identifier for the entry. - - Value must be between 0 and 65535. - type: int - required: yes - message: - description: - - The message for the given log entry. - type: str - required: yes - entry_type: - description: - - Indicates the entry being written to the log is of a specific type. - type: str - choices: [ Error, FailureAudit, Information, SuccessAudit, Warning ] - category: - description: - - A numeric task category associated with the category message file for the log source. - type: int - raw_data: - description: - - Binary data associated with the log entry. - - Value must be a comma-separated array of 8-bit unsigned integers (0 to 255). - type: str -notes: - - This module will always report a change when writing an event entry. -seealso: -- module: win_eventlog -author: - - Andrew Saraceni (@andrewsaraceni) -''' - -EXAMPLES = r''' -- name: Write an entry to a Windows event log - win_eventlog_entry: - log: MyNewLog - source: NewLogSource1 - event_id: 1234 - message: This is a test log entry. - -- name: Write another entry to a different Windows event log - win_eventlog_entry: - log: AnotherLog - source: MyAppSource - event_id: 5000 - message: An error has occurred. - entry_type: Error - category: 5 - raw_data: 10,20 -''' - -RETURN = r''' -# Default return values -''' diff --git a/lib/ansible/modules/windows/win_file_compression.ps1 b/lib/ansible/modules/windows/win_file_compression.ps1 deleted file mode 100644 index 893cc50278..0000000000 --- a/lib/ansible/modules/windows/win_file_compression.ps1 +++ /dev/null @@ -1,118 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Micah Hunsberger -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -Set-StrictMode -Version 2 - -$spec = @{ - options = @{ - path = @{ type = 'path'; required = $true } - state = @{ type = 'str'; default = 'present'; choices = 'absent', 'present' } - recurse = @{ type = 'bool'; default = $false } - force = @{ type = 'bool'; default = $true } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$path = $module.Params.path -$state = $module.Params.state -$recurse = $module.Params.recurse -$force = $module.Params.force - -$module.Result.rc = 0 - -if(-not (Test-Path -LiteralPath $path)) { - $module.FailJson("Path to item, $path, does not exist.") -} - -$item = Get-Item -LiteralPath $path -Force # Use -Force for hidden files -if (-not $item.PSIsContainer -and $recurse) { - $module.Warn("The recurse option has no effect when path is not a folder.") -} - -$cim_params = @{ - ClassName = 'Win32_LogicalDisk' - Filter = "DeviceId='$($item.PSDrive.Name):'" - Property = @('FileSystem', 'SupportsFileBasedCompression') -} -$drive_info = Get-CimInstance @cim_params -if ($drive_info.SupportsFileBasedCompression -eq $false) { - $module.FailJson("Path, $path, is not on a filesystemi '$($drive_info.FileSystem)' that supports file based compression.") -} - -function Get-ReturnCodeMessage { - param( - [int]$code - ) - switch ($code) { - 0 { return "The request was successful." } - 2 { return "Access was denied." } - 8 { return "An unspecified failure occurred." } - 9 { return "The name specified was not valid." } - 10 { return "The object specified already exists." } - 11 { return "The file system is not NTFS." } - 12 { return "The platform is not Windows." } - 13 { return "The drive is not the same." } - 14 { return "The directory is not empty." } - 15 { return "There has been a sharing violation." } - 16 { return "The start file specified was not valid." } - 17 { return "A privilege required for the operation is not held." } - 21 { return "A parameter specified is not valid." } - } -} - -function Get-EscapedFileName { - param( - [string]$FullName - ) - return $FullName.Replace("\","\\").Replace("'","\'") -} - -$is_compressed = ($item.Attributes -band [System.IO.FileAttributes]::Compressed) -eq [System.IO.FileAttributes]::Compressed -$needs_changed = $is_compressed -ne ($state -eq 'present') - -if($force -and $recurse -and $item.PSIsContainer) { - if (-not $needs_changed) { - # Check the subfolders and files - $entries_to_check = $item.EnumerateFileSystemInfos("*", [System.IO.SearchOption]::AllDirectories) - foreach ($entry in $entries_to_check) { - $is_compressed = ($entry.Attributes -band [System.IO.FileAttributes]::Compressed) -eq [System.IO.FileAttributes]::Compressed - if ($is_compressed -ne ($state -eq 'present')) { - $needs_changed = $true - break - } - } - } -} - -if($needs_changed) { - $module.Result.changed = $true - if ($item.PSIsContainer) { - $cim_obj = Get-CimInstance -ClassName 'Win32_Directory' -Filter "Name='$(Get-EscapedFileName -FullName $item.FullName)'" - } else { - $cim_obj = Get-CimInstance -ClassName 'CIM_LogicalFile' -Filter "Name='$(Get-EscapedFileName -FullName $item.FullName)'" - } - if($state -eq 'present') { - if(-not $module.CheckMode) { - $ret = Invoke-CimMethod -InputObject $cim_obj -MethodName 'CompressEx' -Arguments @{ Recursive = $recurse } - $module.Result.rc = $ret.ReturnValue - } - } else { - if(-not $module.CheckMode) { - $ret = $ret = Invoke-CimMethod -InputObject $cim_obj -MethodName 'UnCompressEx' -Arguments @{ Recursive = $recurse } - $module.Result.rc = $ret.ReturnValue - } - } -} - -$module.Result.msg = Get-ReturnCodeMessage -code $module.Result.rc -if($module.Result.rc -ne 0) { - $module.FailJson($module.Result.msg) -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_file_compression.py b/lib/ansible/modules/windows/win_file_compression.py deleted file mode 100644 index 3bc59cade0..0000000000 --- a/lib/ansible/modules/windows/win_file_compression.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Micah Hunsberger (@mhunsber) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_file_compression -version_added: '2.10' -short_description: Alters the compression of files and directories on NTFS partitions. -description: - - This module sets the compressed attribute for files and directories on a filesystem that supports it like NTFS. - - NTFS compression can be used to save disk space. -options: - path: - description: - - The full path of the file or directory to modify. - - The path must exist on file system that supports compression like NTFS. - required: yes - type: path - state: - description: - - Set to C(present) to ensure the I(path) is compressed. - - Set to C(absent) to ensure the I(path) is not compressed. - type: str - choices: - - absent - - present - default: present - recurse: - description: - - Whether to recursively apply changes to all subdirectories and files. - - This option only has an effect when I(path) is a directory. - - When set to C(false), only applies changes to I(path). - - When set to C(true), applies changes to I(path) and all subdirectories and files. - type: bool - default: false - force: - description: - - This option only has an effect when I(recurse) is C(true) - - If C(true), will check the compressed state of all subdirectories and files - and make a change if any are different from I(compressed). - - If C(false), will only make a change if the compressed state of I(path) is different from I(compressed). - - If the folder structure is complex or contains a lot of files, it is recommended to set this - option to C(false) so that not every file has to be checked. - type: bool - default: true -author: - - Micah Hunsberger (@mhunsber) -notes: - - C(win_file_compression) sets the file system's compression state, it does not create a zip archive file. - - For more about NTFS Compression, see U(http://www.ntfs.com/ntfs-compressed.htm) -''' - -EXAMPLES = r''' -- name: Compress log files directory - win_file_compression: - path: C:\Logs - state: present - -- name: Decompress log files directory - win_file_compression: - path: C:\Logs - state: absent - -- name: Compress reports directory and all subdirectories - win_file_compression: - path: C:\business\reports - state: present - recurse: yes - -# This will only check C:\business\reports for the compressed state -# If C:\business\reports is compressed, it will not make a change -# even if one of the child items is uncompressed - -- name: Compress reports directory and all subdirectories (quick) - win_file_compression: - path: C:\business\reports - compressed: yes - recurse: yes - force: no -''' - -RETURN = r''' -rc: - description: - - The return code of the compress/uncompress operation. - - If no changes are made or the operation is successful, rc is 0. - returned: always - sample: 0 - type: int -''' diff --git a/lib/ansible/modules/windows/win_file_version.ps1 b/lib/ansible/modules/windows/win_file_version.ps1 deleted file mode 100644 index 7fc6b557ef..0000000000 --- a/lib/ansible/modules/windows/win_file_version.ps1 +++ /dev/null @@ -1,63 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Sam Liu <sam.liu@activenetwork.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args $args -supports_check_mode $true - -$result = @{ - win_file_version = @{} - changed = $false -} - -$path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -resultobj $result - -If (-Not (Test-Path -Path $path -PathType Leaf)){ - Fail-Json $result "Specified path $path does not exist or is not a file." -} -$ext = [System.IO.Path]::GetExtension($path) -If ( $ext -notin '.exe', '.dll'){ - Fail-Json $result "Specified path $path is not a valid file type; must be DLL or EXE." -} - -Try { - $_version_fields = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($path) - $file_version = $_version_fields.FileVersion - If ($null -eq $file_version){ - $file_version = '' - } - $product_version = $_version_fields.ProductVersion - If ($null -eq $product_version){ - $product_version= '' - } - $file_major_part = $_version_fields.FileMajorPart - If ($null -eq $file_major_part){ - $file_major_part= '' - } - $file_minor_part = $_version_fields.FileMinorPart - If ($null -eq $file_minor_part){ - $file_minor_part= '' - } - $file_build_part = $_version_fields.FileBuildPart - If ($null -eq $file_build_part){ - $file_build_part = '' - } - $file_private_part = $_version_fields.FilePrivatePart - If ($null -eq $file_private_part){ - $file_private_part = '' - } -} -Catch{ - Fail-Json $result "Error: $_.Exception.Message" -} - -$result.win_file_version.path = $path.toString() -$result.win_file_version.file_version = $file_version.toString() -$result.win_file_version.product_version = $product_version.toString() -$result.win_file_version.file_major_part = $file_major_part.toString() -$result.win_file_version.file_minor_part = $file_minor_part.toString() -$result.win_file_version.file_build_part = $file_build_part.toString() -$result.win_file_version.file_private_part = $file_private_part.toString() -Exit-Json $result; diff --git a/lib/ansible/modules/windows/win_file_version.py b/lib/ansible/modules/windows/win_file_version.py deleted file mode 100644 index 390927821b..0000000000 --- a/lib/ansible/modules/windows/win_file_version.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Sam Liu <sam.liu@activenetwork.com> -# 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_file_version -version_added: "2.1" -short_description: Get DLL or EXE file build version -description: - - Get DLL or EXE file build version. -notes: - - This module will always return no change. -options: - path: - description: - - File to get version. - - Always provide absolute path. - type: path - required: yes -seealso: -- module: win_file -author: -- Sam Liu (@SamLiu79) -''' - -EXAMPLES = r''' -- name: Get acm instance version - win_file_version: - path: C:\Windows\System32\cmd.exe - register: exe_file_version - -- debug: - msg: '{{ exe_file_version }}' -''' - -RETURN = r''' -path: - description: file path - returned: always - type: str - -file_version: - description: File version number.. - returned: no error - type: str - -product_version: - description: The version of the product this file is distributed with. - returned: no error - type: str - -file_major_part: - description: the major part of the version number. - returned: no error - type: str - -file_minor_part: - description: the minor part of the version number of the file. - returned: no error - type: str - -file_build_part: - description: build number of the file. - returned: no error - type: str - -file_private_part: - description: file private part number. - returned: no error - type: str -''' diff --git a/lib/ansible/modules/windows/win_firewall.ps1 b/lib/ansible/modules/windows/win_firewall.ps1 deleted file mode 100644 index 2a628a3527..0000000000 --- a/lib/ansible/modules/windows/win_firewall.ps1 +++ /dev/null @@ -1,68 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Michael Eaton <meaton@iforium.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" -$firewall_profiles = @('Domain', 'Private', 'Public') - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$profiles = Get-AnsibleParam -obj $params -name "profiles" -type "list" -default @("Domain", "Private", "Public") -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -failifempty $true -validateset 'disabled','enabled' - -$result = @{ - changed = $false - profiles = $profiles - state = $state -} - -try { - get-command Get-NetFirewallProfile > $null - get-command Set-NetFirewallProfile > $null -} -catch { - Fail-Json $result "win_firewall requires Get-NetFirewallProfile and Set-NetFirewallProfile Cmdlets." -} - -Try { - - ForEach ($profile in $firewall_profiles) { - - $currentstate = (Get-NetFirewallProfile -Name $profile).Enabled - $result.$profile = @{ - enabled = ($currentstate -eq 1) - considered = ($profiles -contains $profile) - currentstate = $currentstate - } - - if ($profiles -notcontains $profile) { - continue - } - - if ($state -eq 'enabled') { - - if ($currentstate -eq $false) { - Set-NetFirewallProfile -name $profile -Enabled true -WhatIf:$check_mode - $result.changed = $true - $result.$profile.enabled = $true - } - - } else { - - if ($currentstate -eq $true) { - Set-NetFirewallProfile -name $profile -Enabled false -WhatIf:$check_mode - $result.changed = $true - $result.$profile.enabled = $false - } - - } - } -} Catch { - Fail-Json $result "an error occurred when attempting to change firewall status for profile $profile $($_.Exception.Message)" -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_firewall.py b/lib/ansible/modules/windows/win_firewall.py deleted file mode 100644 index 62556b4901..0000000000 --- a/lib/ansible/modules/windows/win_firewall.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Michael Eaton <meaton@iforium.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_firewall -version_added: '2.4' -short_description: Enable or disable the Windows Firewall -description: -- Enable or Disable Windows Firewall profiles. -requirements: - - This module requires Windows Management Framework 5 or later. -options: - profiles: - description: - - Specify one or more profiles to change. - type: list - choices: [ Domain, Private, Public ] - default: [ Domain, Private, Public ] - state: - description: - - Set state of firewall for given profile. - type: str - choices: [ disabled, enabled ] -seealso: -- module: win_firewall_rule -author: -- Michael Eaton (@michaeldeaton) -''' - -EXAMPLES = r''' -- name: Enable firewall for Domain, Public and Private profiles - win_firewall: - state: enabled - profiles: - - Domain - - Private - - Public - tags: enable_firewall - -- name: Disable Domain firewall - win_firewall: - state: disabled - profiles: - - Domain - tags: disable_firewall -''' - -RETURN = r''' -enabled: - description: Current firewall status for chosen profile (after any potential change). - returned: always - type: bool - sample: true -profiles: - description: Chosen profile. - returned: always - type: str - sample: Domain -state: - description: Desired state of the given firewall profile(s). - returned: always - type: list - sample: enabled -''' diff --git a/lib/ansible/modules/windows/win_firewall_rule.ps1 b/lib/ansible/modules/windows/win_firewall_rule.ps1 deleted file mode 100644 index 3e6544eac4..0000000000 --- a/lib/ansible/modules/windows/win_firewall_rule.ps1 +++ /dev/null @@ -1,257 +0,0 @@ -#!powershell - -# Copyright: (c) 2014, Timothy Vandenbrande <timothy.vandenbrande@gmail.com> -# Copyright: (c) 2017, Artem Zinenko <zinenkoartem@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -function Parse-ProtocolType { - param($protocol) - - $protocolNumber = $protocol -as [int] - if ($protocolNumber -is [int]) { - return $protocolNumber - } - - switch -wildcard ($protocol) { - "tcp" { return [System.Net.Sockets.ProtocolType]::Tcp -as [int] } - "udp" { return [System.Net.Sockets.ProtocolType]::Udp -as [int] } - "icmpv4*" { return [System.Net.Sockets.ProtocolType]::Icmp -as [int] } - "icmpv6*" { return [System.Net.Sockets.ProtocolType]::IcmpV6 -as [int] } - default { throw "Unknown protocol '$protocol'." } - } -} - -# See 'Direction' constants here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364724(v=vs.85).aspx -function Parse-Direction { - param($directionStr) - - switch ($directionStr) { - "in" { return 1 } - "out" { return 2 } - default { throw "Unknown direction '$directionStr'." } - } -} - -# See 'Action' constants here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364724(v=vs.85).aspx -function Parse-Action { - param($actionStr) - - switch ($actionStr) { - "block" { return 0 } - "allow" { return 1 } - default { throw "Unknown action '$actionStr'." } - } -} - -# Profile enum values: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366303(v=vs.85).aspx -function Parse-Profiles -{ - param($profilesList) - - $profiles = ($profilesList | Select-Object -Unique | ForEach-Object { - switch ($_) { - "domain" { return 1 } - "private" { return 2 } - "public" { return 4 } - default { throw "Unknown profile '$_'." } - } - } | Measure-Object -Sum).Sum - - if ($profiles -eq 7) { return 0x7fffffff } - return $profiles -} - -function Parse-InterfaceTypes -{ - param($interfaceTypes) - - return ($interfaceTypes | Select-Object -Unique | ForEach-Object { - switch ($_) { - "wireless" { return "Wireless" } - "lan" { return "Lan" } - "ras" { return "RemoteAccess" } - default { throw "Unknown interface type '$_'." } - } - }) -Join "," -} - -function Parse-EdgeTraversalOptions -{ - param($edgeTraversalOptionsStr) - - switch ($edgeTraversalOptionsStr) { - "yes" { return 1 } - "deferapp" { return 2 } - "deferuser" { return 3 } - default { throw "Unknown edge traversal options '$edgeTraversalOptionsStr'." } - } -} - -function Parse-SecureFlags -{ - param($secureFlagsStr) - - switch ($secureFlagsStr) { - "authnoencap" { return 1 } - "authenticate" { return 2 } - "authdynenc" { return 3 } - "authenc" { return 4 } - default { throw "Unknown secure flags '$secureFlagsStr'." } - } -} - -$ErrorActionPreference = "Stop" - -$result = @{ - changed = $false -} - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -failifempty $true -$description = Get-AnsibleParam -obj $params -name "description" -type "str" -$direction = Get-AnsibleParam -obj $params -name "direction" -type "str" -validateset "in","out" -$action = Get-AnsibleParam -obj $params -name "action" -type "str" -validateset "allow","block" -$program = Get-AnsibleParam -obj $params -name "program" -type "str" -$group = Get-AnsibleParam -obj $params -name "group" -type "str" -$service = Get-AnsibleParam -obj $params -name "service" -type "str" -$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -aliases "enable" -$profiles = Get-AnsibleParam -obj $params -name "profiles" -type "list" -aliases "profile" -$localip = Get-AnsibleParam -obj $params -name "localip" -type "str" -$remoteip = Get-AnsibleParam -obj $params -name "remoteip" -type "str" -$localport = Get-AnsibleParam -obj $params -name "localport" -type "str" -$remoteport = Get-AnsibleParam -obj $params -name "remoteport" -type "str" -$protocol = Get-AnsibleParam -obj $params -name "protocol" -type "str" -$interfacetypes = Get-AnsibleParam -obj $params -name "interfacetypes" -type "list" -$edge = Get-AnsibleParam -obj $params -name "edge" -type "str" -validateset "no","yes","deferapp","deferuser" -$security = Get-AnsibleParam -obj $params -name "security" -type "str" -validateset "notrequired","authnoencap","authenticate","authdynenc","authenc" -$icmp_type_code = Get-AnsibleParam -obj $params -name "icmp_type_code" -type "list" - -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent" - -if ($diff_support) { - $result.diff = @{} - $result.diff.prepared = "" -} - -if ($null -ne $icmp_type_code) { - # COM representation is just "<type>:<code>,<type2>:<code>" so we just join our list - $icmp_type_code = $icmp_type_code -join "," -} - -try { - $fw = New-Object -ComObject HNetCfg.FwPolicy2 - - $existingRule = $fw.Rules | Where-Object { $_.Name -eq $name } - - if ($existingRule -is [System.Array]) { - Fail-Json $result "Multiple firewall rules with name '$name' found." - } - - # INetFwRule interface description: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365344(v=vs.85).aspx - $new_rule = New-Object -ComObject HNetCfg.FWRule - $new_rule.Name = $name - # the default for enabled in module description is "true", but the actual COM object defaults to "false" when created - if ($null -ne $enabled) { $new_rule.Enabled = $enabled } else { $new_rule.Enabled = $true } - if ($null -ne $description) { $new_rule.Description = $description } - if ($null -ne $group) { $new_rule.Grouping = $group } - if ($null -ne $program -and $program -ne "any") { $new_rule.ApplicationName = [System.Environment]::ExpandEnvironmentVariables($program) } - if ($null -ne $service -and $program -ne "any") { $new_rule.ServiceName = $service } - if ($null -ne $protocol -and $protocol -ne "any") { $new_rule.Protocol = Parse-ProtocolType -protocol $protocol } - if ($null -ne $localport -and $localport -ne "any") { $new_rule.LocalPorts = $localport } - if ($null -ne $remoteport -and $remoteport -ne "any") { $new_rule.RemotePorts = $remoteport } - if ($null -ne $localip -and $localip -ne "any") { $new_rule.LocalAddresses = $localip } - if ($null -ne $remoteip -and $remoteip -ne "any") { $new_rule.RemoteAddresses = $remoteip } - if ($null -ne $icmp_type_code -and $icmp_type_code -ne "any") { $new_rule.IcmpTypesAndCodes = $icmp_type_code } - if ($null -ne $direction) { $new_rule.Direction = Parse-Direction -directionStr $direction } - if ($null -ne $action) { $new_rule.Action = Parse-Action -actionStr $action } - # Profiles value cannot be a uint32, but the "all profiles" value (0x7FFFFFFF) will often become a uint32, so must cast to [int] - if ($null -ne $profiles) { $new_rule.Profiles = [int](Parse-Profiles -profilesList $profiles) } - if ($null -ne $interfacetypes -and @(Compare-Object -ReferenceObject $interfacetypes -DifferenceObject @("any")).Count -ne 0) { $new_rule.InterfaceTypes = Parse-InterfaceTypes -interfaceTypes $interfacetypes } - if ($null -ne $edge -and $edge -ne "no") { - # EdgeTraversalOptions property exists only from Windows 7/Windows Server 2008 R2: https://msdn.microsoft.com/en-us/library/windows/desktop/dd607256(v=vs.85).aspx - if ($new_rule | Get-Member -Name 'EdgeTraversalOptions') { - $new_rule.EdgeTraversalOptions = Parse-EdgeTraversalOptions -edgeTraversalOptionsStr $edge - } - } - if ($null -ne $security -and $security -ne "notrequired") { - # SecureFlags property exists only from Windows 8/Windows Server 2012: https://msdn.microsoft.com/en-us/library/windows/desktop/hh447465(v=vs.85).aspx - if ($new_rule | Get-Member -Name 'SecureFlags') { - $new_rule.SecureFlags = Parse-SecureFlags -secureFlagsStr $security - } - } - - $fwPropertiesToCompare = @('Name','Description','Direction','Action','ApplicationName','Grouping','ServiceName','Enabled','Profiles','LocalAddresses','RemoteAddresses','LocalPorts','RemotePorts','Protocol','InterfaceTypes', 'EdgeTraversalOptions', 'SecureFlags','IcmpTypesAndCodes') - $userPassedArguments = @($name, $description, $direction, $action, $program, $group, $service, $enabled, $profiles, $localip, $remoteip, $localport, $remoteport, $protocol, $interfacetypes, $edge, $security, $icmp_type_code) - - if ($state -eq "absent") { - if ($null -eq $existingRule) { - $result.msg = "Firewall rule '$name' does not exist." - } else { - if ($diff_support) { - foreach ($prop in $fwPropertiesToCompare) { - $result.diff.prepared += "-[$($prop)='$($existingRule.$prop)']`n" - } - } - - if (-not $check_mode) { - $fw.Rules.Remove($existingRule.Name) - } - $result.changed = $true - $result.msg = "Firewall rule '$name' removed." - } - } elseif ($state -eq "present") { - if ($null -eq $existingRule) { - if ($diff_support) { - foreach ($prop in $fwPropertiesToCompare) { - $result.diff.prepared += "+[$($prop)='$($new_rule.$prop)']`n" - } - } - - if (-not $check_mode) { - $fw.Rules.Add($new_rule) - } - $result.changed = $true - $result.msg = "Firewall rule '$name' created." - } else { - for($i = 0; $i -lt $fwPropertiesToCompare.Length; $i++) { - $prop = $fwPropertiesToCompare[$i] - if($null -ne $userPassedArguments[$i]) { # only change values the user passes in task definition - if ($existingRule.$prop -ne $new_rule.$prop) { - if ($diff_support) { - $result.diff.prepared += "-[$($prop)='$($existingRule.$prop)']`n" - $result.diff.prepared += "+[$($prop)='$($new_rule.$prop)']`n" - } - - if (-not $check_mode) { - # Profiles value cannot be a uint32, but the "all profiles" value (0x7FFFFFFF) will often become a uint32, so must cast to [int] - # to prevent InvalidCastException under PS5+ - If($prop -eq 'Profiles') { - $existingRule.Profiles = [int] $new_rule.$prop - } - Else { - $existingRule.$prop = $new_rule.$prop - } - } - $result.changed = $true - } - } - } - if ($result.changed) { - $result.msg = "Firewall rule '$name' changed." - } else { - $result.msg = "Firewall rule '$name' already exists." - } - } - } -} catch [Exception] { - $ex = $_ - $result['exception'] = $($ex | Out-String) - Fail-Json $result $ex.Exception.Message -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_firewall_rule.py b/lib/ansible/modules/windows/win_firewall_rule.py deleted file mode 100644 index d25468d362..0000000000 --- a/lib/ansible/modules/windows/win_firewall_rule.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2014, Timothy Vandenbrande <timothy.vandenbrande@gmail.com> -# Copyright: (c) 2017, Artem Zinenko <zinenkoartem@gmail.com> -# 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_firewall_rule -version_added: "2.0" -short_description: Windows firewall automation -description: - - Allows you to create/remove/update firewall rules. -options: - enabled: - description: - - Whether this firewall rule is enabled or disabled. - - Defaults to C(true) when creating a new rule. - type: bool - aliases: [ enable ] - state: - description: - - Should this rule be added or removed. - type: str - choices: [ absent, present ] - default: present - name: - description: - - The rule's display name. - type: str - required: yes - group: - description: - - The group name for the rule. - version_added: '2.9' - type: str - direction: - description: - - Whether this rule is for inbound or outbound traffic. - - Defaults to C(in) when creating a new rule. - type: str - choices: [ in, out ] - action: - description: - - What to do with the items this rule is for. - - Defaults to C(allow) when creating a new rule. - type: str - choices: [ allow, block ] - description: - description: - - Description for the firewall rule. - type: str - localip: - description: - - The local ip address this rule applies to. - - Set to C(any) to apply to all local ip addresses. - - Defaults to C(any) when creating a new rule. - type: str - remoteip: - description: - - The remote ip address/range this rule applies to. - - Set to C(any) to apply to all remote ip addresses. - - Defaults to C(any) when creating a new rule. - type: str - localport: - description: - - The local port this rule applies to. - - Set to C(any) to apply to all local ports. - - Defaults to C(any) when creating a new rule. - - Must have I(protocol) set - type: str - remoteport: - description: - - The remote port this rule applies to. - - Set to C(any) to apply to all remote ports. - - Defaults to C(any) when creating a new rule. - - Must have I(protocol) set - type: str - program: - description: - - The program this rule applies to. - - Set to C(any) to apply to all programs. - - Defaults to C(any) when creating a new rule. - type: str - service: - description: - - The service this rule applies to. - - Set to C(any) to apply to all services. - - Defaults to C(any) when creating a new rule. - type: str - protocol: - description: - - The protocol this rule applies to. - - Set to C(any) to apply to all services. - - Defaults to C(any) when creating a new rule. - type: str - profiles: - description: - - The profile this rule applies to. - - Defaults to C(domain,private,public) when creating a new rule. - type: list - aliases: [ profile ] - icmp_type_code: - description: - - The ICMP types and codes for the rule. - - This is only valid when I(protocol) is C(icmpv4) or C(icmpv6). - - Each entry follows the format C(type:code) where C(type) is the type - number and C(code) is the code number for that type or C(*) for all - codes. - - Set the value to just C(*) to apply the rule for all ICMP type codes. - - See U(https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml) - for a list of ICMP types and the codes that apply to them. - type: list - version_added: '2.10' -seealso: -- module: win_firewall -author: - - Artem Zinenko (@ar7z1) - - Timothy Vandenbrande (@TimothyVandenbrande) -''' - -EXAMPLES = r''' -- name: Firewall rule to allow SMTP on TCP port 25 - win_firewall_rule: - name: SMTP - localport: 25 - action: allow - direction: in - protocol: tcp - state: present - enabled: yes - -- name: Firewall rule to allow RDP on TCP port 3389 - win_firewall_rule: - name: Remote Desktop - localport: 3389 - action: allow - direction: in - protocol: tcp - profiles: private - state: present - enabled: yes - -- name: Firewall rule to be created for application group - win_firewall_rule: - name: SMTP - group: application - localport: 25 - action: allow - direction: in - protocol: tcp - state: present - enabled: yes - -- name: Firewall rule to allow port range - win_firewall_rule: - name: Sample port range - localport: 5000-5010 - action: allow - direction: in - protocol: tcp - state: present - enabled: yes - -- name: Firewall rule to allow ICMP v4 echo (ping) - win_firewall_rule: - name: ICMP Allow incoming V4 echo request - enabled: yes - state: present - profiles: private - action: allow - direction: in - protocol: icmpv4 - icmp_type_code: - - '8:*' - -- name: Firewall rule to alloc ICMP v4 on all type codes - win_firewall_rule: - name: ICMP Allow incoming V4 echo request - enabled: yes - state: present - profiles: private - action: allow - direction: in - protocol: icmpv4 - icmp_type_code: '*' -''' diff --git a/lib/ansible/modules/windows/win_format.ps1 b/lib/ansible/modules/windows/win_format.ps1 deleted file mode 100644 index b5fd3ae038..0000000000 --- a/lib/ansible/modules/windows/win_format.ps1 +++ /dev/null @@ -1,200 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic -#AnsibleRequires -OSVersion 6.2 - -Set-StrictMode -Version 2 - -$ErrorActionPreference = "Stop" - -$spec = @{ - options = @{ - drive_letter = @{ type = "str" } - path = @{ type = "str" } - label = @{ type = "str" } - new_label = @{ type = "str" } - file_system = @{ type = "str"; choices = "ntfs", "refs", "exfat", "fat32", "fat" } - allocation_unit_size = @{ type = "int" } - large_frs = @{ type = "bool" } - full = @{ type = "bool"; default = $false } - compress = @{ type = "bool" } - integrity_streams = @{ type = "bool" } - force = @{ type = "bool"; default = $false } - } - mutually_exclusive = @( - ,@('drive_letter', 'path', 'label') - ) - required_one_of = @( - ,@('drive_letter', 'path', 'label') - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$drive_letter = $module.Params.drive_letter -$path = $module.Params.path -$label = $module.Params.label -$new_label = $module.Params.new_label -$file_system = $module.Params.file_system -$allocation_unit_size = $module.Params.allocation_unit_size -$large_frs = $module.Params.large_frs -$full_format = $module.Params.full -$compress_volume = $module.Params.compress -$integrity_streams = $module.Params.integrity_streams -$force_format = $module.Params.force - -# Some pre-checks -if ($null -ne $drive_letter -and $drive_letter -notmatch "^[a-zA-Z]$") { - $module.FailJson("The parameter drive_letter should be a single character A-Z") -} -if ($integrity_streams -eq $true -and $file_system -ne "refs") { - $module.FailJson("Integrity streams can be enabled only on ReFS volumes. You specified: $($file_system)") -} -if ($compress_volume -eq $true) { - if ($file_system -eq "ntfs") { - if ($null -ne $allocation_unit_size -and $allocation_unit_size -gt 4096) { - $module.FailJson("NTFS compression is not supported for allocation unit sizes above 4096") - } - } - else { - $module.FailJson("Compression can be enabled only on NTFS volumes. You specified: $($file_system)") - } -} - -function Get-AnsibleVolume { - param( - $DriveLetter, - $Path, - $Label - ) - - if ($null -ne $DriveLetter) { - try { - $volume = Get-Volume -DriveLetter $DriveLetter - } catch { - $module.FailJson("There was an error retrieving the volume using drive_letter $($DriveLetter): $($_.Exception.Message)", $_) - } - } - elseif ($null -ne $Path) { - try { - $volume = Get-Volume -Path $Path - } catch { - $module.FailJson("There was an error retrieving the volume using path $($Path): $($_.Exception.Message)", $_) - } - } - elseif ($null -ne $Label) { - try { - $volume = Get-Volume -FileSystemLabel $Label - } catch { - $module.FailJson("There was an error retrieving the volume using label $($Label): $($_.Exception.Message)", $_) - } - } - else { - $module.FailJson("Unable to locate volume: drive_letter, path and label were not specified") - } - - return $volume -} - -function Format-AnsibleVolume { - param( - $Path, - $Label, - $FileSystem, - $Full, - $UseLargeFRS, - $Compress, - $SetIntegrityStreams, - $AllocationUnitSize - ) - $parameters = @{ - Path = $Path - Full = $Full - } - if ($null -ne $UseLargeFRS) { - $parameters.Add("UseLargeFRS", $UseLargeFRS) - } - if ($null -ne $SetIntegrityStreams) { - $parameters.Add("SetIntegrityStreams", $SetIntegrityStreams) - } - if ($null -ne $Compress){ - $parameters.Add("Compress", $Compress) - } - if ($null -ne $Label) { - $parameters.Add("NewFileSystemLabel", $Label) - } - if ($null -ne $FileSystem) { - $parameters.Add("FileSystem", $FileSystem) - } - if ($null -ne $AllocationUnitSize) { - $parameters.Add("AllocationUnitSize", $AllocationUnitSize) - } - - Format-Volume @parameters -Confirm:$false | Out-Null - -} - -$ansible_volume = Get-AnsibleVolume -DriveLetter $drive_letter -Path $path -Label $label -$ansible_file_system = $ansible_volume.FileSystem -$ansible_volume_size = $ansible_volume.Size -$ansible_volume_alu = (Get-CimInstance -ClassName Win32_Volume -Filter "DeviceId = '$($ansible_volume.path.replace('\','\\'))'" -Property BlockSize).BlockSize - -$ansible_partition = Get-Partition -Volume $ansible_volume - -if (-not $force_format -and $null -ne $allocation_unit_size -and $ansible_volume_alu -ne 0 -and $null -ne $ansible_volume_alu -and $allocation_unit_size -ne $ansible_volume_alu) { - $module.FailJson("Force format must be specified since target allocation unit size: $($allocation_unit_size) is different from the current allocation unit size of the volume: $($ansible_volume_alu)") -} - -foreach ($access_path in $ansible_partition.AccessPaths) { - if ($access_path -ne $Path) { - if ($null -ne $file_system -and - -not [string]::IsNullOrEmpty($ansible_file_system) -and - $file_system -ne $ansible_file_system) - { - if (-not $force_format) - { - $no_files_in_volume = (Get-ChildItem -LiteralPath $access_path -ErrorAction SilentlyContinue | Measure-Object).Count -eq 0 - if($no_files_in_volume) - { - $module.FailJson("Force format must be specified since target file system: $($file_system) is different from the current file system of the volume: $($ansible_file_system.ToLower())") - } - else - { - $module.FailJson("Force format must be specified to format non-pristine volumes") - } - } - } - else - { - $pristine = -not $force_format - } - } -} - -if ($force_format) { - if (-not $module.CheckMode) { - Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size - } - $module.Result.changed = $true -} -else { - if ($pristine) { - if ($null -eq $new_label) { - $new_label = $ansible_volume.FileSystemLabel - } - # Conditions for formatting - if ($ansible_volume_size -eq 0 -or - $ansible_volume.FileSystemLabel -ne $new_label) { - if (-not $module.CheckMode) { - Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size - } - $module.Result.changed = $true - } - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_format.py b/lib/ansible/modules/windows/win_format.py deleted file mode 100644 index f8f18ed7b7..0000000000 --- a/lib/ansible/modules/windows/win_format.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com> -# 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_format -version_added: '2.8' -short_description: Formats an existing volume or a new volume on an existing partition on Windows -description: - - The M(win_format) module formats an existing volume or a new volume on an existing partition on Windows -options: - drive_letter: - description: - - Used to specify the drive letter of the volume to be formatted. - type: str - path: - description: - - Used to specify the path to the volume to be formatted. - type: str - label: - description: - - Used to specify the label of the volume to be formatted. - type: str - new_label: - description: - - Used to specify the new file system label of the formatted volume. - type: str - file_system: - description: - - Used to specify the file system to be used when formatting the target volume. - type: str - choices: [ ntfs, refs, exfat, fat32, fat ] - allocation_unit_size: - description: - - Specifies the cluster size to use when formatting the volume. - - If no cluster size is specified when you format a partition, defaults are selected based on - the size of the partition. - - This value must be a multiple of the physical sector size of the disk. - type: int - large_frs: - description: - - Specifies that large File Record System (FRS) should be used. - type: bool - compress: - description: - - Enable compression on the resulting NTFS volume. - - NTFS compression is not supported where I(allocation_unit_size) is more than 4096. - type: bool - integrity_streams: - description: - - Enable integrity streams on the resulting ReFS volume. - type: bool - full: - description: - - A full format writes to every sector of the disk, takes much longer to perform than the - default (quick) format, and is not recommended on storage that is thinly provisioned. - - Specify C(true) for full format. - type: bool - force: - description: - - Specify if formatting should be forced for volumes that are not created from new partitions - or if the source and target file system are different. - type: bool -notes: - - Microsoft Windows Server 2012 or Microsoft Windows 8 or newer is required to use this module. To check if your system is compatible, see - U(https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version). - - One of three parameters (I(drive_letter), I(path) and I(label)) are mandatory to identify the target - volume but more than one cannot be specified at the same time. - - This module is idempotent if I(force) is not specified and file system labels remain preserved. - - For more information, see U(https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/format-msft-volume) -seealso: - - module: win_disk_facts - - module: win_partition -author: - - Varun Chopra (@chopraaa) <v@chopraaa.com> -''' - -EXAMPLES = r''' -- name: Create a partition with drive letter D and size 5 GiB - win_partition: - drive_letter: D - partition_size: 5 GiB - disk_number: 1 - -- name: Full format the newly created partition as NTFS and label it - win_format: - drive_letter: D - file_system: NTFS - new_label: Formatted - full: True -''' - -RETURN = r''' -# -''' diff --git a/lib/ansible/modules/windows/win_hosts.ps1 b/lib/ansible/modules/windows/win_hosts.ps1 deleted file mode 100644 index 9e617c6664..0000000000 --- a/lib/ansible/modules/windows/win_hosts.ps1 +++ /dev/null @@ -1,257 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Micah Hunsberger (@mhunsber) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -Set-StrictMode -Version 2 -$ErrorActionPreference = "Stop" - -$spec = @{ - options = @{ - state = @{ type = "str"; choices = "absent", "present"; default = "present" } - aliases = @{ type = "list"; elements = "str" } - canonical_name = @{ type = "str" } - ip_address = @{ type = "str" } - action = @{ type = "str"; choices = "add", "remove", "set"; default = "set" } - } - required_if = @(,@( "state", "present", @("canonical_name", "ip_address"))) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$state = $module.Params.state -$aliases = $module.Params.aliases -$canonical_name = $module.Params.canonical_name -$ip_address = $module.Params.ip_address -$action = $module.Params.action - -$tmp = [ipaddress]::None -if($ip_address -and -not [ipaddress]::TryParse($ip_address, [ref]$tmp)){ - $module.FailJson("win_hosts: Argument ip_address needs to be a valid ip address, but was $ip_address") -} -$ip_address_type = $tmp.AddressFamily - -$hosts_file = Get-Item -LiteralPath "$env:SystemRoot\System32\drivers\etc\hosts" - -Function Get-CommentIndex($line) { - $c_index = $line.IndexOf('#') - if($c_index -lt 0) { - $c_index = $line.Length - } - return $c_index -} - -Function Get-HostEntryParts($line) { - $success = $true - $c_index = Get-CommentIndex -line $line - $pure_line = $line.Substring(0,$c_index).Trim() - $bits = $pure_line -split "\s+" - if($bits.Length -lt 2){ - return @{ - success = $false - ip_address = "" - ip_type = "" - canonical_name = "" - aliases = @() - } - } - $ip_obj = [ipaddress]::None - if(-not [ipaddress]::TryParse($bits[0], [ref]$ip_obj) ){ - $success = $false - } - $cname = $bits[1] - $als = New-Object string[] ($bits.Length - 2) - [array]::Copy($bits, 2, $als, 0, $als.Length) - return @{ - success = $success - ip_address = $ip_obj.IPAddressToString - ip_type = $ip_obj.AddressFamily - canonical_name = $cname - aliases = $als - } -} - -Function Find-HostName($line, $name) { - $c_idx = Get-CommentIndex -line $line - $re = New-Object regex ("\s+$($name.Replace('.',"\."))(\s|$)", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) - $match = $re.Match($line, 0, $c_idx) - return $match -} - -Function Remove-HostEntry($list, $idx) { - $module.Result.changed = $true - $list.RemoveAt($idx) -} - -Function Add-HostEntry($list, $cname, $aliases, $ip) { - $module.Result.changed = $true - $line = "$ip $cname $($aliases -join ' ')" - $list.Add($line) | Out-Null -} - -Function Remove-HostnamesFromEntry($list, $idx, $aliases) { - $line = $list[$idx] - $line_removed = $false - - foreach($name in $aliases){ - $match = Find-HostName -line $line -name $name - if($match.Success){ - $line = $line.Remove($match.Index + 1, $match.Length -1) - # was this the last alias? (check for space characters after trimming) - if($line.Substring(0,(Get-CommentIndex -line $line)).Trim() -inotmatch "\s") { - $list.RemoveAt($idx) - $line_removed = $true - # we're done - return @{ - line_removed = $line_removed - } - } - } - } - if($line -ne $list[$idx]){ - $module.Result.changed = $true - $list[$idx] = $line - } - return @{ - line_removed = $line_removed - } -} - -Function Add-AliasesToEntry($list, $idx, $aliases) { - $line = $list[$idx] - foreach($name in $aliases){ - $match = Find-HostName -line $line -name $name - if(-not $match.Success) { - # just add the alias before the comment - $line = $line.Insert((Get-CommentIndex -line $line), " $name ") - } - } - if($line -ne $list[$idx]){ - $module.Result.changed = $true - $list[$idx] = $line - } -} - -$hosts_lines = New-Object System.Collections.ArrayList - -Get-Content -LiteralPath $hosts_file.FullName | ForEach-Object { $hosts_lines.Add($_) } | Out-Null -$module.Diff.before = ($hosts_lines -join "`n") + "`n" - -if ($state -eq 'absent') { - # go through and remove canonical_name and ip - for($idx = 0; $idx -lt $hosts_lines.Count; $idx++) { - $entry = $hosts_lines[$idx] - # skip comment lines - if(-not $entry.Trim().StartsWith('#')) { - $entry_parts = Get-HostEntryParts -line $entry - if($entry_parts.success) { - if(-not $ip_address -or $entry_parts.ip_address -eq $ip_address) { - if(-not $canonical_name -or $entry_parts.canonical_name -eq $canonical_name) { - if(Remove-HostEntry -list $hosts_lines -idx $idx){ - # keep index correct if we removed the line - $idx = $idx - 1 - } - } - } - } - } - } -} -if($state -eq 'present') { - $entry_idx = -1 - $aliases_to_keep = @() - # go through lines, find the entry and determine what to remove based on action - for($idx = 0; $idx -lt $hosts_lines.Count; $idx++) { - $entry = $hosts_lines[$idx] - # skip comment lines - if(-not $entry.Trim().StartsWith('#')) { - $entry_parts = Get-HostEntryParts -line $entry - if($entry_parts.success) { - $aliases_to_remove = @() - if($entry_parts.ip_address -eq $ip_address) { - if($entry_parts.canonical_name -eq $canonical_name) { - $entry_idx = $idx - - if($action -eq 'set') { - $aliases_to_remove = $entry_parts.aliases | Where-Object { $aliases -notcontains $_ } - } elseif($action -eq 'remove') { - $aliases_to_remove = $aliases - } - } else { - # this is the right ip_address, but not the cname we were looking for. - # we need to make sure none of aliases or canonical_name exist for this entry - # since the given canonical_name should be an A/AAAA record, - # and aliases should be cname records for the canonical_name. - $aliases_to_remove = $aliases + $canonical_name - } - } else { - # this is not the ip_address we are looking for - if ($ip_address_type -eq $entry_parts.ip_type) { - if ($entry_parts.canonical_name -eq $canonical_name) { - Remove-HostEntry -list $hosts_lines -idx $idx - $idx = $idx - 1 - if ($action -ne "set") { - # keep old aliases intact - $aliases_to_keep += $entry_parts.aliases | Where-Object { ($aliases + $aliases_to_keep + $canonical_name) -notcontains $_ } - } - } elseif ($action -eq "remove") { - $aliases_to_remove = $canonical_name - } elseif ($aliases -contains $entry_parts.canonical_name) { - Remove-HostEntry -list $hosts_lines -idx $idx - $idx = $idx - 1 - if ($action -eq "add") { - # keep old aliases intact - $aliases_to_keep += $entry_parts.aliases | Where-Object { ($aliases + $aliases_to_keep + $canonical_name) -notcontains $_ } - } - } else { - $aliases_to_remove = $aliases + $canonical_name - } - } else { - # TODO: Better ipv6 support. There is odd behavior for when an alias can be used for both ipv6 and ipv4 - } - } - - if($aliases_to_remove) { - if((Remove-HostnamesFromEntry -list $hosts_lines -idx $idx -aliases $aliases_to_remove).line_removed) { - $idx = $idx - 1 - } - } - } - } - } - - if($entry_idx -ge 0) { - $aliases_to_add = @() - $entry_parts = Get-HostEntryParts -line $hosts_lines[$entry_idx] - if($action -eq 'remove') { - $aliases_to_add = $aliases_to_keep | Where-Object { $entry_parts.aliases -notcontains $_ } - } else { - $aliases_to_add = ($aliases + $aliases_to_keep) | Where-Object { $entry_parts.aliases -notcontains $_ } - } - - if($aliases_to_add) { - Add-AliasesToEntry -list $hosts_lines -idx $entry_idx -aliases $aliases_to_add - } - } else { - # add the entry at the end - if($action -eq 'remove') { - if($aliases_to_keep) { - Add-HostEntry -list $hosts_lines -ip $ip_address -cname $canonical_name -aliases $aliases_to_keep - } else { - Add-HostEntry -list $hosts_lines -ip $ip_address -cname $canonical_name - } - } else { - Add-HostEntry -list $hosts_lines -ip $ip_address -cname $canonical_name -aliases ($aliases + $aliases_to_keep) - } - } -} - -$module.Diff.after = ($hosts_lines -join "`n") + "`n" -if( $module.Result.changed -and -not $module.CheckMode ) { - Set-Content -LiteralPath $hosts_file.FullName -Value $hosts_lines -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_hosts.py b/lib/ansible/modules/windows/win_hosts.py deleted file mode 100644 index 9fd2d1d10d..0000000000 --- a/lib/ansible/modules/windows/win_hosts.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Micah Hunsberger (@mhunsber) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_hosts -version_added: '2.8' -short_description: Manages hosts file entries on Windows. -description: - - Manages hosts file entries on Windows. - - Maps IPv4 or IPv6 addresses to canonical names. - - Adds, removes, or sets cname records for ip and hostname pairs. - - Modifies %windir%\\system32\\drivers\\etc\\hosts. -options: - state: - description: - - Whether the entry should be present or absent. - - If only I(canonical_name) is provided when C(state=absent), then - all hosts entries with the canonical name of I(canonical_name) - will be removed. - - If only I(ip_address) is provided when C(state=absent), then all - hosts entries with the ip address of I(ip_address) will be removed. - - If I(ip_address) and I(canonical_name) are both omitted when - C(state=absent), then all hosts entries will be removed. - choices: - - absent - - present - default: present - type: str - canonical_name: - description: - - A canonical name for the host entry. - - required for C(state=present). - type: str - ip_address: - description: - - The ip address for the host entry. - - Can be either IPv4 (A record) or IPv6 (AAAA record). - - Required for C(state=present). - type: str - aliases: - description: - - A list of additional names (cname records) for the host entry. - - Only applicable when C(state=present). - type: list - action: - choices: - - add - - remove - - set - description: - - Controls the behavior of I(aliases). - - Only applicable when C(state=present). - - If C(add), each alias in I(aliases) will be added to the host entry. - - If C(set), each alias in I(aliases) will be added to the host entry, - and other aliases will be removed from the entry. - default: set - type: str -author: - - Micah Hunsberger (@mhunsber) -notes: - - Each canonical name can only be mapped to one IPv4 and one IPv6 address. - If I(canonical_name) is provided with C(state=present) and is found - to be mapped to another IP address that is the same type as, but unique - from I(ip_address), then I(canonical_name) and all I(aliases) will - be removed from the entry and added to an entry with the provided IP address. - - Each alias can only be mapped to one canonical name. If I(aliases) is provided - with C(state=present) and an alias is found to be mapped to another canonical - name, then the alias will be removed from the entry and either added to or removed - from (depending on I(action)) an entry with the provided canonical name. -seealso: - - module: win_template - - module: win_file - - module: win_copy -''' - -EXAMPLES = r''' -- name: Add 127.0.0.1 as an A record for localhost - win_hosts: - state: present - canonical_name: localhost - ip_address: 127.0.0.1 - -- name: Add ::1 as an AAAA record for localhost - win_hosts: - state: present - canonical_name: localhost - ip_address: '::1' - -- name: Remove 'bar' and 'zed' from the list of aliases for foo (192.168.1.100) - win_hosts: - state: present - canoncial_name: foo - ip_address: 192.168.1.100 - action: remove - aliases: - - bar - - zed - -- name: Remove hosts entries with canonical name 'bar' - win_hosts: - state: absent - canonical_name: bar - -- name: Remove 10.2.0.1 from the list of hosts - win_hosts: - state: absent - ip_address: 10.2.0.1 - -- name: Ensure all name resolution is handled by DNS - win_hosts: - state: absent -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_hotfix.ps1 b/lib/ansible/modules/windows/win_hotfix.ps1 deleted file mode 100644 index 1681763203..0000000000 --- a/lib/ansible/modules/windows/win_hotfix.ps1 +++ /dev/null @@ -1,239 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$hotfix_kb = Get-AnsibleParam -obj $params -name "hotfix_kb" -type "str" -$hotfix_identifier = Get-AnsibleParam -obj $params -name "hotfix_identifier" -type "str" -$state = Get-AnsibleParam -obj $params -name "state" -type "state" -default "present" -validateset "absent","present" -$source = Get-AnsibleParam -obj $params -name "source" -type "path" - -$result = @{ - changed = $false - reboot_required = $false -} - -if (Get-Module -Name DISM -ListAvailable) { - Import-Module -Name DISM -} else { - # Server 2008 R2 doesn't have the DISM module installed on the path, check the Windows ADK path - $adk_root = [System.Environment]::ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\Windows Kits\*\Assessment and Deployment Kit\Deployment Tools\amd64\DISM") - if (Test-Path -Path $adk_root) { - Import-Module -Name (Get-Item -Path $adk_root).FullName - } else { - Fail-Json $result "The DISM PS module needs to be installed, this can be done through the windows-adk chocolately package" - } -} - - -Function Extract-MSU($msu) { - $temp_path = [IO.Path]::GetTempPath() - $temp_foldername = [Guid]::NewGuid() - $output_path = Join-Path -Path $temp_path -ChildPath $temp_foldername - New-Item -Path $output_path -ItemType Directory | Out-Null - - $expand_args = @($msu, $output_path, "-F:*") - - try { - &expand.exe $expand_args | Out-NUll - } catch { - Fail-Json $result "failed to run expand.exe $($expand_args): $($_.Exception.Message)" - } - if ($LASTEXITCODE -ne 0) { - Fail-Json $result "failed to run expand.exe $($expand_args): RC = $LASTEXITCODE" - } - - return $output_path -} - -Function Get-HotfixMetadataFromName($name) { - try { - $dism_package_info = Get-WindowsPackage -Online -PackageName $name - } catch { - # build a basic stub for a missing result - $dism_package_info = @{ - PackageState = "NotPresent" - Description = "" - PackageName = $name - } - } - - if ($dism_package_info.Description -match "(KB\d*)") { - $hotfix_kb = $Matches[0] - } else { - $hotfix_kb = "UNKNOWN" - } - - $metadata = @{ - name = $dism_package_info.PackageName - state = $dism_package_info.PackageState - kb = $hotfix_kb - } - - return $metadata -} - -Function Get-HotfixMetadataFromFile($extract_path) { - # MSU contents https://support.microsoft.com/en-us/help/934307/description-of-the-windows-update-standalone-installer-in-windows - $metadata_path = Get-ChildItem -Path $extract_path | Where-Object { $_.Extension -eq ".xml" } - if ($null -eq $metadata_path) { - Fail-Json $result "failed to get metadata xml inside MSU file, cannot get hotfix metadata required for this task" - } - [xml]$xml = Get-Content -Path $metadata_path.FullName - - $cab_source_filename = $xml.unattend.servicing.package.source.GetAttribute("location") - $cab_source_filename = Split-Path -Path $cab_source_filename -Leaf - $cab_file = Join-Path -Path $extract_path -ChildPath $cab_source_filename - - try { - $dism_package_info = Get-WindowsPackage -Online -PackagePath $cab_file - } catch { - Fail-Json $result "failed to get DISM package metadata from path $($extract_path): $($_.Exception.Message)" - } - if ($dism_package_info.Applicable -eq $false) { - Fail-Json $result "hotfix package is not applicable for this server" - } - - $package_properties_path = Get-ChildItem -Path $extract_path | Where-Object { $_.Extension -eq ".txt" } - if ($null -eq $package_properties_path) { - $hotfix_kb = "UNKNOWN" - } else { - $package_ini = Get-Content -Path $package_properties_path.FullName - $entry = $package_ini | Where-Object { $_.StartsWith("KB Article Number") } - if ($null -eq $entry) { - $hotfix_kb = "UNKNOWN" - } else { - $hotfix_kb = ($entry -split '=')[-1] - $hotfix_kb = "KB$($hotfix_kb.Substring(1, $hotfix_kb.Length - 2))" - } - } - - $metadata = @{ - path = $cab_file - name = $dism_package_info.PackageName - state = $dism_package_info.PackageState - kb = $hotfix_kb - } - - return $metadata -} - -Function Get-HotfixMetadataFromKB($kb) { - # I really hate doing it this way - $packages = Get-WindowsPackage -Online - $identifier = $packages | Where-Object { $_.PackageName -like "*$kb*" } - - if ($null -eq $identifier) { - # still haven't found the KB, need to loop through the results and check the description - foreach ($package in $packages) { - $raw_metadata = Get-HotfixMetadataFromName -name $package.PackageName - if ($raw_metadata.kb -eq $kb) { - $identifier = $raw_metadata - break - } - } - - # if we still haven't found the package then we need to throw an error - if ($null -eq $metadata) { - Fail-Json $result "failed to get DISM package from KB, to continue specify hotfix_identifier instead" - } - } else { - $metadata = Get-HotfixMetadataFromName -name $identifier.PackageName - } - - return $metadata -} - -if ($state -eq "absent") { - # uninstall hotfix - # this is a pretty poor way of doing this, is there a better way? - - if ($null -ne $hotfix_identifier) { - $hotfix_metadata = Get-HotfixMetadataFromName -name $hotfix_identifier - } elseif ($null -ne $hotfix_kb) { - $hotfix_install_info = Get-Hotfix -Id $hotfix_kb -ErrorAction SilentlyContinue - if ($null -ne $hotfix_install_info) { - $hotfix_metadata = Get-HotfixMetadataFromKB -kb $hotfix_kb - } else { - $hotfix_metadata = @{state = "NotPresent"} - } - } else { - Fail-Json $result "either hotfix_identifier or hotfix_kb needs to be set when state=absent" - } - - # how do we want to deal with the other states? - if ($hotfix_metadata.state -eq "UninstallPending") { - $result.identifier = $hotfix_metadata.name - $result.kb = $hotfix_metadata.kb - $result.reboot_required = $true - } elseif ($hotfix_metadata.state -eq "Installed") { - $result.identifier = $hotfix_metadata.name - $result.kb = $hotfix_metadata.kb - - if (-not $check_mode) { - try { - $remove_result = Remove-WindowsPackage -Online -PackageName $hotfix_metadata.name -NoRestart - } catch { - Fail-Json $result "failed to remove package $($hotfix_metadata.name): $($_.Exception.Message)" - } - $result.reboot_required = $remove_Result.RestartNeeded - } - - $result.changed = $true - } -} else { - if ($null -eq $source) { - Fail-Json $result "source must be set when state=present" - } - if (-not (Test-Path -Path $source -PathType Leaf)) { - Fail-Json $result "the path set for source $source does not exist or is not a file" - } - - # while we do extract the file in check mode we need to do so for valid checking - $extract_path = Extract-MSU -msu $source - try { - $hotfix_metadata = Get-HotfixMetadataFromFile -extract_path $extract_path - - # validate the hotfix matches if the hotfix id has been passed in - if ($null -ne $hotfix_identifier) { - if ($hotfix_metadata.name -ne $hotfix_identifier) { - Fail-Json $result "the hotfix identifier $hotfix_identifier does not match with the source msu identifier $($hotfix_metadata.name), please omit or specify the correct identifier to continue" - } - } - if ($null -ne $hotfix_kb) { - if ($hotfix_metadata.kb -ne $hotfix_kb) { - Fail-Json $result "the hotfix KB $hotfix_kb does not match with the source msu KB $($hotfix_metadata.kb), please omit or specify the correct KB to continue" - } - } - - $result.identifier = $hotfix_metadata.name - $result.kb = $hotfix_metadata.kb - - # how do we want to deal with other states - if ($hotfix_metadata.state -eq "InstallPending") { - # return the reboot required flag, should we fail here instead - $result.reboot_required = $true - } elseif ($hotfix_metadata.state -ne "Installed") { - if (-not $check_mode) { - try { - $install_result = Add-WindowsPackage -Online -PackagePath $hotfix_metadata.path -NoRestart - } catch { - Fail-Json $result "failed to add windows package from path $($hotfix_metadata.path): $($_.Exception.Message)" - } - $result.reboot_required = $install_result.RestartNeeded - } - $result.changed = $true - } - } finally { - Remove-Item -Path $extract_path -Force -Recurse - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_hotfix.py b/lib/ansible/modules/windows/win_hotfix.py deleted file mode 100644 index f5f3bb9c05..0000000000 --- a/lib/ansible/modules/windows/win_hotfix.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub, actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_hotfix -version_added: '2.4' -short_description: Install and uninstalls Windows hotfixes -description: -- Install, uninstall a Windows hotfix. -options: - hotfix_identifier: - description: - - The name of the hotfix as shown in DISM, see examples for details. - - This or C(hotfix_kb) MUST be set when C(state=absent). - - If C(state=present) then the hotfix at C(source) will be validated - against this value, if it does not match an error will occur. - - You can get the identifier by running - 'Get-WindowsPackage -Online -PackagePath path-to-cab-in-msu' after - expanding the msu file. - type: str - hotfix_kb: - description: - - The name of the KB the hotfix relates to, see examples for details. - - This or C(hotfix_identifier) MUST be set when C(state=absent). - - If C(state=present) then the hotfix at C(source) will be validated - against this value, if it does not match an error will occur. - - Because DISM uses the identifier as a key and doesn't refer to a KB in - all cases it is recommended to use C(hotfix_identifier) instead. - type: str - state: - description: - - Whether to install or uninstall the hotfix. - - When C(present), C(source) MUST be set. - - When C(absent), C(hotfix_identifier) or C(hotfix_kb) MUST be set. - type: str - default: present - choices: [ absent, present ] - source: - description: - - The path to the downloaded hotfix .msu file. - - This MUST be set if C(state=present) and MUST be a .msu hotfix file. - type: path -notes: -- This must be run on a host that has the DISM powershell module installed and - a Powershell version >= 4. -- This module is installed by default on Windows 8 and Server 2012 and newer. -- You can manually install this module on Windows 7 and Server 2008 R2 by - installing the Windows ADK - U(https://developer.microsoft.com/en-us/windows/hardware/windows-assessment-deployment-kit), - see examples to see how to do it with chocolatey. -- You can download hotfixes from U(https://www.catalog.update.microsoft.com/Home.aspx). -seealso: -- module: win_package -- module: win_updates -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Install Windows ADK with DISM for Server 2008 R2 - win_chocolatey: - name: windows-adk - version: 8.100.26866.0 - state: present - install_args: /features OptionId.DeploymentTools - -- name: Install hotfix without validating the KB and Identifier - win_hotfix: - source: C:\temp\windows8.1-kb3172729-x64_e8003822a7ef4705cbb65623b72fd3cec73fe222.msu - state: present - register: hotfix_install - -- win_reboot: - when: hotfix_install.reboot_required - -- name: Install hotfix validating KB - win_hotfix: - hotfix_kb: KB3172729 - source: C:\temp\windows8.1-kb3172729-x64_e8003822a7ef4705cbb65623b72fd3cec73fe222.msu - state: present - register: hotfix_install - -- win_reboot: - when: hotfix_install.reboot_required - -- name: Install hotfix validating Identifier - win_hotfix: - hotfix_identifier: Package_for_KB3172729~31bf3856ad364e35~amd64~~6.3.1.0 - source: C:\temp\windows8.1-kb3172729-x64_e8003822a7ef4705cbb65623b72fd3cec73fe222.msu - state: present - register: hotfix_install - -- win_reboot: - when: hotfix_install.reboot_required - -- name: Uninstall hotfix with Identifier - win_hotfix: - hotfix_identifier: Package_for_KB3172729~31bf3856ad364e35~amd64~~6.3.1.0 - state: absent - register: hotfix_uninstall - -- win_reboot: - when: hotfix_uninstall.reboot_required - -- name: Uninstall hotfix with KB (not recommended) - win_hotfix: - hotfix_kb: KB3172729 - state: absent - register: hotfix_uninstall - -- win_reboot: - when: hotfix_uninstall.reboot_required -''' - -RETURN = r''' -identifier: - description: The DISM identifier for the hotfix. - returned: success - type: str - sample: Package_for_KB3172729~31bf3856ad364e35~amd64~~6.3.1.0 -kb: - description: The KB the hotfix relates to. - returned: success - type: str - sample: KB3172729 -reboot_required: - description: Whether a reboot is required for the install or uninstall to - finalise. - returned: success - type: str - sample: true -''' diff --git a/lib/ansible/modules/windows/win_http_proxy.ps1 b/lib/ansible/modules/windows/win_http_proxy.ps1 deleted file mode 100644 index 04ea473b99..0000000000 --- a/lib/ansible/modules/windows/win_http_proxy.ps1 +++ /dev/null @@ -1,267 +0,0 @@ -#!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() - diff --git a/lib/ansible/modules/windows/win_http_proxy.py b/lib/ansible/modules/windows/win_http_proxy.py deleted file mode 100644 index d9abb858e2..0000000000 --- a/lib/ansible/modules/windows/win_http_proxy.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/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''' -# -''' diff --git a/lib/ansible/modules/windows/win_iis_virtualdirectory.ps1 b/lib/ansible/modules/windows/win_iis_virtualdirectory.ps1 deleted file mode 100644 index 7d63897994..0000000000 --- a/lib/ansible/modules/windows/win_iis_virtualdirectory.ps1 +++ /dev/null @@ -1,99 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -$params = Parse-Args $args -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$site = Get-AnsibleParam -obj $params -name "site" -type "str" -failifempty $true -$application = Get-AnsibleParam -obj $params -name "application" -type "str" -$physical_path = Get-AnsibleParam -obj $params -name "physical_path" -type "str" -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present" - -# Ensure WebAdministration module is loaded -if ($null -eq (Get-Module "WebAdministration" -ErrorAction SilentlyContinue)) { - Import-Module WebAdministration -} - -# Result -$result = @{ - directory = @{} - changed = $false -}; - -# Construct path -$directory_path = if($application) { - "IIS:\Sites\$($site)\$($application)\$($name)" -} else { - "IIS:\Sites\$($site)\$($name)" -} - -# Directory info -$directory = if($application) { - Get-WebVirtualDirectory -Site $site -Name $name -Application $application -} else { - Get-WebVirtualDirectory -Site $site -Name $name -} - -try { - # Add directory - If(($state -eq 'present') -and (-not $directory)) { - If (-not $physical_path) { - Fail-Json -obj $result -message "missing required arguments: physical_path" - } - If (-not (Test-Path $physical_path)) { - Fail-Json -obj $result -message "specified folder must already exist: physical_path" - } - - $directory_parameters = @{ - Site = $site - Name = $name - PhysicalPath = $physical_path - } - - If ($application) { - $directory_parameters.Application = $application - } - - $directory = New-WebVirtualDirectory @directory_parameters -Force - $result.changed = $true - } - - # Remove directory - If ($state -eq 'absent' -and $directory) { - Remove-Item $directory_path -Recurse -Force - $result.changed = $true - } - - $directory = Get-WebVirtualDirectory -Site $site -Name $name - If($directory) { - - # Change Physical Path if needed - if($physical_path) { - If (-not (Test-Path $physical_path)) { - Fail-Json -obj $result -message "specified folder must already exist: physical_path" - } - - $vdir_folder = Get-Item $directory.PhysicalPath - $folder = Get-Item $physical_path - If($folder.FullName -ne $vdir_folder.FullName) { - Set-ItemProperty $directory_path -name physicalPath -value $physical_path - $result.changed = $true - } - } - } -} catch { - Fail-Json $result $_.Exception.Message -} - -# Result -$directory = Get-WebVirtualDirectory -Site $site -Name $name -$result.directory = @{ - PhysicalPath = $directory.PhysicalPath -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_iis_virtualdirectory.py b/lib/ansible/modules/windows/win_iis_virtualdirectory.py deleted file mode 100644 index a8cc49e072..0000000000 --- a/lib/ansible/modules/windows/win_iis_virtualdirectory.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# 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_iis_virtualdirectory -version_added: "2.0" -short_description: Configures a virtual directory in IIS -description: - - Creates, Removes and configures a virtual directory in IIS. -options: - name: - description: - - The name of the virtual directory to create or remove. - type: str - required: yes - state: - description: - - Whether to add or remove the specified virtual directory. - - Removing will remove the virtual directory and all under it (Recursively). - type: str - choices: [ absent, present ] - default: present - site: - description: - - The site name under which the virtual directory is created or exists. - type: str - required: yes - application: - description: - - The application under which the virtual directory is created or exists. - type: str - physical_path: - description: - - The physical path to the folder in which the new virtual directory is created. - - The specified folder must already exist. - type: str -seealso: -- module: win_iis_webapplication -- module: win_iis_webapppool -- module: win_iis_webbinding -- module: win_iis_website -author: -- Henrik Wallström (@henrikwallstrom) -''' - -EXAMPLES = r''' -- name: Create a virtual directory if it does not exist - win_iis_virtualdirectory: - name: somedirectory - site: somesite - state: present - physical_path: C:\virtualdirectory\some - -- name: Remove a virtual directory if it exists - win_iis_virtualdirectory: - name: somedirectory - site: somesite - state: absent - -- name: Create a virtual directory on an application if it does not exist - win_iis_virtualdirectory: - name: somedirectory - site: somesite - application: someapp - state: present - physical_path: C:\virtualdirectory\some -''' diff --git a/lib/ansible/modules/windows/win_iis_webapplication.ps1 b/lib/ansible/modules/windows/win_iis_webapplication.ps1 deleted file mode 100644 index 92fdbbd9e5..0000000000 --- a/lib/ansible/modules/windows/win_iis_webapplication.ps1 +++ /dev/null @@ -1,138 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$site = Get-AnsibleParam -obj $params -name "site" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present" -$physical_path = Get-AnsibleParam -obj $params -name "physical_path" -type "str" -aliases "path" -$application_pool = Get-AnsibleParam -obj $params -name "application_pool" -type "str" -$connect_as = Get-AnsibleParam -obj $params -name 'connect_as' -type 'str' -validateset 'specific_user', 'pass_through' -$username = Get-AnsibleParam -obj $params -name "username" -type "str" -failifempty ($connect_as -eq 'specific_user') -$password = Get-AnsibleParam -obj $params -name "password" -type "str" -failifempty ($connect_as -eq 'specific_user') - -$result = @{ - application_pool = $application_pool - changed = $false - physical_path = $physical_path -} - -# Ensure WebAdministration module is loaded -if ($null -eq (Get-Module "WebAdministration" -ErrorAction SilentlyContinue)) { - Import-Module WebAdministration -} - -# Application info -$application = Get-WebApplication -Site $site -Name $name -$website = Get-Website -Name $site - -# Set ApplicationPool to current if not specified -if (!$application_pool) { - $application_pool = $website.applicationPool -} - -try { - # Add application - if (($state -eq 'present') -and (-not $application)) { - if (-not $physical_path) { - Fail-Json $result "missing required arguments: path" - } - if (-not (Test-Path -Path $physical_path)) { - Fail-Json $result "specified folder must already exist: path" - } - - $application_parameters = @{ - Name = $name - PhysicalPath = $physical_path - Site = $site - } - - if ($application_pool) { - $application_parameters.ApplicationPool = $application_pool - } - - if (-not $check_mode) { - $application = New-WebApplication @application_parameters -Force - } - $result.changed = $true - } - - # Remove application - if ($state -eq 'absent' -and $application) { - $application = Remove-WebApplication -Site $site -Name $name -WhatIf:$check_mode - $result.changed = $true - } - - $application = Get-WebApplication -Site $site -Name $name - if ($application) { - - # Change Physical Path if needed - if ($physical_path) { - if (-not (Test-Path -Path $physical_path)) { - Fail-Json $result "specified folder must already exist: path" - } - - $app_folder = Get-Item $application.PhysicalPath - $folder = Get-Item $physical_path - if ($folder.FullName -ne $app_folder.FullName) { - Set-ItemProperty "IIS:\Sites\$($site)\$($name)" -name physicalPath -value $physical_path -WhatIf:$check_mode - $result.changed = $true - } - } - - # Change Application Pool if needed - if ($application_pool) { - if ($application_pool -ne $application.applicationPool) { - Set-ItemProperty "IIS:\Sites\$($site)\$($name)" -name applicationPool -value $application_pool -WhatIf:$check_mode - $result.changed = $true - } - } - - # Change username and password if needed - $app_user = Get-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' - $app_pass = Get-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'password' - if ($connect_as -eq 'pass_through') { - if ($app_user -ne '') { - Clear-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' -WhatIf:$check_mode - $result.changed = $true - } - if ($app_pass -ne '') { - Clear-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'password' -WhatIf:$check_mode - $result.changed = $true - } - } elseif ($connect_as -eq 'specific_user') { - if ($app_user -ne $username) { - Set-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' -Value $username -WhatIf:$check_mode - $result.changed = $true - } - if ($app_pass -ne $password) { - Set-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'password' -Value $password -WhatIf:$check_mode - $result.changed = $true - } - } - } -} catch { - Fail-Json $result $_.Exception.Message -} - -# When in check-mode or on removal, this may fail -$application = Get-WebApplication -Site $site -Name $name -if ($application) { - $app_user = Get-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' - if ($app_user -eq '') { - $result.connect_as = 'pass_through' - } else { - $result.connect_as = 'specific_user' - } - - $result.physical_path = $application.PhysicalPath - $result.application_pool = $application.ApplicationPool -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_iis_webapplication.py b/lib/ansible/modules/windows/win_iis_webapplication.py deleted file mode 100644 index 2fad6a4807..0000000000 --- a/lib/ansible/modules/windows/win_iis_webapplication.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# 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_iis_webapplication -version_added: "2.0" -short_description: Configures IIS web applications -description: -- Creates, removes, and configures IIS web applications. -options: - name: - description: - - Name of the web application. - type: str - required: yes - site: - description: - - Name of the site on which the application is created. - type: str - required: yes - state: - description: - - State of the web application. - type: str - choices: [ absent, present ] - default: present - physical_path: - description: - - The physical path on the remote host to use for the new application. - - The specified folder must already exist. - type: str - application_pool: - description: - - The application pool in which the new site executes. - - If not specified, the application pool of the current website will be used. - type: str - connect_as: - description: - - The type of authentication to use for this application. Either C(pass_through) or C(specific_user) - - If C(pass_through), IIS will use the identity of the user or application pool identity to access the file system or network. - - If C(specific_user), IIS will use the credentials provided in I(username) and I(password) to access the file system or network. - type: str - choices: [pass_through, specific_user] - version_added: '2.10' - username: - description: - - Specifies the user name of an account that can access configuration files and content for this application. - - Required when I(connect_as) is set to C(specific_user). - type: str - version_added: '2.10' - password: - description: - - The password associated with I(username). - - Required when I(connect_as) is set to C(specific_user). - type: str - version_added: '2.10' -seealso: -- module: win_iis_virtualdirectory -- module: win_iis_webapppool -- module: win_iis_webbinding -- module: win_iis_website -author: -- Henrik Wallström (@henrikwallstrom) -''' - -EXAMPLES = r''' -- name: Add ACME webapplication on IIS. - win_iis_webapplication: - name: api - site: acme - state: present - physical_path: C:\apps\acme\api -''' - -RETURN = r''' -application_pool: - description: The used/implemented application_pool value. - returned: success - type: str - sample: DefaultAppPool -physical_path: - description: The used/implemented physical_path value. - returned: success - type: str - sample: C:\apps\acme\api -connect_as: - description: How IIS will try to authenticate to the physical_path. - returned: when the application exists - type: str - sample: specific_user -''' diff --git a/lib/ansible/modules/windows/win_iis_webapppool.ps1 b/lib/ansible/modules/windows/win_iis_webapppool.ps1 deleted file mode 100644 index 79167ba02e..0000000000 --- a/lib/ansible/modules/windows/win_iis_webapppool.ps1 +++ /dev/null @@ -1,307 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = 'Stop' - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateSet "started","restarted","stopped","absent","present" -$result = @{ - changed = $false - attributes = @{} - info = @{ - name = $name - state = $state - attributes = @{} - cpu = @{} - failure = @{} - processModel = @{} - recycling = @{ - periodicRestart = @{} - } - } -} - -# Stores the free form attributes for the module -$attributes = @{} -$input_attributes = Get-AnsibleParam -obj $params -name "attributes" -if ($input_attributes) { - if ($input_attributes -is [System.Collections.Hashtable]) { - # Uses dict style parameters, newer and recommended style - $attributes = $input_attributes - } else { - Fail-Json -obj $result -message "Using a string for the attributes parameter is not longer supported, please use a dict instead" - } -} -$result.attributes = $attributes - -Function Get-DotNetClassForAttribute($attribute_parent) { - switch ($attribute_parent) { - "attributes" { [Microsoft.Web.Administration.ApplicationPool] } - "cpu" { [Microsoft.Web.Administration.ApplicationPoolCpu] } - "failure" { [Microsoft.Web.Administration.ApplicationPoolFailure] } - "processModel" { [Microsoft.Web.Administration.ApplicationPoolProcessModel] } - "recycling" { [Microsoft.Web.Administration.ApplicationPoolRecycling] } - default { [Microsoft.Web.Administration.ApplicationPool] } - } -} - -Function Convert-CollectionToList($collection) { - $list = @() - - if ($collection -is [String]) { - $raw_list = $collection -split "," - foreach ($entry in $raw_list) { - $list += $entry.Trim() - } - } elseif ($collection -is [Microsoft.IIs.PowerShell.Framework.ConfigurationElement]) { - # the collection is the value from IIS itself, we need to conver accordingly - foreach ($entry in $collection.Collection) { - $list += $entry.Value.ToString() - } - } elseif ($collection -isnot [Array]) { - $list += $collection - } else { - $list = $collection - } - - return ,$list -} - -Function Compare-Values($current, $new) { - if ($null -eq $current) { - return $true - } - - if ($current -is [Array]) { - if ($new -isnot [Array]) { - return $true - } - - if ($current.Count -ne $new.Count) { - return $true - } - for ($i = 0; $i -lt $current.Count; $i++) { - if ($current[$i] -ne $new[$i]) { - return $true - } - } - } else { - if ($current -ne $new) { - return $true - } - } - return $false -} - -Function Convert-ToPropertyValue($pool, $attribute_key, $attribute_value) { - # Will convert the new value to the enum value expected and cast accordingly to the type - if ([bool]($attribute_value.PSobject.Properties -match "Value")) { - $attribute_value = $attribute_value.Value - } - $attribute_key_split = $attribute_key -split "\." - if ($attribute_key_split.Length -eq 1) { - $attribute_parent = "attributes" - $attribute_child = $attribute_key - $attribute_meta = $pool.Attributes | Where-Object { $_.Name -eq $attribute_child } - } elseif ($attribute_key_split.Length -gt 1) { - $attribute_parent = $attribute_key_split[0] - $attribute_key_split = $attribute_key_split[1..$($attribute_key_split.Length - 1)] - $parent = $pool.$attribute_parent - - foreach ($key in $attribute_key_split) { - $attribute_meta = $parent.Attributes | Where-Object { $_.Name -eq $key } - $parent = $parent.$key - if ($null -eq $attribute_meta) { - $attribute_meta = $parent - } - } - $attribute_child = $attribute_key_split[-1] - } - - if ($attribute_meta) { - if (($attribute_meta.PSObject.Properties.Name -eq "Collection").Count -gt 0) { - return ,(Convert-CollectionToList -collection $attribute_value) - } - $type = $attribute_meta.Schema.Type - $value = $attribute_value - if ($type -eq "enum") { - # Attempt to convert the value from human friendly to enum value - use existing value if we fail - $dot_net_class = Get-DotNetClassForAttribute -attribute_parent $attribute_parent - $enum_attribute_name = $attribute_child.Substring(0,1).ToUpper() + $attribute_child.Substring(1) - $enum = $dot_net_class.GetProperty($enum_attribute_name).PropertyType.FullName - if ($enum) { - $enum_values = [Enum]::GetValues($enum) - foreach ($enum_value in $enum_values) { - if ($attribute_value.GetType() -is $enum_value.GetType()) { - if ($enum_value -eq $attribute_value) { - $value = $enum_value - break - } - } else { - if ([System.String]$enum_value -eq [System.String]$attribute_value) { - $value = $enum_value - break - } - } - } - } - } - # Try and cast the variable using the chosen type, revert to the default if it fails - Set-Variable -Name casted_value -Value ($value -as ([type] $attribute_meta.TypeName)) - if ($null -eq $casted_value) { - $value - } else { - $casted_value - } - } else { - $attribute_value - } -} - -# Ensure WebAdministration module is loaded -if ($null -eq (Get-Module -Name "WebAdministration" -ErrorAction SilentlyContinue)) { - Import-Module WebAdministration - $web_admin_dll_path = Join-Path $env:SystemRoot system32\inetsrv\Microsoft.Web.Administration.dll - Add-Type -Path $web_admin_dll_path -} - -$pool = Get-Item -Path IIS:\AppPools\$name -ErrorAction SilentlyContinue -if ($state -eq "absent") { - # Remove pool if present - if ($pool) { - try { - Remove-WebAppPool -Name $name -WhatIf:$check_mode - } catch { - Fail-Json $result "Failed to remove Web App pool $($name): $($_.Exception.Message)" - } - $result.changed = $true - } -} else { - # Add pool if absent - if (-not $pool) { - if (-not $check_mode) { - try { - New-WebAppPool -Name $name > $null - } catch { - Fail-Json $result "Failed to create new Web App Pool $($name): $($_.Exception.Message)" - } - } - $result.changed = $true - # If in check mode this pool won't actually exists so skip it - if (-not $check_mode) { - $pool = Get-Item -Path IIS:\AppPools\$name - } - } - - # Cannot run the below in check mode if the pool did not always exist - if ($pool) { - # Modify pool based on parameters - foreach ($attribute in $attributes.GetEnumerator()) { - $attribute_key = $attribute.Name - $new_raw_value = $attribute.Value - $new_value = Convert-ToPropertyValue -pool $pool -attribute_key $attribute_key -attribute_value $new_raw_value - - $current_raw_value = Get-ItemProperty -Path IIS:\AppPools\$name -Name $attribute_key -ErrorAction SilentlyContinue - $current_value = Convert-ToPropertyValue -pool $pool -attribute_key $attribute_key -attribute_value $current_raw_value - - $changed = Compare-Values -current $current_value -new $new_value - if ($changed -eq $true) { - if ($new_value -is [Array]) { - try { - Clear-ItemProperty -Path IIS:\AppPools\$name -Name $attribute_key -WhatIf:$check_mode - } catch { - Fail-Json -obj $result -message "Failed to clear attribute to Web App Pool $name. Attribute: $attribute_key, Exception: $($_.Exception.Message)" - } - foreach ($value in $new_value) { - try { - New-ItemProperty -Path IIS:\AppPools\$name -Name $attribute_key -Value @{value=$value} -WhatIf:$check_mode > $null - } catch { - Fail-Json -obj $result -message "Failed to add new attribute to Web App Pool $name. Attribute: $attribute_key, Value: $value, Exception: $($_.Exception.Message)" - } - } - } else { - try { - Set-ItemProperty -Path IIS:\AppPools\$name -Name $attribute_key -Value $new_value -WhatIf:$check_mode - } catch { - Fail-Json $result "Failed to set attribute to Web App Pool $name. Attribute: $attribute_key, Value: $new_value, Exception: $($_.Exception.Message)" - } - } - $result.changed = $true - } - } - - # Set the state of the pool - if ($pool.State -eq "Stopped") { - if ($state -eq "started" -or $state -eq "restarted") { - if (-not $check_mode) { - try { - Start-WebAppPool -Name $name > $null - } catch { - Fail-Json $result "Failed to start Web App Pool $($name): $($_.Exception.Message)" - } - } - $result.changed = $true - } - } else { - if ($state -eq "stopped") { - if (-not $check_mode) { - try { - Stop-WebAppPool -Name $name > $null - } catch { - Fail-Json $result "Failed to stop Web App Pool $($name): $($_.Exception.Message)" - } - } - $result.changed = $true - } elseif ($state -eq "restarted") { - if (-not $check_mode) { - try { - Restart-WebAppPool -Name $name > $null - } catch { - Fail-Json $result "Failed to restart Web App Pool $($name): $($_.Exception.Message)" - } - } - $result.changed = $true - } - } - } -} - -# Get all the current attributes for the pool -$pool = Get-Item -Path IIS:\AppPools\$name -ErrorAction SilentlyContinue -$elements = @("attributes", "cpu", "failure", "processModel", "recycling") - -foreach ($element in $elements) { - if ($element -eq "attributes") { - $attribute_collection = $pool.Attributes - $attribute_parent = $pool - } else { - $attribute_collection = $pool.$element.Attributes - $attribute_parent = $pool.$element - } - - foreach ($attribute in $attribute_collection) { - $attribute_name = $attribute.Name - if ($attribute_name -notlike "*password*") { - $attribute_value = $attribute_parent.$attribute_name - - $result.info.$element.Add($attribute_name, $attribute_value) - } - } -} - -# Manually get the periodicRestart attributes in recycling -foreach ($attribute in $pool.recycling.periodicRestart.Attributes) { - $attribute_name = $attribute.Name - $attribute_value = $pool.recycling.periodicRestart.$attribute_name - $result.info.recycling.periodicRestart.Add($attribute_name, $attribute_value) -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_iis_webapppool.py b/lib/ansible/modules/windows/win_iis_webapppool.py deleted file mode 100644 index d5a79c7328..0000000000 --- a/lib/ansible/modules/windows/win_iis_webapppool.py +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# 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_iis_webapppool -version_added: "2.0" -short_description: Configure IIS Web Application Pools -description: - - Creates, removes and configures an IIS Web Application Pool. -options: - attributes: - description: - - This field is a free form dictionary value for the application pool - attributes. - - These attributes are based on the naming standard at - U(https://www.iis.net/configreference/system.applicationhost/applicationpools/add#005), - see the examples section for more details on how to set this. - - You can also set the attributes of child elements like cpu and - processModel, see the examples to see how it is done. - - While you can use the numeric values for enums it is recommended to use - the enum name itself, e.g. use SpecificUser instead of 3 for - processModel.identityType. - - managedPipelineMode may be either "Integrated" or "Classic". - - startMode may be either "OnDemand" or "AlwaysRunning". - - Use C(state) module parameter to modify the state of the app pool. - - When trying to set 'processModel.password' and you receive a 'Value - does fall within the expected range' error, you have a corrupted - keystore. Please follow - U(http://structuredsight.com/2014/10/26/im-out-of-range-youre-out-of-range/) - to help fix your host. - name: - description: - - Name of the application pool. - type: str - required: yes - state: - description: - - The state of the application pool. - - If C(absent) will ensure the app pool is removed. - - If C(present) will ensure the app pool is configured and exists. - - If C(restarted) will ensure the app pool exists and will restart, this - is never idempotent. - - If C(started) will ensure the app pool exists and is started. - - If C(stopped) will ensure the app pool exists and is stopped. - type: str - choices: [ absent, present, restarted, started, stopped ] - default: present -seealso: -- module: win_iis_virtualdirectory -- module: win_iis_webapplication -- module: win_iis_webbinding -- module: win_iis_website -author: -- Henrik Wallström (@henrikwallstrom) -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Return information about an existing application pool - win_iis_webapppool: - name: DefaultAppPool - state: present - -- name: Create a new application pool in 'Started' state - win_iis_webapppool: - name: AppPool - state: started - -- name: Stop an application pool - win_iis_webapppool: - name: AppPool - state: stopped - -- name: Restart an application pool (non-idempotent) - win_iis_webapppool: - name: AppPool - state: restarted - -- name: Change application pool attributes using new dict style - win_iis_webapppool: - name: AppPool - attributes: - managedRuntimeVersion: v4.0 - autoStart: no - -- name: Creates an application pool, sets attributes and starts it - win_iis_webapppool: - name: AnotherAppPool - state: started - attributes: - managedRuntimeVersion: v4.0 - autoStart: no - -# In the below example we are setting attributes in child element processModel -# https://www.iis.net/configreference/system.applicationhost/applicationpools/add/processmodel -- name: Manage child element and set identity of application pool - win_iis_webapppool: - name: IdentitiyAppPool - state: started - attributes: - managedPipelineMode: Classic - processModel.identityType: SpecificUser - processModel.userName: '{{ansible_user}}' - processModel.password: '{{ansible_password}}' - processModel.loadUserProfile: true - -- name: Manage a timespan attribute - win_iis_webapppool: - name: TimespanAppPool - state: started - attributes: - # Timespan with full string "day:hour:minute:second.millisecond" - recycling.periodicRestart.time: "00:00:05:00.000000" - recycling.periodicRestart.schedule: ["00:10:00", "05:30:00"] - # Shortened timespan "hour:minute:second" - processModel.pingResponseTime: "00:03:00" -''' - -RETURN = r''' -attributes: - description: Application Pool attributes that were set and processed by this - module invocation. - returned: success - type: dict - sample: - enable32BitAppOnWin64: "true" - managedRuntimeVersion: "v4.0" - managedPipelineMode: "Classic" -info: - description: Information on current state of the Application Pool. See - https://www.iis.net/configreference/system.applicationhost/applicationpools/add#005 - for the full list of return attributes based on your IIS version. - returned: success - type: complex - sample: - contains: - attributes: - description: Key value pairs showing the current Application Pool attributes. - returned: success - type: dict - sample: - autoStart: true - managedRuntimeLoader: "webengine4.dll" - managedPipelineMode: "Classic" - name: "DefaultAppPool" - CLRConfigFile: "" - passAnonymousToken: true - applicationPoolSid: "S-1-5-82-1352790163-598702362-1775843902-1923651883-1762956711" - queueLength: 1000 - managedRuntimeVersion: "v4.0" - state: "Started" - enableConfigurationOverride: true - startMode: "OnDemand" - enable32BitAppOnWin64: true - cpu: - description: Key value pairs showing the current Application Pool cpu attributes. - returned: success - type: dict - sample: - action: "NoAction" - limit: 0 - resetInterval: - Days: 0 - Hours: 0 - failure: - description: Key value pairs showing the current Application Pool failure attributes. - returned: success - type: dict - sample: - autoShutdownExe: "" - orphanActionExe: "" - rapidFailProtextionInterval: - Days: 0 - Hours: 0 - name: - description: Name of Application Pool that was processed by this module invocation. - returned: success - type: str - sample: "DefaultAppPool" - processModel: - description: Key value pairs showing the current Application Pool processModel attributes. - returned: success - type: dict - sample: - identityType: "ApplicationPoolIdentity" - logonType: "LogonBatch" - pingInterval: - Days: 0 - Hours: 0 - recycling: - description: Key value pairs showing the current Application Pool recycling attributes. - returned: success - type: dict - sample: - disallowOverlappingRotation: false - disallowRotationOnConfigChange: false - logEventOnRecycle: "Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory" - state: - description: Current runtime state of the pool as the module completed. - returned: success - type: str - sample: "Started" -''' diff --git a/lib/ansible/modules/windows/win_iis_webbinding.ps1 b/lib/ansible/modules/windows/win_iis_webbinding.ps1 deleted file mode 100644 index ffbd1866ae..0000000000 --- a/lib/ansible/modules/windows/win_iis_webbinding.ps1 +++ /dev/null @@ -1,377 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam $params -name "name" -type str -failifempty $true -aliases 'website' -$state = Get-AnsibleParam $params "state" -default "present" -validateSet "present","absent" -$host_header = Get-AnsibleParam $params -name "host_header" -type str -$protocol = Get-AnsibleParam $params -name "protocol" -type str -default 'http' -$port = Get-AnsibleParam $params -name "port" -default '80' -$ip = Get-AnsibleParam $params -name "ip" -default '*' -$certificateHash = Get-AnsibleParam $params -name "certificate_hash" -type str -default ([string]::Empty) -$certificateStoreName = Get-AnsibleParam $params -name "certificate_store_name" -type str -default ([string]::Empty) -$sslFlags = Get-AnsibleParam $params -name "ssl_flags" -default '0' -ValidateSet '0','1','2','3' - -$result = @{ - changed = $false -} - -################# -### Functions ### -################# -function Create-BindingInfo { - $ht = @{ - 'bindingInformation' = $args[0].bindingInformation - 'ip' = $args[0].bindingInformation.split(':')[0] - 'port' = [int]$args[0].bindingInformation.split(':')[1] - 'hostheader' = $args[0].bindingInformation.split(':')[2] - #'isDsMapperEnabled' = $args[0].isDsMapperEnabled - 'protocol' = $args[0].protocol - 'certificateStoreName' = $args[0].certificateStoreName - 'certificateHash' = $args[0].certificateHash - } - - #handle sslflag support - If ([version][System.Environment]::OSVersion.Version -lt [version]'6.2') - { - $ht.sslFlags = 'not supported' - } - Else - { - $ht.sslFlags = [int]$args[0].sslFlags - } - - Return $ht -} - -# Used instead of get-webbinding to ensure we always return a single binding -# We can't filter properly with get-webbinding...ex get-webbinding ip * returns all bindings -# pass it $binding_parameters hashtable -function Get-SingleWebBinding { - - Try { - $site_bindings = get-webbinding -name $args[0].name - } - Catch { - # 2k8r2 throws this error when you run get-webbinding with no bindings in iis - If (-not $_.Exception.Message.CompareTo('Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value')) - { - Throw $_.Exception.Message - } - Else { return } - } - - Foreach ($binding in $site_bindings) - { - $splits = $binding.bindingInformation -split ':' - - if ( - $args[0].protocol -eq $binding.protocol -and - $args[0].ipaddress -eq $splits[0] -and - $args[0].port -eq $splits[1] -and - $args[0].hostheader -eq $splits[2] - ) - { - Return $binding - } - } -} - - -############################# -### Pre-Action Validation ### -############################# -$os_version = [version][System.Environment]::OSVersion.Version - -# Ensure WebAdministration module is loaded -If ($os_version -lt [version]'6.1') -{ - Try { - Add-PSSnapin WebAdministration - } - Catch { - Fail-Json -obj $result -message "The WebAdministration snap-in is not present. Please make sure it is installed." - } -} -Else -{ - Try { - Import-Module WebAdministration - } - Catch { - Fail-Json -obj $result -message "Failed to load WebAdministration module. Is IIS installed? $($_.Exception.Message)" - } -} - -# ensure website targetted exists. -Name filter doesn't work on 2k8r2 so do where-object instead -$website_check = get-website | Where-Object {$_.name -eq $name} -If (-not $website_check) -{ - Fail-Json -obj $result -message "Unable to retrieve website with name $Name. Make sure the website name is valid and exists." -} - -# if OS older than 2012 (6.2) and ssl flags are set, fail. Otherwise toggle sni_support -If ($os_version -lt [version]'6.2') -{ - If ($sslFlags -ne 0) - { - Fail-Json -obj $result -message "SNI and Certificate Store support is not available for systems older than 2012 (6.2)" - } - $sni_support = $false #will cause the sslflags check later to skip -} -Else -{ - $sni_support = $true -} - -# make sure ssl flags only specified with https protocol -If ($protocol -ne 'https' -and $sslFlags -gt 0) -{ - Fail-Json -obj $result -message "SSLFlags can only be set for HTTPS protocol" -} - -# validate certificate details if provided -# we don't do anything with cert on state: absent, so only validate present -If ($certificateHash -and $state -eq 'present') -{ - If ($protocol -ne 'https') - { - Fail-Json -obj $result -message "You can only provide a certificate thumbprint when protocol is set to https" - } - - #apply default for cert store name - If (-Not $certificateStoreName) - { - $certificateStoreName = 'my' - } - - #validate cert path - $cert_path = "cert:\LocalMachine\$certificateStoreName\$certificateHash" - If (-Not (Test-Path $cert_path) ) - { - Fail-Json -obj $result -message "Unable to locate certificate at $cert_path" - } -} - -# make sure binding info is valid for central cert store if sslflags -gt 1 -If ($sslFlags -gt 1 -and ($certificateHash -ne [string]::Empty -or $certificateStoreName -ne [string]::Empty)) -{ - Fail-Json -obj $result -message "You set sslFlags to $sslFlags. This indicates you wish to use the Central Certificate Store feature. - This cannot be used in combination with certficiate_hash and certificate_store_name. When using the Central Certificate Store feature, - the certificate is automatically retrieved from the store rather than manually assigned to the binding." -} - -# disallow host_header: '*' -If ($host_header -eq '*') -{ - Fail-Json -obj $result -message "To make or remove a catch-all binding, please omit the host_header parameter entirely rather than specify host_header *" -} - -########################## -### start action items ### -########################## - -# create binding search splat -$binding_parameters = @{ - Name = $name - Protocol = $protocol - Port = $port - IPAddress = $ip -} - -# insert host header to search if specified, otherwise it will return * (all bindings matching protocol/ip) -If ($host_header) -{ - $binding_parameters.HostHeader = $host_header -} -Else -{ - $binding_parameters.HostHeader = [string]::Empty -} - -# Get bindings matching parameters -Try { - $current_bindings = Get-SingleWebBinding $binding_parameters -} -Catch { - Fail-Json -obj $result -message "Failed to retrieve bindings with Get-SingleWebBinding - $($_.Exception.Message)" -} - -################################################ -### Remove binding or exit if already absent ### -################################################ -If ($current_bindings -and $state -eq 'absent') -{ - Try { - #there is a bug in this method that will result in all bindings being removed if the IP in $current_bindings is a * - #$current_bindings | Remove-WebBinding -verbose -WhatIf:$check_mode - - #another method that did not work. It kept failing to match on element and removed everything. - #$element = @{protocol="$protocol";bindingInformation="$ip`:$port`:$host_header"} - #Remove-WebconfigurationProperty -filter $current_bindings.ItemXPath -Name Bindings.collection -AtElement $element -WhatIf #:$check_mode - - #this method works - [array]$bindings = Get-WebconfigurationProperty -filter $current_bindings.ItemXPath -Name Bindings.collection - - $index = Foreach ($item in $bindings) { - If ( $protocol -eq $item.protocol -and $current_bindings.bindingInformation -eq $item.bindingInformation ) { - $bindings.indexof($item) - break - } - } - - Remove-WebconfigurationProperty -filter $current_bindings.ItemXPath -Name Bindings.collection -AtIndex $index -WhatIf:$check_mode - $result.changed = $true - } - - Catch { - Fail-Json -obj $result -message "Failed to remove the binding from IIS - $($_.Exception.Message)" - } - - # removing bindings from iis may not also remove them from iis:\sslbindings - - $result.operation_type = 'removed' - $result.binding_info = $current_bindings | ForEach-Object {Create-BindingInfo $_} - Exit-Json -obj $result -} -ElseIf (-Not $current_bindings -and $state -eq 'absent') -{ - # exit changed: false since it's already gone - Exit-Json -obj $result -} - - -################################ -### Modify existing bindings ### -################################ -<# -since we have already have the parameters available to get-webbinding, -we just need to check here for the ones that are not available which are the -ssl settings (hash, store, sslflags). If they aren't set we update here, or -exit with changed: false -#> -ElseIf ($current_bindings) -{ - #ran into a strange edge case in testing where I was able to retrieve bindings but not expand all the properties - #when adding a self-signed wildcard cert to a binding. it seemed to permanently break the binding. only removing it - #would cause the error to stop. - Try { - $null = $current_bindings | Select-Object * - } - Catch { - Fail-Json -obj $result -message "Found a matching binding, but failed to expand it's properties (get-binding | FL *). In testing, this was caused by using a self-signed wildcard certificate. $($_.Exception.Message)" - } - - # check if there is a match on the ssl parameters - If ( ($current_bindings.sslFlags -ne $sslFlags -and $sni_support) -or - $current_bindings.certificateHash -ne $certificateHash -or - $current_bindings.certificateStoreName -ne $certificateStoreName) - { - # match/update SNI - If ($current_bindings.sslFlags -ne $sslFlags -and $sni_support) - { - Try { - Set-WebBinding -Name $name -IPAddress $ip -Port $port -HostHeader $host_header -PropertyName sslFlags -value $sslFlags -whatif:$check_mode - $result.changed = $true - } - Catch { - Fail-Json -obj $result -message "Failed to update sslFlags on binding - $($_.Exception.Message)" - } - - # Refresh the binding object since it has been changed - Try { - $current_bindings = Get-SingleWebBinding $binding_parameters - } - Catch { - Fail-Json -obj $result -message "Failed to refresh bindings after setting sslFlags - $($_.Exception.Message)" - } - } - # match/update certificate - If ($current_bindings.certificateHash -ne $certificateHash -or $current_bindings.certificateStoreName -ne $certificateStoreName) - { - If (-Not $check_mode) - { - Try { - $current_bindings.AddSslCertificate($certificateHash,$certificateStoreName) - } - Catch { - Fail-Json -obj $result -message "Failed to set new SSL certificate - $($_.Exception.Message)" - } - } - } - $result.changed = $true - $result.operation_type = 'updated' - $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State - $result.binding_info = Create-BindingInfo (Get-SingleWebBinding $binding_parameters) - Exit-Json -obj $result #exit changed true - } - Else - { - $result.operation_type = 'matched' - $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State - $result.binding_info = Create-BindingInfo (Get-SingleWebBinding $binding_parameters) - Exit-Json -obj $result #exit changed false - } -} - -######################## -### Add new bindings ### -######################## -ElseIf (-not $current_bindings -and $state -eq 'present') -{ - # add binding. this creates the binding, but does not apply a certificate to it. - Try - { - If (-not $check_mode) - { - If ($sni_support) - { - New-WebBinding @binding_parameters -SslFlags $sslFlags -Force - } - Else - { - New-WebBinding @binding_parameters -Force - } - } - $result.changed = $true - } - Catch - { - $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State - Fail-Json -obj $result -message "Failed at creating new binding (note: creating binding and adding ssl are separate steps) - $($_.Exception.Message)" - } - - # add certificate to binding - If ($certificateHash -and -not $check_mode) - { - Try { - #$new_binding = get-webbinding -Name $name -IPAddress $ip -port $port -Protocol $protocol -hostheader $host_header - $new_binding = Get-SingleWebBinding $binding_parameters - $new_binding.addsslcertificate($certificateHash,$certificateStoreName) - } - Catch { - $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State - Fail-Json -obj $result -message "Failed to set new SSL certificate - $($_.Exception.Message)" - } - } - - $result.changed = $true - $result.operation_type = 'added' - $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State - - # incase there are no bindings we do a check before calling Create-BindingInfo - $web_binding = Get-SingleWebBinding $binding_parameters - if ($web_binding) { - $result.binding_info = Create-BindingInfo $web_binding - } else { - $result.binding_info = $null - } - Exit-Json $result -} diff --git a/lib/ansible/modules/windows/win_iis_webbinding.py b/lib/ansible/modules/windows/win_iis_webbinding.py deleted file mode 100644 index 27fdd28f5c..0000000000 --- a/lib/ansible/modules/windows/win_iis_webbinding.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> -# Copyright: (c) 2017, Henrik Wallström <henrik@wallstroms.nu> -# 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_iis_webbinding -version_added: "2.0" -short_description: Configures a IIS Web site binding -description: - - Creates, removes and configures a binding to an existing IIS Web site. -options: - name: - description: - - Names of web site. - type: str - required: yes - aliases: [ website ] - state: - description: - - State of the binding. - type: str - choices: [ absent, present ] - default: present - port: - description: - - The port to bind to / use for the new site. - type: str - default: 80 - ip: - description: - - The IP address to bind to / use for the new site. - type: str - default: '*' - host_header: - description: - - The host header to bind to / use for the new site. - - If you are creating/removing a catch-all binding, omit this parameter rather than defining it as '*'. - type: str - protocol: - description: - - The protocol to be used for the Web binding (usually HTTP, HTTPS, or FTP). - type: str - default: http - certificate_hash: - description: - - Certificate hash (thumbprint) for the SSL binding. The certificate hash is the unique identifier for the certificate. - type: str - certificate_store_name: - description: - - Name of the certificate store where the certificate for the binding is located. - type: str - default: my - ssl_flags: - description: - - This parameter is only valid on Server 2012 and newer. - - Primarily used for enabling and disabling server name indication (SNI). - - Set to C(0) to disable SNI. - - Set to C(1) to enable SNI. - type: str - version_added: "2.5" -seealso: -- module: win_iis_virtualdirectory -- module: win_iis_webapplication -- module: win_iis_webapppool -- module: win_iis_website -author: - - Noah Sparks (@nwsparks) - - Henrik Wallström (@henrikwallstrom) -''' - -EXAMPLES = r''' -- name: Add a HTTP binding on port 9090 - win_iis_webbinding: - name: Default Web Site - port: 9090 - state: present - -- name: Remove the HTTP binding on port 9090 - win_iis_webbinding: - name: Default Web Site - port: 9090 - state: absent - -- name: Remove the default http binding - win_iis_webbinding: - name: Default Web Site - port: 80 - ip: '*' - state: absent - -- name: Add a HTTPS binding - win_iis_webbinding: - name: Default Web Site - protocol: https - port: 443 - ip: 127.0.0.1 - certificate_hash: B0D0FA8408FC67B230338FCA584D03792DA73F4C - state: present - -- name: Add a HTTPS binding with host header and SNI enabled - win_iis_webbinding: - name: Default Web Site - protocol: https - port: 443 - host_header: test.com - ssl_flags: 1 - certificate_hash: D1A3AF8988FD32D1A3AF8988FD323792DA73F4C - state: present -''' - -RETURN = r''' -website_state: - description: - - The state of the website being targetted - - Can be helpful in case you accidentally cause a binding collision - which can result in the targetted site being stopped - returned: always - type: str - sample: "Started" - version_added: "2.5" -operation_type: - description: - - The type of operation performed - - Can be removed, updated, matched, or added - returned: on success - type: str - sample: "removed" - version_added: "2.5" -binding_info: - description: - - Information on the binding being manipulated - returned: on success - type: dict - sample: |- - "binding_info": { - "bindingInformation": "127.0.0.1:443:", - "certificateHash": "FF3910CE089397F1B5A77EB7BAFDD8F44CDE77DD", - "certificateStoreName": "MY", - "hostheader": "", - "ip": "127.0.0.1", - "port": 443, - "protocol": "https", - "sslFlags": "not supported" - } - version_added: "2.5" -''' diff --git a/lib/ansible/modules/windows/win_iis_website.ps1 b/lib/ansible/modules/windows/win_iis_website.ps1 deleted file mode 100644 index e7b6cab8bf..0000000000 --- a/lib/ansible/modules/windows/win_iis_website.ps1 +++ /dev/null @@ -1,180 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -$params = Parse-Args $args -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$application_pool = Get-AnsibleParam -obj $params -name "application_pool" -type "str" -$physical_path = Get-AnsibleParam -obj $params -name "physical_path" -type "str" -$site_id = Get-AnsibleParam -obj $params -name "site_id" -type "str" -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -validateset "absent","restarted","started","stopped" - -# Binding Parameters -$bind_port = Get-AnsibleParam -obj $params -name "port" -type "int" -$bind_ip = Get-AnsibleParam -obj $params -name "ip" -type "str" -$bind_hostname = Get-AnsibleParam -obj $params -name "hostname" -type "str" - -# Custom site Parameters from string where properties -# are separated by a pipe and property name/values by colon. -# Ex. "foo:1|bar:2" -$parameters = Get-AnsibleParam -obj $params -name "parameters" -type "str" -if($null -ne $parameters) { - $parameters = @($parameters -split '\|' | ForEach-Object { - return ,($_ -split "\:", 2); - }) -} - - -# Ensure WebAdministration module is loaded -if ($null -eq (Get-Module "WebAdministration" -ErrorAction SilentlyContinue)) { - Import-Module WebAdministration -} - -# Result -$result = @{ - site = @{} - changed = $false -} - -# Site info -$site = Get-Website | Where-Object { $_.Name -eq $name } - -Try { - # Add site - If(($state -ne 'absent') -and (-not $site)) { - If (-not $physical_path) { - Fail-Json -obj $result -message "missing required arguments: physical_path" - } - ElseIf (-not (Test-Path $physical_path)) { - Fail-Json -obj $result -message "specified folder must already exist: physical_path" - } - - $site_parameters = @{ - Name = $name - PhysicalPath = $physical_path - } - - If ($application_pool) { - $site_parameters.ApplicationPool = $application_pool - } - - If ($site_id) { - $site_parameters.ID = $site_id - } - - If ($bind_port) { - $site_parameters.Port = $bind_port - } - - If ($bind_ip) { - $site_parameters.IPAddress = $bind_ip - } - - If ($bind_hostname) { - $site_parameters.HostHeader = $bind_hostname - } - - # Fix for error "New-Item : Index was outside the bounds of the array." - # This is a bug in the New-WebSite commandlet. Apparently there must be at least one site configured in IIS otherwise New-WebSite crashes. - # For more details, see http://stackoverflow.com/questions/3573889/ps-c-new-website-blah-throws-index-was-outside-the-bounds-of-the-array - $sites_list = get-childitem -Path IIS:\sites - if ($null -eq $sites_list) { - if ($site_id) { - $site_parameters.ID = $site_id - } else { - $site_parameters.ID = 1 - } - } - - $site = New-Website @site_parameters -Force - $result.changed = $true - } - - # Remove site - If ($state -eq 'absent' -and $site) { - $site = Remove-Website -Name $name - $result.changed = $true - } - - $site = Get-Website | Where-Object { $_.Name -eq $name } - If($site) { - # Change Physical Path if needed - if($physical_path) { - If (-not (Test-Path $physical_path)) { - Fail-Json -obj $result -message "specified folder must already exist: physical_path" - } - - $folder = Get-Item $physical_path - If($folder.FullName -ne $site.PhysicalPath) { - Set-ItemProperty "IIS:\Sites\$($site.Name)" -name physicalPath -value $folder.FullName - $result.changed = $true - } - } - - # Change Application Pool if needed - if($application_pool) { - If($application_pool -ne $site.applicationPool) { - Set-ItemProperty "IIS:\Sites\$($site.Name)" -name applicationPool -value $application_pool - $result.changed = $true - } - } - - # Set properties - if($parameters) { - $parameters | ForEach-Object { - $property_value = Get-ItemProperty "IIS:\Sites\$($site.Name)" $_[0] - - switch ($property_value.GetType().Name) - { - "ConfigurationAttribute" { $parameter_value = $property_value.value } - "String" { $parameter_value = $property_value } - } - - if((-not $parameter_value) -or ($parameter_value) -ne $_[1]) { - Set-ItemProperty -LiteralPath "IIS:\Sites\$($site.Name)" $_[0] $_[1] - $result.changed = $true - } - } - } - - # Set run state - if ((($state -eq 'stopped') -or ($state -eq 'restarted')) -and ($site.State -eq 'Started')) - { - Stop-Website -Name $name -ErrorAction Stop - $result.changed = $true - } - if ((($state -eq 'started') -and ($site.State -eq 'Stopped')) -or ($state -eq 'restarted')) - { - Start-Website -Name $name -ErrorAction Stop - $result.changed = $true - } - } -} -Catch -{ - Fail-Json -obj $result -message $_.Exception.Message -} - -if ($state -ne 'absent') -{ - $site = Get-Website | Where-Object { $_.Name -eq $name } -} - -if ($site) -{ - $result.site = @{ - Name = $site.Name - ID = $site.ID - State = $site.State - PhysicalPath = $site.PhysicalPath - ApplicationPool = $site.applicationPool - Bindings = @($site.Bindings.Collection | ForEach-Object { $_.BindingInformation }) - } -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_iis_website.py b/lib/ansible/modules/windows/win_iis_website.py deleted file mode 100644 index c059b4171c..0000000000 --- a/lib/ansible/modules/windows/win_iis_website.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Henrik Wallström <henrik@wallstroms.nu> -# 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_iis_website -version_added: "2.0" -short_description: Configures a IIS Web site -description: - - Creates, Removes and configures a IIS Web site. -options: - name: - description: - - Names of web site. - type: str - required: yes - site_id: - description: - - Explicitly set the IIS numeric ID for a site. - - Note that this value cannot be changed after the website has been created. - type: str - version_added: "2.1" - state: - description: - - State of the web site - type: str - choices: [ absent, started, stopped, restarted ] - physical_path: - description: - - The physical path on the remote host to use for the new site. - - The specified folder must already exist. - type: str - application_pool: - description: - - The application pool in which the new site executes. - type: str - port: - description: - - The port to bind to / use for the new site. - type: int - ip: - description: - - The IP address to bind to / use for the new site. - type: str - hostname: - description: - - The host header to bind to / use for the new site. - type: str - ssl: - description: - - Enables HTTPS binding on the site.. - type: str - parameters: - description: - - Custom site Parameters from string where properties are separated by a pipe and property name/values by colon Ex. "foo:1|bar:2" - type: str -seealso: -- module: win_iis_virtualdirectory -- module: win_iis_webapplication -- module: win_iis_webapppool -- module: win_iis_webbinding -author: -- Henrik Wallström (@henrikwallstrom) -''' - -EXAMPLES = r''' - -# Start a website - -- name: Acme IIS site - win_iis_website: - name: Acme - state: started - port: 80 - ip: 127.0.0.1 - hostname: acme.local - application_pool: acme - physical_path: C:\sites\acme - parameters: logfile.directory:C:\sites\logs - register: website - -# Remove Default Web Site and the standard port 80 binding -- name: Remove Default Web Site - win_iis_website: - name: "Default Web Site" - state: absent - -# Some commandline examples: - -# This return information about an existing host -# $ ansible -i vagrant-inventory -m win_iis_website -a "name='Default Web Site'" window -# host | success >> { -# "changed": false, -# "site": { -# "ApplicationPool": "DefaultAppPool", -# "Bindings": [ -# "*:80:" -# ], -# "ID": 1, -# "Name": "Default Web Site", -# "PhysicalPath": "%SystemDrive%\\inetpub\\wwwroot", -# "State": "Stopped" -# } -# } - -# This stops an existing site. -# $ ansible -i hosts -m win_iis_website -a "name='Default Web Site' state=stopped" host - -# This creates a new site. -# $ ansible -i hosts -m win_iis_website -a "name=acme physical_path=C:\\sites\\acme" host - -# Change logfile. -# $ ansible -i hosts -m win_iis_website -a "name=acme physical_path=C:\\sites\\acme" host -''' diff --git a/lib/ansible/modules/windows/win_inet_proxy.ps1 b/lib/ansible/modules/windows/win_inet_proxy.ps1 deleted file mode 100644 index 3b0420f648..0000000000 --- a/lib/ansible/modules/windows/win_inet_proxy.ps1 +++ /dev/null @@ -1,495 +0,0 @@ -#!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() diff --git a/lib/ansible/modules/windows/win_inet_proxy.py b/lib/ansible/modules/windows/win_inet_proxy.py deleted file mode 100644 index 8a3449f399..0000000000 --- a/lib/ansible/modules/windows/win_inet_proxy.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/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 omitted, 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''' -# -''' diff --git a/lib/ansible/modules/windows/win_initialize_disk.ps1 b/lib/ansible/modules/windows/win_initialize_disk.ps1 deleted file mode 100644 index 556c74b372..0000000000 --- a/lib/ansible/modules/windows/win_initialize_disk.ps1 +++ /dev/null @@ -1,151 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Brant Evans <bevans@redhat.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic -#AnsibleRequires -OSVersion 6.2 - -Set-StrictMode -Version 2 - -$spec = @{ - options = @{ - disk_number = @{ type = "int" } - uniqueid = @{ type = "str" } - path = @{ type = "str" } - style = @{ type = "str"; choices = "gpt", "mbr"; default = "gpt" } - online = @{ type = "bool"; default = $true } - force = @{ type = "bool"; default = $false } - } - mutually_exclusive = @( - ,@('disk_number', 'uniqueid', 'path') - ) - required_one_of = @( - ,@('disk_number', 'uniqueid', 'path') - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$disk_number = $module.Params.disk_number -$uniqueid = $module.Params.uniqueid -$path = $module.Params.path -$partition_style = $module.Params.style -$bring_online = $module.Params.online -$force_init = $module.Params.force - -function Get-AnsibleDisk { - param( - $DiskNumber, - $UniqueId, - $Path - ) - - if ($null -ne $DiskNumber) { - try { - $disk = Get-Disk -Number $DiskNumber - } catch { - $module.FailJson("There was an error retrieving the disk using disk_number $($DiskNumber): $($_.Exception.Message)") - } - } elseif ($null -ne $UniqueId) { - try { - $disk = Get-Disk -UniqueId $UniqueId - } catch { - $module.FailJson("There was an error retrieving the disk using id $($UniqueId): $($_.Exception.Message)") - } - } elseif ($null -ne $Path) { - try { - $disk = Get-Disk -Path $Path - } catch { - $module.FailJson("There was an error retrieving the disk using path $($Path): $($_.Exception.Message)") - } - } else { - $module.FailJson("Unable to retrieve disk: disk_number, id, or path was not specified") - } - - return $disk -} - -function Initialize-AnsibleDisk { - param( - $AnsibleDisk, - $PartitionStyle - ) - - if ($AnsibleDisk.IsReadOnly) { - $module.FailJson("Unable to initialize disk as it is read-only") - } - - $parameters = @{ - Number = $AnsibleDisk.Number - PartitionStyle = $PartitionStyle - } - - if (-Not $module.CheckMode) { - Initialize-Disk @parameters -Confirm:$false - } - - $module.Result.changed = $true -} - -function Clear-AnsibleDisk { - param( - $AnsibleDisk - ) - - $parameters = @{ - Number = $AnsibleDisk.Number - } - - if (-Not $module.CheckMode) { - Clear-Disk @parameters -RemoveData -RemoveOEM -Confirm:$false - } -} - -function Set-AnsibleDisk { - param( - $AnsibleDisk, - $BringOnline - ) - - $refresh_disk_status = $false - - if ($BringOnline) { - if (-Not $module.CheckMode) { - if ($AnsibleDisk.IsOffline) { - Set-Disk -Number $AnsibleDisk.Number -IsOffline:$false - $refresh_disk_status = $true - } - - if ($AnsibleDisk.IsReadOnly) { - Set-Disk -Number $AnsibleDisk.Number -IsReadOnly:$false - $refresh_disk_status = $true - } - } - } - - if ($refresh_disk_status) { - $AnsibleDisk = Get-AnsibleDisk -DiskNumber $AnsibleDisk.Number - } - - return $AnsibleDisk -} - -$ansible_disk = Get-AnsibleDisk -DiskNumber $disk_number -UniqueId $uniqueid -Path $path -$ansible_part_style = $ansible_disk.PartitionStyle - -if ("RAW" -eq $ansible_part_style) { - $ansible_disk = Set-AnsibleDisk -AnsibleDisk $ansible_disk -BringOnline $bring_online - Initialize-AnsibleDisk -AnsibleDisk $ansible_disk -PartitionStyle $partition_style -} else { - if (($ansible_part_style -ne $partition_style.ToUpper()) -And -Not $force_init) { - $module.FailJson("Force initialization must be specified since the target partition style: $($partition_style.ToLower()) is different from the current partition style: $($ansible_part_style.ToLower())") - } elseif ($force_init) { - $ansible_disk = Set-AnsibleDisk -AnsibleDisk $ansible_disk -BringOnline $bring_online - Clear-AnsibleDisk -AnsibleDisk $ansible_disk - Initialize-AnsibleDisk -AnsibleDisk $ansible_disk -PartitionStyle $partition_style - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_initialize_disk.py b/lib/ansible/modules/windows/win_initialize_disk.py deleted file mode 100644 index 8328bb0294..0000000000 --- a/lib/ansible/modules/windows/win_initialize_disk.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2019, Brant Evans <bevans@redhat.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = ''' ---- -module: win_initialize_disk - -short_description: Initializes disks on Windows Server - -version_added: "2.10" - -description: - - "The M(win_initialize_disk) module initializes disks" - -options: - disk_number: - description: - - Used to specify the disk number of the disk to be initialized. - type: int - uniqueid: - description: - - Used to specify the uniqueid of the disk to be initialized. - type: str - path: - description: - - Used to specify the path to the disk to be initialized. - type: str - style: - description: - - The partition style to use for the disk. Valid options are mbr or gpt. - type: str - choices: [ gpt, mbr ] - default: gpt - online: - description: - - If the disk is offline and/or readonly update the disk to be online and not readonly. - type: bool - default: true - force: - description: - - Specify if initializing should be forced for disks that are already initialized. - type: bool - -notes: - - One of three parameters (I(disk_number), I(uniqueid), and I(path)) are mandatory to identify the target disk, but - more than one cannot be specified at the same time. - - A minimum Operating System Version of Server 2012 or Windows 8 is required to use this module. - - This module is idempotent if I(force) is not specified. - -seealso: - - module: win_disk_facts - - module: win_partition - - module: win_format - -author: - - Brant Evans (@branic) -''' - -EXAMPLES = ''' -- name: Initialize a disk - win_initialize_disk: - disk_number: 1 - -- name: Initialize a disk with an MBR partition style - win_initialize_disk: - disk_number: 1 - style: mbr - -- name: Forcefully initiallize a disk - win_initialize_disk: - disk_number: 2 - force: yes -''' - -RETURN = ''' -# -''' diff --git a/lib/ansible/modules/windows/win_lineinfile.ps1 b/lib/ansible/modules/windows/win_lineinfile.ps1 deleted file mode 100644 index 38dd8b8bc0..0000000000 --- a/lib/ansible/modules/windows/win_lineinfile.ps1 +++ /dev/null @@ -1,450 +0,0 @@ -#!powershell - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.Backup - -function WriteLines($outlines, $path, $linesep, $encodingobj, $validate, $check_mode) { - Try { - $temppath = [System.IO.Path]::GetTempFileName(); - } - Catch { - Fail-Json @{} "Cannot create temporary file! ($($_.Exception.Message))"; - } - $joined = $outlines -join $linesep; - [System.IO.File]::WriteAllText($temppath, $joined, $encodingobj); - - If ($validate) { - - If (-not ($validate -like "*%s*")) { - Fail-Json @{} "validate must contain %s: $validate"; - } - - $validate = $validate.Replace("%s", $temppath); - - $parts = [System.Collections.ArrayList] $validate.Split(" "); - $cmdname = $parts[0]; - - $cmdargs = $validate.Substring($cmdname.Length + 1); - - $process = [Diagnostics.Process]::Start($cmdname, $cmdargs); - $process.WaitForExit(); - - If ($process.ExitCode -ne 0) { - [string] $output = $process.StandardOutput.ReadToEnd(); - [string] $error = $process.StandardError.ReadToEnd(); - Remove-Item $temppath -force; - Fail-Json @{} "failed to validate $cmdname $cmdargs with error: $output $error"; - } - - } - - # Commit changes to the path - $cleanpath = $path.Replace("/", "\"); - Try { - Copy-Item -Path $temppath -Destination $cleanpath -Force -WhatIf:$check_mode; - } - Catch { - Fail-Json @{} "Cannot write to: $cleanpath ($($_.Exception.Message))"; - } - - Try { - Remove-Item -Path $temppath -Force -WhatIf:$check_mode; - } - Catch { - Fail-Json @{} "Cannot remove temporary file: $temppath ($($_.Exception.Message))"; - } - - return $joined; - -} - - -# Implement the functionality for state == 'present' -function Present($path, $regex, $line, $insertafter, $insertbefore, $create, $backup, $backrefs, $validate, $encodingobj, $linesep, $check_mode, $diff_support) { - - # Note that we have to clean up the path because ansible wants to treat / and \ as - # interchangeable in windows pathnames, but .NET framework internals do not support that. - $cleanpath = $path.Replace("/", "\"); - - # Check if path exists. If it does not exist, either create it if create == "yes" - # was specified or fail with a reasonable error message. - If (-not (Test-Path -LiteralPath $path)) { - If (-not $create) { - Fail-Json @{} "Path $path does not exist !"; - } - # Create new empty file, using the specified encoding to write correct BOM - [System.IO.File]::WriteAllLines($cleanpath, "", $encodingobj); - } - - # Initialize result information - $result = @{ - backup = ""; - changed = $false; - msg = ""; - } - - # Read the dest file lines using the indicated encoding into a mutable ArrayList. - $before = [System.IO.File]::ReadAllLines($cleanpath, $encodingobj) - If ($null -eq $before) { - $lines = New-Object System.Collections.ArrayList; - } - Else { - $lines = [System.Collections.ArrayList] $before; - } - - if ($diff_support) { - $result.diff = @{ - before = $before -join $linesep; - } - } - - # Compile the regex specified, if provided - $mre = $null; - If ($regex) { - $mre = New-Object Regex $regex, 'Compiled'; - } - - # Compile the regex for insertafter or insertbefore, if provided - $insre = $null; - If ($insertafter -and $insertafter -ne "BOF" -and $insertafter -ne "EOF") { - $insre = New-Object Regex $insertafter, 'Compiled'; - } - ElseIf ($insertbefore -and $insertbefore -ne "BOF") { - $insre = New-Object Regex $insertbefore, 'Compiled'; - } - - # index[0] is the line num where regex has been found - # index[1] is the line num where insertafter/insertbefore has been found - $index = -1, -1; - $lineno = 0; - - # The latest match object and matched line - $matched_line = ""; - - # Iterate through the lines in the file looking for matches - Foreach ($cur_line in $lines) { - If ($regex) { - $m = $mre.Match($cur_line); - $match_found = $m.Success; - If ($match_found) { - $matched_line = $cur_line; - } - } - Else { - $match_found = $line -ceq $cur_line; - } - If ($match_found) { - $index[0] = $lineno; - } - ElseIf ($insre -and $insre.Match($cur_line).Success) { - If ($insertafter) { - $index[1] = $lineno + 1; - } - If ($insertbefore) { - $index[1] = $lineno; - } - } - $lineno = $lineno + 1; - } - - If ($index[0] -ne -1) { - If ($backrefs) { - $new_line = [regex]::Replace($matched_line, $regex, $line); - } - Else { - $new_line = $line; - } - If ($lines[$index[0]] -cne $new_line) { - $lines[$index[0]] = $new_line; - $result.changed = $true; - $result.msg = "line replaced"; - } - } - ElseIf ($backrefs) { - # No matches - no-op - } - ElseIf ($insertbefore -eq "BOF" -or $insertafter -eq "BOF") { - $lines.Insert(0, $line); - $result.changed = $true; - $result.msg = "line added"; - } - ElseIf ($insertafter -eq "EOF" -or $index[1] -eq -1) { - $lines.Add($line) > $null; - $result.changed = $true; - $result.msg = "line added"; - } - Else { - $lines.Insert($index[1], $line); - $result.changed = $true; - $result.msg = "line added"; - } - - # Write changes to the path if changes were made - If ($result.changed) { - - # Write backup file if backup == "yes" - If ($backup) { - $result.backup_file = Backup-File -path $path -WhatIf:$check_mode - # Ensure backward compatibility (deprecate in future) - $result.backup = $result.backup_file - } - - $writelines_params = @{ - outlines = $lines - path = $path - linesep = $linesep - encodingobj = $encodingobj - validate = $validate - check_mode = $check_mode - } - $after = WriteLines @writelines_params; - - if ($diff_support) { - $result.diff.after = $after; - } - } - - $result.encoding = $encodingobj.WebName; - - Exit-Json $result; -} - - -# Implement the functionality for state == 'absent' -function Absent($path, $regex, $line, $backup, $validate, $encodingobj, $linesep, $check_mode, $diff_support) { - - # Check if path exists. If it does not exist, fail with a reasonable error message. - If (-not (Test-Path -LiteralPath $path)) { - Fail-Json @{} "Path $path does not exist !"; - } - - # Initialize result information - $result = @{ - backup = ""; - changed = $false; - msg = ""; - } - - # Read the dest file lines using the indicated encoding into a mutable ArrayList. Note - # that we have to clean up the path because ansible wants to treat / and \ as - # interchangeable in windows pathnames, but .NET framework internals do not support that. - $cleanpath = $path.Replace("/", "\"); - $before = [System.IO.File]::ReadAllLines($cleanpath, $encodingobj); - If ($null -eq $before) { - $lines = New-Object System.Collections.ArrayList; - } - Else { - $lines = [System.Collections.ArrayList] $before; - } - - if ($diff_support) { - $result.diff = @{ - before = $before -join $linesep; - } - } - - # Compile the regex specified, if provided - $cre = $null; - If ($regex) { - $cre = New-Object Regex $regex, 'Compiled'; - } - - $found = New-Object System.Collections.ArrayList; - $left = New-Object System.Collections.ArrayList; - - Foreach ($cur_line in $lines) { - If ($regex) { - $m = $cre.Match($cur_line); - $match_found = $m.Success; - } - Else { - $match_found = $line -ceq $cur_line; - } - If ($match_found) { - $found.Add($cur_line) > $null; - $result.changed = $true; - } - Else { - $left.Add($cur_line) > $null; - } - } - - # Write changes to the path if changes were made - If ($result.changed) { - - # Write backup file if backup == "yes" - If ($backup) { - $result.backup_file = Backup-File -path $path -WhatIf:$check_mode - # Ensure backward compatibility (deprecate in future) - $result.backup = $result.backup_file - } - - $writelines_params = @{ - outlines = $left - path = $path - linesep = $linesep - encodingobj = $encodingobj - validate = $validate - check_mode = $check_mode - } - $after = WriteLines @writelines_params; - - if ($diff_support) { - $result.diff.after = $after; - } - } - - $result.encoding = $encodingobj.WebName; - $result.found = $found.Count; - $result.msg = "$($found.Count) line(s) removed"; - - Exit-Json $result; -} - - -# Parse the parameters file dropped by the Ansible machinery -$params = Parse-Args $args -supports_check_mode $true; -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false; -$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false; - -# Initialize defaults for input parameters. -$path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -aliases "dest","destfile","name"; -$regex = Get-AnsibleParam -obj $params -name "regex" -type "str" -aliases "regexp"; -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"; -$line = Get-AnsibleParam -obj $params -name "line" -type "str"; -$backrefs = Get-AnsibleParam -obj $params -name "backrefs" -type "bool" -default $false; -$insertafter = Get-AnsibleParam -obj $params -name "insertafter" -type "str"; -$insertbefore = Get-AnsibleParam -obj $params -name "insertbefore" -type "str"; -$create = Get-AnsibleParam -obj $params -name "create" -type "bool" -default $false; -$backup = Get-AnsibleParam -obj $params -name "backup" -type "bool" -default $false; -$validate = Get-AnsibleParam -obj $params -name "validate" -type "str"; -$encoding = Get-AnsibleParam -obj $params -name "encoding" -type "str" -default "auto"; -$newline = Get-AnsibleParam -obj $params -name "newline" -type "str" -default "windows" -validateset "unix","windows"; - -# Fail if the path is not a file -If (Test-Path -LiteralPath $path -PathType "container") { - Fail-Json @{} "Path $path is a directory"; -} - -# Default to windows line separator - probably most common -$linesep = "`r`n" -If ($newline -eq "unix") { - $linesep = "`n"; -} - -# Figure out the proper encoding to use for reading / writing the target file. - -# The default encoding is UTF-8 without BOM -$encodingobj = [System.Text.UTF8Encoding] $false; - -# If an explicit encoding is specified, use that instead -If ($encoding -ne "auto") { - $encodingobj = [System.Text.Encoding]::GetEncoding($encoding); -} - -# Otherwise see if we can determine the current encoding of the target file. -# If the file doesn't exist yet (create == 'yes') we use the default or -# explicitly specified encoding set above. -ElseIf (Test-Path -LiteralPath $path) { - - # Get a sorted list of encodings with preambles, longest first - $max_preamble_len = 0; - $sortedlist = New-Object System.Collections.SortedList; - Foreach ($encodinginfo in [System.Text.Encoding]::GetEncodings()) { - $encoding = $encodinginfo.GetEncoding(); - $plen = $encoding.GetPreamble().Length; - If ($plen -gt $max_preamble_len) { - $max_preamble_len = $plen; - } - If ($plen -gt 0) { - $sortedlist.Add(-($plen * 1000000 + $encoding.CodePage), $encoding) > $null; - } - } - - # Get the first N bytes from the file, where N is the max preamble length we saw - [Byte[]]$bom = Get-Content -Encoding Byte -ReadCount $max_preamble_len -TotalCount $max_preamble_len -LiteralPath $path; - - # Iterate through the sorted encodings, looking for a full match. - $found = $false; - Foreach ($encoding in $sortedlist.GetValueList()) { - $preamble = $encoding.GetPreamble(); - If ($preamble -and $bom) { - Foreach ($i in 0..($preamble.Length - 1)) { - If ($i -ge $bom.Length) { - break; - } - If ($preamble[$i] -ne $bom[$i]) { - break; - } - ElseIf ($i + 1 -eq $preamble.Length) { - $encodingobj = $encoding; - $found = $true; - } - } - If ($found) { - break; - } - } - } -} - - -# Main dispatch - based on the value of 'state', perform argument validation and -# call the appropriate handler function. -If ($state -eq "present") { - - If ($backrefs -and -not $regex) { - Fail-Json @{} "regexp= is required with backrefs=true"; - } - - If (-not $line) { - Fail-Json @{} "line= is required with state=present"; - } - - If ($insertbefore -and $insertafter) { - Add-Warning $result "Both insertbefore and insertafter parameters found, ignoring `"insertafter=$insertafter`"" - } - - If (-not $insertbefore -and -not $insertafter) { - $insertafter = "EOF"; - } - - $present_params = @{ - path = $path - regex = $regex - line = $line - insertafter = $insertafter - insertbefore = $insertbefore - create = $create - backup = $backup - backrefs = $backrefs - validate = $validate - encodingobj = $encodingobj - linesep = $linesep - check_mode = $check_mode - diff_support = $diff_support - } - Present @present_params; - -} -ElseIf ($state -eq "absent") { - - If (-not $regex -and -not $line) { - Fail-Json @{} "one of line= or regexp= is required with state=absent"; - } - - $absent_params = @{ - path = $path - regex = $regex - line = $line - backup = $backup - validate = $validate - encodingobj = $encodingobj - linesep = $linesep - check_mode = $check_mode - diff_support = $diff_support - } - Absent @absent_params; -} diff --git a/lib/ansible/modules/windows/win_lineinfile.py b/lib/ansible/modules/windows/win_lineinfile.py deleted file mode 100644 index f4fb7f5afa..0000000000 --- a/lib/ansible/modules/windows/win_lineinfile.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# 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_lineinfile -short_description: Ensure a particular line is in a file, or replace an existing line using a back-referenced regular expression -description: - - This module will search a file for a line, and ensure that it is present or absent. - - This is primarily useful when you want to change a single line in a file only. -version_added: "2.0" -options: - path: - description: - - The path of the file to modify. - - Note that the Windows path delimiter C(\) must be escaped as C(\\) when the line is double quoted. - - Before Ansible 2.3 this option was only usable as I(dest), I(destfile) and I(name). - type: path - required: yes - aliases: [ dest, destfile, name ] - backup: - description: - - Determine whether a backup should be created. - - When set to C(yes), create a backup file including the timestamp information - so you can get the original file back if you somehow clobbered it incorrectly. - type: bool - default: no - regex: - description: - - The regular expression to look for in every line of the file. For C(state=present), the pattern to replace if found; only the last line found - will be replaced. For C(state=absent), the pattern of the line to remove. Uses .NET compatible regular expressions; - see U(https://msdn.microsoft.com/en-us/library/hs600312%28v=vs.110%29.aspx). - aliases: [ "regexp" ] - state: - description: - - Whether the line should be there or not. - type: str - choices: [ absent, present ] - default: present - line: - description: - - Required for C(state=present). The line to insert/replace into the file. If C(backrefs) is set, may contain backreferences that will get - expanded with the C(regexp) capture groups if the regexp matches. - - Be aware that the line is processed first on the controller and thus is dependent on yaml quoting rules. Any double quoted line - will have control characters, such as '\r\n', expanded. To print such characters literally, use single or no quotes. - type: str - backrefs: - description: - - Used with C(state=present). If set, line can contain backreferences (both positional and named) that will get populated if the C(regexp) - matches. This flag changes the operation of the module slightly; C(insertbefore) and C(insertafter) will be ignored, and if the C(regexp) - doesn't match anywhere in the file, the file will be left unchanged. - - If the C(regexp) does match, the last matching line will be replaced by the expanded line parameter. - type: bool - default: no - insertafter: - description: - - Used with C(state=present). If specified, the line will be inserted after the last match of specified regular expression. A special value is - available; C(EOF) for inserting the line at the end of the file. - - If specified regular expression has no matches, EOF will be used instead. May not be used with C(backrefs). - type: str - choices: [ EOF, '*regex*' ] - default: EOF - insertbefore: - description: - - Used with C(state=present). If specified, the line will be inserted before the last match of specified regular expression. A value is available; - C(BOF) for inserting the line at the beginning of the file. - - If specified regular expression has no matches, the line will be inserted at the end of the file. May not be used with C(backrefs). - type: str - choices: [ BOF, '*regex*' ] - create: - description: - - Used with C(state=present). If specified, the file will be created if it does not already exist. By default it will fail if the file is missing. - type: bool - default: no - validate: - description: - - Validation to run before copying into place. Use %s in the command to indicate the current file to validate. - - The command is passed securely so shell features like expansion and pipes won't work. - type: str - encoding: - description: - - Specifies the encoding of the source text file to operate on (and thus what the output encoding will be). The default of C(auto) will cause - the module to auto-detect the encoding of the source file and ensure that the modified file is written with the same encoding. - - An explicit encoding can be passed as a string that is a valid value to pass to the .NET framework System.Text.Encoding.GetEncoding() method - - see U(https://msdn.microsoft.com/en-us/library/system.text.encoding%28v=vs.110%29.aspx). - - This is mostly useful with C(create=yes) if you want to create a new file with a specific encoding. If C(create=yes) is specified without a - specific encoding, the default encoding (UTF-8, no BOM) will be used. - type: str - default: auto - newline: - description: - - Specifies the line separator style to use for the modified file. This defaults to the windows line separator (C(\r\n)). Note that the indicated - line separator will be used for file output regardless of the original line separator that appears in the input file. - type: str - choices: [ unix, windows ] - default: windows -notes: - - As of Ansible 2.3, the I(dest) option has been changed to I(path) as default, but I(dest) still works as well. -seealso: -- module: assemble -- module: lineinfile -author: -- Brian Lloyd (@brianlloyd) -''' - -EXAMPLES = r''' -# Before Ansible 2.3, option 'dest', 'destfile' or 'name' was used instead of 'path' -- name: Insert path without converting \r\n - win_lineinfile: - path: c:\file.txt - line: c:\return\new - -- win_lineinfile: - path: C:\Temp\example.conf - regex: '^name=' - line: 'name=JohnDoe' - -- win_lineinfile: - path: C:\Temp\example.conf - regex: '^name=' - state: absent - -- win_lineinfile: - path: C:\Temp\example.conf - regex: '^127\.0\.0\.1' - line: '127.0.0.1 localhost' - -- win_lineinfile: - path: C:\Temp\httpd.conf - regex: '^Listen ' - insertafter: '^#Listen ' - line: Listen 8080 - -- win_lineinfile: - path: C:\Temp\services - regex: '^# port for http' - insertbefore: '^www.*80/tcp' - line: '# port for http by default' - -- name: Create file if it doesn't exist with a specific encoding - win_lineinfile: - path: C:\Temp\utf16.txt - create: yes - encoding: utf-16 - line: This is a utf-16 encoded file - -- name: Add a line to a file and ensure the resulting file uses unix line separators - win_lineinfile: - path: C:\Temp\testfile.txt - line: Line added to file - newline: unix - -- name: Update a line using backrefs - win_lineinfile: - path: C:\Temp\example.conf - backrefs: yes - regex: '(^name=)' - line: '$1JohnDoe' -''' - -RETURN = r''' -backup: - description: - - Name of the backup file that was created. - - This is now deprecated, use C(backup_file) instead. - returned: if backup=yes - type: str - sample: C:\Path\To\File.txt.11540.20150212-220915.bak -backup_file: - description: Name of the backup file that was created. - returned: if backup=yes - type: str - sample: C:\Path\To\File.txt.11540.20150212-220915.bak -''' diff --git a/lib/ansible/modules/windows/win_mapped_drive.ps1 b/lib/ansible/modules/windows/win_mapped_drive.ps1 deleted file mode 100644 index aab65d455c..0000000000 --- a/lib/ansible/modules/windows/win_mapped_drive.ps1 +++ /dev/null @@ -1,444 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.AccessToken -#AnsibleRequires -CSharpUtil Ansible.Basic -#Requires -Module Ansible.ModuleUtils.AddType - -$spec = @{ - options = @{ - letter = @{ type = "str"; required = $true } - path = @{ type = "path"; } - state = @{ type = "str"; default = "present"; choices = @("absent", "present") } - username = @{ type = "str" } - password = @{ type = "str"; no_log = $true } - } - required_if = @( - ,@("state", "present", @("path")) - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$letter = $module.Params.letter -$path = $module.Params.path -$state = $module.Params.state -$username = $module.Params.username -$password = $module.Params.password - -if ($letter -notmatch "^[a-zA-z]{1}$") { - $module.FailJson("letter must be a single letter from A-Z, was: $letter") -} -$letter_root = "$($letter):" - -$module.Diff.before = "" -$module.Diff.after = "" - -Add-CSharpType -AnsibleModule $module -References @' -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; - -namespace Ansible.MappedDrive -{ - internal class NativeHelpers - { - public enum ResourceScope : uint - { - Connected = 0x00000001, - GlobalNet = 0x00000002, - Remembered = 0x00000003, - Recent = 0x00000004, - Context = 0x00000005, - } - - [Flags] - public enum ResourceType : uint - { - Any = 0x0000000, - Disk = 0x00000001, - Print = 0x00000002, - Reserved = 0x00000008, - Unknown = 0xFFFFFFFF, - } - - public enum CloseFlags : uint - { - None = 0x00000000, - UpdateProfile = 0x00000001, - } - - [Flags] - public enum AddFlags : uint - { - UpdateProfile = 0x00000001, - UpdateRecent = 0x00000002, - Temporary = 0x00000004, - Interactive = 0x00000008, - Prompt = 0x00000010, - Redirect = 0x00000080, - CurrentMedia = 0x00000200, - CommandLine = 0x00000800, - CmdSaveCred = 0x00001000, - CredReset = 0x00002000, - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct NETRESOURCEW - { - public ResourceScope dwScope; - public ResourceType dwType; - public UInt32 dwDisplayType; - public UInt32 dwUsage; - [MarshalAs(UnmanagedType.LPWStr)] public string lpLocalName; - [MarshalAs(UnmanagedType.LPWStr)] public string lpRemoteName; - [MarshalAs(UnmanagedType.LPWStr)] public string lpComment; - [MarshalAs(UnmanagedType.LPWStr)] public string lpProvider; - } - } - - internal class NativeMethods - { - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool CloseHandle( - IntPtr hObject); - - [DllImport("advapi32.dll", SetLastError = true)] - public static extern bool ImpersonateLoggedOnUser( - IntPtr hToken); - - [DllImport("advapi32.dll", SetLastError = true)] - public static extern bool RevertToSelf(); - - [DllImport("Mpr.dll", CharSet = CharSet.Unicode)] - public static extern UInt32 WNetAddConnection2W( - NativeHelpers.NETRESOURCEW lpNetResource, - [MarshalAs(UnmanagedType.LPWStr)] string lpPassword, - [MarshalAs(UnmanagedType.LPWStr)] string lpUserName, - NativeHelpers.AddFlags dwFlags); - - [DllImport("Mpr.dll", CharSet = CharSet.Unicode)] - public static extern UInt32 WNetCancelConnection2W( - [MarshalAs(UnmanagedType.LPWStr)] string lpName, - NativeHelpers.CloseFlags dwFlags, - bool fForce); - - [DllImport("Mpr.dll")] - public static extern UInt32 WNetCloseEnum( - IntPtr hEnum); - - [DllImport("Mpr.dll", CharSet = CharSet.Unicode)] - public static extern UInt32 WNetEnumResourceW( - IntPtr hEnum, - ref Int32 lpcCount, - SafeMemoryBuffer lpBuffer, - ref UInt32 lpBufferSize); - - [DllImport("Mpr.dll", CharSet = CharSet.Unicode)] - public static extern UInt32 WNetOpenEnumW( - NativeHelpers.ResourceScope dwScope, - NativeHelpers.ResourceType dwType, - UInt32 dwUsage, - IntPtr lpNetResource, - out IntPtr lphEnum); - } - - 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; - } - } - - internal class Impersonation : IDisposable - { - private IntPtr hToken = IntPtr.Zero; - - public Impersonation(IntPtr token) - { - hToken = token; - if (token != IntPtr.Zero) - if (!NativeMethods.ImpersonateLoggedOnUser(hToken)) - throw new Win32Exception("Failed to impersonate token with ImpersonateLoggedOnUser()"); - } - - public void Dispose() - { - if (hToken != null) - NativeMethods.RevertToSelf(); - GC.SuppressFinalize(this); - } - ~Impersonation() { Dispose(); } - } - - public class DriveInfo - { - public string Drive; - public string Path; - } - - 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 Utils - { - private const UInt32 ERROR_SUCCESS = 0x00000000; - private const UInt32 ERROR_NO_MORE_ITEMS = 0x0000103; - - public static void AddMappedDrive(string drive, string path, IntPtr iToken, string username = null, string password = null) - { - NativeHelpers.NETRESOURCEW resource = new NativeHelpers.NETRESOURCEW - { - dwType = NativeHelpers.ResourceType.Disk, - lpLocalName = drive, - lpRemoteName = path, - }; - NativeHelpers.AddFlags dwFlags = NativeHelpers.AddFlags.UpdateProfile; - // While WNetAddConnection2W supports user/pass, this is only used for the first connection and the - // password is not remembered. We will delete the username mapping afterwards as it interferes with - // the implicit credential cache used in Windows - using (Impersonation imp = new Impersonation(iToken)) - { - UInt32 res = NativeMethods.WNetAddConnection2W(resource, password, username, dwFlags); - if (res != ERROR_SUCCESS) - throw new Win32Exception((int)res, String.Format("Failed to map {0} to '{1}' with WNetAddConnection2W()", drive, path)); - } - } - - public static List<DriveInfo> GetMappedDrives(IntPtr iToken) - { - using (Impersonation imp = new Impersonation(iToken)) - { - IntPtr enumPtr = IntPtr.Zero; - UInt32 res = NativeMethods.WNetOpenEnumW(NativeHelpers.ResourceScope.Remembered, NativeHelpers.ResourceType.Disk, - 0, IntPtr.Zero, out enumPtr); - if (res != ERROR_SUCCESS) - throw new Win32Exception((int)res, "WNetOpenEnumW()"); - - List<DriveInfo> resources = new List<DriveInfo>(); - try - { - // MS recommend a buffer size of 16 KiB - UInt32 bufferSize = 16384; - int lpcCount = -1; - - // keep iterating the enum until ERROR_NO_MORE_ITEMS is returned - do - { - using (SafeMemoryBuffer buffer = new SafeMemoryBuffer((int)bufferSize)) - { - res = NativeMethods.WNetEnumResourceW(enumPtr, ref lpcCount, buffer, ref bufferSize); - if (res == ERROR_NO_MORE_ITEMS) - continue; - else if (res != ERROR_SUCCESS) - throw new Win32Exception((int)res, "WNetEnumResourceW()"); - lpcCount = lpcCount < 0 ? 0 : lpcCount; - - NativeHelpers.NETRESOURCEW[] rawResources = new NativeHelpers.NETRESOURCEW[lpcCount]; - PtrToStructureArray(rawResources, buffer.DangerousGetHandle()); - foreach (NativeHelpers.NETRESOURCEW resource in rawResources) - { - DriveInfo currentDrive = new DriveInfo - { - Drive = resource.lpLocalName, - Path = resource.lpRemoteName, - }; - resources.Add(currentDrive); - } - } - } - while (res != ERROR_NO_MORE_ITEMS); - } - finally - { - NativeMethods.WNetCloseEnum(enumPtr); - } - - return resources; - } - } - - public static void RemoveMappedDrive(string drive, IntPtr iToken) - { - using (Impersonation imp = new Impersonation(iToken)) - { - UInt32 res = NativeMethods.WNetCancelConnection2W(drive, NativeHelpers.CloseFlags.UpdateProfile, true); - if (res != ERROR_SUCCESS) - throw new Win32Exception((int)res, String.Format("Failed to remove mapped drive {0} with WNetCancelConnection2W()", drive)); - } - } - - private static void PtrToStructureArray<T>(T[] array, IntPtr ptr) - { - IntPtr ptrOffset = ptr; - for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T)))) - array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T)); - } - } -} -'@ - -Function Get-LimitedToken { - $h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess() - $h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Duplicate, Query") - - try { - # If we don't have a Full token, we don't need to get the limited one to set a mapped drive - $tet = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($h_token) - if ($tet -ne [Ansible.AccessToken.TokenElevationType]::Full) { - return - } - - foreach ($system_token in [Ansible.AccessToken.TokenUtil]::EnumerateUserTokens("S-1-5-18", "Duplicate")) { - # To get the TokenLinkedToken we need the SeTcbPrivilege, not all SYSTEM tokens have this assigned so - # we need to check before impersonating that token - $token_privileges = [Ansible.AccessToken.TokenUtil]::GetTokenPrivileges($system_token) - if ($null -eq ($token_privileges | Where-Object { $_.Name -eq "SeTcbPrivilege" })) { - continue - } - - [Ansible.AccessToken.TokenUtil]::ImpersonateToken($system_token) - try { - return [Ansible.AccessToken.TokenUtil]::GetTokenLinkedToken($h_token) - } finally { - [Ansible.AccessToken.TokenUtil]::RevertToSelf() - } - } - } finally { - $h_token.Dispose() - } -} - -<# -When we run with become and UAC is enabled, the become process will most likely be the Admin/Full token. This is -an issue with the WNetConnection APIs as the Full token is unable to add/enumerate/remove connections due to -Windows storing the connection details on each token session ID. Unless EnabledLinkedConnections (reg key) is -set to 1, the Full token is unable to manage connections in a persisted way whereas the Limited token is. This -is similar to running 'net use' normally and an admin process is unable to see those and vice versa. - -To overcome this problem, we attempt to get a handle on the Limited token for the current logon and impersonate -that before making any WNetConnection calls. If the token is not split, or we are already running on the Limited -token then no impersonatoin is used/required. This allows the module to run with become (required to access the -credential store) but still be able to manage the mapped connections. - -These are the following scenarios we have to handle; - - 1. Run without become - A network logon is usually not split so GetLimitedToken() will return $null and no impersonation is needed - 2. Run with become on admin user with admin priv - We will have a Full token, GetLimitedToken() will return the limited token and impersonation is used - 3. Run with become on admin user without admin priv - We are already running with a Limited token, GetLimitedToken() return $nul and no impersonation is needed - 4. Run with become on standard user - There's no split token, GetLimitedToken() will return $null and no impersonation is needed -#> -$impersonation_token = Get-LimitedToken - -try { - $i_token_ptr = [System.IntPtr]::Zero - if ($null -ne $impersonation_token) { - $i_token_ptr = $impersonation_token.DangerousGetHandle() - } - - $existing_targets = [Ansible.MappedDrive.Utils]::GetMappedDrives($i_token_ptr) - $existing_target = $existing_targets | Where-Object { $_.Drive -eq $letter_root } - - if ($existing_target) { - $module.Diff.before = @{ - letter = $letter - path = $existing_target.Path - } - } - - if ($state -eq "absent") { - if ($null -ne $existing_target) { - if ($null -ne $path -and $existing_target.Path -ne $path) { - $module.FailJson("did not delete mapped drive $letter, the target path is pointing to a different location at $( $existing_target.Path )") - } - if (-not $module.CheckMode) { - [Ansible.MappedDrive.Utils]::RemoveMappedDrive($letter_root, $i_token_ptr) - } - - $module.Result.changed = $true - } - } else { - $physical_drives = Get-PSDrive -PSProvider "FileSystem" - if ($letter -in $physical_drives.Name) { - $module.FailJson("failed to create mapped drive $letter, this letter is in use and is pointing to a non UNC path") - } - - # PowerShell converts a $null value to "" when crossing the .NET marshaler, we need to convert the input - # to a missing value so it uses the defaults. We also need to Invoke it with MethodInfo.Invoke so the defaults - # are still used - $input_username = $username - if ($null -eq $username) { - $input_username = [Type]::Missing - } - $input_password = $password - if ($null -eq $password) { - $input_password = [Type]::Missing - } - $add_method = [Ansible.MappedDrive.Utils].GetMethod("AddMappedDrive") - - if ($null -ne $existing_target) { - if ($existing_target.Path -ne $path) { - if (-not $module.CheckMode) { - [Ansible.MappedDrive.Utils]::RemoveMappedDrive($letter_root, $i_token_ptr) - $add_method.Invoke($null, [Object[]]@($letter_root, $path, $i_token_ptr, $input_username, $input_password)) - } - $module.Result.changed = $true - } - } else { - if (-not $module.CheckMode) { - $add_method.Invoke($null, [Object[]]@($letter_root, $path, $i_token_ptr, $input_username, $input_password)) - } - - $module.Result.changed = $true - } - - # If username was set and we made a change, remove the UserName value so Windows will continue to use the cred - # cache. If we don't do this then the drive will fail to map in the future as WNetAddConnection does not cache - # the password and relies on the credential store. - if ($null -ne $username -and $module.Result.changed -and -not $module.CheckMode) { - Set-ItemProperty -Path HKCU:\Network\$letter -Name UserName -Value "" -WhatIf:$module.CheckMode - } - - $module.Diff.after = @{ - letter = $letter - path = $path - } - } -} finally { - if ($null -ne $impersonation_token) { - $impersonation_token.Dispose() - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_mapped_drive.py b/lib/ansible/modules/windows/win_mapped_drive.py deleted file mode 100644 index 3b2a0f6372..0000000000 --- a/lib/ansible/modules/windows/win_mapped_drive.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub, actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_mapped_drive -version_added: '2.4' -short_description: Map network drives for users -description: -- Allows you to modify mapped network drives for individual users. -- Also support WebDAV endpoints in the UNC form. -options: - letter: - description: - - The letter of the network path to map to. - - This letter must not already be in use with Windows. - type: str - required: yes - password: - description: - - The password for C(username) that is used when testing the initial - connection. - - This is never saved with a mapped drive, use the M(win_credential) module - to persist a username and password for a host. - type: str - path: - description: - - The UNC path to map the drive to. - - If pointing to a WebDAV location this must still be in a UNC path in the - format C(\\hostname\path) and not a URL, see examples for more details. - - To specify a C(https) WebDAV path, add C(@SSL) after the hostname. To - specify a custom WebDAV port add C(@<port num>) after the C(@SSL) or - hostname portion of the UNC path, e.g. C(\\server@SSL@1234) or - C(\\server@1234). - - This is required if C(state=present). - - If C(state=absent) and I(path) is not set, the module will delete the - mapped drive regardless of the target. - - If C(state=absent) and the I(path) is set, the module will throw an error - if path does not match the target of the mapped drive. - type: path - state: - description: - - If C(present) will ensure the mapped drive exists. - - If C(absent) will ensure the mapped drive does not exist. - type: str - choices: [ absent, present ] - default: present - username: - description: - - The username that is used when testing the initial connection. - - This is never saved with a mapped drive, the M(win_credential) module - to persist a username and password for a host. - - This is required if the mapped drive requires authentication with - custom credentials and become, or CredSSP cannot be used. - - If become or CredSSP is used, any credentials saved with - M(win_credential) will automatically be used instead. - type: str -notes: -- You cannot use this module to access a mapped drive in another Ansible task, - drives mapped with this module are only accessible when logging in - interactively with the user through the console or RDP. -- It is recommend to run this module with become or CredSSP when the remote - path requires authentication. -- When using become or CredSSP, the task will have access to any local - credentials stored in the user's vault. -- If become or CredSSP is not available, the I(username) and I(password) - options can be used for the initial authentication but these are not - persisted. -- WebDAV paths must have the WebDAV client feature installed for this module to - map those paths. This is installed by default on desktop Windows editions but - Windows Server hosts need to install the C(WebDAV-Redirector) feature using - M(win_feature). -seealso: -- module: win_credential -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Create a mapped drive under Z - win_mapped_drive: - letter: Z - path: \\domain\appdata\accounting - -- name: Delete any mapped drives under Z - win_mapped_drive: - letter: Z - state: absent - -- name: Only delete the mapped drive Z if the paths match (error is thrown otherwise) - win_mapped_drive: - letter: Z - path: \\domain\appdata\accounting - state: absent - -- name: Create mapped drive with credentials and save the username and password - block: - - name: Save the network credentials required for the mapped drive - win_credential: - name: server - type: domain_password - username: username@DOMAIN - secret: Password01 - state: present - - - name: Create a mapped drive that requires authentication - win_mapped_drive: - letter: M - path: \\SERVER\C$ - state: present - vars: - # become is required to save and retrieve the credentials in the tasks - ansible_become: yes - ansible_become_method: runas - ansible_become_user: '{{ ansible_user }}' - ansible_become_pass: '{{ ansible_password }}' - -- name: Create mapped drive with credentials that do not persist on the next logon - win_mapped_drive: - letter: M - path: \\SERVER\C$ - state: present - username: '{{ ansible_user }}' - password: '{{ ansible_password }}' - -# This should only be required for Windows Server OS' -- name: Ensure WebDAV client feature is installed - win_feature: - name: WebDAV-Redirector - state: present - register: webdav_feature - -- name: Reboot after installing WebDAV client feature - win_reboot: - when: webdav_feature.reboot_required - -- name: Map the HTTPS WebDAV location - win_mapped_drive: - letter: W - path: \\live.sysinternals.com@SSL\tools # https://live.sysinternals.com/tools - state: present -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_msg.ps1 b/lib/ansible/modules/windows/win_msg.ps1 deleted file mode 100644 index 63fe680024..0000000000 --- a/lib/ansible/modules/windows/win_msg.ps1 +++ /dev/null @@ -1,52 +0,0 @@ -#!powershell - -# Copyright: (c) 2016, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -# -$stopwatch = [system.diagnostics.stopwatch]::startNew() - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$display_seconds = Get-AnsibleParam -obj $params -name "display_seconds" -type "int" -default "10" -$msg = Get-AnsibleParam -obj $params -name "msg" -type "str" -default "Hello world!" -$to = Get-AnsibleParam -obj $params -name "to" -type "str" -default "*" -$wait = Get-AnsibleParam -obj $params -name "wait" -type "bool" -default $false - -$result = @{ - changed = $false - display_seconds = $display_seconds - msg = $msg - wait = $wait -} - -if ($msg.Length -gt 255) { - Fail-Json -obj $result -message "msg length must be less than 256 characters, current length: $($msg.Length)" -} - -$msg_args = @($to, "/TIME:$display_seconds") - -if ($wait) { - $msg_args += "/W" -} - -$msg_args += $msg -if (-not $check_mode) { - $output = & msg.exe $msg_args 2>&1 - $result.rc = $LASTEXITCODE -} - -$endsend_at = Get-Date| Out-String -$stopwatch.Stop() - -$result.changed = $true -$result.runtime_seconds = $stopwatch.Elapsed.TotalSeconds -$result.sent_localtime = $endsend_at.Trim() - -if ($result.rc -ne 0 ) { - Fail-Json -obj $result -message "$output" -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_msg.py b/lib/ansible/modules/windows/win_msg.py deleted file mode 100644 index 4f9ea959de..0000000000 --- a/lib/ansible/modules/windows/win_msg.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_msg -version_added: "2.3" -short_description: Sends a message to logged in users on Windows hosts -description: - - Wraps the msg.exe command in order to send messages to Windows hosts. -options: - to: - description: - - Who to send the message to. Can be a username, sessionname or sessionid. - type: str - default: '*' - display_seconds: - description: - - How long to wait for receiver to acknowledge message, in seconds. - type: int - default: 10 - wait: - description: - - Whether to wait for users to respond. Module will only wait for the number of seconds specified in display_seconds or 10 seconds if not specified. - However, if I(wait) is C(yes), the message is sent to each logged on user in turn, waiting for the user to either press 'ok' or for - the timeout to elapse before moving on to the next user. - type: bool - default: 'no' - msg: - description: - - The text of the message to be displayed. - - The message must be less than 256 characters. - type: str - default: Hello world! -notes: - - This module must run on a windows host, so ensure your play targets windows - hosts, or delegates to a windows host. - - Messages are only sent to the local host where the module is run. - - The module does not support sending to users listed in a file. - - Setting wait to C(yes) can result in long run times on systems with many logged in users. -seealso: -- module: win_say -- module: win_toast -author: -- Jon Hawkesworth (@jhawkesworth) -''' - -EXAMPLES = r''' -- name: Warn logged in users of impending upgrade - win_msg: - display_seconds: 60 - msg: Automated upgrade about to start. Please save your work and log off before {{ deployment_start_time }} -''' - -RETURN = r''' -msg: - description: Test of the message that was sent. - returned: changed - type: str - sample: Automated upgrade about to start. Please save your work and log off before 22 July 2016 18:00:00 -display_seconds: - description: Value of display_seconds module parameter. - returned: success - type: str - sample: 10 -rc: - description: The return code of the API call. - returned: always - type: int - sample: 0 -runtime_seconds: - description: How long the module took to run on the remote windows host. - returned: success - type: str - sample: 22 July 2016 17:45:51 -sent_localtime: - description: local time from windows host when the message was sent. - returned: success - type: str - sample: 22 July 2016 17:45:51 -wait: - description: Value of wait module parameter. - returned: success - type: bool - sample: false -''' diff --git a/lib/ansible/modules/windows/win_netbios.ps1 b/lib/ansible/modules/windows/win_netbios.ps1 deleted file mode 100644 index 0179a40260..0000000000 --- a/lib/ansible/modules/windows/win_netbios.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -#!powershell - -# Copyright: (c) 2019, Thomas Moore (@tmmruk) <hi@tmmr.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -$spec = @{ - options = @{ - state = @{ type = "str"; choices = "enabled", "disabled", "default"; required = $true } - adapter_names = @{ type = "list"; required = $false } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) -$module.Result.reboot_required = $false - -$state = $module.Params.state -$adapter_names = $module.Params.adapter_names - -switch ( $state ) -{ - 'default'{ $netbiosoption = 0 } - enabled { $netbiosoption = 1 } - disabled { $netbiosoption = 2 } -} - -if(-not $adapter_names) -{ - # Target all network adapters on the system - $get_params = @{ - ClassName = 'Win32_NetworkAdapterConfiguration' - Filter = 'IPEnabled=true' - Property = @('MacAddress', 'TcpipNetbiosOptions') - } - $target_adapters_config = Get-CimInstance @get_params -} -else -{ - $get_params = @{ - Class = 'Win32_NetworkAdapter' - Filter = ($adapter_names | ForEach-Object -Process { "NetConnectionId='$_'" }) -join " OR " - KeyOnly = $true - } - $target_adapters_config = Get-CimInstance @get_params | Get-CimAssociatedInstance -ResultClass 'Win32_NetworkAdapterConfiguration' - if(($target_adapters_config | Measure-Object).Count -ne $adapter_names.Count) - { - $module.FailJson("Not all of the target adapter names could be found on the system. No configuration changes have been made. $adapter_names") - } -} - -foreach($adapter in $target_adapters_config) -{ - if($adapter.TcpipNetbiosOptions -ne $netbiosoption) - { - if(-not $module.CheckMode) - { - $result = Invoke-CimMethod -InputObject $adapter -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions=$netbiosoption} - switch ( $result.ReturnValue ) - { - 0 { <# Success no reboot required #> } - 1 { $module.Result.reboot_required = $true } - 100 { $module.Warn("DHCP not enabled on adapter $($adapter.MacAddress). Unable to set default. Try using disabled or enabled options instead.") } - default { $module.FailJson("An error occurred while setting TcpipNetbios options on adapter $($adapter.MacAddress). Return code $($result.ReturnValue).") } - } - } - $module.Result.changed = $true - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_netbios.py b/lib/ansible/modules/windows/win_netbios.py deleted file mode 100644 index a3bd0dfac0..0000000000 --- a/lib/ansible/modules/windows/win_netbios.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Thomas Moore (@tmmruk) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_netbios -version_added: '2.9' -short_description: Manage NetBIOS over TCP/IP settings on Windows. -description: - - Enables or disables NetBIOS on Windows network adapters. - - Can be used to protect a system against NBT-NS poisoning and avoid NBNS broadcast storms. - - Settings can be applied system wide or per adapter. -options: - state: - description: - - Whether NetBIOS should be enabled, disabled, or default (use setting from DHCP server or if static IP address is assigned enable NetBIOS). - choices: - - enabled - - disabled - - default - required: yes - type: str - adapter_names: - description: - - List of adapter names for which to manage NetBIOS settings. If this option is omitted then configuration is applied to all adapters on the system. - - The adapter name used is the connection caption in the Network Control Panel or via C(Get-NetAdapter), eg C(Ethernet 2). - type: list - required: no - -author: - - Thomas Moore (@tmmruk) -notes: - - Changing NetBIOS settings does not usually require a reboot and will take effect immediately. - - UDP port 137/138/139 will no longer be listening once NetBIOS is disabled. -''' - -EXAMPLES = r''' -- name: Disable NetBIOS system wide - win_netbios: - state: disabled - -- name: Disable NetBIOS on Ethernet2 - win_netbios: - state: disabled - adapter_names: - - Ethernet2 - -- name: Enable NetBIOS on Public and Backup adapters - win_netbios: - state: enabled - adapter_names: - - Public - - Backup - -- name: Set NetBIOS to system default on all adapters - win_netbios: - state: default - -''' - -RETURN = r''' -reboot_required: - description: Boolean value stating whether a system reboot is required. - returned: always - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_nssm.ps1 b/lib/ansible/modules/windows/win_nssm.ps1 deleted file mode 100644 index e351beebf5..0000000000 --- a/lib/ansible/modules/windows/win_nssm.ps1 +++ /dev/null @@ -1,498 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, George Frank <george@georgefrank.net> -# Copyright: (c) 2015, Adam Keech <akeech@chathamfinancial.com> -# Copyright: (c) 2015, Hans-Joachim Kliemeck <git@kliemeck.de> -# Copyright: (c) 2019, Kevin Subileau (@ksubileau) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil - -$ErrorActionPreference = "Stop" - -$start_modes_map = @{ - "auto" = "SERVICE_AUTO_START" - "delayed" = "SERVICE_DELAYED_AUTO_START" - "manual" = "SERVICE_DEMAND_START" - "disabled" = "SERVICE_DISABLED" -} - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","started","stopped","restarted" -resultobj $result -$display_name = Get-AnsibleParam -obj $params -name 'display_name' -type 'str' -$description = Get-AnsibleParam -obj $params -name 'description' -type 'str' - -$application = Get-AnsibleParam -obj $params -name "application" -type "path" -$appDirectory = Get-AnsibleParam -obj $params -name "working_directory" -aliases "app_directory","chdir" -type "path" -$appParameters = Get-AnsibleParam -obj $params -name "app_parameters" -$appArguments = Get-AnsibleParam -obj $params -name "arguments" -aliases "app_parameters_free_form" - -$stdoutFile = Get-AnsibleParam -obj $params -name "stdout_file" -type "path" -$stderrFile = Get-AnsibleParam -obj $params -name "stderr_file" -type "path" - -$executable = Get-AnsibleParam -obj $params -name "executable" -type "path" -default "nssm.exe" - -$app_rotate_bytes = Get-AnsibleParam -obj $params -name "app_rotate_bytes" -type "int" -default 104858 -$app_rotate_online = Get-AnsibleParam -obj $params -name "app_rotate_online" -type "int" -default 0 -validateset 0,1 -$app_stop_method_console = Get-AnsibleParam -obj $params -name "app_stop_method_console" -type "int" -$app_stop_method_skip = Get-AnsibleParam -obj $params -name "app_stop_method_skip" -type "int" -validateset 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - -# Deprecated options since 2.8. Remove in 2.12 -$startMode = Get-AnsibleParam -obj $params -name "start_mode" -type "str" -default "auto" -validateset $start_modes_map.Keys -resultobj $result -$dependencies = Get-AnsibleParam -obj $params -name "dependencies" -type "list" -$user = Get-AnsibleParam -obj $params -name "user" -type "str" -$password = Get-AnsibleParam -obj $params -name "password" -type "str" - -$result = @{ - changed = $false -} -$diff_text = $null - -function Invoke-NssmCommand { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true,ValueFromRemainingArguments=$true)] - [string[]]$arguments - ) - - $command = Argv-ToString -arguments (@($executable) + $arguments) - $result = Run-Command -command $command - - $result.arguments = $command - - return $result -} - -function Get-NssmServiceStatus { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service - ) - - return Invoke-NssmCommand -arguments @("status", $service) -} - -function Get-NssmServiceParameter { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service, - [Parameter(Mandatory=$true)] - [Alias("param")] - [string]$parameter, - [Parameter(Mandatory=$false)] - [string]$subparameter - ) - - $arguments = @("get", $service, $parameter) - if($subparameter -ne "") { - $arguments += $subparameter - } - return Invoke-NssmCommand -arguments $arguments -} - -function Set-NssmServiceParameter { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service, - [Parameter(Mandatory=$true)] - [string]$parameter, - [Parameter(Mandatory=$true,ValueFromRemainingArguments=$true)] - [Alias("value")] - [string[]]$arguments - ) - - return Invoke-NssmCommand -arguments (@("set", $service, $parameter) + $arguments) -} - -function Reset-NssmServiceParameter { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service, - [Parameter(Mandatory=$true)] - [Alias("param")] - [string]$parameter - ) - - return Invoke-NssmCommand -arguments @("reset", $service, $parameter) -} - -function Update-NssmServiceParameter { - <# - .SYNOPSIS - A generic cmdlet to idempotently set a nssm service parameter. - .PARAMETER service - [String] The service name - .PARAMETER parameter - [String] The name of the nssm parameter to set. - .PARAMETER arguments - [String[]] Target value (or list of value) or array of arguments to pass to the 'nssm set' command. - .PARAMETER compare - [scriptblock] An optionnal idempotency check scriptblock that must return true when - the current value is equal to the desired value. Usefull when 'nssm get' doesn't return - the same value as 'nssm set' takes in argument, like for the ObjectName parameter. - #> - [CmdletBinding(SupportsShouldProcess=$true)] - param( - [Parameter(Mandatory=$true)] - [string]$service, - - [Parameter(Mandatory=$true)] - [string]$parameter, - - [Parameter(Mandatory=$true,ValueFromRemainingArguments=$true)] - [AllowEmptyString()] - [AllowNull()] - [Alias("value")] - [string[]]$arguments, - - [Parameter()] - [scriptblock]$compare = {param($actual,$expected) @(Compare-Object -ReferenceObject $actual -DifferenceObject $expected).Length -eq 0} - ) - - if($null -eq $arguments) { return } - $arguments = @($arguments | Where-Object { $_ -ne '' }) - - $nssm_result = Get-NssmServiceParameter -service $service -parameter $parameter - - if ($nssm_result.rc -ne 0) { - $result.nssm_error_cmd = $nssm_result.arguments - $result.nssm_error_log = $nssm_result.stderr - Fail-Json -obj $result -message "Error retrieving $parameter for service ""$service""" - } - - $current_values = @($nssm_result.stdout.split("`n`r") | Where-Object { $_ -ne '' }) - - if (-not $compare.Invoke($current_values,$arguments)) { - if ($PSCmdlet.ShouldProcess($service, "Update '$parameter' parameter")) { - if($arguments.Count -gt 0) { - $nssm_result = Set-NssmServiceParameter -service $service -parameter $parameter -arguments $arguments - } - else { - $nssm_result = Reset-NssmServiceParameter -service $service -parameter $parameter - } - - if ($nssm_result.rc -ne 0) { - $result.nssm_error_cmd = $nssm_result.arguments - $result.nssm_error_log = $nssm_result.stderr - Fail-Json -obj $result -message "Error setting $parameter for service ""$service""" - } - } - - $script:diff_text += "-$parameter = $($current_values -join ', ')`n+$parameter = $($arguments -join ', ')`n" - $result.changed_by = $parameter - $result.changed = $true - } -} - -function Test-NssmServiceExists { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service - ) - - return [bool](Get-Service -Name $service -ErrorAction SilentlyContinue) -} - -function Invoke-NssmStart { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service - ) - - $nssm_result = Invoke-NssmCommand -arguments @("start", $service) - - if ($nssm_result.rc -ne 0) { - $result.nssm_error_cmd = $nssm_result.arguments - $result.nssm_error_log = $nssm_result.stderr - Fail-Json -obj $result -message "Error starting service ""$service""" - } -} - -function Invoke-NssmStop { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [string]$service - ) - - $nssm_result = Invoke-NssmCommand -arguments @("stop", $service) - - if ($nssm_result.rc -ne 0) { - $result.nssm_error_cmd = $nssm_result.arguments - $result.nssm_error_log = $nssm_result.stderr - Fail-Json -obj $result -message "Error stopping service ""$service""" - } -} - -function Start-NssmService { - [CmdletBinding(SupportsShouldProcess=$true)] - param( - [Parameter(Mandatory=$true)] - [string]$service - ) - - $currentStatus = Get-NssmServiceStatus -service $service - - if ($currentStatus.rc -ne 0) { - $result.nssm_error_cmd = $currentStatus.arguments - $result.nssm_error_log = $currentStatus.stderr - Fail-Json -obj $result -message "Error starting service ""$service""" - } - - if ($currentStatus.stdout -notlike "*SERVICE_RUNNING*") { - if ($PSCmdlet.ShouldProcess($service, "Start service")) { - switch -wildcard ($currentStatus.stdout) { - "*SERVICE_STOPPED*" { Invoke-NssmStart -service $service } - - "*SERVICE_CONTINUE_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service } - "*SERVICE_PAUSE_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service } - "*SERVICE_PAUSED*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service } - "*SERVICE_START_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service } - "*SERVICE_STOP_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service } - } - } - - $result.changed_by = "start_service" - $result.changed = $true - } -} - -function Stop-NssmService { - [CmdletBinding(SupportsShouldProcess=$true)] - param( - [Parameter(Mandatory=$true)] - [string]$service - ) - - $currentStatus = Get-NssmServiceStatus -service $service - - if ($currentStatus.rc -ne 0) { - $result.nssm_error_cmd = $currentStatus.arguments - $result.nssm_error_log = $currentStatus.stderr - Fail-Json -obj $result -message "Error stopping service ""$service""" - } - - if ($currentStatus.stdout -notlike "*SERVICE_STOPPED*") { - if ($PSCmdlet.ShouldProcess($service, "Stop service")) { - Invoke-NssmStop -service $service - } - - $result.changed_by = "stop_service" - $result.changed = $true - } -} - -if (($null -ne $appParameters) -and ($null -ne $appArguments)) { - Fail-Json $result "'app_parameters' and 'arguments' are mutually exclusive but have both been set." -} - -# Backward compatibility for old parameters style. Remove the block bellow in 2.12 -if ($null -ne $appParameters) { - Add-DeprecationWarning -obj $result -message "The parameter 'app_parameters' will be removed soon, use 'arguments' instead" -version 2.12 - - if ($appParameters -isnot [string]) { - Fail-Json -obj $result -message "The app_parameters parameter must be a string representing a dictionary." - } - - # Convert dict-as-string form to list - $escapedAppParameters = $appParameters.TrimStart("@").TrimStart("{").TrimEnd("}").Replace("; ","`n").Replace("\","\\") - $appParametersHash = ConvertFrom-StringData -StringData $escapedAppParameters - - $appParamsArray = @() - $appParametersHash.GetEnumerator() | Foreach-Object { - if ($_.Name -ne "_") { - $appParamsArray += $_.Name - } - $appParamsArray += $_.Value - } - $appArguments = @($appParamsArray) - - # The rest of the code should use only the new $appArguments variable -} - -if ($state -in @("started","stopped","restarted")) { - Add-DeprecationWarning -obj $result -message "The values 'started', 'stopped', and 'restarted' for 'state' will be removed soon, use the win_service module to start or stop the service instead" -version 2.12 -} -if ($params.ContainsKey('start_mode')) { - Add-DeprecationWarning -obj $result -message "The parameter 'start_mode' will be removed soon, use the win_service module instead" -version 2.12 -} -if ($null -ne $dependencies) { - Add-DeprecationWarning -obj $result -message "The parameter 'dependencies' will be removed soon, use the win_service module instead" -version 2.12 -} -if ($null -ne $user) { - Add-DeprecationWarning -obj $result -message "The parameter 'user' will be removed soon, use the win_service module instead" -version 2.12 -} -if ($null -ne $password) { - Add-DeprecationWarning -obj $result -message "The parameter 'password' will be removed soon, use the win_service module instead" -version 2.12 -} - -if ($state -ne 'absent') { - if ($null -eq $application) { - Fail-Json -obj $result -message "The application parameter must be defined when the state is not absent." - } - - if (-not (Test-Path -LiteralPath $application -PathType Leaf)) { - Fail-Json -obj $result -message "The application specified ""$application"" does not exist on the host." - } - - if($null -eq $appDirectory) { - $appDirectory = (Get-Item -LiteralPath $application).DirectoryName - } - - if ($user -and -not $password) { - Fail-Json -obj $result -message "User without password is informed for service ""$name""" - } -} - - -$service_exists = Test-NssmServiceExists -service $name - -if ($state -eq 'absent') { - if ($service_exists) { - if(-not $check_mode) { - if ((Get-Service -Name $name).Status -ne "Stopped") { - $nssm_result = Invoke-NssmStop -service $name - } - - $nssm_result = Invoke-NssmCommand -arguments @("remove", $name, "confirm") - - if ($nssm_result.rc -ne 0) { - $result.nssm_error_cmd = $nssm_result.arguments - $result.nssm_error_log = $nssm_result.stderr - Fail-Json -obj $result -message "Error removing service ""$name""" - } - } - - $diff_text += "-[$name]" - $result.changed_by = "remove_service" - $result.changed = $true - } -} else { - $diff_text_added_prefix = '' - if (-not $service_exists) { - if(-not $check_mode) { - $nssm_result = Invoke-NssmCommand -arguments @("install", $name, $application) - - if ($nssm_result.rc -ne 0) { - $result.nssm_error_cmd = $nssm_result.arguments - $result.nssm_error_log = $nssm_result.stderr - Fail-Json -obj $result -message "Error installing service ""$name""" - } - $service_exists = $true - } - - $diff_text_added_prefix = '+' - $result.changed_by = "install_service" - $result.changed = $true - } - - $diff_text += "$diff_text_added_prefix[$name]`n" - - # We cannot configure a service that was created above in check mode as it won't actually exist - if ($service_exists) { - $common_params = @{ - service = $name - WhatIf = $check_mode - } - - Update-NssmServiceParameter -parameter "Application" -value $application @common_params - Update-NssmServiceParameter -parameter "DisplayName" -value $display_name @common_params - Update-NssmServiceParameter -parameter "Description" -value $description @common_params - - Update-NssmServiceParameter -parameter "AppDirectory" -value $appDirectory @common_params - - - if ($null -ne $appArguments) { - $singleLineParams = "" - if ($appArguments -is [array]) { - $singleLineParams = Argv-ToString -arguments $appArguments - } else { - $singleLineParams = $appArguments.ToString() - } - - $result.nssm_app_parameters = $appArguments - $result.nssm_single_line_app_parameters = $singleLineParams - - Update-NssmServiceParameter -parameter "AppParameters" -value $singleLineParams @common_params - } - - - Update-NssmServiceParameter -parameter "AppStdout" -value $stdoutFile @common_params - Update-NssmServiceParameter -parameter "AppStderr" -value $stderrFile @common_params - - ### - # Setup file rotation so we don't accidentally consume too much disk - ### - - #set files to overwrite - Update-NssmServiceParameter -parameter "AppStdoutCreationDisposition" -value 2 @common_params - Update-NssmServiceParameter -parameter "AppStderrCreationDisposition" -value 2 @common_params - - #enable file rotation - Update-NssmServiceParameter -parameter "AppRotateFiles" -value 1 @common_params - - #don't rotate until the service restarts - Update-NssmServiceParameter -parameter "AppRotateOnline" -value $app_rotate_online @common_params - - #both of the below conditions must be met before rotation will happen - #minimum age before rotating - Update-NssmServiceParameter -parameter "AppRotateSeconds" -value 86400 @common_params - - #minimum size before rotating - Update-NssmServiceParameter -parameter "AppRotateBytes" -value $app_rotate_bytes @common_params - - - ############## DEPRECATED block since 2.8. Remove in 2.12 ############## - Update-NssmServiceParameter -parameter "DependOnService" -arguments $dependencies @common_params - if ($user) { - $fullUser = $user - if (-Not($user.contains("@")) -And ($user.Split("\").count -eq 1)) { - $fullUser = ".\" + $user - } - - # Use custom compare callback to test only the username (and not the password) - Update-NssmServiceParameter -parameter "ObjectName" -arguments @($fullUser, $password) -compare {param($actual,$expected) $actual[0] -eq $expected[0]} @common_params - } - $mappedMode = $start_modes_map.$startMode - Update-NssmServiceParameter -parameter "Start" -value $mappedMode @common_params - if ($state -in "stopped","restarted") { - Stop-NssmService @common_params - } - - if($state -in "started","restarted") { - Start-NssmService @common_params - } - ######################################################################## - - # Added per users` requests - if ($null -ne $app_stop_method_console) - { - Update-NssmServiceParameter -parameter "AppStopMethodConsole" -value $app_stop_method_console @common_params - } - - if ($null -ne $app_stop_method_skip) - { - Update-NssmServiceParameter -parameter "AppStopMethodSkip" -value $app_stop_method_skip @common_params - } - } -} - -if ($diff_mode -and $result.changed -eq $true) { - $result.diff = @{ - prepared = $diff_text - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_nssm.py b/lib/ansible/modules/windows/win_nssm.py deleted file mode 100644 index 1d15676e91..0000000000 --- a/lib/ansible/modules/windows/win_nssm.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Heyo -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_nssm -version_added: "2.0" -short_description: Install a service using NSSM -description: - - Install a Windows service using the NSSM wrapper. - - NSSM is a service helper which doesn't suck. See U(https://nssm.cc/) for more information. -requirements: - - "nssm >= 2.24.0 # (install via M(win_chocolatey)) C(win_chocolatey: name=nssm)" -options: - name: - description: - - Name of the service to operate on. - type: str - required: true - state: - description: - - State of the service on the system. - - Values C(started), C(stopped), and C(restarted) are deprecated since v2.8, - please use the M(win_service) module instead to start, stop or restart the service. - type: str - choices: [ absent, present, started, stopped, restarted ] - default: present - application: - description: - - The application binary to run as a service - - Required when I(state) is C(present), C(started), C(stopped), or C(restarted). - type: path - executable: - description: - - The location of the NSSM utility (in case it is not located in your PATH). - type: path - default: nssm.exe - version_added: "2.8.0" - description: - description: - - The description to set for the service. - type: str - version_added: "2.8.0" - display_name: - description: - - The display name to set for the service. - type: str - version_added: "2.8.0" - working_directory: - version_added: "2.8.0" - description: - - The working directory to run the service executable from (defaults to the directory containing the application binary) - type: path - aliases: [ app_directory, chdir ] - stdout_file: - description: - - Path to receive output. - type: path - stderr_file: - description: - - Path to receive error output. - type: path - app_parameters: - description: - - A string representing a dictionary of parameters to be passed to the application when it starts. - - DEPRECATED since v2.8, please use I(arguments) instead. - - This is mutually exclusive with I(arguments). - type: str - arguments: - description: - - Parameters to be passed to the application when it starts. - - This can be either a simple string or a list. - - This parameter was renamed from I(app_parameters_free_form) in 2.8. - - This is mutually exclusive with I(app_parameters). - aliases: [ app_parameters_free_form ] - type: str - version_added: "2.3" - dependencies: - description: - - Service dependencies that has to be started to trigger startup, separated by comma. - - DEPRECATED since v2.8, please use the M(win_service) module instead. - type: list - user: - description: - - User to be used for service startup. - - DEPRECATED since v2.8, please use the M(win_service) module instead. - type: str - password: - description: - - Password to be used for service startup. - - DEPRECATED since v2.8, please use the M(win_service) module instead. - type: str - start_mode: - description: - - If C(auto) is selected, the service will start at bootup. - - C(delayed) causes a delayed but automatic start after boot (added in version 2.5). - - C(manual) means that the service will start only when another service needs it. - - C(disabled) means that the service will stay off, regardless if it is needed or not. - - DEPRECATED since v2.8, please use the M(win_service) module instead. - type: str - choices: [ auto, delayed, disabled, manual ] - default: auto - app_rotate_bytes: - description: - - NSSM will not rotate any file which is smaller than the configured number of bytes. - type: int - default: 104858 - version_added: "2.10" - app_rotate_online: - description: - - If set to 1, nssm can rotate files which grow to the configured file size limit while the service is running. - type: int - choices: - - 0 - - 1 - default: 0 - version_added: "2.10" - app_stop_method_console: - description: - - Time to wait after sending Control-C. - type: int - version_added: "2.10" - app_stop_method_skip: - description: - - To disable service shutdown methods, set to the sum of one or more of the numbers - - 1 - Don't send Control-C to the console. - - 2 - Don't send WM_CLOSE to windows. - - 4 - Don't send WM_QUIT to threads. - - 8 - Don't call TerminateProcess(). - type: int - choices: - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - - 13 - - 14 - - 15 - version_added: "2.10" -seealso: - - module: win_service -notes: - - The service will NOT be started after its creation when C(state=present). - - Once the service is created, you can use the M(win_service) module to start it or configure - some additionals properties, such as its startup type, dependencies, service account, and so on. -author: - - Adam Keech (@smadam813) - - George Frank (@georgefrank) - - Hans-Joachim Kliemeck (@h0nIg) - - Michael Wild (@themiwi) - - Kevin Subileau (@ksubileau) - - Shachaf Goldstein (@Shachaf92) -''' - -EXAMPLES = r''' -- name: Install the foo service - win_nssm: - name: foo - application: C:\windows\foo.exe - -# This will yield the following command: C:\windows\foo.exe bar "true" -- name: Install the Consul service with a list of parameters - win_nssm: - name: Consul - application: C:\consul\consul.exe - arguments: - - agent - - -config-dir=C:\consul\config - -# This is strictly equivalent to the previous example -- name: Install the Consul service with an arbitrary string of parameters - win_nssm: - name: Consul - application: C:\consul\consul.exe - arguments: agent -config-dir=C:\consul\config - - -# Install the foo service, and then configure and start it with win_service -- name: Install the foo service, redirecting stdout and stderr to the same file - win_nssm: - name: foo - application: C:\windows\foo.exe - stdout_file: C:\windows\foo.log - stderr_file: C:\windows\foo.log - -- name: Configure and start the foo service using win_service - win_service: - name: foo - dependencies: [ adf, tcpip ] - username: foouser - password: secret - start_mode: manual - state: started - -- name: Remove the foo service - win_nssm: - name: foo - state: absent -''' diff --git a/lib/ansible/modules/windows/win_pagefile.ps1 b/lib/ansible/modules/windows/win_pagefile.ps1 deleted file mode 100644 index 2c37f200c1..0000000000 --- a/lib/ansible/modules/windows/win_pagefile.ps1 +++ /dev/null @@ -1,202 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Liran Nisanov <lirannis@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -######## - -Function Remove-Pagefile($path, $whatif) -{ - Get-CIMInstance Win32_PageFileSetting | Where-Object { $_.Name -eq $path } | Remove-CIMInstance -WhatIf:$whatif -} - -Function Get-Pagefile($path) -{ - Get-CIMInstance Win32_PageFileSetting | Where-Object { $_.Name -eq $path } -} - -######## - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name '_ansible_check_mode' -type 'bool' -default $false - -$automatic = Get-AnsibleParam -obj $params -name "automatic" -type "bool" -$drive = Get-AnsibleParam -obj $params -name "drive" -type "str" -$fullPath = $drive + ":\pagefile.sys" -$initialSize = Get-AnsibleParam -obj $params -name "initial_size" -type "int" -$maximumSize = Get-AnsibleParam -obj $params -name "maximum_size" -type "int" -$override = Get-AnsibleParam -obj $params -name "override" -type "bool" -default $true -$removeAll = Get-AnsibleParam -obj $params -name "remove_all" -type "bool" -default $false -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "query" -validateset "present","absent","query" -$systemManaged = Get-AnsibleParam -obj $params -name "system_managed" -type "bool" -default $false -$testPath = Get-AnsibleParam -obj $params -name "test_path" -type "bool" -default $true - -$result = @{ - changed = $false -} - -if ($removeAll) { - $currentPageFiles = Get-CIMInstance Win32_PageFileSetting - if ($null -ne $currentPageFiles) { - $currentPageFiles | Remove-CIMInstance -WhatIf:$check_mode > $null - $result.changed = $true - } -} - -if ($null -ne $automatic) { - # change autmoatic managed pagefile - try { - $computerSystem = Get-CIMInstance -Class win32_computersystem - } catch { - Fail-Json $result "Failed to query WMI computer system object $($_.Exception.Message)" - } - if ($computerSystem.AutomaticManagedPagefile -ne $automatic) { - if (-not $check_mode) { - try { - $computerSystem | Set-CimInstance -Property @{automaticmanagedpagefile="$automatic"} > $null - } catch { - Fail-Json $result "Failed to set AutomaticManagedPagefile $($_.Exception.Message)" - } - } - $result.changed = $true - } -} - -if ($state -eq "absent") { - # Remove pagefile - if ($null -ne (Get-Pagefile $fullPath)) - { - try { - Remove-Pagefile $fullPath -whatif:$check_mode - } catch { - Fail-Json $result "Failed to remove pagefile $($_.Exception.Message)" - } - $result.changed = $true - } -} elseif ($state -eq "present") { - # Remove current pagefile - if ($override) { - if ($null -ne (Get-Pagefile $fullPath)) - { - try { - Remove-Pagefile $fullPath -whatif:$check_mode - } catch { - Fail-Json $result "Failed to remove current pagefile $($_.Exception.Message)" - } - $result.changed = $true - } - } - - # Make sure drive is accessible - if (($test_path) -and (-not (Test-Path "${drive}:"))) { - Fail-Json $result "Unable to access '${drive}:' drive" - } - - $curPagefile = Get-Pagefile $fullPath - - # Set pagefile - if ($null -eq $curPagefile) { - try { - $pagefile = New-CIMInstance -Class Win32_PageFileSetting -Arguments @{name = $fullPath;} -WhatIf:$check_mode - } catch { - Fail-Json $result "Failed to create pagefile $($_.Exception.Message)" - } - if (-not ($systemManaged -or $check_mode)) { - try { - $pagefile | Set-CimInstance -Property @{ InitialSize = $initialSize; MaximumSize = $maximumSize} - } catch { - $originalExceptionMessage = $($_.Exception.Message) - # Try workaround before failing - try { - Remove-Pagefile $fullPath -whatif:$check_mode - } catch { - Fail-Json $result "Failed to remove pagefile before workaround $($_.Exception.Message) Original exception: $originalExceptionMessage" - } - try { - $pagingFilesValues = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management").PagingFiles - } catch { - Fail-Json $result "Failed to get pagefile settings from the registry for workaround $($_.Exception.Message) Original exception: $originalExceptionMessage" - } - $pagingFilesValues += "$fullPath $initialSize $maximumSize" - try { - Set-ItemProperty -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" "PagingFiles" $pagingFilesValues - } catch { - Fail-Json $result "Failed to set pagefile settings to the registry for workaround $($_.Exception.Message) Original exception: $originalExceptionMessage" - } - } - } - $result.changed = $true - }else - { - $CurPageFileSystemManaged = (Get-CimInstance -ClassName win32_Pagefile -Property 'System' -Filter "name='$($fullPath.Replace('\','\\'))'").System - if ((-not $check_mode) -and - -not ($systemManaged -or $CurPageFileSystemManaged) -and - ( ($curPagefile.InitialSize -ne $initialSize) -or - ($curPagefile.maximumSize -ne $maximumSize))) - { - $curPagefile.InitialSize = $initialSize - $curPagefile.MaximumSize = $maximumSize - try { - $curPagefile.Put() | out-null - } catch { - $originalExceptionMessage = $($_.Exception.Message) - # Try workaround before failing - try { - Remove-Pagefile $fullPath -whatif:$check_mode - } catch { - Fail-Json $result "Failed to remove pagefile before workaround $($_.Exception.Message) Original exception: $originalExceptionMessage" - } - try { - $pagingFilesValues = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management").PagingFiles - } catch { - Fail-Json $result "Failed to get pagefile settings from the registry for workaround $($_.Exception.Message) Original exception: $originalExceptionMessage" - } - $pagingFilesValues += "$fullPath $initialSize $maximumSize" - try { - Set-ItemProperty -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" -Name "PagingFiles" -Value $pagingFilesValues - } catch { - Fail-Json $result "Failed to set pagefile settings to the registry for workaround $($_.Exception.Message) Original exception: $originalExceptionMessage" - } - } - $result.changed = $true - } - } -} elseif ($state -eq "query") { - $result.pagefiles = @() - - if ($null -eq $drive) { - try { - $pagefiles = Get-CIMInstance Win32_PageFileSetting - } catch { - Fail-Json $result "Failed to query all pagefiles $($_.Exception.Message)" - } - } else { - try { - $pagefiles = Get-Pagefile $fullPath - } catch { - Fail-Json $result "Failed to query specific pagefile $($_.Exception.Message)" - } - } - - # Get all pagefiles - foreach ($currentPagefile in $pagefiles) { - $currentPagefileObject = @{ - name = $currentPagefile.Name - initial_size = $currentPagefile.InitialSize - maximum_size = $currentPagefile.MaximumSize - caption = $currentPagefile.Caption - description = $currentPagefile.Description - } - $result.pagefiles += ,$currentPagefileObject - } - - # Get automatic managed pagefile state - try { - $result.automatic_managed_pagefiles = (Get-CIMInstance -Class win32_computersystem).AutomaticManagedPagefile - } catch { - Fail-Json $result "Failed to query automatic managed pagefile state $($_.Exception.Message)" - } -} -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_pagefile.py b/lib/ansible/modules/windows/win_pagefile.py deleted file mode 100644 index af65d97772..0000000000 --- a/lib/ansible/modules/windows/win_pagefile.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Liran Nisanov <lirannis@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_pagefile -version_added: "2.4" -short_description: Query or change pagefile configuration -description: - - Query current pagefile configuration. - - Enable/Disable AutomaticManagedPagefile. - - Create new or override pagefile configuration. -options: - drive: - description: - - The drive of the pagefile. - type: str - initial_size: - description: - - The initial size of the pagefile in megabytes. - type: int - maximum_size: - description: - - The maximum size of the pagefile in megabytes. - type: int - override: - description: - - Override the current pagefile on the drive. - type: bool - default: yes - system_managed: - description: - - Configures current pagefile to be managed by the system. - type: bool - default: no - automatic: - description: - - Configures AutomaticManagedPagefile for the entire system. - type: bool - remove_all: - description: - - Remove all pagefiles in the system, not including automatic managed. - type: bool - default: no - test_path: - description: - - Use Test-Path on the drive to make sure the drive is accessible before creating the pagefile. - type: bool - default: yes - state: - description: - - State of the pagefile. - type: str - choices: [ absent, present, query ] - default: query -notes: -- There is difference between automatic managed pagefiles that configured once for the entire system and system managed pagefile that configured per pagefile. -- InitialSize 0 and MaximumSize 0 means the pagefile is managed by the system. -- Value out of range exception may be caused by several different issues, two common problems - No such drive, Pagefile size is too small. -- Setting a pagefile when AutomaticManagedPagefile is on will disable the AutomaticManagedPagefile. -author: -- Liran Nisanov (@LiranNis) -''' - -EXAMPLES = r''' -- name: Query pagefiles configuration - win_pagefile: - -- name: Query C pagefile - win_pagefile: - drive: C - -- name: Set C pagefile, don't override if exists - win_pagefile: - drive: C - initial_size: 1024 - maximum_size: 1024 - override: no - state: present - -- name: Set C pagefile, override if exists - win_pagefile: - drive: C - initial_size: 1024 - maximum_size: 1024 - state: present - -- name: Remove C pagefile - win_pagefile: - drive: C - state: absent - -- name: Remove all current pagefiles, enable AutomaticManagedPagefile and query at the end - win_pagefile: - remove_all: yes - automatic: yes - -- name: Remove all pagefiles disable AutomaticManagedPagefile and set C pagefile - win_pagefile: - drive: C - initial_size: 2048 - maximum_size: 2048 - remove_all: yes - automatic: no - state: present - -- name: Set D pagefile, override if exists - win_pagefile: - drive: d - initial_size: 1024 - maximum_size: 1024 - state: present -''' - -RETURN = r''' -automatic_managed_pagefiles: - description: Whether the pagefiles is automatically managed. - returned: When state is query. - type: bool - sample: true -pagefiles: - description: Contains caption, description, initial_size, maximum_size and name for each pagefile in the system. - returned: When state is query. - type: list - sample: - [{"caption": "c:\\ 'pagefile.sys'", "description": "'pagefile.sys' @ c:\\", "initial_size": 2048, "maximum_size": 2048, "name": "c:\\pagefile.sys"}, - {"caption": "d:\\ 'pagefile.sys'", "description": "'pagefile.sys' @ d:\\", "initial_size": 1024, "maximum_size": 1024, "name": "d:\\pagefile.sys"}] - -''' diff --git a/lib/ansible/modules/windows/win_partition.ps1 b/lib/ansible/modules/windows/win_partition.ps1 deleted file mode 100644 index 2bd1ffc0d3..0000000000 --- a/lib/ansible/modules/windows/win_partition.ps1 +++ /dev/null @@ -1,326 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Varun Chopra (@chopraaa) <v@chopraaa.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic -#AnsibleRequires -OSVersion 6.2 - -Set-StrictMode -Version 2 - -$ErrorActionPreference = "Stop" - -$spec = @{ - options = @{ - state = @{ type = "str"; choices = "absent", "present"; default = "present" } - drive_letter = @{ type = "str" } - disk_number = @{ type = "int" } - partition_number = @{ type = "int" } - partition_size = @{ type = "str" } - read_only = @{ type = "bool" } - active = @{ type = "bool" } - hidden = @{ type = "bool" } - offline = @{ type = "bool" } - mbr_type = @{ type = "str"; choices = "fat12", "fat16", "extended", "huge", "ifs", "fat32" } - gpt_type = @{ type = "str"; choices = "system_partition", "microsoft_reserved", "basic_data", "microsoft_recovery" } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$state = $module.Params.state -$drive_letter = $module.Params.drive_letter -$disk_number = $module.Params.disk_number -$partition_number = $module.Params.partition_number -$partition_size = $module.Params.partition_size -$read_only = $module.Params.read_only -$active = $module.Params.active -$hidden = $module.Params.hidden -$offline = $module.Params.offline -$mbr_type = $module.Params.mbr_type -$gpt_type = $module.Params.gpt_type - -$size_is_maximum = $false -$ansible_partition = $false -$ansible_partition_size = $null -$partition_style = $null - -$gpt_styles = @{ - system_partition = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" - microsoft_reserved = "e3c9e316-0b5c-4db8-817d-f92df00215ae" - basic_data = "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" - microsoft_recovery = "de94bba4-06d1-4d40-a16a-bfd50179d6ac" -} - -$mbr_styles = @{ - fat12 = 1 - fat16 = 4 - extended = 5 - huge = 6 - ifs = 7 - fat32 = 12 -} - -function Convert-SizeToBytes { - param( - $Size, - $Units - ) - - switch ($Units) { - "B" { return $Size } - "KB" { return 1000 * $Size } - "KiB" { return 1024 * $Size } - "MB" { return [Math]::Pow(1000, 2) * $Size } - "MiB" { return [Math]::Pow(1024, 2) * $Size } - "GB" { return [Math]::Pow(1000, 3) * $Size } - "GiB" { return [Math]::Pow(1024, 3) * $Size } - "TB" { return [Math]::Pow(1000, 4) * $Size } - "TiB" { return [Math]::Pow(1024, 4) * $Size } - } -} - -if ($null -ne $partition_size) { - if ($partition_size -eq -1) { - $size_is_maximum = $true - } - elseif ($partition_size -match '^(?<Size>[0-9]+)[ ]*(?<Units>b|kb|kib|mb|mib|gb|gib|tb|tib)$') { - $ansible_partition_size = Convert-SizeToBytes -Size $Matches.Size -Units $Matches.Units - } - else { - $module.FailJson("Invalid partition size. B, KB, KiB, MB, MiB, GB, GiB, TB, TiB are valid partition size units") - } -} - -# If partition_exists, we can change or delete it; otherwise we only need the disk to create a new partition -if ($null -ne $disk_number -and $null -ne $partition_number) { - $ansible_partition = Get-Partition -DiskNumber $disk_number -PartitionNumber $partition_number -ErrorAction SilentlyContinue -} -# Check if drive_letter is either auto-assigned or a character from A-Z -elseif ($drive_letter -and -not ($disk_number -and $partition_number)) { - if ($drive_letter -eq "auto" -or $drive_letter -match "^[a-zA-Z]$") { - $ansible_partition = Get-Partition -DriveLetter $drive_letter -ErrorAction SilentlyContinue - } - else { - $module.FailJson("Incorrect usage of drive_letter: specify a drive letter from A-Z or use 'auto' to automatically assign a drive letter") - } -} -elseif ($disk_number) { - try { - Get-Disk -Number $disk_number | Out-Null - } catch { - $module.FailJson("Specified disk does not exist") - } -} -else { - $module.FailJson("You must provide disk_number, partition_number") -} - -# Partition can't have two partition styles -if ($null -ne $gpt_type -and $null -ne $mbr_type) { - $module.FailJson("Cannot specify both GPT and MBR partition styles. Check which partition style is supported by the disk") -} - -function New-AnsiblePartition { - param( - $DiskNumber, - $Letter, - $SizeMax, - $Size, - $MbrType, - $GptType, - $Style - ) - - $parameters = @{ - DiskNumber = $DiskNumber - } - - if ($null -ne $Letter) { - switch ($Letter) { - "auto" { - $parameters.Add("AssignDriveLetter", $True) - } - default { - $parameters.Add("DriveLetter", $Letter) - } - } - } - - if ($null -ne $Size) { - $parameters.Add("Size", $Size) - } - - if ($null -ne $MbrType) { - $parameters.Add("MbrType", $Style) - } - - if ($null -ne $GptType) { - $parameters.Add("GptType", $Style) - } - - try { - $new_partition = New-Partition @parameters - } catch { - $module.FailJson("Unable to create a new partition: $($_.Exception.Message)", $_) - } - - return $new_partition -} - - -function Set-AnsiblePartitionState { - param( - $hidden, - $read_only, - $active, - $partition - ) - - $parameters = @{ - DiskNumber = $partition.DiskNumber - PartitionNumber = $partition.PartitionNumber - } - - if ($hidden -NotIn ($null, $partition.IsHidden)) { - $parameters.Add("IsHidden", $hidden) - } - - if ($read_only -NotIn ($null, $partition.IsReadOnly)) { - $parameters.Add("IsReadOnly", $read_only) - } - - if ($active -NotIn ($null, $partition.IsActive)) { - $parameters.Add("IsActive", $active) - } - - try { - Set-Partition @parameters - } catch { - $module.FailJson("Error changing state of partition: $($_.Exception.Message)", $_) - } -} - - -if ($ansible_partition) { - if ($state -eq "absent") { - try { - Remove-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -Confirm:$false -WhatIf:$module.CheckMode - } catch { - $module.FailJson("There was an error removing the partition: $($_.Exception.Message)", $_) - } - $module.Result.changed = $true - } - else { - - if ($null -ne $gpt_type -and $gpt_styles.$gpt_type -ne $ansible_partition.GptType) { - $module.FailJson("gpt_type is not a valid parameter for existing partitions") - } - if ($null -ne $mbr_type -and $mbr_styles.$mbr_type -ne $ansible_partition.MbrType) { - $module.FailJson("mbr_type is not a valid parameter for existing partitions") - } - - if ($partition_size) { - try { - $max_supported_size = (Get-PartitionSupportedSize -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber).SizeMax - } catch { - $module.FailJson("Unable to get maximum supported partition size: $($_.Exception.Message)", $_) - } - if ($size_is_maximum) { - $ansible_partition_size = $max_supported_size - } - if ($ansible_partition_size -ne $ansible_partition.Size -and ($ansible_partition_size - $ansible_partition.Size -gt 1049000 -or $ansible_partition.Size - $ansible_partition_size -gt 1049000)) { - if ($ansible_partition.IsReadOnly) { - $module.FailJson("Unable to resize partition: Partition is read only") - } else { - try { - Resize-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -Size $ansible_partition_size -WhatIf:$module.CheckMode - } catch { - $module.FailJson("Unable to change partition size: $($_.Exception.Message)", $_) - } - $module.Result.changed = $true - } - } elseif ($ansible_partition_size -gt $max_supported_size) { - $module.FailJson("Specified partition size exceeds size supported by the partition") - } - } - - if ($drive_letter -NotIn ("auto", $null, $ansible_partition.DriveLetter)) { - if (-not $module.CheckMode) { - try { - Set-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -NewDriveLetter $drive_letter - } catch { - $module.FailJson("Unable to change drive letter: $($_.Exception.Message)", $_) - } - } - $module.Result.changed = $true - } - } -} -else { - if ($state -eq "present") { - if ($null -eq $disk_number) { - $module.FailJson("Missing required parameter: disk_number") - } - if ($null -eq $ansible_partition_size -and -not $size_is_maximum){ - $module.FailJson("Missing required parameter: partition_size") - } - if (-not $size_is_maximum) { - try { - $max_supported_size = (Get-Disk -Number $disk_number).LargestFreeExtent - } catch { - $module.FailJson("Unable to get maximum size supported by disk: $($_.Exception.Message)", $_) - } - - if ($ansible_partition_size -gt $max_supported_size) { - $module.FailJson("Partition size is not supported by disk. Use partition_size: -1 to get maximum size") - } - } else { - $ansible_partition_size = (Get-Disk -Number $disk_number).LargestFreeExtent - } - - $supp_part_type = (Get-Disk -Number $disk_number).PartitionStyle - if ($null -ne $mbr_type) { - if ($supp_part_type -eq "MBR" -and $mbr_styles.ContainsKey($mbr_type)) { - $partition_style = $mbr_styles.$mbr_type - } else { - $module.FailJson("Incorrect partition style specified") - } - } - if ($null -ne $gpt_type) { - if ($supp_part_type -eq "GPT" -and $gpt_styles.ContainsKey($gpt_type)) { - $partition_style = $gpt_styles.$gpt_type - } else { - $module.FailJson("Incorrect partition style specified") - } - } - - if (-not $module.CheckMode) { - $ansible_partition = New-AnsiblePartition -DiskNumber $disk_number -Letter $drive_letter -Size $ansible_partition_size -MbrType $mbr_type -GptType $gpt_type -Style $partition_style - } - $module.Result.changed = $true - } -} - -if ($state -eq "present" -and $ansible_partition) { - if ($offline -NotIn ($null, $ansible_partition.IsOffline)) { - if (-not $module.CheckMode) { - try { - Set-Partition -DiskNumber $ansible_partition.DiskNumber -PartitionNumber $ansible_partition.PartitionNumber -IsOffline $offline - } catch { - $module.FailJson("Error setting partition offline: $($_.Exception.Message)", $_) - } - } - $module.Result.changed = $true - } - - if ($hidden -NotIn ($null, $ansible_partition.IsHidden) -or $read_only -NotIn ($null, $ansible_partition.IsReadOnly) -or $active -NotIn ($null, $ansible_partition.IsActive)) { - if (-not $module.CheckMode) { - Set-AnsiblePartitionState -hidden $hidden -read_only $read_only -active $active -partition $ansible_partition - } - $module.Result.changed = $true - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_partition.py b/lib/ansible/modules/windows/win_partition.py deleted file mode 100644 index 18d45282d4..0000000000 --- a/lib/ansible/modules/windows/win_partition.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Varun Chopra (@chopraaa) <v@chopraaa.com> -# 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_partition -version_added: '2.8' -short_description: Creates, changes and removes partitions on Windows Server -description: - - The M(win_partition) module can create, modify or delete a partition on a disk -options: - state: - description: - - Used to specify the state of the partition. Use C(absent) to specify if a partition should be removed - and C(present) to specify if the partition should be created or updated. - type: str - choices: [ absent, present] - default: present - drive_letter: - description: - - Used for accessing partitions if I(disk_number) and I(partition_number) are not provided. - - Use C(auto) for automatically assigning a drive letter, or a letter A-Z for manually assigning a drive letter to a new partition. - If not specified, no drive letter is assigned when creating a new partition. - type: str - disk_number: - description: - - Disk number is mandatory for creating new partitions. - - A combination of I(disk_number) and I(partition_number) can be used to specify the partition instead of I(drive_letter) if required. - type: int - partition_number: - description: - - Used in conjunction with I(disk_number) to uniquely identify a partition. - type: int - partition_size: - description: - - Specify size of the partition in B, KB, KiB, MB, MiB, GB, GiB, TB or TiB. Use -1 to specify maximum supported size. - - Partition size is mandatory for creating a new partition but not for updating or deleting a partition. - - The decimal SI prefixes kilo, mega, giga, tera, etc., are powers of 10^3 = 1000. The binary prefixes kibi, mebi, gibi, tebi, etc. - respectively refer to the corresponding power of 2^10 = 1024. - Thus, a gigabyte (GB) is 1000000000 (1000^3) bytes while 1 gibibyte (GiB) is 1073741824 (1024^3) bytes. - type: str - read_only: - description: - - Make the partition read only, restricting changes from being made to the partition. - type: bool - active: - description: - - Specifies if the partition is active and can be used to start the system. This property is only valid when the disk's partition style is MBR. - type: bool - hidden: - description: - - Hides the target partition, making it undetectable by the mount manager. - type: bool - offline: - description: - - Sets the partition offline. - - Adding a mount point (such as a drive letter) will cause the partition to go online again. - type: bool - required: no - mbr_type: - description: - - Specify the partition's MBR type if the disk's partition style is MBR. - - This only applies to new partitions. - - This does not relate to the partitions file system formatting. - type: str - choices: [ fat12, fat16, extended, huge, ifs, fat32 ] - gpt_type: - description: - - Specify the partition's GPT type if the disk's partition style is GPT. - - This only applies to new partitions. - - This does not relate to the partitions file system formatting. - type: str - choices: [ system_partition, microsoft_reserved, basic_data, microsoft_recovery ] - -notes: - - A minimum Operating System Version of 6.2 is required to use this module. To check if your OS is compatible, see - U(https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version). - - This module cannot be used for removing the drive letter associated with a partition, initializing a disk or, file system formatting. - - Idempotence works only if you're specifying a drive letter or other unique attributes such as a combination of disk number and partition number. - - For more information, see U(https://msdn.microsoft.com/en-us/library/windows/desktop/hh830524.aspx). -author: - - Varun Chopra (@chopraaa) <v@chopraaa.com> -''' - -EXAMPLES = r''' -- name: Create a partition with drive letter D and size 5 GiB - win_partition: - drive_letter: D - partition_size: 5 GiB - disk_number: 1 - -- name: Resize previously created partition to it's maximum size and change it's drive letter to E - win_partition: - drive_letter: E - partition_size: -1 - partition_number: 1 - disk_number: 1 - -- name: Delete partition - win_partition: - disk_number: 1 - partition_number: 1 - state: absent -''' - -RETURN = r''' -# -''' diff --git a/lib/ansible/modules/windows/win_pester.ps1 b/lib/ansible/modules/windows/win_pester.ps1 deleted file mode 100644 index f3f2ebdced..0000000000 --- a/lib/ansible/modules/windows/win_pester.ps1 +++ /dev/null @@ -1,116 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Erwan Quelin (@equelin) <erwan.quelin@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -$spec = @{ - options = @{ - output_file = @{ type = "str" } - output_format = @{ type = "str"; default = "NunitXML" } - path = @{ type = "str"; required = $true } - tags = @{ type = "list"; elements = "str" } - test_parameters = @{ type = "dict" } - version = @{ type = "str"; aliases = @(,"minimum_version") } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$output_file = $module.Params.output_file -$output_format = $module.Params.output_format -$path = $module.Params.path -$tags = $module.Params.tags -$test_parameters = $module.Params.test_parameters -$version = $module.Params.version - -Try { - $version = [version]$version -} -Catch { - $module.FailJson("Value '$version' for parameter 'minimum_version' is not a valid version format") -} - -# Make sure path is a real path -Try { - $path = $path.TrimEnd("\") - $path = (Get-item -LiteralPath $path).FullName -} -Catch { - $module.FailJson("Cannot find file or directory: '$path' as it does not exist") -} - -# Import Pester module if available -$Pester = 'Pester' - -If (-not (Get-Module -Name $Pester -ErrorAction SilentlyContinue)) { - If (Get-Module -Name $Pester -ListAvailable -ErrorAction SilentlyContinue) { - Import-Module $Pester - } else { - $module.FailJson("Cannot find module: $Pester. Check if pester is installed, and if it is not, install using win_psmodule or win_chocolatey.") - } -} - -# Add actual pester's module version in the ansible's result variable -$Pester_version = (Get-Module -Name $Pester).Version.ToString() -$module.Result.pester_version = $Pester_version - -# Test if the Pester module is available with a version greater or equal than the one specified in the $version parameter -If ((-not (Get-Module -Name $Pester -ErrorAction SilentlyContinue | Where-Object {$_.Version -ge $version})) -and ($version)) { - $module.FailJson("$Pester version is not greater or equal to $version") -} - -#Prepare Invoke-Pester parameters depending of the Pester's version. -#Invoke-Pester output deactivation behave differently depending on the Pester's version -If ($module.Result.pester_version -ge "4.0.0") { - $Parameters = @{ - "show" = "none" - "PassThru" = $True - } -} else { - $Parameters = @{ - "quiet" = $True - "PassThru" = $True - } -} - -if($tags.count){ - $Parameters.Tag = $tags -} - -if($output_file){ - $Parameters.OutputFile = $output_file - $Parameters.OutputFormat = $output_format -} - -# Run Pester tests -If (Test-Path -LiteralPath $path -PathType Leaf) { - $test_parameters_check_mode_msg = '' - if ($test_parameters.keys.count) { - $Parameters.Script = @{Path = $Path ; Parameters = $test_parameters } - $test_parameters_check_mode_msg = " with $($test_parameters.keys -join ',') parameters" - } - else { - $Parameters.Script = $Path - } - - if ($module.CheckMode) { - $module.Result.output = "Run pester test in the file: $path$test_parameters_check_mode_msg" - } else { - $module.Result.output = Invoke-Pester @Parameters - } -} else { - $Parameters.Script = $path - - if ($module.CheckMode) { - $module.Result.output = "Run Pester test(s): $path" - } else { - $module.Result.output = Invoke-Pester @Parameters - } -} - -$module.Result.changed = $true - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_pester.py b/lib/ansible/modules/windows/win_pester.py deleted file mode 100644 index c17009fb70..0000000000 --- a/lib/ansible/modules/windows/win_pester.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, 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_pester -short_description: Run Pester tests on Windows hosts -version_added: "2.6" -description: - - Run Pester tests on Windows hosts. - - Test files have to be available on the remote host. -requirements: - - Pester -options: - path: - description: - - Path to a pester test file or a folder where tests can be found. - - If the path is a folder, the module will consider all ps1 files as Pester tests. - type: str - required: true - tags: - description: - - Runs only tests in Describe blocks with specified Tags values. - - Accepts multiple comma separated tags. - type: list - version_added: '2.9' - output_file: - description: - - Generates an output test report. - type: str - version_added: '2.10' - output_format: - description: - - Format of the test report to be generated. - - This parameter is to be used with output_file option. - type: str - default: NunitXML - version_added: '2.10' - test_parameters: - description: - - Allows to specify parameters to the test script. - type: dict - version_added: '2.9' - version: - description: - - Minimum version of the pester module that has to be available on the remote host. - type: str - aliases: - - minimum_version -author: - - Erwan Quelin (@equelin) - - Prasoon Karunan V (@prasoonkarunan) -''' - -EXAMPLES = r''' -- name: Get facts - setup: - -- name: Add Pester module - action: - module_name: "{{ 'win_psmodule' if ansible_powershell_version >= 5 else 'win_chocolatey' }}" - name: Pester - state: present - -- name: Run the pester test provided in the path parameter. - win_pester: - path: C:\Pester - -- name: Run the pester tests only for the tags specified. - win_pester: - path: C:\Pester\TestScript.tests - tags: CI,UnitTests - -# Run pesters tests files that are present in the specified folder -# ensure that the pester module version available is greater or equal to the version parameter. -- name: Run the pester test present in a folder and check the Pester module version. - win_pester: - path: C:\Pester\test01.test.ps1 - version: 4.1.0 - -- name: Run the pester test present in a folder with given script parameters. - win_pester: - path: C:\Pester\test04.test.ps1 - test_parameters: - Process: lsass - Service: bits - -- name: Run the pester test present in a folder and generate NunitXML test result.. - win_pester: - path: C:\Pester\test04.test.ps1 - output_file: c:\Pester\resullt\testresult.xml -''' - -RETURN = r''' -pester_version: - description: Version of the pester module found on the remote host. - returned: always - type: str - sample: 4.3.1 -output: - description: Results of the Pester tests. - returned: success - type: list - sample: false -''' diff --git a/lib/ansible/modules/windows/win_power_plan.ps1 b/lib/ansible/modules/windows/win_power_plan.ps1 deleted file mode 100644 index 9c4a871c39..0000000000 --- a/lib/ansible/modules/windows/win_power_plan.ps1 +++ /dev/null @@ -1,207 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, 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 = @{ - name = @{ type = "str"; required = $true } - } - supports_check_mode = $true -} -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$name = $module.Params.name - -$module.Result.power_plan_name = $name -$module.Result.power_plan_enabled = $null -$module.Result.all_available_plans = $null - -Add-CSharpType -References @" -using System; -using System.Runtime.InteropServices; - -namespace Ansible.WinPowerPlan -{ - public enum AccessFlags : uint - { - AccessScheme = 16, - AccessSubgroup = 17, - AccessIndividualSetting = 18 - } - - public class NativeMethods - { - [DllImport("Kernel32.dll", SetLastError = true)] - public static extern IntPtr LocalFree( - IntPtr hMen); - - [DllImport("PowrProf.dll")] - public static extern UInt32 PowerEnumerate( - IntPtr RootPowerKey, - IntPtr SchemeGuid, - IntPtr SubGroupOfPowerSettingsGuid, - AccessFlags AccessFlags, - UInt32 Index, - IntPtr Buffer, - ref UInt32 BufferSize); - - [DllImport("PowrProf.dll")] - public static extern UInt32 PowerGetActiveScheme( - IntPtr UserRootPowerKey, - out IntPtr ActivePolicyGuid); - - [DllImport("PowrProf.dll")] - public static extern UInt32 PowerReadFriendlyName( - IntPtr RootPowerKey, - Guid SchemeGuid, - IntPtr SubGroupOfPowerSettingsGuid, - IntPtr PowerSettingGuid, - IntPtr Buffer, - ref UInt32 BufferSize); - - [DllImport("PowrProf.dll")] - public static extern UInt32 PowerSetActiveScheme( - IntPtr UserRootPowerKey, - Guid SchemeGuid); - } -} -"@ - -Function Get-LastWin32ErrorMessage { - param([Int]$ErrorCode) - $exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode - $error_msg = "{0} - (Win32 Error Code {1} - 0x{1:X8})" -f $exp.Message, $ErrorCode - return $error_msg -} - -Function Get-PlanName { - param([Guid]$Plan) - - $buffer_size = 0 - $buffer = [IntPtr]::Zero - [Ansible.WinPowerPlan.NativeMethods]::PowerReadFriendlyName([IntPtr]::Zero, $Plan, [IntPtr]::Zero, [IntPtr]::Zero, - $buffer, [ref]$buffer_size) > $null - - $buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($buffer_size) - try { - $res = [Ansible.WinPowerPlan.NativeMethods]::PowerReadFriendlyName([IntPtr]::Zero, $Plan, [IntPtr]::Zero, - [IntPtr]::Zero, $buffer, [ref]$buffer_size) - - if ($res -ne 0) { - $err_msg = Get-LastWin32ErrorMessage -ErrorCode $res - $module.FailJson("Failed to get name for power scheme $Plan - $err_msg") - } - - return [System.Runtime.InteropServices.Marshal]::PtrToStringUni($buffer) - } finally { - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($buffer) - } -} - -Function Get-PowerPlans { - $plans = @{} - - $i = 0 - while ($true) { - $buffer_size = 0 - $buffer = [IntPtr]::Zero - $res = [Ansible.WinPowerPlan.NativeMethods]::PowerEnumerate([IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, - [Ansible.WinPowerPlan.AccessFlags]::AccessScheme, $i, $buffer, [ref]$buffer_size) - - if ($res -eq 259) { - # 259 == ERROR_NO_MORE_ITEMS, there are no more power plans to enumerate - break - } elseif ($res -notin @(0, 234)) { - # 0 == ERROR_SUCCESS and 234 == ERROR_MORE_DATA - $err_msg = Get-LastWin32ErrorMessage -ErrorCode $res - $module.FailJson("Failed to get buffer size on local power schemes at index $i - $err_msg") - } - - $buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($buffer_size) - try { - $res = [Ansible.WinPowerPlan.NativeMethods]::PowerEnumerate([IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, - [Ansible.WinPowerPlan.AccessFlags]::AccessScheme, $i, $buffer, [ref]$buffer_size) - - if ($res -eq 259) { - # Server 2008 does not return 259 in the first call above so we do an additional check here - break - } elseif ($res -notin @(0, 234, 259)) { - $err_msg = Get-LastWin32ErrorMessage -ErrorCode $res - $module.FailJson("Failed to enumerate local power schemes at index $i - $err_msg") - } - $scheme_guid = [System.Runtime.InteropServices.Marshal]::PtrToStructure($buffer, [Type][Guid]) - } finally { - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($buffer) - } - $scheme_name = Get-PlanName -Plan $scheme_guid - $plans.$scheme_name = $scheme_guid - - $i += 1 - } - - return $plans -} - -Function Get-ActivePowerPlan { - $buffer = [IntPtr]::Zero - $res = [Ansible.WinPowerPlan.NativeMethods]::PowerGetActiveScheme([IntPtr]::Zero, [ref]$buffer) - if ($res -ne 0) { - $err_msg = Get-LastWin32ErrorMessage -ErrorCode $res - $module.FailJson("Failed to get the active power plan - $err_msg") - } - - try { - $active_guid = [System.Runtime.InteropServices.Marshal]::PtrToStructure($buffer, [Type][Guid]) - } finally { - [Ansible.WinPowerPlan.NativeMethods]::LocalFree($buffer) > $null - } - - return $active_guid -} - -Function Set-ActivePowerPlan { - [CmdletBinding(SupportsShouldProcess=$true)] - param([Guid]$Plan) - - $res = 0 - if ($PSCmdlet.ShouldProcess($Plan, "Set Power Plan")) { - $res = [Ansible.WinPowerPlan.NativeMethods]::PowerSetActiveScheme([IntPtr]::Zero, $Plan) - } - - if ($res -ne 0) { - $err_msg = Get-LastWin32ErrorMessage -ErrorCode $res - $module.FailJson("Failed to set the active power plan to $Plan - $err_msg") - } -} - -# Get all local power plans and the current active plan -$plans = Get-PowerPlans -$active_plan = Get-ActivePowerPlan -$module.Result.all_available_plans = @{} -foreach ($plan_info in $plans.GetEnumerator()) { - $module.Result.all_available_plans.($plan_info.Key) = $plan_info.Value -eq $active_plan -} - -if ($name -notin $plans.Keys) { - $module.FailJson("Defined power_plan: ($name) is not available") -} -$plan_guid = $plans.$name -$is_active = $active_plan -eq $plans.$name -$module.Result.power_plan_enabled = $is_active - -if (-not $is_active) { - Set-ActivePowerPlan -Plan $plan_guid -WhatIf:$module.CheckMode - $module.Result.changed = $true - $module.Result.power_plan_enabled = $true - foreach ($plan_info in $plans.GetEnumerator()) { - $is_active = $plan_info.Value -eq $plan_guid - $module.Result.all_available_plans.($plan_info.Key) = $is_active - } -} - -$module.ExitJson() - diff --git a/lib/ansible/modules/windows/win_power_plan.py b/lib/ansible/modules/windows/win_power_plan.py deleted file mode 100644 index 6f358993cc..0000000000 --- a/lib/ansible/modules/windows/win_power_plan.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, 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_power_plan -short_description: Changes the power plan of a Windows system -description: - - This module will change the power plan of a Windows system to the defined string. - - Windows defaults to C(balanced) which will cause CPU throttling. In some cases it can be preferable - to change the mode to C(high performance) to increase CPU performance. -version_added: "2.4" -options: - name: - description: - - String value that indicates the desired power plan. - - The power plan must already be present on the system. - - Commonly there will be options for C(balanced) and C(high performance). - type: str - required: yes -author: - - Noah Sparks (@nwsparks) -''' - -EXAMPLES = r''' -- name: Change power plan to high performance - win_power_plan: - name: high performance -''' - -RETURN = r''' -power_plan_name: - description: Value of the intended power plan. - returned: always - type: str - sample: balanced -power_plan_enabled: - description: State of the intended power plan. - returned: success - type: bool - sample: true -all_available_plans: - description: The name and enabled state of all power plans. - returned: always - type: dict - sample: | - { - "High performance": false, - "Balanced": true, - "Power saver": false - } -''' diff --git a/lib/ansible/modules/windows/win_product_facts.ps1 b/lib/ansible/modules/windows/win_product_facts.ps1 deleted file mode 100644 index 2fbf334119..0000000000 --- a/lib/ansible/modules/windows/win_product_facts.ps1 +++ /dev/null @@ -1,85 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -# This modules does not accept any options -$spec = @{ - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -# First try to find the product key from ACPI -try { - $product_key = (Get-CimInstance -Class SoftwareLicensingService).OA3xOriginalProductKey -} catch { - $product_key = $null -} - -if (-not $product_key) { - # Else try to get it from the registry instead - try { - $data = Get-ItemPropertyValue -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" -Name DigitalProductId - } catch { - $data = $null - } - - # And for Windows 2008 R2 - if (-not $data) { - try { - $data = Get-ItemPropertyValue -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" -Name DigitalProductId4 - } catch { - $data = $null - } - } - - if ($data) { - $product_key = $null - $hexdata = $data[52..66] - $chardata = "B","C","D","F","G","H","J","K","M","P","Q","R","T","V","W","X","Y","2","3","4","6","7","8","9" - - # Decode base24 binary data - for ($i = 24; $i -ge 0; $i--) { - $k = 0 - for ($j = 14; $j -ge 0; $j--) { - $k = $k * 256 -bxor $hexdata[$j] - $hexdata[$j] = [math]::truncate($k / 24) - $k = $k % 24 - } - $product_key = $chardata[$k] + $product_key - if (($i % 5 -eq 0) -and ($i -ne 0)) { - $product_key = "-" + $product_key - } - } - } -} - -# Retrieve license information -$license_info = Get-CimInstance SoftwareLicensingProduct | Where-Object PartialProductKey - -$winlicense_status = switch ($license_info.LicenseStatus) { - 0 { "Unlicensed" } - 1 { "Licensed" } - 2 { "OOBGrace" } - 3 { "OOTGrace" } - 4 { "NonGenuineGrace" } - 5 { "Notification" } - 6 { "ExtendedGrace" } - default { $null } -} - -$winlicense_edition = $license_info.Name -$winlicense_channel = $license_info.ProductKeyChannel - -$module.Result.ansible_facts = @{ - ansible_os_product_id = (Get-CimInstance Win32_OperatingSystem).SerialNumber - ansible_os_product_key = $product_key - ansible_os_license_edition = $winlicense_edition - ansible_os_license_channel = $winlicense_channel - ansible_os_license_status = $winlicense_status -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_product_facts.py b/lib/ansible/modules/windows/win_product_facts.py deleted file mode 100644 index a85ffe8dec..0000000000 --- a/lib/ansible/modules/windows/win_product_facts.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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_product_facts -short_description: Provides Windows product and license information -description: -- Provides Windows product and license information. -version_added: '2.5' -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- name: Get product id and product key - win_product_facts: - -- name: Display Windows edition - debug: - var: ansible_os_license_edition - -- name: Display Windows license status - debug: - var: ansible_os_license_status -''' - -RETURN = r''' -ansible_facts: - description: Dictionary containing all the detailed information about the Windows product and license. - returned: always - type: complex - contains: - ansible_os_license_channel: - description: The Windows license channel. - returned: always - type: str - sample: Volume:MAK - version_added: '2.8' - ansible_os_license_edition: - description: The Windows license edition. - returned: always - type: str - sample: Windows(R) ServerStandard edition - version_added: '2.8' - ansible_os_license_status: - description: The Windows license status. - returned: always - type: str - sample: Licensed - version_added: '2.8' - ansible_os_product_id: - description: The Windows product ID. - returned: always - type: str - sample: 00326-10000-00000-AA698 - ansible_os_product_key: - description: The Windows product key. - returned: always - type: str - sample: T49TD-6VFBW-VV7HY-B2PXY-MY47H -''' diff --git a/lib/ansible/modules/windows/win_psexec.ps1 b/lib/ansible/modules/windows/win_psexec.ps1 deleted file mode 100644 index 04a512706e..0000000000 --- a/lib/ansible/modules/windows/win_psexec.ps1 +++ /dev/null @@ -1,152 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil - -# See also: https://technet.microsoft.com/en-us/sysinternals/pxexec.aspx - -$spec = @{ - options = @{ - command = @{ type='str'; required=$true } - executable = @{ type='path'; default='psexec.exe' } - hostnames = @{ type='list' } - username = @{ type='str' } - password = @{ type='str'; no_log=$true } - chdir = @{ type='path' } - wait = @{ type='bool'; default=$true } - nobanner = @{ type='bool'; default=$false } - noprofile = @{ type='bool'; default=$false } - elevated = @{ type='bool'; default=$false } - limited = @{ type='bool'; default=$false } - system = @{ type='bool'; default=$false } - interactive = @{ type='bool'; default=$false } - session = @{ type='int' } - priority = @{ type='str'; choices=@( 'background', 'low', 'belownormal', 'abovenormal', 'high', 'realtime' ) } - timeout = @{ type='int' } - } -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$command = $module.Params.command -$executable = $module.Params.executable -$hostnames = $module.Params.hostnames -$username = $module.Params.username -$password = $module.Params.password -$chdir = $module.Params.chdir -$wait = $module.Params.wait -$nobanner = $module.Params.nobanner -$noprofile = $module.Params.noprofile -$elevated = $module.Params.elevated -$limited = $module.Params.limited -$system = $module.Params.system -$interactive = $module.Params.interactive -$session = $module.Params.session -$priority = $module.Params.Priority -$timeout = $module.Params.timeout - -$module.Result.changed = $true - -If (-Not (Get-Command $executable -ErrorAction SilentlyContinue)) { - $module.FailJson("Executable '$executable' was not found.") -} - -$arguments = [System.Collections.Generic.List`1[String]]@($executable) - -If ($nobanner -eq $true) { - $arguments.Add("-nobanner") -} - -# Support running on local system if no hostname is specified -If ($hostnames) { - $hostname_argument = ($hostnames | sort -Unique) -join ',' - $arguments.Add("\\$hostname_argument") -} - -# Username is optional -If ($null -ne $username) { - $arguments.Add("-u") - $arguments.Add($username) -} - -# Password is optional -If ($null -ne $password) { - $arguments.Add("-p") - $arguments.Add($password) -} - -If ($null -ne $chdir) { - $arguments.Add("-w") - $arguments.Add($chdir) -} - -If ($wait -eq $false) { - $arguments.Add("-d") -} - -If ($noprofile -eq $true) { - $arguments.Add("-e") -} - -If ($elevated -eq $true) { - $arguments.Add("-h") -} - -If ($system -eq $true) { - $arguments.Add("-s") -} - -If ($interactive -eq $true) { - $arguments.Add("-i") - If ($null -ne $session) { - $arguments.Add($session) - } -} - -If ($limited -eq $true) { - $arguments.Add("-l") -} - -If ($null -ne $priority) { - $arguments.Add("-$priority") -} - -If ($null -ne $timeout) { - $arguments.Add("-n") - $arguments.Add($timeout) -} - -$arguments.Add("-accepteula") - -$argument_string = Argv-ToString -arguments $arguments - -# Add the command at the end of the argument string, we don't want to escape -# that as psexec doesn't expect it to be one arg -$argument_string += " $command" - -$start_datetime = [DateTime]::UtcNow -$module.Result.psexec_command = $argument_string - -$command_result = Run-Command -command $argument_string - -$end_datetime = [DateTime]::UtcNow - -$module.Result.stdout = $command_result.stdout -$module.Result.stderr = $command_result.stderr - -If ($wait -eq $true) { - $module.Result.rc = $command_result.rc -} else { - $module.Result.rc = 0 - $module.Result.pid = $command_result.rc -} - -$module.Result.start = $start_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") -$module.Result.end = $end_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") -$module.Result.delta = $($end_datetime - $start_datetime).ToString("h\:mm\:ss\.ffffff") - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_psexec.py b/lib/ansible/modules/windows/win_psexec.py deleted file mode 100644 index c3fc37e4a6..0000000000 --- a/lib/ansible/modules/windows/win_psexec.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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_psexec -version_added: '2.3' -short_description: Runs commands (remotely) as another (privileged) user -description: -- Run commands (remotely) through the PsExec service. -- Run commands as another (domain) user (with elevated privileges). -requirements: -- Microsoft PsExec -options: - command: - description: - - The command line to run through PsExec (limited to 260 characters). - type: str - required: yes - executable: - description: - - The location of the PsExec utility (in case it is not located in your PATH). - type: path - default: psexec.exe - hostnames: - description: - - The hostnames to run the command. - - If not provided, the command is run locally. - type: list - username: - description: - - The (remote) user to run the command as. - - If not provided, the current user is used. - type: str - password: - description: - - The password for the (remote) user to run the command as. - - This is mandatory in order authenticate yourself. - type: str - chdir: - description: - - Run the command from this (remote) directory. - type: path - nobanner: - description: - - Do not display the startup banner and copyright message. - - This only works for specific versions of the PsExec binary. - type: bool - default: no - version_added: '2.4' - noprofile: - description: - - Run the command without loading the account's profile. - type: bool - default: no - elevated: - description: - - Run the command with elevated privileges. - type: bool - default: no - interactive: - description: - - Run the program so that it interacts with the desktop on the remote system. - type: bool - default: no - session: - description: - - Specifies the session ID to use. - - This parameter works in conjunction with I(interactive). - - It has no effect when I(interactive) is set to C(no). - type: int - version_added: '2.7' - limited: - description: - - Run the command as limited user (strips the Administrators group and allows only privileges assigned to the Users group). - type: bool - default: no - system: - description: - - Run the remote command in the System account. - type: bool - default: no - priority: - description: - - Used to run the command at a different priority. - choices: [ abovenormal, background, belownormal, high, low, realtime ] - timeout: - description: - - The connection timeout in seconds - type: int - wait: - description: - - Wait for the application to terminate. - - Only use for non-interactive applications. - type: bool - default: yes -notes: -- More information related to Microsoft PsExec is available from - U(https://technet.microsoft.com/en-us/sysinternals/bb897553.aspx) -seealso: -- module: psexec -- module: raw -- module: win_command -- module: win_shell -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- name: Test the PsExec connection to the local system (target node) with your user - win_psexec: - command: whoami.exe - -- name: Run regedit.exe locally (on target node) as SYSTEM and interactively - win_psexec: - command: regedit.exe - interactive: yes - system: yes - -- name: Run the setup.exe installer on multiple servers using the Domain Administrator - win_psexec: - command: E:\setup.exe /i /IACCEPTEULA - hostnames: - - remote_server1 - - remote_server2 - username: DOMAIN\Administrator - password: some_password - priority: high - -- name: Run PsExec from custom location C:\Program Files\sysinternals\ - win_psexec: - command: netsh advfirewall set allprofiles state off - executable: C:\Program Files\sysinternals\psexec.exe - hostnames: [ remote_server ] - password: some_password - priority: low -''' - -RETURN = r''' -cmd: - description: The complete command line used by the module, including PsExec call and additional options. - returned: always - type: str - sample: psexec.exe -nobanner \\remote_server -u "DOMAIN\Administrator" -p "some_password" -accepteula E:\setup.exe -pid: - description: The PID of the async process created by PsExec. - returned: when C(wait=False) - type: int - sample: 1532 -rc: - description: The return code for the command. - returned: always - type: int - sample: 0 -stdout: - description: The standard output from the command. - returned: always - type: str - sample: Success. -stderr: - description: The error output from the command. - returned: always - type: str - sample: Error 15 running E:\setup.exe -''' diff --git a/lib/ansible/modules/windows/win_psmodule.ps1 b/lib/ansible/modules/windows/win_psmodule.ps1 deleted file mode 100644 index b24178e49f..0000000000 --- a/lib/ansible/modules/windows/win_psmodule.ps1 +++ /dev/null @@ -1,468 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> -# Copyright: (c) 2017, Daniele Lazzari <lazzari@mailup.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -# win_psmodule (Windows PowerShell modules Additions/Removals/Updates) - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$required_version = Get-AnsibleParam -obj $params -name "required_version" -type "str" -$minimum_version = Get-AnsibleParam -obj $params -name "minimum_version" -type "str" -$maximum_version = Get-AnsibleParam -obj $params -name "maximum_version" -type "str" -$repo = Get-AnsibleParam -obj $params -name "repository" -type "str" -$url = Get-AnsibleParam -obj $params -name "url" -type str -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present", "absent", "latest" -$allow_clobber = Get-AnsibleParam -obj $params -name "allow_clobber" -type "bool" -default $false -$skip_publisher_check = Get-AnsibleParam -obj $params -name "skip_publisher_check" -type "bool" -default $false -$allow_prerelease = Get-AnsibleParam -obj $params -name "allow_prerelease" -type "bool" -default $false - -$result = @{changed = $false - output = "" - nuget_changed = $false - repository_changed = $false} - -Function Install-NugetProvider { - Param( - [Bool]$CheckMode - ) - $PackageProvider = Get-PackageProvider -ListAvailable | Where-Object { ($_.name -eq 'Nuget') -and ($_.version -ge "2.8.5.201") } - if (-not($PackageProvider)){ - try { - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -WhatIf:$CheckMode | out-null - $result.changed = $true - $result.nuget_changed = $true - } - catch [ System.Exception ] { - $ErrorMessage = "Problems adding package provider: $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - } -} - -Function Install-PrereqModule { - Param( - [Switch]$TestInstallationOnly, - [Bool]$CheckMode - ) - - # Those are minimum required versions of modules. - $PrereqModules = @{ - PackageManagement = '1.1.7' - PowerShellGet = '1.6.0' - } - - [Bool]$PrereqModulesInstalled = $true - - ForEach ( $Name in $PrereqModules.Keys ) { - - $ExistingPrereqModule = Get-Module -ListAvailable | Where-Object { ($_.name -eq $Name) -and ($_.version -ge $PrereqModules[$Name]) } - - if ( -not $ExistingPrereqModule ) { - if ( $TestInstallationOnly ) { - $PrereqModulesInstalled = $false - } - else { - try { - $install_params = @{ - Name = $Name - MinimumVersion = $PrereqModules[$Name] - Force = $true - WhatIf = $CheckMode - } - if ((Get-Command -Name Install-Module).Parameters.ContainsKey('SkipPublisherCheck')) { - $install_params.SkipPublisherCheck = $true - } - - Install-Module @install_params > $null - - if ( $Name -eq 'PowerShellGet' ) { - # An order has to be reverted due to dependency - Remove-Module -Name PowerShellGet, PackageManagement -Force - Import-Module -Name PowerShellGet, PackageManagement -Force - } - - $result.changed = $true - } - catch [ System.Exception ] { - $ErrorMessage = "Problems adding a prerequisite module $Name $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - } - } - } - - if ( $TestInstallationOnly ) { - $PrereqModulesInstalled - } -} - -Function Get-PsModule { - Param( - [Parameter(Mandatory=$true)] - [String]$Name, - [String]$RequiredVersion, - [String]$MinimumVersion, - [String]$MaximumVersion - ) - - $ExistingModule = @{ - Exists = $false - Version = "" - } - - $ExistingModules = Get-Module -Listavailable | Where-Object {($_.name -eq $Name)} - $ExistingModulesCount = $($ExistingModules | Measure-Object).Count - - if ( $ExistingModulesCount -gt 0 ) { - - $ExistingModules | Add-Member -MemberType ScriptProperty -Name FullVersion -Value { if ( $null -ne ( $this.PrivateData ) ) { [String]"$($this.Version)-$(($this | Select-Object -ExpandProperty PrivateData).PSData.Prerelease)".TrimEnd('-') } else { [String]"$($this.Version)" } } - - if ( -not ($RequiredVersion -or - $MinimumVersion -or - $MaximumVersion) ) { - - $ReturnedModule = $ExistingModules | Select-Object -First 1 - } - elseif ( $RequiredVersion ) { - $ReturnedModule = $ExistingModules | Where-Object -FilterScript { $_.FullVersion -eq $RequiredVersion } - } - elseif ( $MinimumVersion -and $MaximumVersion ) { - $ReturnedModule = $ExistingModules | Where-Object -FilterScript { $MinimumVersion -le $_.Version -and $MaximumVersion -ge $_.Version } | Select-Object -First 1 - } - elseif ( $MinimumVersion ) { - $ReturnedModule = $ExistingModules | Where-Object -FilterScript { $MinimumVersion -le $_.Version } | Select-Object -First 1 - } - elseif ( $MaximumVersion ) { - $ReturnedModule = $ExistingModules | Where-Object -FilterScript { $MaximumVersion -ge $_.Version } | Select-Object -First 1 - } - } - - $ReturnedModuleCount = ($ReturnedModule | Measure-Object).Count - - if ( $ReturnedModuleCount -eq 1 ) { - $ExistingModule.Exists = $true - $ExistingModule.Version = $ReturnedModule.FullVersion - } - - $ExistingModule -} - -Function Add-DefinedParameter { - Param ( - [Parameter(Mandatory=$true)] - [Hashtable]$Hashtable, - [Parameter(Mandatory=$true)] - [String[]]$ParametersNames - ) - - ForEach ($ParameterName in $ParametersNames) { - $ParameterVariable = Get-Variable -Name $ParameterName -ErrorAction Ignore - if ( $ParameterVariable.Value -and $Hashtable.Keys -notcontains $ParameterName ){ - $Hashtable.Add($ParameterName,$ParameterVariable.Value) - } - } - - $Hashtable -} - -Function Install-PsModule { - Param( - [Parameter(Mandatory=$true)] - [String]$Name, - [String]$RequiredVersion, - [String]$MinimumVersion, - [String]$MaximumVersion, - [String]$Repository, - [Bool]$AllowClobber, - [Bool]$SkipPublisherCheck, - [Bool]$AllowPrerelease, - [Bool]$CheckMode - ) - - $ExistingModuleBefore = Get-PsModule -Name $Name -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion - - if ( -not $ExistingModuleBefore.Exists ) { - try { - # Install NuGet provider if needed. - Install-NugetProvider -CheckMode $CheckMode - - $ht = @{ - Name = $Name - WhatIf = $CheckMode - Force = $true - } - - [String[]]$ParametersNames = @("RequiredVersion","MinimumVersion","MaximumVersion","AllowPrerelease","AllowClobber","SkipPublisherCheck","Repository") - - $ht = Add-DefinedParameter -Hashtable $ht -ParametersNames $ParametersNames - - Install-Module @ht -ErrorVariable ErrorDetails | out-null - - $result.changed = $true - $result.output = "Module $($Name) installed" - } - catch [ System.Exception ] { - $ErrorMessage = "Problems installing $($Name) module: $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - } - else { - $result.output = "Module $($Name) already present" - } -} - -Function Remove-PsModule { - Param( - [Parameter(Mandatory=$true)] - [String]$Name, - [String]$RequiredVersion, - [String]$MinimumVersion, - [String]$MaximumVersion, - [Bool]$CheckMode - ) - # If module is present, uninstalls it. - if (Get-Module -Listavailable | Where-Object {$_.name -eq $Name}) { - try { - $ht = @{ - Name = $Name - Confirm = $false - Force = $true - } - - $ExistingModuleBefore = Get-PsModule -Name $Name -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion - - [String[]]$ParametersNames = @("RequiredVersion","MinimumVersion","MaximumVersion") - - $ht = Add-DefinedParameter -Hashtable $ht -ParametersNames $ParametersNames - - if ( -not ( $RequiredVersion -or $MinimumVersion -or $MaximumVersion ) ) { - $ht.Add("AllVersions", $true) - } - - if ( $ExistingModuleBefore.Exists) { - # The Force parameter overwrite the WhatIf parameter - if ( -not $CheckMode ) { - Uninstall-Module @ht -ErrorVariable ErrorDetails | out-null - } - $result.changed = $true - $result.output = "Module $($Name) removed" - } - } - catch [ System.Exception ] { - $ErrorMessage = "Problems uninstalling $($Name) module: $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - } - else { - $result.output = "Module $($Name) removed" - } -} - -Function Find-LatestPsModule { - Param( - [Parameter(Mandatory=$true)] - [String]$Name, - [String]$Repository, - [Bool]$AllowPrerelease, - [Bool]$CheckMode - ) - - try { - $ht = @{ - Name = $Name - } - - [String[]]$ParametersNames = @("AllowPrerelease","Repository") - - $ht = Add-DefinedParameter -Hashtable $ht -ParametersNames $ParametersNames - - $LatestModule = Find-Module @ht - $LatestModuleVersion = $LatestModule.Version - } - catch [ System.Exception ] { - $ErrorMessage = "Cant find the module $($Name): $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - - $LatestModuleVersion -} - -Function Install-Repository { - Param( - [Parameter(Mandatory=$true)] - [string]$Name, - [Parameter(Mandatory=$true)] - [string]$Url, - [bool]$CheckMode - ) - Add-DeprecationWarning -obj $result -message "Adding a repo with this module is deprecated, the repository parameter should only be used to select a repo. Use win_psrepository to manage repos" -version 2.12 - # Install NuGet provider if needed. - Install-NugetProvider -CheckMode $CheckMode - - $Repos = (Get-PSRepository).SourceLocation - - # If repository isn't already present, try to register it as trusted. - if ($Repos -notcontains $Url){ - try { - if ( -not ($CheckMode) ) { - Register-PSRepository -Name $Name -SourceLocation $Url -InstallationPolicy Trusted -ErrorAction Stop - } - $result.changed = $true - $result.repository_changed = $true - } - catch { - $ErrorMessage = "Problems registering $($Name) repository: $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - } -} - -Function Remove-Repository{ - Param( - [Parameter(Mandatory=$true)] - [string]$Name, - [bool]$CheckMode - ) - Add-DeprecationWarning -obj $result -message "Removing a repo with this module is deprecated, use win_psrepository to manage repos" -version 2.12 - - $Repo = (Get-PSRepository).Name - - # Try to remove the repository - if ($Repo -contains $Name){ - try { - if ( -not ($CheckMode) ) { - Unregister-PSRepository -Name $Name -ErrorAction Stop - } - $result.changed = $true - $result.repository_changed = $true - } - catch [ System.Exception ] { - $ErrorMessage = "Problems unregistering $($Name)repository: $($_.Exception.Message)" - Fail-Json $result $ErrorMessage - } - } -} - -# Check PowerShell version, fail if < 5.0 and required modules are not installed -$PsVersion = $PSVersionTable.PSVersion -if ($PsVersion.Major -lt 5 ) { - $PrereqModulesInstalled = Install-PrereqModule -TestInstallationOnly - if ( -not $PrereqModulesInstalled ) { - $ErrorMessage = "Modules PowerShellGet and PackageManagement in versions 1.6.0 and 1.1.7 respectively have to be installed before using the win_psmodule." - Fail-Json $result $ErrorMessage - } -} - -if ( $required_version -and ( $minimum_version -or $maximum_version ) ) { - $ErrorMessage = "Parameters required_version and minimum/maximum_version are mutually exclusive." - Fail-Json $result $ErrorMessage -} - -if ( $allow_prerelease -and ( $minimum_version -or $maximum_version ) ) { - $ErrorMessage = "Parameters minimum_version, maximum_version can't be used with the parameter allow_prerelease." - Fail-Json $result $ErrorMessage -} - -if ( $allow_prerelease -and $state -eq "absent" ) { - $ErrorMessage = "The parameter allow_prerelease can't be used with state set to 'absent'." - Fail-Json $result $ErrorMessage -} - -if ( ($state -eq "latest") -and - ( $required_version -or $minimum_version -or $maximum_version ) ) { - $ErrorMessage = "When the parameter state is equal 'latest' you can use any of required_version, minimum_version, maximum_version." - Fail-Json $result $ErrorMessage -} - -if ( $repo -and (-not $url) ) { - $RepositoryExists = Get-PSRepository -Name $repo -ErrorAction Ignore - if ( $null -eq $RepositoryExists) { - $ErrorMessage = "The repository $repo doesn't exist." - Fail-Json $result $ErrorMessage - } - -} - -if ( ($allow_clobber -or $allow_prerelease -or $skip_publisher_check -or - $required_version -or $minimum_version -or $maximum_version) ) { - # Update the PowerShellGet and PackageManagement modules. - # It's required to support AllowClobber, AllowPrerelease parameters. - Install-PrereqModule -CheckMode $check_mode -} - -Import-Module -Name PackageManagement, PowerShellGet - -if ($state -eq "present") { - if (($repo) -and ($url)) { - Install-Repository -Name $repo -Url $url -CheckMode $check_mode - } - else { - $ErrorMessage = "Repository Name and Url are mandatory if you want to add a new repository" - } - - if ($name) { - $ht = @{ - Name = $name - RequiredVersion = $required_version - MinimumVersion = $minimum_version - MaximumVersion = $maximum_version - Repository = $repo - AllowClobber = $allow_clobber - SkipPublisherCheck = $skip_publisher_check - AllowPrerelease = $allow_prerelease - CheckMode = $check_mode - } - Install-PsModule @ht - } -} -elseif ($state -eq "absent") { - if ($repo) { - Remove-Repository -Name $repo -CheckMode $check_mode - } - - if ($name) { - $ht = @{ - Name = $Name - CheckMode = $check_mode - RequiredVersion = $required_version - MinimumVersion = $minimum_version - MaximumVersion = $maximum_version - } - Remove-PsModule @ht - } -} -elseif ( $state -eq "latest") { - - $ht = @{ - Name = $Name - AllowPrerelease = $allow_prerelease - Repository = $repo - CheckMode = $check_mode - } - - $LatestVersion = Find-LatestPsModule @ht - - $ExistingModule = Get-PsModule $Name - - if ( $LatestVersion.Version -ne $ExistingModule.Version ) { - - $ht = @{ - Name = $Name - RequiredVersion = $LatestVersion - Repository = $repo - AllowClobber = $allow_clobber - SkipPublisherCheck = $skip_publisher_check - AllowPrerelease = $allow_prerelease - CheckMode = $check_mode - } - Install-PsModule @ht - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_psmodule.py b/lib/ansible/modules/windows/win_psmodule.py deleted file mode 100644 index d779e99c9c..0000000000 --- a/lib/ansible/modules/windows/win_psmodule.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> -# Copyright: (c) 2017, Daniele Lazzari <lazzari@mailup.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_psmodule -version_added: "2.4" -short_description: Adds or removes a Windows PowerShell module -description: - - This module helps to install Windows PowerShell modules and register custom modules repository on Windows-based systems. -options: - name: - description: - - Name of the Windows PowerShell module that has to be installed. - type: str - required: yes - state: - description: - - If C(present) a new module is installed. - - If C(absent) a module is removed. - - If C(latest) a module is updated to the newest version. This option was added in version 2.8. - type: str - choices: [ absent, latest, present ] - default: present - required_version: - description: - - The exact version of the PowerShell module that has to be installed. - type: str - version_added: "2.8" - minimum_version: - description: - - The minimum version of the PowerShell module that has to be installed. - type: str - version_added: "2.8" - maximum_version: - description: - - The maximum version of the PowerShell module that has to be installed. - type: str - version_added: "2.8" - allow_clobber: - description: - - If C(yes) allows install modules that contains commands those have the same names as commands that already exists. - type: bool - default: no - skip_publisher_check: - description: - - If C(yes), allows you to install a different version of a module that already exists on your computer in the case when a different one - is not digitally signed by a trusted publisher and the newest existing module is digitally signed by a trusted publisher. - type: bool - default: no - version_added: "2.8" - allow_prerelease: - description: - - If C(yes) installs modules marked as prereleases. - - It doesn't work with the parameters C(minimum_version) and/or C(maximum_version). - - It doesn't work with the C(state) set to absent. - type: bool - default: no - version_added: "2.8" - repository: - description: - - Name of the custom repository to use. - type: str - url: - description: - - URL of the custom repository to register. - - This option is deprecated and will be removed in Ansible 2.12. Use the - M(win_psrepository) module instead. - type: str -notes: - - PowerShell modules needed - - PowerShellGet >= 1.6.0 - - PackageManagement >= 1.1.7 - - PowerShell package provider needed - - NuGet >= 2.8.5.201 - - On PowerShell 5.x required modules and a package provider will be updated under the first run of the win_psmodule module. - - On PowerShell 3.x and 4.x you have to install them before using the win_psmodule. -seealso: -- module: win_psrepository -author: -- Wojciech Sciesinski (@it-praktyk) -- Daniele Lazzari (@dlazz) -''' - -EXAMPLES = r''' ---- -- name: Add a PowerShell module - win_psmodule: - name: PowerShellModule - state: present - -- name: Add an exact version of PowerShell module - win_psmodule: - name: PowerShellModule - required_version: "4.0.2" - state: present - -- name: Install or update an existing PowerShell module to the newest version - win_psmodule: - name: PowerShellModule - state: latest - -- name: Install newer version of built-in Windows module - win_psmodule: - name: Pester - skip_publisher_check: yes - state: present - -- name: Add a PowerShell module and register a repository - win_psmodule: - name: MyCustomModule - repository: MyRepository - state: present - -- name: Add a PowerShell module from a specific repository - win_psmodule: - name: PowerShellModule - repository: MyRepository - state: present - -- name: Remove a PowerShell module - win_psmodule: - name: PowerShellModule - state: absent -''' - -RETURN = r''' ---- -output: - description: A message describing the task result. - returned: always - sample: "Module PowerShellCookbook installed" - type: str -nuget_changed: - description: True when Nuget package provider is installed. - returned: always - type: bool - sample: true -repository_changed: - description: True when a custom repository is installed or removed. - returned: always - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_psrepository.ps1 b/lib/ansible/modules/windows/win_psrepository.ps1 deleted file mode 100644 index da637f02eb..0000000000 --- a/lib/ansible/modules/windows/win_psrepository.ps1 +++ /dev/null @@ -1,68 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> -# Copyright: (c) 2017, Daniele Lazzari <lazzari@mailup.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -# win_psrepository (Windows PowerShell repositories Additions/Removals/Updates) - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$source = Get-AnsibleParam -obj $params -name "source" -type "str" -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present", "absent" -$installationpolicy = Get-AnsibleParam -obj $params -name "installation_policy" -type "str" -validateset "trusted", "untrusted" - -$result = @{"changed" = $false} - -Function Update-NuGetPackageProvider { - $PackageProvider = Get-PackageProvider -ListAvailable | Where-Object { ($_.name -eq 'Nuget') -and ($_.version -ge "2.8.5.201") } - if ($null -eq $PackageProvider) { - Find-PackageProvider -Name Nuget -ForceBootstrap -IncludeDependencies -Force | Out-Null - } -} - -$Repo = Get-PSRepository -Name $name -ErrorAction Ignore -if ($state -eq "present") { - if ($null -eq $Repo) { - if ($null -eq $installationpolicy) { - $installationpolicy = "trusted" - } - if (-not $check_mode) { - Update-NuGetPackageProvider - Register-PSRepository -Name $name -SourceLocation $source -InstallationPolicy $installationpolicy - } - $result.changed = $true - } - else { - $changed_properties = @{} - - if ($Repo.SourceLocation -ne $source) { - $changed_properties.SourceLocation = $source - } - - if ($null -ne $installationpolicy -and $Repo.InstallationPolicy -ne $installationpolicy) { - $changed_properties.InstallationPolicy = $installationpolicy - } - - if ($changed_properties.Count -gt 0) { - if (-not $check_mode) { - Update-NuGetPackageProvider - Set-PSRepository -Name $name @changed_properties - } - $result.changed = $true - } - } -} -elseif ($state -eq "absent" -and $null -ne $Repo) { - if (-not $check_mode) { - Update-NuGetPackageProvider - Unregister-PSRepository -Name $name - } - $result.changed = $true -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_psrepository.py b/lib/ansible/modules/windows/win_psrepository.py deleted file mode 100644 index a4faa1c6cf..0000000000 --- a/lib/ansible/modules/windows/win_psrepository.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> -# Copyright: (c) 2017, Daniele Lazzari <lazzari@mailup.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_psrepository -version_added: "2.8" -short_description: Adds, removes or updates a Windows PowerShell repository. -description: - - This module helps to add, remove and update Windows PowerShell repository on Windows-based systems. -options: - name: - description: - - Name of the repository to work with. - type: str - required: yes - source: - description: - - Specifies the URI for discovering and installing modules from this repository. - - A URI can be a NuGet server feed (most common situation), HTTP, HTTPS, FTP or file location. - type: str - state: - description: - - If C(present) a new repository is added or updated. - - If C(absent) a repository is removed. - type: str - choices: [ absent, present ] - default: present - installation_policy: - description: - - Sets the C(InstallationPolicy) of a repository. - - Will default to C(trusted) when creating a new repository. - type: str - choices: [ trusted, untrusted ] -notes: - - PowerShell modules needed - - PowerShellGet >= 1.6.0 - - PackageManagement >= 1.1.7 - - PowerShell package provider needed - - NuGet >= 2.8.5.201 - - See the examples on how to update the NuGet package provider. - - You can not use C(win_psrepository) to re-register (add) removed PSGallery, use the command C(Register-PSRepository -Default) instead. -seealso: -- module: win_psmodule -author: -- Wojciech Sciesinski (@it-praktyk) -''' - -EXAMPLES = ''' ---- -- name: Ensure the required NuGet package provider version is installed - win_shell: Find-PackageProvider -Name Nuget -ForceBootstrap -IncludeDependencies -Force - -- name: Add a PowerShell module and register a repository - win_psrepository: - name: MyRepository - source: https://myrepo.com - state: present - -- name: Remove a PowerShell repository - win_psrepository: - name: MyRepository - state: absent -''' - -RETURN = ''' -''' diff --git a/lib/ansible/modules/windows/win_psrepository_info.ps1 b/lib/ansible/modules/windows/win_psrepository_info.ps1 deleted file mode 100644 index 93e3585bef..0000000000 --- a/lib/ansible/modules/windows/win_psrepository_info.ps1 +++ /dev/null @@ -1,68 +0,0 @@ -#!powershell - -# Copyright: (c) 2020, Brian Scholer <@briantist> -# 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.CamelConversion -#Requires -Module PowerShellGet - -$spec = @{ - options = @{ - name = @{ type = 'str' ; default = '*' } - } - supports_check_mode = $true -} -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -function Convert-ObjectToSnakeCase { - <# - .SYNOPSIS - Converts an object with CamelCase properties to a dictionary with snake_case keys. - Works in the spirit of and depends on the existing CamelConversion module util. - #> - [CmdletBinding()] - param( - [Parameter(Mandatory=$true,ValueFromPipeline=$true)] - [OutputType([System.Collections.Specialized.OrderedDictionary])] - [Object] - $InputObject , - - [Parameter()] - [Switch] - $NoRecurse , - - [Parameter()] - [Switch] - $OmitNull - ) - - Process { - $result = [Ordered]@{} - foreach ($property in $InputObject.PSObject.Properties) { - $value = $property.Value - if (-not $NoRecurse -and $value -is [System.Collections.IDictionary]) { - $value = Convert-DictToSnakeCase -dict $value - } - elseif (-not $NoRecurse -and ($value -is [Array] -or $value -is [System.Collections.ArrayList])) { - $value = Convert-ListToSnakeCase -list $value - } - elseif ($null -eq $value) { - if ($OmitNull) { - continue - } - } - elseif (-not $NoRecurse -and $value -isnot [System.ValueType] -and $value -isnot [string]) { - $value = Convert-ObjectToSnakeCase -InputObject $value - } - - $name = Convert-StringToSnakeCase -string $property.Name - $result[$name] = $value - } - $result - } -} - -$module.Result.repositories = @(Get-PSRepository -Name $module.Params.name | Convert-ObjectToSnakeCase) - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_psrepository_info.py b/lib/ansible/modules/windows/win_psrepository_info.py deleted file mode 100644 index 1bcbee1bb5..0000000000 --- a/lib/ansible/modules/windows/win_psrepository_info.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Brian Scholer <@briantist> -# 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_psrepository_info -version_added: '2.10' -short_description: Gather information about PSRepositories -description: - - Gather information about all or a specific PSRepository. -options: - name: - description: - - The name of the repository to retrieve. - - Supports any wildcard pattern supported by C(Get-PSRepository). - - If omitted then all repositories will returned. - type: str - default: '*' -requirements: - - C(PowerShellGet) module -seealso: - - module: win_psrepository -author: - - Brian Scholer (@briantist) -''' - -EXAMPLES = r''' -- name: Get info for a single repository - win_psrepository_info: - name: PSGallery - register: repo_info - -- name: Find all repositories that start with 'MyCompany' - win_psrepository_info: - name: MyCompany* - -- name: Get info for all repositories - win_psrepository_info: - register: repo_info - -- name: Remove all repositories that don't have a publish_location set - win_psrepository: - name: "{{ item }}" - state: absent - loop: "{{ repo_info.repositories | rejectattr('publish_location', 'none') | list }}" -''' - -RETURN = r''' -repositories: - description: - - A list of repositories (or an empty list is there are none). - returned: always - type: list - elements: dict - contains: - name: - description: - - The name of the repository. - type: str - sample: PSGallery - installation_policy: - description: - - The installation policy of the repository. The sample values are the only possible values. - type: str - sample: - - Trusted - - Untrusted - trusted: - description: - - A boolean flag reflecting the value of C(installation_policy) as to whether the repository is trusted. - type: bool - package_management_provider: - description: - - The name of the package management provider for this repository. - type: str - sample: NuGet - provider_options: - description: - - Provider-specific options for this repository. - type: dict - source_location: - description: - - The location used to find and retrieve modules. This should always have a value. - type: str - sample: https://www.powershellgallery.com/api/v2 - publish_location: - description: - - The location used to publish modules. - type: str - sample: https://www.powershellgallery.com/api/v2/package/ - script_source_location: - description: - - The location used to find and retrieve scripts. - type: str - sample: https://www.powershellgallery.com/api/v2/items/psscript - script_publish_location: - description: - - The location used to publish scripts. - type: str - sample: https://www.powershellgallery.com/api/v2/package/ - registered: - description: - - Whether the module is registered. Should always be C(True) - type: bool -''' diff --git a/lib/ansible/modules/windows/win_rabbitmq_plugin.ps1 b/lib/ansible/modules/windows/win_rabbitmq_plugin.ps1 deleted file mode 100644 index 1c79f0c41e..0000000000 --- a/lib/ansible/modules/windows/win_rabbitmq_plugin.ps1 +++ /dev/null @@ -1,157 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -function Get-EnabledPlugins($rabbitmq_plugins_cmd) -{ - $list_plugins_cmd = "$rabbitmq_plugins_cmd list -E -m" - try { - $enabled_plugins = @(Invoke-Expression "& $list_plugins_cmd" | Where-Object { $_ }) - return ,$enabled_plugins - } - catch { - Fail-Json -obj $result -message "Can't execute `"$($list_plugins_cmd)`": $($_.Exception.Message)" - } -} - -function Enable-Plugin($rabbitmq_plugins_cmd, $plugin_name) -{ - $enable_plugin_cmd = "$rabbitmq_plugins_cmd enable $plugin_name" - try { - Invoke-Expression "& $enable_plugin_cmd" - } - catch { - Fail-Json -obj $result -message "Can't execute `"$($enable_plugin_cmd)`": $($_.Exception.Message)" - } -} - -function Disable-Plugin($rabbitmq_plugins_cmd, $plugin_name) -{ - $enable_plugin_cmd = "$rabbitmq_plugins_cmd disable $plugin_name" - try { - Invoke-Expression "& $enable_plugin_cmd" - } - catch { - Fail-Json -obj $result -message "Can't execute `"$($enable_plugin_cmd)`": $($_.Exception.Message)" - } -} - -function Get-RabbitmqPathFromRegistry -{ - $reg64Path = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ" - $reg32Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ" - - if (Test-Path $reg64Path) { - $regPath = $reg64Path - } elseif (Test-Path $reg32Path) { - $regPath = $reg32Path - } - - if ($regPath) { - $path = Split-Path -Parent (Get-ItemProperty $regPath "UninstallString").UninstallString - $version = (Get-ItemProperty $regPath "DisplayVersion").DisplayVersion - return "$path\rabbitmq_server-$version" - } -} - -function Get-RabbitmqBinPath($installation_path) -{ - $result = Join-Path -Path $installation_path -ChildPath 'bin' - if (Test-Path $result) { - return $result - } - - $result = Join-Path -Path $installation_path -ChildPath 'sbin' - if (Test-Path $result) { - return $result - } -} - -$ErrorActionPreference = "Stop" - -$result = @{ - changed = $false - enabled = @() - disabled = @() -} - -$params = Parse-Args $args -supports_check_mode $true; -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$names = Get-AnsibleParam -obj $params -name "names" -type "str" -failifempty $true -aliases "name" -$new_only = Get-AnsibleParam -obj $params -name "new_only" -type "bool" -default $false -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "enabled" -validateset "enabled","disabled" -$prefix = Get-AnsibleParam -obj $params -name "prefix" -type "str" - -if ($diff_support) { - $result.diff = @{} - $result.diff.prepared = "" -} - -$plugins = $names.Split(",") - -if ($prefix) { - $rabbitmq_bin_path = Get-RabbitmqBinPath -installation_path $prefix - if (-not $rabbitmq_bin_path) { - Fail-Json -obj $result -message "No binary folder in prefix `"$($prefix)`"" - } -} else { - $rabbitmq_reg_path = Get-RabbitmqPathFromRegistry - if ($rabbitmq_reg_path) { - $rabbitmq_bin_path = Get-RabbitmqBinPath -installation_path $rabbitmq_reg_path - } -} - -if ($rabbitmq_bin_path) { - $rabbitmq_plugins_cmd = "'$(Join-Path -Path $rabbitmq_bin_path -ChildPath "rabbitmq-plugins")'" -} else { - $rabbitmq_plugins_cmd = "rabbitmq-plugins" -} - -$enabled_plugins = Get-EnabledPlugins -rabbitmq_plugins_cmd $rabbitmq_plugins_cmd - -if ($state -eq "enabled") { - $plugins_to_enable = $plugins | Where-Object {-not ($enabled_plugins -contains $_)} - foreach ($plugin in $plugins_to_enable) { - if (-not $check_mode) { - Enable-Plugin -rabbitmq_plugins_cmd $rabbitmq_plugins_cmd -plugin_name $plugin - } - if ($diff_support) { - $result.diff.prepared += "+[$plugin]`n" - } - $result.enabled += $plugin - $result.changed = $true - } - - if (-not $new_only) { - $plugins_to_disable = $enabled_plugins | Where-Object {-not ($plugins -contains $_)} - foreach ($plugin in $plugins_to_disable) { - if (-not $check_mode) { - Disable-Plugin -rabbitmq_plugins_cmd $rabbitmq_plugins_cmd -plugin_name $plugin - } - if ($diff_support) { - $result.diff.prepared += "-[$plugin]`n" - } - $result.disabled += $plugin - $result.changed = $true - } - } -} else { - $plugins_to_disable = $enabled_plugins | Where-Object {$plugins -contains $_} - foreach ($plugin in $plugins_to_disable) { - if (-not $check_mode) { - Disable-Plugin -rabbitmq_plugins_cmd $rabbitmq_plugins_cmd -plugin_name $plugin - } - if ($diff_support) { - $result.diff.prepared += "-[$plugin]`n" - } - $result.disabled += $plugin - $result.changed = $true - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_rabbitmq_plugin.py b/lib/ansible/modules/windows/win_rabbitmq_plugin.py deleted file mode 100644 index 783559c595..0000000000 --- a/lib/ansible/modules/windows/win_rabbitmq_plugin.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, 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_rabbitmq_plugin -short_description: Manage RabbitMQ plugins -description: - - Manage RabbitMQ plugins. -version_added: "2.4" -options: - names: - description: - - Comma-separated list of plugin names. - type: str - required: yes - aliases: [ name ] - new_only: - description: - - Only enable missing plugins. - - Does not disable plugins that are not in the names list. - type: bool - default: no - state: - description: - - Specify if plugins are to be enabled or disabled. - type: str - choices: [ disabled, enabled ] - default: enabled - prefix: - description: - - Specify a custom install prefix to a Rabbit. - type: str -author: - - Artem Zinenko (@ar7z1) -''' - -EXAMPLES = r''' -- name: Enables the rabbitmq_management plugin - win_rabbitmq_plugin: - names: rabbitmq_management - state: enabled -''' - -RETURN = r''' -enabled: - description: List of plugins enabled during task run. - returned: always - type: list - sample: ["rabbitmq_management"] -disabled: - description: List of plugins disabled during task run. - returned: always - type: list - sample: ["rabbitmq_management"] -''' diff --git a/lib/ansible/modules/windows/win_rds_cap.ps1 b/lib/ansible/modules/windows/win_rds_cap.ps1 deleted file mode 100644 index 1bc61ce475..0000000000 --- a/lib/ansible/modules/windows/win_rds_cap.ps1 +++ /dev/null @@ -1,357 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Kevin Subileau (@ksubileau) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.SID - -$ErrorActionPreference = "Stop" - -# List of authentication methods as string. Used for parameter validation and conversion to integer flag, so order is important! -$auth_methods_set = @("none", "password", "smartcard", "both") -# List of session timeout actions as string. Used for parameter validation and conversion to integer flag, so order is important! -$session_timeout_actions_set = @("disconnect", "reauth") - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present","enabled","disabled" -$auth_method = Get-AnsibleParam -obj $params -name "auth_method" -type "str" -validateset $auth_methods_set -$order = Get-AnsibleParam -obj $params -name "order" -type "int" -$session_timeout = Get-AnsibleParam -obj $params -name "session_timeout" -type "int" -$session_timeout_action = Get-AnsibleParam -obj $params -name "session_timeout_action" -type "str" -default "disconnect" -validateset $session_timeout_actions_set -$idle_timeout = Get-AnsibleParam -obj $params -name "idle_timeout" -type "int" -$allow_only_sdrts_servers = Get-AnsibleParam -obj $params -name "allow_only_sdrts_servers" -type "bool" -$user_groups = Get-AnsibleParam -obj $params -name "user_groups" -type "list" -$computer_groups = Get-AnsibleParam -obj $params -name "computer_groups" -type "list" - -# Device redirections -$redirect_clipboard = Get-AnsibleParam -obj $params -name "redirect_clipboard" -type "bool" -$redirect_drives = Get-AnsibleParam -obj $params -name "redirect_drives" -type "bool" -$redirect_printers = Get-AnsibleParam -obj $params -name "redirect_printers" -type "bool" -$redirect_serial = Get-AnsibleParam -obj $params -name "redirect_serial" -type "bool" -$redirect_pnp = Get-AnsibleParam -obj $params -name "redirect_pnp" -type "bool" - - -function Get-CAP([string] $name) { - $cap_path = "RDS:\GatewayServer\CAP\$name" - $cap = @{ - Name = $name - } - - # Fetch CAP properties - Get-ChildItem -Path $cap_path | ForEach-Object { $cap.Add($_.Name,$_.CurrentValue) } - # Convert boolean values - $cap.Enabled = $cap.Status -eq 1 - $cap.Remove("Status") - $cap.AllowOnlySDRTSServers = $cap.AllowOnlySDRTSServers -eq 1 - - # Convert multiple choices values - $cap.AuthMethod = $auth_methods_set[$cap.AuthMethod] - $cap.SessionTimeoutAction = $session_timeout_actions_set[$cap.SessionTimeoutAction] - - # Fetch CAP device redirection settings - $cap.DeviceRedirection = @{} - Get-ChildItem -Path "$cap_path\DeviceRedirection" | ForEach-Object { $cap.DeviceRedirection.Add($_.Name, ($_.CurrentValue -eq 1)) } - - # Fetch CAP user and computer groups in Down-Level Logon format - $cap.UserGroups = @( - Get-ChildItem -Path "$cap_path\UserGroups" | - Select-Object -ExpandProperty Name | - ForEach-Object { Convert-FromSID -sid (Convert-ToSID -account_name $_) } - ) - $cap.ComputerGroups = @( - Get-ChildItem -Path "$cap_path\ComputerGroups" | - Select-Object -ExpandProperty Name | - ForEach-Object { Convert-FromSID -sid (Convert-ToSID -account_name $_) } - ) - - return $cap -} - -function Set-CAPPropertyValue { - [CmdletBinding(SupportsShouldProcess=$true)] - param ( - [Parameter(Mandatory=$true)] - [string] $name, - [Parameter(Mandatory=$true)] - [string] $property, - [Parameter(Mandatory=$true)] - $value, - [Parameter()] - $resultobj = @{} - ) - - $cap_path = "RDS:\GatewayServer\CAP\$name" - - try { - Set-Item -Path "$cap_path\$property" -Value $value -ErrorAction Stop - } catch { - Fail-Json -obj $resultobj -message "Failed to set property $property of CAP ${name}: $($_.Exception.Message)" - } -} - -$result = @{ - changed = $false -} -$diff_text = $null - -# Validate CAP name -if ($name -match "[*/\\;:?`"<>|\t]+") { - Fail-Json -obj $result -message "Invalid character in CAP name." -} - -# Validate user groups -if ($null -ne $user_groups) { - if ($user_groups.Count -lt 1) { - Fail-Json -obj $result -message "Parameter 'user_groups' cannot be an empty list." - } - - $user_groups = $user_groups | ForEach-Object { - $group = $_ - # Test that the group is resolvable on the local machine - $sid = Convert-ToSID -account_name $group - if (!$sid) { - Fail-Json -obj $result -message "$group is not a valid user group on the host machine or domain" - } - - # Return the normalized group name in Down-Level Logon format - Convert-FromSID -sid $sid - } - $user_groups = @($user_groups) -} - -# Validate computer groups -if ($null -ne $computer_groups) { - $computer_groups = $computer_groups | ForEach-Object { - $group = $_ - # Test that the group is resolvable on the local machine - $sid = Convert-ToSID -account_name $group - if (!$sid) { - Fail-Json -obj $result -message "$group is not a valid computer group on the host machine or domain" - } - - # Return the normalized group name in Down-Level Logon format - Convert-FromSID -sid $sid - } - $computer_groups = @($computer_groups) -} - -# Validate order parameter -if ($null -ne $order -and $order -lt 1) { - Fail-Json -obj $result -message "Parameter 'order' must be a strictly positive integer." -} - -# Ensure RemoteDesktopServices module is loaded -if ($null -eq (Get-Module -Name RemoteDesktopServices -ErrorAction SilentlyContinue)) { - Import-Module -Name RemoteDesktopServices -} - -# Check if a CAP with the given name already exists -$cap_exist = Test-Path -Path "RDS:\GatewayServer\CAP\$name" - -if ($state -eq 'absent') { - if ($cap_exist) { - Remove-Item -Path "RDS:\GatewayServer\CAP\$name" -Recurse -WhatIf:$check_mode - $diff_text += "-[$name]" - $result.changed = $true - } -} else { - $diff_text_added_prefix = '' - if (-not $cap_exist) { - if ($null -eq $user_groups) { - Fail-Json -obj $result -message "User groups must be defined to create a new CAP." - } - - # Auth method is required when creating a new CAP. Set it to password by default. - if ($null -eq $auth_method) { - $auth_method = "password" - } - - # Create a new CAP - if (-not $check_mode) { - $CapArgs = @{ - Name = $name - UserGroupNames = $user_groups -join ';' - } - $return = Invoke-CimMethod -Namespace Root\CIMV2\TerminalServices -ClassName Win32_TSGatewayConnectionAuthorizationPolicy -MethodName Create -Arguments $CapArgs - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to create CAP $name (code: $($return.ReturnValue))" - } - } - - $cap_exist = -not $check_mode - - $diff_text_added_prefix = '+' - $result.changed = $true - } - - $diff_text += "$diff_text_added_prefix[$name]`n" - - # We cannot configure a CAP that was created above in check mode as it won't actually exist - if($cap_exist) { - $cap = Get-CAP -Name $name - $wmi_cap = Get-CimInstance -ClassName Win32_TSGatewayConnectionAuthorizationPolicy -Namespace Root\CIMv2\TerminalServices -Filter "name='$($name)'" - - if ($state -in @('disabled', 'enabled')) { - $cap_enabled = $state -ne 'disabled' - if ($cap.Enabled -ne $cap_enabled) { - $diff_text += "-State = $(@('disabled', 'enabled')[[int]$cap.Enabled])`n+State = $state`n" - Set-CAPPropertyValue -Name $name -Property Status -Value ([int]$cap_enabled) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - } - - if ($null -ne $auth_method -and $auth_method -ne $cap.AuthMethod) { - $diff_text += "-AuthMethod = $($cap.AuthMethod)`n+AuthMethod = $auth_method`n" - Set-CAPPropertyValue -Name $name -Property AuthMethod -Value ([array]::IndexOf($auth_methods_set, $auth_method)) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $order -and $order -ne $cap.EvaluationOrder) { - # Order cannot be greater than the total number of existing CAPs (InvalidArgument exception) - $cap_count = (Get-ChildItem -Path "RDS:\GatewayServer\CAP").Count - if($order -gt $cap_count) { - Add-Warning -obj $result -message "Given value '$order' for parameter 'order' is greater than the number of existing CAPs. The actual order will be capped to '$cap_count'." - $order = $cap_count - } - - $diff_text += "-Order = $($cap.EvaluationOrder)`n+Order = $order`n" - Set-CAPPropertyValue -Name $name -Property EvaluationOrder -Value $order -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $session_timeout -and ($session_timeout -ne $cap.SessionTimeout -or $session_timeout_action -ne $cap.SessionTimeoutAction)) { - try { - Set-Item -Path "RDS:\GatewayServer\CAP\$name\SessionTimeout" ` - -Value $session_timeout ` - -SessionTimeoutAction ([array]::IndexOf($session_timeout_actions_set, $session_timeout_action)) ` - -ErrorAction Stop ` - -WhatIf:$check_mode - } catch { - Fail-Json -obj $result -message "Failed to set property ComputerGroupType of RAP ${name}: $($_.Exception.Message)" - } - - $diff_text += "-SessionTimeoutAction = $($cap.SessionTimeoutAction)`n+SessionTimeoutAction = $session_timeout_action`n" - $diff_text += "-SessionTimeout = $($cap.SessionTimeout)`n+SessionTimeout = $session_timeout`n" - $result.changed = $true - } - - if ($null -ne $idle_timeout -and $idle_timeout -ne $cap.IdleTimeout) { - $diff_text += "-IdleTimeout = $($cap.IdleTimeout)`n+IdleTimeout = $idle_timeout`n" - Set-CAPPropertyValue -Name $name -Property IdleTimeout -Value $idle_timeout -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $allow_only_sdrts_servers -and $allow_only_sdrts_servers -ne $cap.AllowOnlySDRTSServers) { - $diff_text += "-AllowOnlySDRTSServers = $($cap.AllowOnlySDRTSServers)`n+AllowOnlySDRTSServers = $allow_only_sdrts_servers`n" - Set-CAPPropertyValue -Name $name -Property AllowOnlySDRTSServers -Value ([int]$allow_only_sdrts_servers) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $redirect_clipboard -and $redirect_clipboard -ne $cap.DeviceRedirection.Clipboard) { - $diff_text += "-RedirectClipboard = $($cap.DeviceRedirection.Clipboard)`n+RedirectClipboard = $redirect_clipboard`n" - Set-CAPPropertyValue -Name $name -Property "DeviceRedirection\Clipboard" -Value ([int]$redirect_clipboard) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $redirect_drives -and $redirect_drives -ne $cap.DeviceRedirection.DiskDrives) { - $diff_text += "-RedirectDrives = $($cap.DeviceRedirection.DiskDrives)`n+RedirectDrives = $redirect_drives`n" - Set-CAPPropertyValue -Name $name -Property "DeviceRedirection\DiskDrives" -Value ([int]$redirect_drives) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $redirect_printers -and $redirect_printers -ne $cap.DeviceRedirection.Printers) { - $diff_text += "-RedirectPrinters = $($cap.DeviceRedirection.Printers)`n+RedirectPrinters = $redirect_printers`n" - Set-CAPPropertyValue -Name $name -Property "DeviceRedirection\Printers" -Value ([int]$redirect_printers) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $redirect_serial -and $redirect_serial -ne $cap.DeviceRedirection.SerialPorts) { - $diff_text += "-RedirectSerial = $($cap.DeviceRedirection.SerialPorts)`n+RedirectSerial = $redirect_serial`n" - Set-CAPPropertyValue -Name $name -Property "DeviceRedirection\SerialPorts" -Value ([int]$redirect_serial) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $redirect_pnp -and $redirect_pnp -ne $cap.DeviceRedirection.PlugAndPlayDevices) { - $diff_text += "-RedirectPnP = $($cap.DeviceRedirection.PlugAndPlayDevices)`n+RedirectPnP = $redirect_pnp`n" - Set-CAPPropertyValue -Name $name -Property "DeviceRedirection\PlugAndPlayDevices" -Value ([int]$redirect_pnp) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $user_groups) { - $groups_to_remove = @($cap.UserGroups | Where-Object { $user_groups -notcontains $_ }) - $groups_to_add = @($user_groups | Where-Object { $cap.UserGroups -notcontains $_ }) - - $user_groups_diff = $null - foreach($group in $groups_to_add) { - if (-not $check_mode) { - $return = $wmi_cap | Invoke-CimMethod -MethodName AddUserGroupNames -Arguments @{ UserGroupNames = $group } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to add user group $($group) (code: $($return.ReturnValue))" - } - } - $user_groups_diff += " +$group`n" - $result.changed = $true - } - - foreach($group in $groups_to_remove) { - if (-not $check_mode) { - $return = $wmi_cap | Invoke-CimMethod -MethodName RemoveUserGroupNames -Arguments @{ UserGroupNames = $group } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to remove user group $($group) (code: $($return.ReturnValue))" - } - } - $user_groups_diff += " -$group`n" - $result.changed = $true - } - - if($user_groups_diff) { - $diff_text += "~UserGroups`n$user_groups_diff" - } - } - - if ($null -ne $computer_groups) { - $groups_to_remove = @($cap.ComputerGroups | Where-Object { $computer_groups -notcontains $_ }) - $groups_to_add = @($computer_groups | Where-Object { $cap.ComputerGroups -notcontains $_ }) - - $computer_groups_diff = $null - foreach($group in $groups_to_add) { - if (-not $check_mode) { - $return = $wmi_cap | Invoke-CimMethod -MethodName AddComputerGroupNames -Arguments @{ ComputerGroupNames = $group } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to add computer group $($group) (code: $($return.ReturnValue))" - } - } - $computer_groups_diff += " +$group`n" - $result.changed = $true - } - - foreach($group in $groups_to_remove) { - if (-not $check_mode) { - $return = $wmi_cap | Invoke-CimMethod -MethodName RemoveComputerGroupNames -Arguments @{ ComputerGroupNames = $group } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to remove computer group $($group) (code: $($return.ReturnValue))" - } - } - $computer_groups_diff += " -$group`n" - $result.changed = $true - } - - if($computer_groups_diff) { - $diff_text += "~ComputerGroups`n$computer_groups_diff" - } - } - } -} - -if ($diff_mode -and $result.changed -eq $true) { - $result.diff = @{ - prepared = $diff_text - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_rds_cap.py b/lib/ansible/modules/windows/win_rds_cap.py deleted file mode 100644 index 6f5c587a79..0000000000 --- a/lib/ansible/modules/windows/win_rds_cap.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Kevin Subileau (@ksubileau) -# 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_rds_cap -short_description: Manage Connection Authorization Policies (CAP) on a Remote Desktop Gateway server -description: - - Creates, removes and configures a Remote Desktop connection authorization policy (RD CAP). - - A RD CAP allows you to specify the users who can connect to a Remote Desktop Gateway server. -version_added: "2.8" -author: - - Kevin Subileau (@ksubileau) -options: - name: - description: - - Name of the connection authorization policy. - type: str - required: yes - state: - description: - - The state of connection authorization policy. - - If C(absent) will ensure the policy is removed. - - If C(present) will ensure the policy is configured and exists. - - If C(enabled) will ensure the policy is configured, exists and enabled. - - If C(disabled) will ensure the policy is configured, exists, but disabled. - type: str - choices: [ absent, enabled, disabled, present ] - default: present - auth_method: - description: - - Specifies how the RD Gateway server authenticates users. - - When a new CAP is created, the default value is C(password). - type: str - choices: [ both, none, password, smartcard ] - order: - description: - - Evaluation order of the policy. - - The CAP in which I(order) is set to a value of '1' is evaluated first. - - By default, a newly created CAP will take the first position. - - If the given value exceed the total number of existing policies, - the policy will take the last position but the evaluation order - will be capped to this number. - type: int - session_timeout: - description: - - The maximum time, in minutes, that a session can be idle. - - A value of zero disables session timeout. - type: int - session_timeout_action: - description: - - The action the server takes when a session times out. - - 'C(disconnect): disconnect the session.' - - 'C(reauth): silently reauthenticate and reauthorize the session.' - type: str - choices: [ disconnect, reauth ] - default: disconnect - idle_timeout: - description: - - Specifies the time interval, in minutes, after which an idle session is disconnected. - - A value of zero disables idle timeout. - type: int - allow_only_sdrts_servers: - description: - - Specifies whether connections are allowed only to Remote Desktop Session Host servers that - enforce Remote Desktop Gateway redirection policy. - type: bool - user_groups: - description: - - A list of user groups that is allowed to connect to the Remote Gateway server. - - Required when a new CAP is created. - type: list - computer_groups: - description: - - A list of computer groups that is allowed to connect to the Remote Gateway server. - type: list - redirect_clipboard: - description: - - Allow clipboard redirection. - type: bool - redirect_drives: - description: - - Allow disk drive redirection. - type: bool - redirect_printers: - description: - - Allow printers redirection. - type: bool - redirect_serial: - description: - - Allow serial port redirection. - type: bool - redirect_pnp: - description: - - Allow Plug and Play devices redirection. - type: bool -requirements: - - Windows Server 2008R2 (6.1) or higher. - - The Windows Feature "RDS-Gateway" must be enabled. -seealso: -- module: win_rds_cap -- module: win_rds_rap -- module: win_rds_settings -''' - -EXAMPLES = r''' -- name: Create a new RDS CAP with a 30 minutes timeout and clipboard redirection enabled - win_rds_cap: - name: My CAP - user_groups: - - BUILTIN\users - session_timeout: 30 - session_timeout_action: disconnect - allow_only_sdrts_servers: yes - redirect_clipboard: yes - redirect_drives: no - redirect_printers: no - redirect_serial: no - redirect_pnp: no - state: enabled -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_rds_rap.ps1 b/lib/ansible/modules/windows/win_rds_rap.ps1 deleted file mode 100644 index deb01a9c34..0000000000 --- a/lib/ansible/modules/windows/win_rds_rap.ps1 +++ /dev/null @@ -1,282 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Kevin Subileau (@ksubileau) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.SID - -$ErrorActionPreference = "Stop" - -# List of authentication methods as string. Used for parameter validation and conversion to integer flag, so order is important! -$computer_group_types = @("rdg_group", "ad_network_resource_group", "allow_any") -$computer_group_types_wmi = @{rdg_group = "RG"; ad_network_resource_group = "CG"; allow_any = "ALL"} - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$description = Get-AnsibleParam -obj $params -name "description" -type "str" -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present","enabled","disabled" -$computer_group_type = Get-AnsibleParam -obj $params -name "computer_group_type" -type "str" -validateset $computer_group_types -$computer_group = Get-AnsibleParam -obj $params -name "computer_group" -type "str" -failifempty ($computer_group_type -eq "ad_network_resource_group" -or $computer_group_type -eq "rdg_group") -$user_groups = Get-AnsibleParam -obj $params -name "user_groups" -type "list" -$allowed_ports = Get-AnsibleParam -obj $params -name "allowed_ports" -type "list" - - -function Get-RAP([string] $name) { - $rap_path = "RDS:\GatewayServer\RAP\$name" - $rap = @{ - Name = $name - } - - # Fetch RAP properties - Get-ChildItem -Path $rap_path | ForEach-Object { $rap.Add($_.Name,$_.CurrentValue) } - # Convert boolean values - $rap.Enabled = $rap.Status -eq 1 - $rap.Remove("Status") - - # Convert computer group name from UPN to Down-Level Logon format - if($rap.ComputerGroupType -ne 2) { - $rap.ComputerGroup = Convert-FromSID -sid (Convert-ToSID -account_name $rap.ComputerGroup) - } - - # Convert multiple choices values - $rap.ComputerGroupType = $computer_group_types[$rap.ComputerGroupType] - - # Convert allowed ports from string to list - if($rap.PortNumbers -eq '*') { - $rap.PortNumbers = @("any") - } else { - $rap.PortNumbers = @($rap.PortNumbers -split ',') - } - - # Fetch RAP user groups in Down-Level Logon format - $rap.UserGroups = @( - Get-ChildItem -Path "$rap_path\UserGroups" | - Select-Object -ExpandProperty Name | - ForEach-Object { Convert-FromSID -sid (Convert-ToSID -account_name $_) } - ) - - return $rap -} - -function Set-RAPPropertyValue { - [CmdletBinding(SupportsShouldProcess=$true)] - param ( - [Parameter(Mandatory=$true)] - [string] $name, - [Parameter(Mandatory=$true)] - [string] $property, - [Parameter(Mandatory=$true)] - $value, - [Parameter()] - $resultobj = @{} - ) - - $rap_path = "RDS:\GatewayServer\RAP\$name" - - try { - Set-Item -Path "$rap_path\$property" -Value $value -ErrorAction stop - } catch { - Fail-Json -obj $resultobj -message "Failed to set property $property of RAP ${name}: $($_.Exception.Message)" - } -} - -$result = @{ - changed = $false -} -$diff_text = $null - -# Validate RAP name -if ($name -match "[*/\\;:?`"<>|\t]+") { - Fail-Json -obj $result -message "Invalid character in RAP name." -} - -# Validate user groups -if ($null -ne $user_groups) { - if ($user_groups.Count -lt 1) { - Fail-Json -obj $result -message "Parameter 'user_groups' cannot be an empty list." - } - - $user_groups = $user_groups | ForEach-Object { - $group = $_ - # Test that the group is resolvable on the local machine - $sid = Convert-ToSID -account_name $group - if (!$sid) { - Fail-Json -obj $result -message "$group is not a valid user group on the host machine or domain." - } - - # Return the normalized group name in Down-Level Logon format - Convert-FromSID -sid $sid - } - $user_groups = @($user_groups) -} - -# Validate computer group parameter -if ($computer_group_type -eq "allow_any" -and $null -ne $computer_group) { - Add-Warning -obj $result -message "Parameter 'computer_group' ignored because the computer_group_type is set to allow_any." -} elseif ($computer_group_type -eq "rdg_group" -and -not (Test-Path -Path "RDS:\GatewayServer\GatewayManagedComputerGroups\$computer_group")) { - Fail-Json -obj $result -message "$computer_group is not a valid gateway managed computer group" -} elseif ($computer_group_type -eq "ad_network_resource_group") { - $sid = Convert-ToSID -account_name $computer_group - if (!$sid) { - Fail-Json -obj $result -message "$computer_group is not a valid computer group on the host machine or domain." - } - # Ensure the group name is in Down-Level Logon format - $computer_group = Convert-FromSID -sid $sid -} - -# Validate port numbers -if ($null -ne $allowed_ports) { - foreach ($port in $allowed_ports) { - if (-not ($port -eq "any" -or ($port -is [int] -and $port -ge 1 -and $port -le 65535))) { - Fail-Json -obj $result -message "$port is not a valid port number." - } - } -} - -# Ensure RemoteDesktopServices module is loaded -if ($null -eq (Get-Module -Name RemoteDesktopServices -ErrorAction SilentlyContinue)) { - Import-Module -Name RemoteDesktopServices -} - -# Check if a RAP with the given name already exists -$rap_exist = Test-Path -Path "RDS:\GatewayServer\RAP\$name" - -if ($state -eq 'absent') { - if ($rap_exist) { - Remove-Item -Path "RDS:\GatewayServer\RAP\$name" -Recurse -WhatIf:$check_mode - $diff_text += "-[$name]" - $result.changed = $true - } -} else { - $diff_text_added_prefix = '' - if (-not $rap_exist) { - if ($null -eq $user_groups) { - Fail-Json -obj $result -message "User groups must be defined to create a new RAP." - } - - # Computer group type is required when creating a new RAP. Set it to allow connect to any resource by default. - if ($null -eq $computer_group_type) { - $computer_group_type = "allow_any" - } - - # Create a new RAP - if (-not $check_mode) { - $RapArgs = @{ - Name = $name - ResourceGroupType = 'ALL' - UserGroupNames = $user_groups -join ';' - ProtocolNames = 'RDP' - PortNumbers = '*' - } - $return = Invoke-CimMethod -Namespace Root\CIMV2\TerminalServices -ClassName Win32_TSGatewayResourceAuthorizationPolicy -MethodName Create -Arguments $RapArgs - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to create RAP $name (code: $($return.ReturnValue))" - } - } - $rap_exist = -not $check_mode - - $diff_text_added_prefix = '+' - $result.changed = $true - } - - $diff_text += "$diff_text_added_prefix[$name]`n" - - # We cannot configure a RAP that was created above in check mode as it won't actually exist - if($rap_exist) { - $rap = Get-RAP -Name $name - $wmi_rap = Get-CimInstance -ClassName Win32_TSGatewayResourceAuthorizationPolicy -Namespace Root\CIMv2\TerminalServices -Filter "name='$($name)'" - - if ($state -in @('disabled', 'enabled')) { - $rap_enabled = $state -ne 'disabled' - if ($rap.Enabled -ne $rap_enabled) { - $diff_text += "-State = $(@('disabled', 'enabled')[[int]$rap.Enabled])`n+State = $state`n" - Set-RAPPropertyValue -Name $name -Property Status -Value ([int]$rap_enabled) -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - } - - if ($null -ne $description -and $description -ne $rap.Description) { - Set-RAPPropertyValue -Name $name -Property Description -Value $description -ResultObj $result -WhatIf:$check_mode - $diff_text += "-Description = $($rap.Description)`n+Description = $description`n" - $result.changed = $true - } - - if ($null -ne $allowed_ports -and @(Compare-Object $rap.PortNumbers $allowed_ports -SyncWindow 0).Count -ne 0) { - $diff_text += "-AllowedPorts = [$($rap.PortNumbers -join ',')]`n+AllowedPorts = [$($allowed_ports -join ',')]`n" - if ($allowed_ports -contains 'any') { $allowed_ports = '*' } - Set-RAPPropertyValue -Name $name -Property PortNumbers -Value $allowed_ports -ResultObj $result -WhatIf:$check_mode - $result.changed = $true - } - - if ($null -ne $computer_group_type -and $computer_group_type -ne $rap.ComputerGroupType) { - $diff_text += "-ComputerGroupType = $($rap.ComputerGroupType)`n+ComputerGroupType = $computer_group_type`n" - if ($computer_group_type -ne "allow_any") { - $diff_text += "+ComputerGroup = $computer_group`n" - } - $return = $wmi_rap | Invoke-CimMethod -MethodName SetResourceGroup -Arguments @{ - ResourceGroupName = $computer_group - ResourceGroupType = $computer_group_types_wmi.$($computer_group_type) - } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to set computer group type to $($computer_group_type) (code: $($return.ReturnValue))" - } - - $result.changed = $true - - } elseif ($null -ne $computer_group -and $computer_group -ne $rap.ComputerGroup) { - $diff_text += "-ComputerGroup = $($rap.ComputerGroup)`n+ComputerGroup = $computer_group`n" - $return = $wmi_rap | Invoke-CimMethod -MethodName SetResourceGroup -Arguments @{ - ResourceGroupName = $computer_group - ResourceGroupType = $computer_group_types_wmi.$($rap.ComputerGroupType) - } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to set computer group name to $($computer_group) (code: $($return.ReturnValue))" - } - $result.changed = $true - } - - if ($null -ne $user_groups) { - $groups_to_remove = @($rap.UserGroups | Where-Object { $user_groups -notcontains $_ }) - $groups_to_add = @($user_groups | Where-Object { $rap.UserGroups -notcontains $_ }) - - $user_groups_diff = $null - foreach($group in $groups_to_add) { - if (-not $check_mode) { - $return = $wmi_rap | Invoke-CimMethod -MethodName AddUserGroupNames -Arguments @{ UserGroupNames = $group } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to add user group $($group) (code: $($return.ReturnValue))" - } - } - $user_groups_diff += " +$group`n" - $result.changed = $true - } - - foreach($group in $groups_to_remove) { - if (-not $check_mode) { - $return = $wmi_rap | Invoke-CimMethod -MethodName RemoveUserGroupNames -Arguments @{ UserGroupNames = $group } - if ($return.ReturnValue -ne 0) { - Fail-Json -obj $result -message "Failed to remove user group $($group) (code: $($return.ReturnValue))" - } - } - $user_groups_diff += " -$group`n" - $result.changed = $true - } - - if($user_groups_diff) { - $diff_text += "~UserGroups`n$user_groups_diff" - } - } - } -} - -if ($diff_mode -and $result.changed -eq $true) { - $result.diff = @{ - prepared = $diff_text - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_rds_rap.py b/lib/ansible/modules/windows/win_rds_rap.py deleted file mode 100644 index c6e60be102..0000000000 --- a/lib/ansible/modules/windows/win_rds_rap.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Kevin Subileau (@ksubileau) -# 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_rds_rap -short_description: Manage Resource Authorization Policies (RAP) on a Remote Desktop Gateway server -description: - - Creates, removes and configures a Remote Desktop resource authorization policy (RD RAP). - - A RD RAP allows you to specify the network resources (computers) that users can connect - to remotely through a Remote Desktop Gateway server. -version_added: "2.8" -author: - - Kevin Subileau (@ksubileau) -options: - name: - description: - - Name of the resource authorization policy. - required: yes - state: - description: - - The state of resource authorization policy. - - If C(absent) will ensure the policy is removed. - - If C(present) will ensure the policy is configured and exists. - - If C(enabled) will ensure the policy is configured, exists and enabled. - - If C(disabled) will ensure the policy is configured, exists, but disabled. - type: str - choices: [ absent, disabled, enabled, present ] - default: present - description: - description: - - Optional description of the resource authorization policy. - type: str - user_groups: - description: - - List of user groups that are associated with this resource authorization policy (RAP). - A user must belong to one of these groups to access the RD Gateway server. - - Required when a new RAP is created. - type: list - allowed_ports: - description: - - List of port numbers through which connections are allowed for this policy. - - To allow connections through any port, specify 'any'. - type: list - computer_group_type: - description: - - 'The computer group type:' - - 'C(rdg_group): RD Gateway-managed group' - - 'C(ad_network_resource_group): Active Directory Domain Services network resource group' - - 'C(allow_any): Allow users to connect to any network resource.' - type: str - choices: [ rdg_group, ad_network_resource_group, allow_any ] - computer_group: - description: - - The computer group name that is associated with this resource authorization policy (RAP). - - This is required when I(computer_group_type) is C(rdg_group) or C(ad_network_resource_group). - type: str -requirements: - - Windows Server 2008R2 (6.1) or higher. - - The Windows Feature "RDS-Gateway" must be enabled. -seealso: -- module: win_rds_cap -- module: win_rds_rap -- module: win_rds_settings -''' - -EXAMPLES = r''' -- name: Create a new RDS RAP - win_rds_rap: - name: My RAP - description: Allow all users to connect to any resource through ports 3389 and 3390 - user_groups: - - BUILTIN\users - computer_group_type: allow_any - allowed_ports: - - 3389 - - 3390 - state: enabled -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_rds_settings.ps1 b/lib/ansible/modules/windows/win_rds_settings.ps1 deleted file mode 100644 index 7a31fe1275..0000000000 --- a/lib/ansible/modules/windows/win_rds_settings.ps1 +++ /dev/null @@ -1,100 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Kevin Subileau (@ksubileau) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -# List of ssl bridging methods as string. Used for parameter validation and conversion to integer flag, so order is important! -$ssl_bridging_methods = @("none", "https_http", "https_https") - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$certificate = Get-AnsibleParam $params -name "certificate_hash" -type "str" -$max_connections = Get-AnsibleParam $params -name "max_connections" -type "int" -$ssl_bridging = Get-AnsibleParam -obj $params -name "ssl_bridging" -type "str" -validateset $ssl_bridging_methods -$enable_only_messaging_capable_clients = Get-AnsibleParam $params -name "enable_only_messaging_capable_clients" -type "bool" - -$result = @{ - changed = $false -} -$diff_text = $null - -# Ensure RemoteDesktopServices module is loaded -if ($null -eq (Get-Module -Name RemoteDesktopServices -ErrorAction SilentlyContinue)) { - Import-Module -Name RemoteDesktopServices -} - -if ($null -ne $certificate) -{ - # Validate cert path - $cert_path = "cert:\LocalMachine\My\$certificate" - If (-not (Test-Path $cert_path) ) - { - Fail-Json -obj $result -message "Unable to locate certificate at $cert_path" - } - - # Get current certificate hash - $current_cert = (Get-Item -Path "RDS:\GatewayServer\SSLCertificate\Thumbprint").CurrentValue - if ($current_cert -ne $certificate) { - Set-Item -Path "RDS:\GatewayServer\SSLCertificate\Thumbprint" -Value $certificate -WhatIf:$check_mode - $diff_text += "-Certificate = $current_cert`n+Certificate = $certificate`n" - $result.changed = $true - } -} - -if ($null -ne $max_connections) -{ - # Set the correct value for unlimited connections - # TODO Use a more explicit value, maybe a string (ex: "max", "none" or "unlimited") ? - If ($max_connections -eq -1) - { - $max_connections = (Get-Item -Path "RDS:\GatewayServer\MaxConnectionsAllowed").CurrentValue - } - - # Get current connections limit - $current_max_connections = (Get-Item -Path "RDS:\GatewayServer\MaxConnections").CurrentValue - if ($current_max_connections -ne $max_connections) { - Set-Item -Path "RDS:\GatewayServer\MaxConnections" -Value $max_connections -WhatIf:$check_mode - $diff_text += "-MaxConnections = $current_max_connections`n+MaxConnections = $max_connections`n" - $result.changed = $true - } -} - -if ($null -ne $ssl_bridging) -{ - $current_ssl_bridging = (Get-Item -Path "RDS:\GatewayServer\SSLBridging").CurrentValue - # Convert the integer value to its representative string - $current_ssl_bridging_str = $ssl_bridging_methods[$current_ssl_bridging] - - if ($current_ssl_bridging_str -ne $ssl_bridging) { - Set-Item -Path "RDS:\GatewayServer\SSLBridging" -Value ([array]::IndexOf($ssl_bridging_methods, $ssl_bridging)) -WhatIf:$check_mode - $diff_text += "-SSLBridging = $current_ssl_bridging_str`n+SSLBridging = $ssl_bridging`n" - $result.changed = $true - } -} - -if ($null -ne $enable_only_messaging_capable_clients) -{ - $current_enable_only_messaging_capable_clients = (Get-Item -Path "RDS:\GatewayServer\EnableOnlyMessagingCapableClients").CurrentValue - # Convert the integer value to boolean - $current_enable_only_messaging_capable_clients = $current_enable_only_messaging_capable_clients -eq 1 - - if ($current_enable_only_messaging_capable_clients -ne $enable_only_messaging_capable_clients) { - Set-Item -Path "RDS:\GatewayServer\EnableOnlyMessagingCapableClients" -Value ([int]$enable_only_messaging_capable_clients) -WhatIf:$check_mode - $diff_text += "-EnableOnlyMessagingCapableClients = $current_enable_only_messaging_capable_clients`n+EnableOnlyMessagingCapableClients = $enable_only_messaging_capable_clients`n" - $result.changed = $true - } -} - -if ($diff_mode -and $result.changed -eq $true) { - $result.diff = @{ - prepared = $diff_text - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_rds_settings.py b/lib/ansible/modules/windows/win_rds_settings.py deleted file mode 100644 index 5919a816e1..0000000000 --- a/lib/ansible/modules/windows/win_rds_settings.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Kevin Subileau (@ksubileau) -# 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_rds_settings -short_description: Manage main settings of a Remote Desktop Gateway server -description: - - Configure general settings of a Remote Desktop Gateway server. -version_added: "2.8" -author: - - Kevin Subileau (@ksubileau) -options: - certificate_hash: - description: - - Certificate hash (thumbprint) for the Remote Desktop Gateway server. The certificate hash is the unique identifier for the certificate. - type: str - max_connections: - description: - - The maximum number of connections allowed. - - If set to C(0), no new connections are allowed. - - If set to C(-1), the number of connections is unlimited. - type: int - ssl_bridging: - description: - - Specifies whether to use SSL Bridging. - - 'C(none): no SSL bridging.' - - 'C(https_http): HTTPS-HTTP bridging.' - - 'C(https_https): HTTPS-HTTPS bridging.' - type: str - choices: [ https_http, https_https, none ] - enable_only_messaging_capable_clients: - description: - - If enabled, only clients that support logon messages and administrator messages can connect. - type: bool -requirements: - - Windows Server 2008R2 (6.1) or higher. - - The Windows Feature "RDS-Gateway" must be enabled. -seealso: -- module: win_rds_cap -- module: win_rds_rap -- module: win_rds_settings -''' - -EXAMPLES = r''' -- name: Configure the Remote Desktop Gateway - win_rds_settings: - certificate_hash: B0D0FA8408FC67B230338FCA584D03792DA73F4C - max_connections: 50 - notify: - - Restart TSGateway service -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_region.ps1 b/lib/ansible/modules/windows/win_region.ps1 deleted file mode 100644 index 7542a8b1d1..0000000000 --- a/lib/ansible/modules/windows/win_region.ps1 +++ /dev/null @@ -1,365 +0,0 @@ -#!powershell - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params "_ansible_check_mode" -type 'bool' -default $false -$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP - -$location = Get-AnsibleParam -obj $params -name 'location' -type 'str' -$format = Get-AnsibleParam -obj $params -name 'format' -type 'str' -$unicode_language = Get-AnsibleParam -obj $params -name 'unicode_language' -type 'str' -$copy_settings = Get-AnsibleParam -obj $params -name 'copy_settings' -type 'bool' -default $false - -$result = @{ - changed = $false - restart_required = $false -} - -# This is used to get the format values based on the LCType enum based through. When running Vista/7/2008/200R2 -$lctype_util = @" -using System; -using System.Text; -using System.Runtime.InteropServices; -using System.ComponentModel; - -namespace Ansible.WinRegion { - - public class NativeMethods - { - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int GetLocaleInfoEx( - String lpLocaleName, - UInt32 LCType, - StringBuilder lpLCData, - int cchData); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int GetSystemDefaultLocaleName( - IntPtr lpLocaleName, - int cchLocaleName); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int GetUserDefaultLocaleName( - IntPtr lpLocaleName, - int cchLocaleName); - } - - public class LocaleHelper { - private String Locale; - - public LocaleHelper(String locale) { - Locale = locale; - } - - public String GetValueFromType(UInt32 LCType) { - StringBuilder data = new StringBuilder(500); - int result = NativeMethods.GetLocaleInfoEx(Locale, LCType, data, 500); - if (result == 0) - throw new Exception(String.Format("Error getting locale info with legacy method: {0}", new Win32Exception(Marshal.GetLastWin32Error()).Message)); - - return data.ToString(); - } - } -} -"@ -$original_tmp = $env:TMP -$env:TMP = $_remote_tmp -Add-Type -TypeDefinition $lctype_util -$env:TMP = $original_tmp - -Function Get-LastWin32ExceptionMessage { - param([int]$ErrorCode) - $exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode - $exp_msg = "{0} (Win32 ErrorCode {1} - 0x{1:X8})" -f $exp.Message, $ErrorCode - return $exp_msg -} - -Function Get-SystemLocaleName { - $max_length = 85 # LOCALE_NAME_MAX_LENGTH - $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($max_length) - - try { - $res = [Ansible.WinRegion.NativeMethods]::GetSystemDefaultLocaleName($ptr, $max_length) - - if ($res -eq 0) { - $err_code = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - $msg = Get-LastWin32ExceptionMessage -Error $err_code - Fail-Json -obj $result -message "Failed to get system locale: $msg" - } - - $system_locale = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr) - } finally { - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ptr) - } - - return $system_locale -} - -Function Get-UserLocaleName { - $max_length = 85 # LOCALE_NAME_MAX_LENGTH - $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($max_length) - - try { - $res = [Ansible.WinRegion.NativeMethods]::GetUserDefaultLocaleName($ptr, $max_length) - - if ($res -eq 0) { - $err_code = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - $msg = Get-LastWin32ExceptionMessage -Error $err_code - Fail-Json -obj $result -message "Failed to get user locale: $msg" - } - - $user_locale = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr) - } finally { - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ptr) - } - - return $user_locale -} - -Function Get-ValidGeoIds($cultures) { - $geo_ids = @() - foreach($culture in $cultures) { - try { - $geo_id = [System.Globalization.RegionInfo]$culture.Name - $geo_ids += $geo_id.GeoId - } catch {} - } - $geo_ids -} - -Function Test-RegistryProperty($reg_key, $property) { - $type = Get-ItemProperty $reg_key -Name $property -ErrorAction SilentlyContinue - if ($null -eq $type) { - $false - } else { - $true - } -} - -Function Copy-RegistryKey($source, $target) { - # Using Copy-Item -Recurse is giving me weird results, doing it recursively - Copy-Item -Path $source -Destination $target -WhatIf:$check_mode - - foreach($key in Get-ChildItem $source) { - $sourceKey = "$source\$($key.PSChildName)" - $targetKey = (Get-Item $source).PSChildName - Copy-RegistryKey -source "$sourceKey" -target "$target\$targetKey" - } -} - -Function Set-UserLocale($culture) { - $reg_key = 'HKCU:\Control Panel\International' - - $lookup = New-Object Ansible.WinRegion.LocaleHelper($culture) - # hex values are from http://www.pinvoke.net/default.aspx/kernel32/GetLocaleInfoEx.html - $wanted_values = @{ - Locale = '{0:x8}' -f ([System.Globalization.CultureInfo]$culture).LCID - LocaleName = $culture - s1159 = $lookup.GetValueFromType(0x00000028) - s2359 = $lookup.GetValueFromType(0x00000029) - sCountry = $lookup.GetValueFromType(0x00000006) - sCurrency = $lookup.GetValueFromType(0x00000014) - sDate = $lookup.GetValueFromType(0x0000001D) - sDecimal = $lookup.GetValueFromType(0x0000000E) - sGrouping = $lookup.GetValueFromType(0x00000010) - sLanguage = $lookup.GetValueFromType(0x00000003) # LOCALE_ABBREVLANGNAME - sList = $lookup.GetValueFromType(0x0000000C) - sLongDate = $lookup.GetValueFromType(0x00000020) - sMonDecimalSep = $lookup.GetValueFromType(0x00000016) - sMonGrouping = $lookup.GetValueFromType(0x00000018) - sMonThousandSep = $lookup.GetValueFromType(0x00000017) - sNativeDigits = $lookup.GetValueFromType(0x00000013) - sNegativeSign = $lookup.GetValueFromType(0x00000051) - sPositiveSign = $lookup.GetValueFromType(0x00000050) - sShortDate = $lookup.GetValueFromType(0x0000001F) - sThousand = $lookup.GetValueFromType(0x0000000F) - sTime = $lookup.GetValueFromType(0x0000001E) - sTimeFormat = $lookup.GetValueFromType(0x00001003) - sYearMonth = $lookup.GetValueFromType(0x00001006) - iCalendarType = $lookup.GetValueFromType(0x00001009) - iCountry = $lookup.GetValueFromType(0x00000005) - iCurrDigits = $lookup.GetValueFromType(0x00000019) - iCurrency = $lookup.GetValueFromType(0x0000001B) - iDate = $lookup.GetValueFromType(0x00000021) - iDigits = $lookup.GetValueFromType(0x00000011) - NumShape = $lookup.GetValueFromType(0x00001014) # LOCALE_IDIGITSUBSTITUTION - iFirstDayOfWeek = $lookup.GetValueFromType(0x0000100C) - iFirstWeekOfYear = $lookup.GetValueFromType(0x0000100D) - iLZero = $lookup.GetValueFromType(0x00000012) - iMeasure = $lookup.GetValueFromType(0x0000000D) - iNegCurr = $lookup.GetValueFromType(0x0000001C) - iNegNumber = $lookup.GetValueFromType(0x00001010) - iPaperSize = $lookup.GetValueFromType(0x0000100A) - iTime = $lookup.GetValueFromType(0x00000023) - iTimePrefix = $lookup.GetValueFromType(0x00001005) - iTLZero = $lookup.GetValueFromType(0x00000025) - } - - if (Test-RegistryProperty -reg_key $reg_key -property 'sShortTime') { - # sShortTime was added after Vista, will check anyway and add in the value if it exists - $wanted_values.sShortTime = $lookup.GetValueFromType(0x00000079) - } - - $properties = Get-ItemProperty $reg_key - foreach($property in $properties.PSObject.Properties) { - if (Test-RegistryProperty -reg_key $reg_key -property $property.Name) { - $name = $property.Name - $old_value = $property.Value - $new_value = $wanted_values.$name - - if ($new_value -ne $old_value) { - Set-ItemProperty -Path $reg_key -Name $name -Value $new_value -WhatIf:$check_mode - $result.changed = $true - } - } - } -} - -Function Set-SystemLocaleLegacy($unicode_language) { - # For when Get/Set-WinSystemLocale is not available (Pre Windows 8 and Server 2012) - $current_language_value = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\Language').Default - $wanted_language_value = '{0:x4}' -f ([System.Globalization.CultureInfo]$unicode_language).LCID - if ($current_language_value -ne $wanted_language_value) { - Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\Language' -Name 'Default' -Value $wanted_language_value -WhatIf:$check_mode - $result.changed = $true - $result.restart_required = $true - } - - # This reads from the non registry (Default) key, the extra prop called (Default) see below for more details - $current_locale_value = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\Locale')."(Default)" - $wanted_locale_value = '{0:x8}' -f ([System.Globalization.CultureInfo]$unicode_language).LCID - if ($current_locale_value -ne $wanted_locale_value) { - # Need to use .net to write property value, Locale has 2 (Default) properties - # 1: The actual (Default) property, we don't want to change Set-ItemProperty writes to this value when using (Default) - # 2: A property called (Default), this is what we want to change and only .net SetValue can do this one - if (-not $check_mode) { - $hive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $env:COMPUTERNAME) - $key = $hive.OpenSubKey("SYSTEM\CurrentControlSet\Control\Nls\Locale", $true) - $key.SetValue("(Default)", $wanted_locale_value, [Microsoft.Win32.RegistryValueKind]::String) - } - $result.changed = $true - $result.restart_required = $true - } - - $codepage_path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage' - $current_codepage_info = Get-ItemProperty $codepage_path - $wanted_codepage_info = ([System.Globalization.CultureInfo]::GetCultureInfo($unicode_language)).TextInfo - - $current_a_cp = $current_codepage_info.ACP - $current_oem_cp = $current_codepage_info.OEMCP - $current_mac_cp = $current_codepage_info.MACCP - $wanted_a_cp = $wanted_codepage_info.ANSICodePage - $wanted_oem_cp = $wanted_codepage_info.OEMCodePage - $wanted_mac_cp = $wanted_codepage_info.MacCodePage - - if ($current_a_cp -ne $wanted_a_cp) { - Set-ItemProperty -Path $codepage_path -Name 'ACP' -Value $wanted_a_cp -WhatIf:$check_mode - $result.changed = $true - $result.restart_required = $true - } - if ($current_oem_cp -ne $wanted_oem_cp) { - Set-ItemProperty -Path $codepage_path -Name 'OEMCP' -Value $wanted_oem_cp -WhatIf:$check_mode - $result.changed = $true - $result.restart_required = $true - } - if ($current_mac_cp -ne $wanted_mac_cp) { - Set-ItemProperty -Path $codepage_path -Name 'MACCP' -Value $wanted_mac_cp -WhatIf:$check_mode - $result.changed = $true - $result.restart_required = $true - } -} - -if ($null -eq $format -and $null -eq $location -and $null -eq $unicode_language) { - Fail-Json $result "An argument for 'format', 'location' or 'unicode_language' needs to be supplied" -} else { - $valid_cultures = [System.Globalization.CultureInfo]::GetCultures('InstalledWin32Cultures') - $valid_geoids = Get-ValidGeoIds -cultures $valid_cultures - - if ($null -ne $location) { - if ($valid_geoids -notcontains $location) { - Fail-Json $result "The argument location '$location' does not contain a valid Geo ID" - } - } - - if ($null -ne $format) { - if ($valid_cultures.Name -notcontains $format) { - Fail-Json $result "The argument format '$format' does not contain a valid Culture Name" - } - } - - if ($null -ne $unicode_language) { - if ($valid_cultures.Name -notcontains $unicode_language) { - Fail-Json $result "The argument unicode_language '$unicode_language' does not contain a valid Culture Name" - } - } -} - -if ($null -ne $location) { - # Get-WinHomeLocation was only added in Server 2012 and above - # Use legacy option if older - if (Get-Command 'Get-WinHomeLocation' -ErrorAction SilentlyContinue) { - $current_location = (Get-WinHomeLocation).GeoId - if ($current_location -ne $location) { - if (-not $check_mode) { - Set-WinHomeLocation -GeoId $location - } - $result.changed = $true - } - } else { - $current_location = (Get-ItemProperty -Path 'HKCU:\Control Panel\International\Geo').Nation - if ($current_location -ne $location) { - Set-ItemProperty -Path 'HKCU:\Control Panel\International\Geo' -Name 'Nation' -Value $location -WhatIf:$check_mode - $result.changed = $true - } - } -} - -if ($null -ne $format) { - # Cannot use Get/Set-Culture as that fails to get and set the culture when running in the PSRP runspace. - $current_format = Get-UserLocaleName - if ($current_format -ne $format) { - Set-UserLocale -culture $format - $result.changed = $true - } -} - -if ($null -ne $unicode_language) { - # Get/Set-WinSystemLocale was only added in Server 2012 and above, use legacy option if older - if (Get-Command 'Get-WinSystemLocale' -ErrorAction SilentlyContinue) { - $current_unicode_language = Get-SystemLocaleName - if ($current_unicode_language -ne $unicode_language) { - if (-not $check_mode) { - Set-WinSystemLocale -SystemLocale $unicode_language - } - $result.changed = $true - $result.restart_required = $true - } - } else { - Set-SystemLocaleLegacy -unicode_language $unicode_language - } -} - -if ($copy_settings -eq $true -and $result.changed -eq $true) { - if (-not $check_mode) { - $defaultHiveKey = 'HKU\TEMP' - reg load $defaultHiveKey 'C:\Users\Default\NTUSER.DAT' - New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS - - $sids = 'TEMP', '.DEFAULT', 'S-1-5-19', 'S-1-5-20' - foreach ($sid in $sids) { - Copy-RegistryKey -source "HKCU:\Keyboard Layout" -target "HKU:\$sid" - Copy-RegistryKey -source "HKCU:\Control Panel\International" -target "HKU:\$sid\Control Panel" - Copy-RegistryKey -source "HKCU:\Control Panel\Input Method" -target "HKU:\$sid\Control Panel" - } - - Remove-PSDrive HKU - [gc]::collect() - reg unload $defaultHiveKey - } - $result.changed = $true -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_region.py b/lib/ansible/modules/windows/win_region.py deleted file mode 100644 index 84ef3be080..0000000000 --- a/lib/ansible/modules/windows/win_region.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Ansible, inc -# 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_region -version_added: "2.3" -short_description: Set the region and format settings -description: - - Set the location settings of a Windows Server. - - Set the format settings of a Windows Server. - - Set the unicode language settings of a Windows Server. - - Copy across these settings to the default profile. -options: - location: - description: - - The location to set for the current user, see - U(https://msdn.microsoft.com/en-us/library/dd374073.aspx) - for a list of GeoIDs you can use and what location it relates to. - - This needs to be set if C(format) or C(unicode_language) is not - set. - type: str - format: - description: - - The language format to set for the current user, see - U(https://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx) - for a list of culture names to use. - - This needs to be set if C(location) or C(unicode_language) is not set. - type: str - unicode_language: - description: - - The unicode language format to set for all users, see - U(https://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx) - for a list of culture names to use. - - This needs to be set if C(location) or C(format) is not set. After setting this - value a reboot is required for it to take effect. - type: str - copy_settings: - description: - - This will copy the current format and location values to new user - profiles and the welcome screen. This will only run if - C(location), C(format) or C(unicode_language) has resulted in a - change. If this process runs then it will always result in a - change. - type: bool - default: no -seealso: -- module: win_timezone -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Set the region format to English United States - win_region: - format: en-US - -- name: Set the region format to English Australia and copy settings to new profiles - win_region: - format: en-AU - copy_settings: yes - -- name: Set the location to United States - win_region: - location: 244 - -# Reboot when region settings change -- name: Set the unicode language to English Great Britain, reboot if required - win_region: - unicode_language: en-GB - register: result - -- win_reboot: - when: result.restart_required - -# Reboot when format, location or unicode has changed -- name: Set format, location and unicode to English Australia and copy settings, reboot if required - win_region: - location: 12 - format: en-AU - unicode_language: en-AU - register: result - -- win_reboot: - when: result.restart_required -''' - -RETURN = r''' -restart_required: - description: Whether a reboot is required for the change to take effect. - returned: success - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_regmerge.ps1 b/lib/ansible/modules/windows/win_regmerge.ps1 deleted file mode 100644 index 44c3ae1b1e..0000000000 --- a/lib/ansible/modules/windows/win_regmerge.ps1 +++ /dev/null @@ -1,97 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.ArgvParser -#Requires -Module Ansible.ModuleUtils.CommandUtil -#Requires -Module Ansible.ModuleUtils.Legacy - -Function Convert-RegistryPath { - Param ( - [parameter(Mandatory=$True)] - [ValidateNotNullOrEmpty()]$Path - ) - - $output = $Path -replace "HKLM:", "HKLM" - $output = $output -replace "HKCU:", "HKCU" - - Return $output -} - -$result = @{ - changed = $false -} -$params = Parse-Args $args - -$path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -resultobj $result -$compare_to = Get-AnsibleParam -obj $params -name "compare_to" -type "str" -resultobj $result - -# check it looks like a reg key, warn if key not present - will happen first time -# only accepting PS-Drive style key names (starting with HKLM etc, not HKEY_LOCAL_MACHINE etc) - -$do_comparison = $False - -If ($compare_to) { - $compare_to_key = $params.compare_to.ToString() - If (Test-Path $compare_to_key -pathType container ) { - $do_comparison = $True - } Else { - $result.compare_to_key_found = $false - } -} - -If ( $do_comparison -eq $True ) { - $guid = [guid]::NewGuid() - $exported_path = $env:TEMP + "\" + $guid.ToString() + 'ansible_win_regmerge.reg' - - $expanded_compare_key = Convert-RegistryPath ($compare_to_key) - - # export from the reg key location to a file - $reg_args = Argv-ToString -Arguments @("reg.exe", "EXPORT", $expanded_compare_key, $exported_path) - $res = Run-Command -command $reg_args - if ($res.rc -ne 0) { - $result.rc = $res.rc - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "error exporting registry '$expanded_compare_key' to '$exported_path'" - } - - # compare the two files - $comparison_result = Compare-Object -ReferenceObject $(Get-Content $path) -DifferenceObject $(Get-Content $exported_path) - - If ($null -ne $comparison_result -and (Get-Member -InputObject $comparison_result -Name "count" -MemberType Properties )) - { - # Something is different, actually do reg merge - $reg_import_args = Argv-ToString -Arguments @("reg.exe", "IMPORT", $path) - $res = Run-Command -command $reg_import_args - if ($res.rc -ne 0) { - $result.rc = $res.rc - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "error importing registry values from '$path'" - } - $result.changed = $true - $result.difference_count = $comparison_result.count - } Else { - $result.difference_count = 0 - } - - Remove-Item $exported_path - $result.compared = $true - -} Else { - # not comparing, merge and report changed - $reg_import_args = Argv-ToString -Arguments @("reg.exe", "IMPORT", $path) - $res = Run-Command -command $reg_import_args - if ($res.rc -ne 0) { - $result.rc = $res.rc - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "error importing registry value from '$path'" - } - $result.changed = $true - $result.compared = $false -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_regmerge.py b/lib/ansible/modules/windows/win_regmerge.py deleted file mode 100644 index 1b5596f945..0000000000 --- a/lib/ansible/modules/windows/win_regmerge.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_regmerge -version_added: "2.1" -short_description: Merges the contents of a registry file into the Windows registry -description: - - Wraps the reg.exe command to import the contents of a registry file. - - Suitable for use with registry files created using M(win_template). - - Windows registry files have a specific format and must be constructed correctly with carriage return and line feed line endings otherwise they will not - be merged. - - Exported registry files often start with a Byte Order Mark which must be removed if the file is to templated using M(win_template). - - Registry file format is described at U(https://support.microsoft.com/en-us/kb/310516) - - See also M(win_template), M(win_regedit) -options: - path: - description: - - The full path including file name to the registry file on the remote machine to be merged - type: path - required: yes - compare_key: - description: - - The parent key to use when comparing the contents of the registry to the contents of the file. Needs to be in HKLM or HKCU part of registry. - Use a PS-Drive style path for example HKLM:\SOFTWARE not HKEY_LOCAL_MACHINE\SOFTWARE - If not supplied, or the registry key is not found, no comparison will be made, and the module will report changed. - type: str -notes: - - Organise your registry files so that they contain a single root registry - key if you want to use the compare_to functionality. - - This module does not force registry settings to be in the state - described in the file. If registry settings have been modified externally - the module will merge the contents of the file but continue to report - differences on subsequent runs. - - To force registry change, use M(win_regedit) with C(state=absent) before - using C(win_regmerge). -seealso: -- module: win_reg_stat -- module: win_regedit -author: -- Jon Hawkesworth (@jhawkesworth) -''' - -EXAMPLES = r''' -- name: Merge in a registry file without comparing to current registry - win_regmerge: - path: C:\autodeploy\myCompany-settings.reg - -- name: Compare and merge registry file - win_regmerge: - path: C:\autodeploy\myCompany-settings.reg - compare_to: HKLM:\SOFTWARE\myCompany -''' - -RETURN = r''' -compare_to_key_found: - description: whether the parent registry key has been found for comparison - returned: when comparison key not found in registry - type: bool - sample: false -difference_count: - description: number of differences between the registry and the file - returned: changed - type: int - sample: 1 -compared: - description: whether a comparison has taken place between the registry and the file - returned: when a comparison key has been supplied and comparison has been attempted - type: bool - sample: true -''' diff --git a/lib/ansible/modules/windows/win_robocopy.ps1 b/lib/ansible/modules/windows/win_robocopy.ps1 deleted file mode 100644 index 6ef83e299e..0000000000 --- a/lib/ansible/modules/windows/win_robocopy.ps1 +++ /dev/null @@ -1,145 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Corwin Brown <corwin.brown@maxpoint.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$src = Get-AnsibleParam -obj $params -name "src" -type "path" -failifempty $true -$dest = Get-AnsibleParam -obj $params -name "dest" -type "path" -failifempty $true -$purge = Get-AnsibleParam -obj $params -name "purge" -type "bool" -default $false -$recurse = Get-AnsibleParam -obj $params -name "recurse" -type "bool" -default $false -$flags = Get-AnsibleParam -obj $params -name "flags" -type "str" - -$result = @{ - changed = $false - dest = $dest - purge = $purge - recurse = $recurse - src = $src -} - -# Search for an Error Message -# Robocopy seems to display an error after 3 '-----' separator lines -Function SearchForError($cmd_output, $default_msg) { - $separator_count = 0 - $error_msg = $default_msg - ForEach ($line in $cmd_output) { - if (-not $line) { - continue - } - - if ($separator_count -ne 3) { - if (Select-String -InputObject $line -pattern "^(\s+)?(\-+)(\s+)?$") { - $separator_count += 1 - } - } else { - if (Select-String -InputObject $line -pattern "error") { - $error_msg = $line - break - } - } - } - - return $error_msg -} - -if (-not (Test-Path -Path $src)) { - Fail-Json $result "$src does not exist!" -} - -# Build Arguments -$robocopy_opts = @($src, $dest) - -if ($check_mode) { - $robocopy_opts += "/l" -} - -if ($null -eq $flags) { - if ($purge) { - $robocopy_opts += "/purge" - } - - if ($recurse) { - $robocopy_opts += "/e" - } -} else { - ForEach ($f in $flags.split(" ")) { - $robocopy_opts += $f - } -} - -$result.flags = $flags -$result.cmd = "$robocopy $robocopy_opts" - -Try { - $robocopy_output = &robocopy $robocopy_opts - $rc = $LASTEXITCODE -} Catch { - Fail-Json $result "Error synchronizing $src to $dest! Msg: $($_.Exception.Message)" -} - -$result.msg = "Success" -$result.output = $robocopy_output -$result.return_code = $rc # Backward compatibility -$result.rc = $rc - -switch ($rc) { - - 0 { - $result.msg = "No files copied." - } - 1 { - $result.msg = "Files copied successfully!" - $result.changed = $true - $result.failed = $false - } - 2 { - $result.msg = "Some Extra files or directories were detected. No files were copied." - Add-Warning $result $result.msg - $result.failed = $false - } - 3 { - $result.msg = "(2+1) Some files were copied. Additional files were present." - Add-Warning $result $result.msg - $result.changed = $true - $result.failed = $false - } - 4 { - $result.msg = "Some mismatched files or directories were detected. Housekeeping might be required!" - Add-Warning $result $result.msg - $result.changed = $true - $result.failed = $false - } - 5 { - $result.msg = "(4+1) Some files were copied. Some files were mismatched." - Add-Warning $result $result.msg - $result.changed = $true - $result.failed = $false - } - 6 { - $result.msg = "(4+2) Additional files and mismatched files exist. No files were copied." - $result.failed = $false - } - 7 { - $result.msg = "(4+1+2) Files were copied, a file mismatch was present, and additional files were present." - Add-Warning $result $result.msg - $result.changed = $true - $result.failed = $false - } - 8 { - Fail-Json $result (SearchForError $robocopy_output "Some files or directories could not be copied!") - } - { @(9, 10, 11, 12, 13, 14, 15) -contains $_ } { - Fail-Json $result (SearchForError $robocopy_output "Fatal error. Check log message!") - } - 16 { - Fail-Json $result (SearchForError $robocopy_output "Serious Error! No files were copied! Do you have permissions to access $src and $dest?") - } - -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_robocopy.py b/lib/ansible/modules/windows/win_robocopy.py deleted file mode 100644 index f652f151a3..0000000000 --- a/lib/ansible/modules/windows/win_robocopy.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Corwin Brown <blakfeld@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_robocopy -version_added: '2.2' -short_description: Synchronizes the contents of two directories using Robocopy -description: -- Synchronizes the contents of files/directories from a source to destination. -- Under the hood this just calls out to RoboCopy, since that should be available - on most modern Windows systems. -options: - src: - description: - - Source file/directory to sync. - type: path - required: yes - dest: - description: - - Destination file/directory to sync (Will receive contents of src). - type: path - required: yes - recurse: - description: - - Includes all subdirectories (Toggles the C(/e) flag to RoboCopy). - - If C(flags) is set, this will be ignored. - type: bool - default: no - purge: - description: - - Deletes any files/directories found in the destination that do not exist in the source. - - Toggles the C(/purge) flag to RoboCopy. - - If C(flags) is set, this will be ignored. - type: bool - default: no - flags: - description: - - Directly supply Robocopy flags. - - If set, C(purge) and C(recurse) will be ignored. - type: str -notes: -- This is not a complete port of the M(synchronize) module. Unlike the M(synchronize) module this only performs the sync/copy on the remote machine, - not from the master to the remote machine. -- This module does not currently support all Robocopy flags. -seealso: -- module: synchronize -- module: win_copy -author: -- Corwin Brown (@blakfeld) -''' - -EXAMPLES = r''' -- name: Sync the contents of one directory to another - win_robocopy: - src: C:\DirectoryOne - dest: C:\DirectoryTwo - -- name: Sync the contents of one directory to another, including subdirectories - win_robocopy: - src: C:\DirectoryOne - dest: C:\DirectoryTwo - recurse: yes - -- name: Sync the contents of one directory to another, and remove any files/directories found in destination that do not exist in the source - win_robocopy: - src: C:\DirectoryOne - dest: C:\DirectoryTwo - purge: yes - -- name: Sync content in recursive mode, removing any files/directories found in destination that do not exist in the source - win_robocopy: - src: C:\DirectoryOne - dest: C:\DirectoryTwo - recurse: yes - purge: yes - -- name: Sync two directories in recursive and purging mode, specifying additional special flags - win_robocopy: - src: C:\DirectoryOne - dest: C:\DirectoryTwo - flags: /E /PURGE /XD SOME_DIR /XF SOME_FILE /MT:32 - -- name: Sync one file from a remote UNC path in recursive and purging mode, specifying additional special flags - win_robocopy: - src: \\Server1\Directory One - dest: C:\DirectoryTwo - flags: file.zip /E /PURGE /XD SOME_DIR /XF SOME_FILE /MT:32 -''' - -RETURN = r''' -cmd: - description: The used command line. - returned: always - type: str - sample: robocopy C:\DirectoryOne C:\DirectoryTwo /e /purge -src: - description: The Source file/directory of the sync. - returned: always - type: str - sample: C:\Some\Path -dest: - description: The Destination file/directory of the sync. - returned: always - type: str - sample: C:\Some\Path -recurse: - description: Whether or not the recurse flag was toggled. - returned: always - type: bool - sample: false -purge: - description: Whether or not the purge flag was toggled. - returned: always - type: bool - sample: false -flags: - description: Any flags passed in by the user. - returned: always - type: str - sample: /e /purge -rc: - description: The return code returned by robocopy. - returned: success - type: int - sample: 1 -output: - description: The output of running the robocopy command. - returned: success - type: str - sample: "------------------------------------\\n ROBOCOPY :: Robust File Copy for Windows \\n------------------------------------\\n " -msg: - description: Output interpreted into a concise message. - returned: always - type: str - sample: No files copied! -''' diff --git a/lib/ansible/modules/windows/win_route.ps1 b/lib/ansible/modules/windows/win_route.ps1 deleted file mode 100644 index f74d482be2..0000000000 --- a/lib/ansible/modules/windows/win_route.ps1 +++ /dev/null @@ -1,104 +0,0 @@ -#!powershell - -# Copyright: (c) 2016, Daniele Lazzari <lazzari@mailup.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -# win_route (Add or remove a network static route) - -$params = Parse-Args $args -supports_check_mode $true - -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false -$dest = Get-AnsibleParam -obj $params -name "destination" -type "str" -failifempty $true -$gateway = Get-AnsibleParam -obj $params -name "gateway" -type "str" -$metric = Get-AnsibleParam -obj $params -name "metric" -type "int" -default 1 -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateSet "present","absent" -$result = @{ - "changed" = $false - "output" = "" - } - -Function Add-Route { - Param ( - [Parameter(Mandatory=$true)] - [string]$Destination, - [Parameter(Mandatory=$true)] - [string]$Gateway, - [Parameter(Mandatory=$true)] - [int]$Metric, - [Parameter(Mandatory=$true)] - [bool]$CheckMode - ) - - - $IpAddress = $Destination.split('/')[0] - - # Check if the static route is already present - $Route = Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '$($IpAddress)'" - if (!($Route)){ - try { - # Find Interface Index - $InterfaceIndex = Find-NetRoute -RemoteIPAddress $Gateway | Select-Object -First 1 -ExpandProperty InterfaceIndex - - # Add network route - New-NetRoute -DestinationPrefix $Destination -NextHop $Gateway -InterfaceIndex $InterfaceIndex -RouteMetric $Metric -ErrorAction Stop -WhatIf:$CheckMode|out-null - $result.changed = $true - $result.output = "Route added" - - } - catch { - $ErrorMessage = $_.Exception.Message - Fail-Json $result $ErrorMessage - } - } - else { - $result.output = "Static route already exists" - } - -} - -Function Remove-Route { - Param ( - [Parameter(Mandatory=$true)] - [string]$Destination, - [bool]$CheckMode - ) - $IpAddress = $Destination.split('/')[0] - $Route = Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '$($IpAddress)'" - if ($Route){ - try { - - Remove-NetRoute -DestinationPrefix $Destination -Confirm:$false -ErrorAction Stop -WhatIf:$CheckMode - $result.changed = $true - $result.output = "Route removed" - } - catch { - $ErrorMessage = $_.Exception.Message - Fail-Json $result $ErrorMessage - } - } - else { - $result.output = "No route to remove" - } - -} - -# Set gateway if null -if(!($gateway)){ - $gateway = "0.0.0.0" -} - - -if ($state -eq "present"){ - - Add-Route -Destination $dest -Gateway $gateway -Metric $metric -CheckMode $check_mode - -} -else { - - Remove-Route -Destination $dest -CheckMode $check_mode - -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_route.py b/lib/ansible/modules/windows/win_route.py deleted file mode 100644 index 51e0366ad9..0000000000 --- a/lib/ansible/modules/windows/win_route.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Daniele Lazzari <lazzari@mailup.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# This is a windows documentation stub. Actual code lives in the .ps1 -# file of the same name. - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_route -version_added: "2.4" -short_description: Add or remove a static route -description: - - Add or remove a static route. -options: - destination: - description: - - Destination IP address in CIDR format (ip address/prefix length). - type: str - required: yes - gateway: - description: - - The gateway used by the static route. - - If C(gateway) is not provided it will be set to C(0.0.0.0). - type: str - metric: - description: - - Metric used by the static route. - type: int - default: 1 - state: - description: - - If C(absent), it removes a network static route. - - If C(present), it adds a network static route. - type: str - choices: [ absent, present ] - default: present -notes: - - Works only with Windows 2012 R2 and newer. -author: -- Daniele Lazzari (@dlazz) -''' - -EXAMPLES = r''' ---- -- name: Add a network static route - win_route: - destination: 192.168.2.10/32 - gateway: 192.168.1.1 - metric: 1 - state: present - -- name: Remove a network static route - win_route: - destination: 192.168.2.10/32 - state: absent -''' -RETURN = r''' -output: - description: A message describing the task result. - returned: always - type: str - sample: "Route added" -''' diff --git a/lib/ansible/modules/windows/win_say.ps1 b/lib/ansible/modules/windows/win_say.ps1 deleted file mode 100644 index 253402b5ec..0000000000 --- a/lib/ansible/modules/windows/win_say.ps1 +++ /dev/null @@ -1,95 +0,0 @@ -#!powershell - -# Copyright: (c) 2016, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -$spec = @{ - options = @{ - msg = @{ type = "str" } - msg_file = @{ type = "path" } - start_sound_path = @{ type = "path" } - end_sound_path = @{ type = "path" } - voice = @{ type = "str" } - speech_speed = @{ type = "int"; default = 0 } - } - mutually_exclusive = @( - ,@('msg', 'msg_file') - ) - required_one_of = @( - ,@('msg', 'msg_file', 'start_sound_path', 'end_sound_path') - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - - -$msg = $module.Params.msg -$msg_file = $module.Params.msg_file -$start_sound_path = $module.Params.start_sound_path -$end_sound_path = $module.Params.end_sound_path -$voice = $module.Params.voice -$speech_speed = $module.Params.speech_speed - -if ($speech_speed -lt -10 -or $speech_speed -gt 10) { - $module.FailJson("speech_speed needs to be an integer in the range -10 to 10. The value $speech_speed is outside this range.") -} - -$words = $null - -if ($msg_file) { - if (-not (Test-Path -Path $msg_file)) { - $module.FailJson("Message file $msg_file could not be found or opened. Ensure you have specified the full path to the file, and the ansible windows user has permission to read the file.") - } - $words = Get-Content $msg_file | Out-String -} - -if ($msg) { - $words = $msg -} - -if ($start_sound_path) { - if (-not (Test-Path -Path $start_sound_path)) { - $module.FailJson("Start sound file $start_sound_path could not be found or opened. Ensure you have specified the full path to the file, and the ansible windows user has permission to read the file.") - } - if (-not $module.CheckMode) { - (new-object Media.SoundPlayer $start_sound_path).playSync() - } -} - -if ($words) { - Add-Type -AssemblyName System.speech - $tts = New-Object System.Speech.Synthesis.SpeechSynthesizer - if ($voice) { - try { - $tts.SelectVoice($voice) - } catch [System.Management.Automation.MethodInvocationException] { - $module.Result.voice_info = "Could not load voice '$voice', using system default voice." - $module.Warn("Could not load voice '$voice', using system default voice.") - } - } - - $module.Result.voice = $tts.Voice.Name - if ($speech_speed -ne 0) { - $tts.Rate = $speech_speed - } - if (-not $module.CheckMode) { - $tts.Speak($words) - } - $tts.Dispose() -} - -if ($end_sound_path) { - if (-not (Test-Path -Path $end_sound_path)) { - $module.FailJson("End sound file $start_sound_path could not be found or opened. Ensure you have specified the full path to the file, and the ansible windows user has permission to read the file.") - } - if (-not $module.CheckMode) { - (new-object Media.SoundPlayer $end_sound_path).playSync() - } -} - -$module.Result.message_text = $words.ToString() - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_say.py b/lib/ansible/modules/windows/win_say.py deleted file mode 100644 index 877fe4f913..0000000000 --- a/lib/ansible/modules/windows/win_say.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_say -version_added: "2.3" -short_description: Text to speech module for Windows to speak messages and optionally play sounds -description: - - Uses .NET libraries to convert text to speech and optionally play .wav sounds. Audio Service needs to be running and some kind of speakers or - headphones need to be attached to the windows target(s) for the speech to be audible. -options: - msg: - description: - - The text to be spoken. - - Use either C(msg) or C(msg_file). - - Optional so that you can use this module just to play sounds. - type: str - msg_file: - description: - - Full path to a windows format text file containing the text to be spoken. - - Use either C(msg) or C(msg_file). - - Optional so that you can use this module just to play sounds. - type: path - voice: - description: - - Which voice to use. See notes for how to discover installed voices. - - If the requested voice is not available the default voice will be used. - Example voice names from Windows 10 are C(Microsoft Zira Desktop) and C(Microsoft Hazel Desktop). - type: str - speech_speed: - description: - - How fast or slow to speak the text. - - Must be an integer value in the range -10 to 10. - - -10 is slowest, 10 is fastest. - type: int - default: 0 - start_sound_path: - description: - - Full path to a C(.wav) file containing a sound to play before the text is spoken. - - Useful on conference calls to alert other speakers that ansible has something to say. - type: path - end_sound_path: - description: - - Full path to a C(.wav) file containing a sound to play after the text has been spoken. - - Useful on conference calls to alert other speakers that ansible has finished speaking. - type: path -notes: - - Needs speakers or headphones to do anything useful. - - | - To find which voices are installed, run the following Powershell commands. - - Add-Type -AssemblyName System.Speech - $speech = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer - $speech.GetInstalledVoices() | ForEach-Object { $_.VoiceInfo } - $speech.Dispose() - - - Speech can be surprisingly slow, so it's best to keep message text short. -seealso: -- module: win_msg -- module: win_toast -author: -- Jon Hawkesworth (@jhawkesworth) -''' - -EXAMPLES = r''' -- name: Warn of impending deployment - win_say: - msg: Warning, deployment commencing in 5 minutes, please log out. - -- name: Using a different voice and a start sound - win_say: - start_sound_path: C:\Windows\Media\ding.wav - msg: Warning, deployment commencing in 5 minutes, please log out. - voice: Microsoft Hazel Desktop - -- name: With start and end sound - win_say: - start_sound_path: C:\Windows\Media\Windows Balloon.wav - msg: New software installed - end_sound_path: C:\Windows\Media\chimes.wav - -- name: Text from file example - win_say: - start_sound_path: C:\Windows\Media\Windows Balloon.wav - msg_file: AppData\Local\Temp\morning_report.txt - end_sound_path: C:\Windows\Media\chimes.wav -''' - -RETURN = r''' -message_text: - description: The text that the module attempted to speak. - returned: success - type: str - sample: "Warning, deployment commencing in 5 minutes." -voice: - description: The voice used to speak the text. - returned: success - type: str - sample: Microsoft Hazel Desktop -voice_info: - description: The voice used to speak the text. - returned: when requested voice could not be loaded - type: str - sample: Could not load voice TestVoice, using system default voice -''' diff --git a/lib/ansible/modules/windows/win_scheduled_task.ps1 b/lib/ansible/modules/windows/win_scheduled_task.ps1 deleted file mode 100644 index ac5938d266..0000000000 --- a/lib/ansible/modules/windows/win_scheduled_task.ps1 +++ /dev/null @@ -1,1133 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Peter Mounce <public@neverrunwithscissors.com> -# Copyright: (c) 2015, Michael Perzel <michaelperzel@gmail.com> -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.SID - -$ErrorActionPreference = "Stop" - -$params = Parse-Args -arguments $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false -$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP - -$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true -$path = Get-AnsibleParam -obj $params -name "path" -type "str" -default "\" -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent", "present" - -# task actions, list of dicts [{path, arguments, working_directory}] -$actions = Get-AnsibleParam -obj $params -name "actions" -type "list" - -# task triggers, list of dicts [{ type, ... }] -$triggers = Get-AnsibleParam -obj $params -name "triggers" -type "list" - -# task Principal properties -$display_name = Get-AnsibleParam -obj $params -name "display_name" -type "str" -$group = Get-AnsibleParam -obj $params -name "group" -type "str" -$logon_type = Get-AnsibleParam -obj $params -name "logon_type" -type "str" -validateset "none","password","s4u","interactive_token","group","service_account","interactive_token_or_password" -$run_level = Get-AnsibleParam -obj $params -name "run_level" -type "str" -validateset "limited", "highest" -aliases "runlevel" -$username = Get-AnsibleParam -obj $params -name "username" -type "str" -aliases "user" -$password = Get-AnsibleParam -obj $params -name "password" -type "str" -$update_password = Get-AnsibleParam -obj $params -name "update_password" -type "bool" -default $true - -# task RegistrationInfo properties -$author = Get-AnsibleParam -obj $params -name "author" -type "str" -$date = Get-AnsibleParam -obj $params -name "date" -type "str" -$description = Get-AnsibleParam -obj $params -name "description" -type "str" -$source = Get-AnsibleParam -obj $params -name "source" -type "str" -$version = Get-AnsibleParam -obj $params -name "version" -type "str" - -# task Settings properties -$allow_demand_start = Get-AnsibleParam -obj $params -name "allow_demand_start" -type "bool" -$allow_hard_terminate = Get-AnsibleParam -obj $params -name "allow_hard_terminate" -type "bool" -$compatibility = Get-AnsibleParam -obj $params -name "compatibility" -type "int" # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383486(v=vs.85).aspx -$delete_expired_task_after = Get-AnsibleParam -obj $params -name "delete_expired_task_after" -type "str" # time string PT... -$disallow_start_if_on_batteries = Get-AnsibleParam -obj $params -name "disallow_start_if_on_batteries" -type "bool" -$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -$execution_time_limit = Get-AnsibleParam -obj $params -name "execution_time_limit" -type "str" # PT72H -$hidden = Get-AnsibleParam -obj $params -name "hidden" -type "bool" -# TODO: support for $idle_settings, needs to be created as a COM object -$multiple_instances = Get-AnsibleParam -obj $params -name "multiple_instances" -type "int" # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383507(v=vs.85).aspx -# TODO: support for $network_settings, needs to be created as a COM object -$priority = Get-AnsibleParam -obj $params -name "priority" -type "int" # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383512(v=vs.85).aspx -$restart_count = Get-AnsibleParam -obj $params -name "restart_count" -type "int" -$restart_interval = Get-AnsibleParam -obj $params -name "restart_interval" -type "str" # time string PT.. -$run_only_if_idle = Get-AnsibleParam -obj $params -name "run_only_if_idle" -type "bool" -$run_only_if_network_available = Get-AnsibleParam -obj $params -name "run_only_if_network_available" -type "bool" -$start_when_available = Get-AnsibleParam -obj $params -name "start_when_available" -type "bool" -$stop_if_going_on_batteries = Get-AnsibleParam -obj $params -name "stop_if_going_on_batteries" -type "bool" -$wake_to_run = Get-AnsibleParam -obj $params -name "wake_to_run" -type "bool" - -$result = @{ - changed = $false -} - -if ($diff_mode) { - $result.diff = @{} -} - -$task_enums = @" -public enum TASK_ACTION_TYPE // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383553(v=vs.85).aspx -{ - TASK_ACTION_EXEC = 0, - // The below are not supported and are only kept for documentation purposes - TASK_ACTION_COM_HANDLER = 5, - TASK_ACTION_SEND_EMAIL = 6, - TASK_ACTION_SHOW_MESSAGE = 7 -} - -public enum TASK_CREATION // https://msdn.microsoft.com/en-us/library/windows/desktop/aa382538(v=vs.85).aspx -{ - TASK_VALIDATE_ONLY = 0x1, - TASK_CREATE = 0x2, - TASK_UPDATE = 0x4, - TASK_CREATE_OR_UPDATE = 0x6, - TASK_DISABLE = 0x8, - TASK_DONT_ADD_PRINCIPAL_ACE = 0x10, - TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20 -} - -public enum TASK_LOGON_TYPE // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383566(v=vs.85).aspx -{ - TASK_LOGON_NONE = 0, - TASK_LOGON_PASSWORD = 1, - TASK_LOGON_S4U = 2, - TASK_LOGON_INTERACTIVE_TOKEN = 3, - TASK_LOGON_GROUP = 4, - TASK_LOGON_SERVICE_ACCOUNT = 5, - TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6 -} - -public enum TASK_RUN_LEVEL // https://msdn.microsoft.com/en-us/library/windows/desktop/aa380747(v=vs.85).aspx -{ - TASK_RUNLEVEL_LUA = 0, - TASK_RUNLEVEL_HIGHEST = 1 -} - -public enum TASK_TRIGGER_TYPE2 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383915(v=vs.85).aspx -{ - TASK_TRIGGER_EVENT = 0, - TASK_TRIGGER_TIME = 1, - TASK_TRIGGER_DAILY = 2, - TASK_TRIGGER_WEEKLY = 3, - TASK_TRIGGER_MONTHLY = 4, - TASK_TRIGGER_MONTHLYDOW = 5, - TASK_TRIGGER_IDLE = 6, - TASK_TRIGGER_REGISTRATION = 7, - TASK_TRIGGER_BOOT = 8, - TASK_TRIGGER_LOGON = 9, - TASK_TRIGGER_SESSION_STATE_CHANGE = 11 -} -"@ - -$original_tmp = $env:TMP -$env:TMP = $_remote_tmp -Add-Type -TypeDefinition $task_enums -$env:TMP = $original_tmp - -######################## -### HELPER FUNCTIONS ### -######################## -Function Convert-SnakeToPascalCase($snake) { - # very basic function to convert snake_case to PascalCase for use in COM - # objects - [regex]$regex = "_(\w)" - $pascal_case = $regex.Replace($snake, { $args[0].Value.Substring(1).ToUpper() }) - $capitalised = $pascal_case.Substring(0, 1).ToUpper() + $pascal_case.Substring(1) - - return $capitalised -} - -Function Compare-Properties($property_name, $parent_property, $map, $enum_map=$null) { - $changes = [System.Collections.ArrayList]@() - - # loop through the passed in map and compare values - # Name = The name of property in the COM object - # Value = The new value to compare the existing value with - foreach ($entry in $map.GetEnumerator()) { - $new_value = $entry.Value - - if ($null -ne $new_value) { - $property_name = $entry.Name - $existing_value = $parent_property.$property_name - if ($existing_value -cne $new_value) { - try { - $parent_property.$property_name = $new_value - } catch { - Fail-Json -obj $result -message "failed to set $property_name property '$property_name' to '$new_value': $($_.Exception.Message)" - } - - if ($null -ne $enum_map -and $enum_map.ContainsKey($property_name)) { - $enum = [type]$enum_map.$property_name - $existing_value = [Enum]::ToObject($enum, $existing_value) - $new_value = [Enum]::ToObject($enum, $new_value) - } - [void]$changes.Add("-$property_name=$existing_value`n+$property_name=$new_value") - } - } - } - - return ,$changes -} - -Function Set-PropertyForComObject($com_object, $name, $arg, $value) { - $com_name = Convert-SnakeToPascalCase -snake $arg - try { - $com_object.$com_name = $value - } catch { - Fail-Json -obj $result -message "failed to set $name property '$com_name' to '$value': $($_.Exception.Message)" - } -} - -Function Compare-PropertyList { - Param( - $collection, # the collection COM object to manipulate, this must contains the Create method - [string]$property_name, # human friendly name of the property object, e.g. action/trigger - [Array]$new, # a list of new properties, passed in by Ansible - [Array]$existing, # a list of existing properties from the COM object collection - [Hashtable]$map, # metadata for the collection, see below for the structure - [string]$enum # the parent enum name for type value - ) - <## map metadata structure - { - collection type [TASK_ACTION_TYPE] for Actions or [TASK_TRIGGER_TYPE2] for Triggers { - mandatory = list of mandatory properties for this type, ansible input name not the COM name - optional = list of optional properties that could be set for this type - # maps the ansible input object name to the COM name, e.g. working_directory = WorkingDirectory - map = { - ansible input name = COM name - } - } - }##> - # used by both Actions and Triggers to compare the collections of that property - - $enum = [type]$enum - $changes = [System.Collections.ArrayList]@() - $new_count = $new.Count - $existing_count = $existing.Count - - for ($i = 0; $i -lt $new_count; $i++) { - if ($i -lt $existing_count) { - $existing_property = $existing[$i] - } else { - $existing_property = $null - } - $new_property = $new[$i] - - # get the type of the property, for action this is set automatically - if (-not $new_property.ContainsKey("type")) { - Fail-Json -obj $result -message "entry for $property_name must contain a type key" - } - $type = $new_property.type - $valid_types = $map.Keys - $property_map = $map.$type - - # now let's validate the args for the property - $mandatory_args = $property_map.mandatory - $optional_args = $property_map.optional - $total_args = $mandatory_args + $optional_args - - # validate the mandatory arguments - foreach ($mandatory_arg in $mandatory_args) { - if (-not $new_property.ContainsKey($mandatory_arg)) { - Fail-Json -obj $result -message "mandatory key '$mandatory_arg' for $($property_name) is not set, mandatory keys are '$($mandatory_args -join "', '")'" - } - } - # throw a warning if in invalid key was set - foreach ($entry in $new_property.GetEnumerator()) { - $key = $entry.Name - if ($key -notin $total_args -and $key -ne "type") { - Add-Warning -obj $result -message "key '$key' for $($property_name) entry is not valid and will be ignored, valid keys are '$($total_args -join "', '")'" - } - } - - # now we have validated the input and have gotten the metadata, let's - # get the diff string - if ($null -eq $existing_property) { - # we have more properties than before,just add to the new - # properties list - $diff_list = [System.Collections.ArrayList]@() - - foreach ($property_arg in $total_args) { - if ($new_property.ContainsKey($property_arg)) { - $com_name = Convert-SnakeToPascalCase -snake $property_arg - $property_value = $new_property.$property_arg - - if ($property_value -is [Hashtable]) { - foreach ($kv in $property_value.GetEnumerator()) { - $sub_com_name = Convert-SnakeToPascalCase -snake $kv.Key - $sub_property_value = $kv.Value - [void]$diff_list.Add("+$com_name.$sub_com_name=$sub_property_value") - } - } else { - [void]$diff_list.Add("+$com_name=$property_value") - } - } - } - - [void]$changes.Add("+$property_name[$i] = {`n +Type=$type`n $($diff_list -join ",`n ")`n+}") - } elseif ([Enum]::ToObject($enum, $existing_property.Type) -ne $type) { - # the types are different so we need to change - $diff_list = [System.Collections.ArrayList]@() - - if ($existing_property.Type -notin $valid_types) { - [void]$diff_list.Add("-UNKNOWN TYPE $($existing_property.Type)") - foreach ($property_args in $total_args) { - if ($new_property.ContainsKey($property_arg)) { - $com_name = Convert-SnakeToPascalCase -snake $property_arg - $property_value = $new_property.$property_arg - - if ($property_value -is [Hashtable]) { - foreach ($kv in $property_value.GetEnumerator()) { - $sub_com_name = Convert-SnakeToPascalCase -snake $kv.Key - $sub_property_value = $kv.Value - [void]$diff_list.Add("+$com_name.$sub_com_name=$sub_property_value") - } - } else { - [void]$diff_list.Add("+$com_name=$property_value") - } - } - } - } else { - # we know the types of the existing property - $existing_type = [Enum]::ToObject([TASK_TRIGGER_TYPE2], $existing_property.Type) - [void]$diff_list.Add("-Type=$existing_type") - [void]$diff_list.Add("+Type=$type") - foreach ($property_arg in $total_args) { - $com_name = Convert-SnakeToPascalCase -snake $property_arg - $property_value = $new_property.$property_arg - $existing_value = $existing_property.$com_name - - if ($property_value -is [Hashtable]) { - foreach ($kv in $property_value.GetEnumerator()) { - $sub_property_value = $kv.Value - $sub_com_name = Convert-SnakeToPascalCase -snake $kv.Key - $sub_existing_value = $existing_property.$com_name.$sub_com_name - - if ($null -ne $sub_property_value) { - [void]$diff_list.Add("+$com_name.$sub_com_name=$sub_property_value") - } - - if ($null -ne $sub_existing_value) { - [void]$diff_list.Add("-$com_name.$sub_com_name=$sub_existing_value") - } - } - } else { - if ($null -ne $property_value) { - [void]$diff_list.Add("+$com_name=$property_value") - } - - if ($null -ne $existing_value) { - [void]$diff_list.Add("-$com_name=$existing_value") - } - } - } - } - - [void]$changes.Add("$property_name[$i] = {`n $($diff_list -join ",`n ")`n}") - } else { - # compare the properties of existing and new - $diff_list = [System.Collections.ArrayList]@() - - foreach ($property_arg in $total_args) { - $com_name = Convert-SnakeToPascalCase -snake $property_arg - $property_value = $new_property.$property_arg - $existing_value = $existing_property.$com_name - - if ($property_value -is [Hashtable]) { - foreach ($kv in $property_value.GetEnumerator()) { - $sub_property_value = $kv.Value - - if ($null -ne $sub_property_value) { - $sub_com_name = Convert-SnakeToPascalCase -snake $kv.Key - $sub_existing_value = $existing_property.$com_name.$sub_com_name - - if ($sub_property_value -cne $sub_existing_value) { - [void]$diff_list.Add("-$com_name.$sub_com_name=$sub_existing_value") - [void]$diff_list.Add("+$com_name.$sub_com_name=$sub_property_value") - } - } - } - } elseif ($null -ne $property_value -and $property_value -cne $existing_value) { - [void]$diff_list.Add("-$com_name=$existing_value") - [void]$diff_list.Add("+$com_name=$property_value") - } - } - - if ($diff_list.Count -gt 0) { - [void]$changes.Add("$property_name[$i] = {`n $($diff_list -join ",`n ")`n}") - } - } - - # finally rebuild the new property collection - $new_object = $collection.Create($type) - foreach ($property_arg in $total_args) { - $new_value = $new_property.$property_arg - if ($new_value -is [Hashtable]) { - $com_name = Convert-SnakeToPascalCase -snake $property_arg - $new_object_property = $new_object.$com_name - - foreach ($kv in $new_value.GetEnumerator()) { - $value = $kv.Value - if ($null -ne $value) { - Set-PropertyForComObject -com_object $new_object_property -name $property_name -arg $kv.Key -value $value - } - } - } elseif ($null -ne $new_value) { - Set-PropertyForComObject -com_object $new_object -name $property_name -arg $property_arg -value $new_value - } - } - } - - # if there were any extra properties not in the new list, create diff str - if ($existing_count -gt $new_count) { - for ($i = $new_count; $i -lt $existing_count; $i++) { - $diff_list = [System.Collections.ArrayList]@() - $existing_property = $existing[$i] - $existing_type = [Enum]::ToObject($enum, $existing_property.Type) - - if ($map.ContainsKey($existing_type)) { - $property_map = $map.$existing_type - $property_args = $property_map.mandatory + $property_map.optional - - foreach ($property_arg in $property_args) { - $com_name = Convert-SnakeToPascalCase -snake $property_arg - $existing_value = $existing_property.$com_name - if ($null -ne $existing_value) { - [void]$diff_list.Add("-$com_name=$existing_value") - } - } - } else { - [void]$diff_list.Add("-UNKNOWN TYPE $existing_type") - } - - [void]$changes.Add("-$property_name[$i] = {`n $($diff_list -join ",`n ")`n-}") - } - } - - return ,$changes -} - -Function Compare-Actions($task_definition) { - # compares the Actions property and returns a list of list of changed - # actions for use in a diff string - # ActionCollection - https://msdn.microsoft.com/en-us/library/windows/desktop/aa446804(v=vs.85).aspx - # Action - https://msdn.microsoft.com/en-us/library/windows/desktop/aa446803(v=vs.85).aspx - if ($null -eq $actions) { - return ,[System.Collections.ArrayList]@() - } - - $task_actions = $task_definition.Actions - $existing_count = $task_actions.Count - - # because we clear the actions and re-add them to keep the order, we need - # to convert the existing actions to a new list. - # The Item property in actions starts at 1 - $existing_actions = [System.Collections.ArrayList]@() - for ($i = 1; $i -le $existing_count; $i++) { - [void]$existing_actions.Add($task_actions.Item($i)) - } - if ($existing_count -gt 0) { - $task_actions.Clear() - } - - $map = @{ - [TASK_ACTION_TYPE]::TASK_ACTION_EXEC = @{ - mandatory = @('path') - optional = @('arguments', 'working_directory') - } - } - $changes = Compare-PropertyList -collection $task_actions -property_name "action" -new $actions -existing $existing_actions -map $map -enum TASK_ACTION_TYPE - - return ,$changes -} - -Function Compare-Principal($task_definition, $task_definition_xml) { - # compares the Principal property and returns a list of changed objects for - # use in a diff string - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382071(v=vs.85).aspx - $principal_map = @{ - DisplayName = $display_name - LogonType = $logon_type - RunLevel = $run_level - } - $enum_map = @{ - LogonType = "TASK_LOGON_TYPE" - RunLevel = "TASK_RUN_LEVEL" - } - $task_principal = $task_definition.Principal - $changes = Compare-Properties -property_name "Principal" -parent_property $task_principal -map $principal_map -enum_map $enum_map - - # Principal.UserId and GroupId only returns the username portion of the - # username, skipping the domain or server name. This makes the - # comparison process useless so we need to parse the task XML to get - # the actual sid/username. Depending on OS version this could be the SID - # or it could be the username, we need to handle that accordingly - $principal_username_sid = $task_definition_xml.Task.Principals.Principal.UserId - if ($null -ne $principal_username_sid -and $principal_username_sid -notmatch "^S-\d-\d+(-\d+){1,14}(-\d+){0,1}$") { - $principal_username_sid = Convert-ToSID -account_name $principal_username_sid - } - $principal_group_sid = $task_definition_xml.Task.Principals.Principal.GroupId - if ($null -ne $principal_group_sid -and $principal_group_sid -notmatch "^S-\d-\d+(-\d+){1,14}(-\d+){0,1}$") { - $principal_group_sid = Convert-ToSID -account_name $principal_group_sid - } - - if ($null -ne $username_sid) { - $new_user_name = Convert-FromSid -sid $username_sid - if ($null -ne $principal_group_sid) { - $existing_account_name = Convert-FromSid -sid $principal_group_sid - [void]$changes.Add("-GroupId=$existing_account_name`n+UserId=$new_user_name") - $task_principal.UserId = $new_user_name - $task_principal.GroupId = $null - } elseif ($null -eq $principal_username_sid) { - [void]$changes.Add("+UserId=$new_user_name") - $task_principal.UserId = $new_user_name - } elseif ($principal_username_sid -ne $username_sid) { - $existing_account_name = Convert-FromSid -sid $principal_username_sid - [void]$changes.Add("-UserId=$existing_account_name`n+UserId=$new_user_name") - $task_principal.UserId = $new_user_name - } - } - if ($null -ne $group_sid) { - $new_group_name = Convert-FromSid -sid $group_sid - if ($null -ne $principal_username_sid) { - $existing_account_name = Convert-FromSid -sid $principal_username_sid - [void]$changes.Add("-UserId=$existing_account_name`n+GroupId=$new_group_name") - $task_principal.UserId = $null - $task_principal.GroupId = $new_group_name - } elseif ($null -eq $principal_group_sid) { - [void]$changes.Add("+GroupId=$new_group_name") - $task_principal.GroupId = $new_group_name - } elseif ($principal_group_sid -ne $group_sid) { - $existing_account_name = Convert-FromSid -sid $principal_group_sid - [void]$changes.Add("-GroupId=$existing_account_name`n+GroupId=$new_group_name") - $task_principal.GroupId = $new_group_name - } - } - - return ,$changes -} - -Function Compare-RegistrationInfo($task_definition) { - # compares the RegistrationInfo property and returns a list of changed - # objects for use in a diff string - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382100(v=vs.85).aspx - $reg_info_map = @{ - Author = $author - Date = $date - Description = $description - Source = $source - Version = $version - } - $changes = Compare-Properties -property_name "RegistrationInfo" -parent_property $task_definition.RegistrationInfo -map $reg_info_map - - return ,$changes -} - -Function Compare-Settings($task_definition) { - # compares the task Settings property and returns a list of changed objects - # for use in a diff string - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383480(v=vs.85).aspx - $settings_map = @{ - AllowDemandStart = $allow_demand_start - AllowHardTerminate = $allow_hard_terminate - Compatibility = $compatibility - DeleteExpiredTaskAfter = $delete_expired_task_after - DisallowStartIfOnBatteries = $disallow_start_if_on_batteries - ExecutionTimeLimit = $execution_time_limit - Enabled = $enabled - Hidden = $hidden - # IdleSettings = $idle_settings # TODO: this takes in a COM object - MultipleInstances = $multiple_instances - # NetworkSettings = $network_settings # TODO: this takes in a COM object - Priority = $priority - RestartCount = $restart_count - RestartInterval = $restart_interval - RunOnlyIfIdle = $run_only_if_idle - RunOnlyIfNetworkAvailable = $run_only_if_network_available - StartWhenAvailable = $start_when_available - StopIfGoingOnBatteries = $stop_if_going_on_batteries - WakeToRun = $wake_to_run - } - $changes = Compare-Properties -property_name "Settings" -parent_property $task_definition.Settings -map $settings_map - - return ,$changes -} - -Function Compare-Triggers($task_definition) { - # compares the task Triggers property and returns a list of changed objects - # for use in a diff string - # TriggerCollection - https://msdn.microsoft.com/en-us/library/windows/desktop/aa383875(v=vs.85).aspx - # Trigger - https://msdn.microsoft.com/en-us/library/windows/desktop/aa383868(v=vs.85).aspx - if ($null -eq $triggers) { - return ,[System.Collections.ArrayList]@() - } - - $task_triggers = $task_definition.Triggers - $existing_count = $task_triggers.Count - - # because we clear the actions and re-add them to keep the order, we need - # to convert the existing actions to a new list. - # The Item property in actions starts at 1 - $existing_triggers = [System.Collections.ArrayList]@() - for ($i = 1; $i -le $existing_count; $i++) { - [void]$existing_triggers.Add($task_triggers.Item($i)) - } - if ($existing_count -gt 0) { - $task_triggers.Clear() - } - - $map = @{ - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_BOOT = @{ - mandatory = @() - optional = @('delay', 'enabled', 'end_boundary', 'execution_time_limit', 'start_boundary', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_DAILY = @{ - mandatory = @('start_boundary') - optional = @('days_interval', 'enabled', 'end_boundary', 'execution_time_limit', 'random_delay', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_EVENT = @{ - mandatory = @('subscription') - # TODO: ValueQueries is a COM object - optional = @('delay', 'enabled', 'end_boundary', 'execution_time_limit', 'start_boundary', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_IDLE = @{ - mandatory = @() - optional = @('enabled', 'end_boundary', 'execution_time_limit', 'start_boundary', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_LOGON = @{ - mandatory = @() - optional = @('delay', 'enabled', 'end_boundary', 'execution_time_limit', 'start_boundary', 'user_id', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_MONTHLYDOW = @{ - mandatory = @('start_boundary') - optional = @('days_of_week', 'enabled', 'end_boundary', 'execution_time_limit', 'months_of_year', 'random_delay', 'run_on_last_week_of_month', 'weeks_of_month', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_MONTHLY = @{ - mandatory = @('days_of_month', 'start_boundary') - optional = @('enabled', 'end_boundary', 'execution_time_limit', 'months_of_year', 'random_delay', 'run_on_last_day_of_month', 'start_boundary', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_REGISTRATION = @{ - mandatory = @() - optional = @('delay', 'enabled', 'end_boundary', 'execution_time_limit', 'start_boundary', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_TIME = @{ - mandatory = @('start_boundary') - optional = @('enabled', 'end_boundary', 'execution_time_limit', 'random_delay', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_WEEKLY = @{ - mandatory = @('days_of_week', 'start_boundary') - optional = @('enabled', 'end_boundary', 'execution_time_limit', 'random_delay', 'weeks_interval', 'repetition') - } - [TASK_TRIGGER_TYPE2]::TASK_TRIGGER_SESSION_STATE_CHANGE = @{ - mandatory = @('days_of_week', 'start_boundary') - optional = @('delay', 'enabled', 'end_boundary', 'execution_time_limit', 'state_change', 'user_id', 'repetition') - } - } - $changes = Compare-PropertyList -collection $task_triggers -property_name "trigger" -new $triggers -existing $existing_triggers -map $map -enum TASK_TRIGGER_TYPE2 - - return ,$changes -} - -Function Test-TaskExists($task_folder, $name) { - # checks if a task exists in the TaskFolder COM object, returns null if the - # task does not exist, otherwise returns the RegisteredTask object - $task = $null - if ($task_folder) { - $raw_tasks = $task_folder.GetTasks(1) # 1 = TASK_ENUM_HIDDEN - - for ($i = 1; $i -le $raw_tasks.Count; $i++) { - if ($raw_tasks.Item($i).Name -eq $name) { - $task = $raw_tasks.Item($i) - break - } - } - } - - return $task -} - -Function Test-XmlDurationFormat($key, $value) { - # validate value is in the Duration Data Type format - # PnYnMnDTnHnMnS - try { - $time_span = [System.Xml.XmlConvert]::ToTimeSpan($value) - return $time_span - } catch [System.FormatException] { - Fail-Json -obj $result -message "trigger option '$key' must be in the XML duration format but was '$value'" - } -} - -###################################### -### VALIDATION/BUILDING OF OPTIONS ### -###################################### -# convert username and group to SID if set -$username_sid = $null -if ($username) { - $username_sid = Convert-ToSID -account_name $username -} -$group_sid = $null -if ($group) { - $group_sid = Convert-ToSID -account_name $group -} - -# validate store_password and logon_type -if ($null -ne $logon_type) { - $full_enum_name = "TASK_LOGON_$($logon_type.ToUpper())" - $logon_type = [TASK_LOGON_TYPE]::$full_enum_name -} - -# now validate the logon_type option with the other parameters -if ($null -ne $username -and $null -ne $group) { - Fail-Json -obj $result -message "username and group can not be set at the same time" -} -if ($null -ne $logon_type) { - if ($logon_type -eq [TASK_LOGON_TYPE]::TASK_LOGON_S4U -and $null -eq $password) { - Fail-Json -obj $result -message "password must be set when logon_type=s4u" - } - - if ($logon_type -eq [TASK_LOGON_TYPE]::TASK_LOGON_GROUP -and $null -eq $group) { - Fail-Json -obj $result -message "group must be set when logon_type=group" - } - - # SIDs == Local System, Local Service and Network Service - if ($logon_type -eq [TASK_LOGON_TYPE]::TASK_LOGON_SERVICE_ACCOUNT -and $username_sid -notin @("S-1-5-18", "S-1-5-19", "S-1-5-20")) { - Fail-Json -obj $result -message "username must be SYSTEM, LOCAL SERVICE or NETWORK SERVICE when logon_type=service_account" - } -} - -# convert the run_level to enum value -if ($null -ne $run_level) { - if ($run_level -eq "limited") { - $run_level = [TASK_RUN_LEVEL]::TASK_RUNLEVEL_LUA - } else { - $run_level = [TASK_RUN_LEVEL]::TASK_RUNLEVEL_HIGHEST - } -} - -# manually add the only support action type for each action - also convert PSCustomObject to Hashtable -for ($i = 0; $i -lt $actions.Count; $i++) { - $action = $actions[$i] - $action.type = [TASK_ACTION_TYPE]::TASK_ACTION_EXEC - if (-not $action.ContainsKey("path")) { - Fail-Json -obj $result -message "action entry must contain the key 'path'" - } - $actions[$i] = $action -} - -# convert and validate the triggers - and convert PSCustomObject to Hashtable -for ($i = 0; $i -lt $triggers.Count; $i++) { - $trigger = $triggers[$i] - $valid_trigger_types = @('event', 'time', 'daily', 'weekly', 'monthly', 'monthlydow', 'idle', 'registration', 'boot', 'logon', 'session_state_change') - if (-not $trigger.ContainsKey("type")) { - Fail-Json -obj $result -message "a trigger entry must contain a key 'type' with a value of '$($valid_trigger_types -join "', '")'" - } - - $trigger_type = $trigger.type - if ($trigger_type -notin $valid_trigger_types) { - Fail-Json -obj $result -message "the specified trigger type '$trigger_type' is not valid, type must be a value of '$($valid_trigger_types -join "', '")'" - } - - $full_enum_name = "TASK_TRIGGER_$($trigger_type.ToUpper())" - $trigger_type = [TASK_TRIGGER_TYPE2]::$full_enum_name - $trigger.type = $trigger_type - - $date_properties = @('start_boundary', 'end_boundary') - foreach ($property_name in $date_properties) { - # validate the date is in the DateTime format - # yyyy-mm-ddThh:mm:ss - if ($trigger.ContainsKey($property_name)) { - $date_value = $trigger.$property_name - try { - $date = Get-Date -Date $date_value -Format "yyyy-MM-dd'T'HH:mm:ssK" - # make sure we convert it to the full string format - $trigger.$property_name = $date.ToString() - } catch [System.Management.Automation.ParameterBindingException] { - Fail-Json -obj $result -message "trigger option '$property_name' must be in the format 'YYYY-MM-DDThh:mm:ss' format but was '$date_value'" - } - } - } - - $time_properties = @('execution_time_limit', 'delay', 'random_delay') - foreach ($property_name in $time_properties) { - if ($trigger.ContainsKey($property_name)) { - $time_span = $trigger.$property_name - Test-XmlDurationFormat -key $property_name -value $time_span - } - } - - if ($trigger.ContainsKey("repetition")) { - if ($trigger.repetition -is [Array]) { - Add-DeprecationWarning -obj $result -message "repetition is a list, should be defined as a dict" -version "2.12" - $trigger.repetition = $trigger.repetition[0] - } - - $interval_timespan = $null - if ($trigger.repetition.ContainsKey("interval") -and $null -ne $trigger.repetition.interval) { - $interval_timespan = Test-XmlDurationFormat -key "interval" -value $trigger.repetition.interval - } - - $duration_timespan = $null - if ($trigger.repetition.ContainsKey("duration") -and $null -ne $trigger.repetition.duration) { - $duration_timespan = Test-XmlDurationFormat -key "duration" -value $trigger.repetition.duration - } - - if ($null -ne $interval_timespan -and $null -ne $duration_timespan -and $interval_timespan -gt $duration_timespan) { - Fail-Json -obj $result -message "trigger repetition option 'interval' value '$($trigger.repetition.interval)' must be less than or equal to 'duration' value '$($trigger.repetition.duration)'" - } - } - - # convert out human readble text to the hex values for these properties - if ($trigger.ContainsKey("days_of_week")) { - $days = $trigger.days_of_week - if ($days -is [String]) { - $days = $days.Split(",").Trim() - } elseif ($days -isnot [Array]) { - $days = @($days) - } - - $day_value = 0 - foreach ($day in $days) { - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382057(v=vs.85).aspx - switch ($day) { - sunday { $day_value = $day_value -bor 0x01 } - monday { $day_value = $day_value -bor 0x02 } - tuesday { $day_value = $day_value -bor 0x04 } - wednesday { $day_value = $day_value -bor 0x08 } - thursday { $day_value = $day_value -bor 0x10 } - friday { $day_value = $day_value -bor 0x20 } - saturday { $day_value = $day_value -bor 0x40 } - default { Fail-Json -obj $result -message "invalid day of week '$day', check the spelling matches the full day name" } - } - } - if ($day_value -eq 0) { - $day_value = $null - } - - $trigger.days_of_week = $day_value - } - if ($trigger.ContainsKey("days_of_month")) { - $days = $trigger.days_of_month - if ($days -is [String]) { - $days = $days.Split(",").Trim() - } elseif ($days -isnot [Array]) { - $days = @($days) - } - - $day_value = 0 - foreach ($day in $days) { - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382063(v=vs.85).aspx - switch ($day) { - 1 { $day_value = $day_value -bor 0x01 } - 2 { $day_value = $day_value -bor 0x02 } - 3 { $day_value = $day_value -bor 0x04 } - 4 { $day_value = $day_value -bor 0x08 } - 5 { $day_value = $day_value -bor 0x10 } - 6 { $day_value = $day_value -bor 0x20 } - 7 { $day_value = $day_value -bor 0x40 } - 8 { $day_value = $day_value -bor 0x80 } - 9 { $day_value = $day_value -bor 0x100 } - 10 { $day_value = $day_value -bor 0x200 } - 11 { $day_value = $day_value -bor 0x400 } - 12 { $day_value = $day_value -bor 0x800 } - 13 { $day_value = $day_value -bor 0x1000 } - 14 { $day_value = $day_value -bor 0x2000 } - 15 { $day_value = $day_value -bor 0x4000 } - 16 { $day_value = $day_value -bor 0x8000 } - 17 { $day_value = $day_value -bor 0x10000 } - 18 { $day_value = $day_value -bor 0x20000 } - 19 { $day_value = $day_value -bor 0x40000 } - 20 { $day_value = $day_value -bor 0x80000 } - 21 { $day_value = $day_value -bor 0x100000 } - 22 { $day_value = $day_value -bor 0x200000 } - 23 { $day_value = $day_value -bor 0x400000 } - 24 { $day_value = $day_value -bor 0x800000 } - 25 { $day_value = $day_value -bor 0x1000000 } - 26 { $day_value = $day_value -bor 0x2000000 } - 27 { $day_value = $day_value -bor 0x4000000 } - 28 { $day_value = $day_value -bor 0x8000000 } - 29 { $day_value = $day_value -bor 0x10000000 } - 30 { $day_value = $day_value -bor 0x20000000 } - 31 { $day_value = $day_value -bor 0x40000000 } - default { Fail-Json -obj $result -message "invalid day of month '$day', please specify numbers from 1-31" } - } - } - if ($day_value -eq 0) { - $day_value = $null - } - $trigger.days_of_month = $day_value - } - if ($trigger.ContainsKey("weeks_of_month")) { - $weeks = $trigger.weeks_of_month - if ($weeks -is [String]) { - $weeks = $weeks.Split(",").Trim() - } elseif ($weeks -isnot [Array]) { - $weeks = @($weeks) - } - - $week_value = 0 - foreach ($week in $weeks) { - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382061(v=vs.85).aspx - switch ($week) { - 1 { $week_value = $week_value -bor 0x01 } - 2 { $week_value = $week_value -bor 0x02 } - 3 { $week_value = $week_value -bor 0x04 } - 4 { $week_value = $week_value -bor 0x08 } - default { Fail-Json -obj $result -message "invalid week of month '$week', please specify weeks from 1-4" } - } - - } - if ($week_value -eq 0) { - $week_value = $null - } - $trigger.weeks_of_month = $week_value - } - if ($trigger.ContainsKey("months_of_year")) { - $months = $trigger.months_of_year - if ($months -is [String]) { - $months = $months.Split(",").Trim() - } elseif ($months -isnot [Array]) { - $months = @($months) - } - - $month_value = 0 - foreach ($month in $months) { - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382064(v=vs.85).aspx - switch ($month) { - january { $month_value = $month_value -bor 0x01 } - february { $month_value = $month_value -bor 0x02 } - march { $month_value = $month_value -bor 0x04 } - april { $month_value = $month_value -bor 0x08 } - may { $month_value = $month_value -bor 0x10 } - june { $month_value = $month_value -bor 0x20 } - july { $month_value = $month_value -bor 0x40 } - august { $month_value = $month_value -bor 0x80 } - september { $month_value = $month_value -bor 0x100 } - october { $month_value = $month_value -bor 0x200 } - november { $month_value = $month_value -bor 0x400 } - december { $month_value = $month_value -bor 0x800 } - default { Fail-Json -obj $result -message "invalid month name '$month', please specify full month name" } - } - } - if ($month_value -eq 0) { - $month_value = $null - } - $trigger.months_of_year = $month_value - } - $triggers[$i] = $trigger -} - -# add \ to start of path if it is not already there -if (-not $path.StartsWith("\")) { - $path = "\$path" -} -# ensure path does not end with \ if more than 1 char -if ($path.EndsWith("\") -and $path.Length -ne 1) { - $path = $path.Substring(0, $path.Length - 1) -} - -######################## -### START CODE BLOCK ### -######################## -$service = New-Object -ComObject Schedule.Service -try { - $service.Connect() -} catch { - Fail-Json -obj $result -message "failed to connect to the task scheduler service: $($_.Exception.Message)" -} - -# check that the path for the task set exists, create if need be -try { - $task_folder = $service.GetFolder($path) -} catch { - $task_folder = $null -} - -# try and get the task at the path -$task = Test-TaskExists -task_folder $task_folder -name $name -$task_path = Join-Path -Path $path -ChildPath $name - -if ($state -eq "absent") { - if ($null -ne $task) { - if (-not $check_mode) { - try { - $task_folder.DeleteTask($name, 0) - } catch { - Fail-Json -obj $result -message "failed to delete task '$name' at path '$path': $($_.Exception.Message)" - } - } - if ($diff_mode) { - $result.diff.prepared = "-[Task]`n-$task_path`n" - } - $result.changed = $true - - # check if current folder has any more tasks - $other_tasks = $task_folder.GetTasks(1) # 1 = TASK_ENUM_HIDDEN - if ($other_tasks.Count -eq 0 -and $task_folder.Name -ne "\") { - try { - $task_folder.DeleteFolder($null, $null) - } catch { - Fail-Json -obj $result -message "failed to delete empty task folder '$path' after task deletion: $($_.Exception.Message)" - } - } - } -} else { - if ($null -eq $task) { - $create_diff_string = "+[Task]`n+$task_path`n`n" - # to create a bare minimum task we need 1 action - if ($null -eq $actions -or $actions.Count -eq 0) { - Fail-Json -obj $result -message "cannot create a task with no actions, set at least one action with a path to an executable" - } - - # Create a bare minimum task here, further properties will be set later on - $task_definition = $service.NewTask(0) - - # Set Actions info - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446803(v=vs.85).aspx - $create_diff_string += "[Actions]`n" - $task_actions = $task_definition.Actions - foreach ($action in $actions) { - $create_diff_string += "+action[0] = {`n +Type=$([TASK_ACTION_TYPE]::TASK_ACTION_EXEC),`n +Path=$($action.path)`n" - $task_action = $task_actions.Create([TASK_ACTION_TYPE]::TASK_ACTION_EXEC) - $task_action.Path = $action.path - if ($null -ne $action.arguments) { - $create_diff_string += " +Arguments=$($action.arguments)`n" - $task_action.Arguments = $action.arguments - } - if ($null -ne $action.working_directory) { - $create_diff_string += " +WorkingDirectory=$($action.working_directory)`n" - $task_action.WorkingDirectory = $action.working_directory - } - $create_diff_string += "+}`n" - } - - # Register the new task - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382577(v=vs.85).aspx - if ($check_mode) { - # Only validate the task in check mode - $task_creation_flags = [TASK_CREATION]::TASK_VALIDATE_ONLY - } else { - # Create the task but do not fire it as we still need to configure it further below - $task_creation_flags = [TASK_CREATION]::TASK_CREATE -bor [TASK_CREATION]::TASK_IGNORE_REGISTRATION_TRIGGERS - } - - # folder doesn't exist, need to create - if ($null -eq $task_folder) { - $task_folder = $service.GetFolder("\") - try { - if (-not $check_mode) { - $task_folder = $task_folder.CreateFolder($path) - } - } catch { - Fail-Json -obj $result -message "failed to create new folder at path '$path': $($_.Exception.Message)" - } - } - - try { - $task = $task_folder.RegisterTaskDefinition($name, $task_definition, $task_creation_flags, $null, $null, $null) - } catch { - Fail-Json -obj $result -message "failed to register new task definition: $($_.Exception.Message)" - } - if ($diff_mode) { - $result.diff.prepared = $create_diff_string - } - - $result.changed = $true - } - - # we cannot configure a task that was created above in check mode as it - # won't actually exist - if ($task) { - $task_definition = $task.Definition - $task_definition_xml = [xml]$task_definition.XmlText - - $action_changes = Compare-Actions -task_definition $task_definition - $principal_changed = Compare-Principal -task_definition $task_definition -task_definition_xml $task_definition_xml - $reg_info_changed = Compare-RegistrationInfo -task_definition $task_definition - $settings_changed = Compare-Settings -task_definition $task_definition - $trigger_changes = Compare-Triggers -task_definition $task_definition - - # compile the diffs into one list with headers - $task_diff = [System.Collections.ArrayList]@() - if ($action_changes.Count -gt 0) { - [void]$task_diff.Add("[Actions]") - foreach ($action_change in $action_changes) { - [void]$task_diff.Add($action_change) - } - [void]$task_diff.Add("`n") - } - if ($principal_changed.Count -gt 0) { - [void]$task_diff.Add("[Principal]") - foreach ($principal_change in $principal_changed) { - [void]$task_diff.Add($principal_change) - } - [void]$task_diff.Add("`n") - } - if ($reg_info_changed.Count -gt 0) { - [void]$task_diff.Add("[Registration Info]") - foreach ($reg_info_change in $reg_info_changed) { - [void]$task_diff.Add($reg_info_change) - } - [void]$task_diff.Add("`n") - } - if ($settings_changed.Count -gt 0) { - [void]$task_diff.Add("[Settings]") - foreach ($settings_change in $settings_changed) { - [void]$task_diff.add($settings_change) - } - [void]$task_diff.Add("`n") - } - if ($trigger_changes.Count -gt 0) { - [void]$task_diff.Add("[Triggers]") - foreach ($trigger_change in $trigger_changes) { - [void]$task_diff.Add("$trigger_change") - } - [void]$task_diff.Add("`n") - } - - if ($null -ne $password -and (($update_password -eq $true) -or ($task_diff.Count -gt 0))) { - # because we can't compare the passwords we just need to reset it - $register_username = $username - $register_password = $password - $register_logon_type = $task_principal.LogonType - } else { - # will inherit from the Principal property values - $register_username = $null - $register_password = $null - $register_logon_type = $null - } - - if ($task_diff.Count -gt 0 -or $null -ne $register_password) { - if ($check_mode) { - # Only validate the task in check mode - $task_creation_flags = [TASK_CREATION]::TASK_VALIDATE_ONLY - } else { - # Create the task - $task_creation_flags = [TASK_CREATION]::TASK_CREATE_OR_UPDATE - } - try { - $task_folder.RegisterTaskDefinition($name, $task_definition, $task_creation_flags, $register_username, $register_password, $register_logon_type) | Out-Null - } catch { - Fail-Json -obj $result -message "failed to modify scheduled task: $($_.Exception.Message)" - } - - $result.changed = $true - - if ($diff_mode) { - $changed_diff_text = $task_diff -join "`n" - if ($null -ne $result.diff.prepared) { - $diff_text = "$($result.diff.prepared)`n$changed_diff_text" - } else { - $diff_text = $changed_diff_text - } - $result.diff.prepared = $diff_text.Trim() - } - } - } -} - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_scheduled_task.py b/lib/ansible/modules/windows/win_scheduled_task.py deleted file mode 100644 index 6e64738451..0000000000 --- a/lib/ansible/modules/windows/win_scheduled_task.py +++ /dev/null @@ -1,546 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, 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_scheduled_task -version_added: "2.0" -short_description: Manage scheduled tasks -description: -- Creates/modified or removes Windows scheduled tasks. -options: - # module definition options - name: - description: - - The name of the scheduled task without the path. - type: str - required: yes - path: - description: - - Task folder in which this task will be stored. - - Will create the folder when C(state=present) and the folder does not - already exist. - - Will remove the folder when C(state=absent) and there are no tasks left - in the folder. - type: str - default: \ - state: - description: - - When C(state=present) will ensure the task exists. - - When C(state=absent) will ensure the task does not exist. - type: str - choices: [ absent, present ] - default: present - - # Action options - actions: - description: - - A list of action to configure for the task. - - See suboptions for details on how to construct each list entry. - - When creating a task there MUST be at least one action but when deleting - a task this can be a null or an empty list. - - The ordering of this list is important, the module will ensure the order - is kept when modifying the task. - - This module only supports the C(ExecAction) type but can still delete the - older legacy types. - type: list - suboptions: - path: - description: - - The path to the executable for the ExecAction. - type: str - required: yes - arguments: - description: - - An argument string to supply for the executable. - type: str - working_directory: - description: - - The working directory to run the executable from. - type: str - version_added: '2.5' - - # Trigger options - triggers: - description: - - A list of triggers to configure for the task. - - See suboptions for details on how to construct each list entry. - - The ordering of this list is important, the module will ensure the order - is kept when modifying the task. - - There are multiple types of triggers, see U(https://msdn.microsoft.com/en-us/library/windows/desktop/aa383868.aspx) - for a list of trigger types and their options. - - The suboption options listed below are not required for all trigger - types, read the description for more details. - type: list - suboptions: - type: - description: - - The trigger type, this value controls what below options are - required. - type: str - required: yes - choices: [ boot, daily, event, idle, logon, monthlydow, monthly, registration, time, weekly, session_state_change ] - enabled: - description: - - Whether to set the trigger to enabled or disabled - - Used in all trigger types. - type: bool - start_boundary: - description: - - The start time for the task, even if the trigger meets the other - start criteria, it won't start until this time is met. - - If you wish to run a task at 9am on a day you still need to specify - the date on which the trigger is activated, you can set any date even - ones in the past. - - Required when C(type) is C(daily), C(monthlydow), C(monthly), - C(time), C(weekly), (session_state_change). - - Optional for the rest of the trigger types. - - This is in ISO 8601 DateTime format C(YYYY-MM-DDThh:mm:ss). - type: str - end_boundary: - description: - - The end time for when the trigger is deactivated. - - This is in ISO 8601 DateTime format C(YYYY-MM-DDThh:mm:ss). - type: str - execution_time_limit: - description: - - The maximum amount of time that the task is allowed to run for. - - Optional for all the trigger types. - - Is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - delay: - description: - - The time to delay the task from running once the trigger has been - fired. - - Optional when C(type) is C(boot), C(event), C(logon), - C(registration), C(session_state_change). - - Is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - random_delay: - description: - - The delay time that is randomly added to the start time of the - trigger. - - Optional when C(type) is C(daily), C(monthlydow), C(monthly), - C(time), C(weekly). - - Is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - subscription: - description: - - Only used and is required for C(type=event). - - The XML query string that identifies the event that fires the - trigger. - type: str - user_id: - description: - - The username that the trigger will target. - - Optional when C(type) is C(logon), C(session_state_change). - - Can be the username or SID of a user. - - When C(type=logon) and you want the trigger to fire when a user in a - group logs on, leave this as null and set C(group) to the group you - wish to trigger. - type: str - days_of_week: - description: - - The days of the week for the trigger. - - Can be a list or comma separated string of full day names e.g. monday - instead of mon. - - Required when C(type) is C(weekly), C(type=session_state_change). - - Optional when C(type=monthlydow). - type: str - days_of_month: - description: - - The days of the month from 1 to 31 for the triggers. - - If you wish to set the trigger for the last day of any month - use C(run_on_last_day_of_month). - - Can be a list or comma separated string of day numbers. - - Required when C(type=monthly). - type: str - weeks_of_month: - description: - - The weeks of the month for the trigger. - - Can be a list or comma separated string of the numbers 1 to 4 - representing the first to 4th week of the month. - - Optional when C(type=monthlydow). - type: str - months_of_year: - description: - - The months of the year for the trigger. - - Can be a list or comma separated string of full month names e.g. - march instead of mar. - - Optional when C(type) is C(monthlydow), C(monthly). - type: str - run_on_last_week_of_month: - description: - - Boolean value that sets whether the task runs on the last week of the - month. - - Optional when C(type) is C(monthlydow). - type: bool - run_on_last_day_of_month: - description: - - Boolean value that sets whether the task runs on the last day of the - month. - - Optional when C(type) is C(monthly). - type: bool - weeks_interval: - description: - - The interval of weeks to run on, e.g. C(1) means every week while - C(2) means every other week. - - Optional when C(type=weekly). - type: int - repetition: - description: - - Allows you to define the repetition action of the trigger that defines how often the task is run and how long the repetition pattern is repeated - after the task is started. - - It takes in the following keys, C(duration), C(interval), C(stop_at_duration_end) - suboptions: - duration: - description: - - Defines how long the pattern is repeated. - - The value is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - - By default this is not set which means it will repeat indefinitely. - type: str - interval: - description: - - The amount of time between each restart of the task. - - The value is written in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - stop_at_duration_end: - description: - - Whether a running instance of the task is stopped at the end of the repetition pattern. - type: bool - version_added: '2.5' - - # Principal options - display_name: - description: - - The name of the user/group that is displayed in the Task Scheduler UI. - type: str - version_added: '2.5' - group: - description: - - The group that will run the task. - - C(group) and C(username) are exclusive to each other and cannot be set - at the same time. - - C(logon_type) can either be not set or equal C(group). - type: str - version_added: '2.5' - logon_type: - description: - - The logon method that the task will run with. - - C(password) means the password will be stored and the task has access - to network resources. - - C(s4u) means the existing token will be used to run the task and no - password will be stored with the task. Means no network or encrypted - files access. - - C(interactive_token) means the user must already be logged on - interactively and will run in an existing interactive session. - - C(group) means that the task will run as a group. - - C(service_account) means that a service account like System, Local - Service or Network Service will run the task. - type: str - choices: [ none, password, s4u, interactive_token, group, service_account, token_or_password ] - version_added: '2.5' - run_level: - description: - - The level of user rights used to run the task. - - If not specified the task will be created with limited rights. - type: str - choices: [ limited, highest ] - aliases: [ runlevel ] - version_added: '2.4' - username: - description: - - The user to run the scheduled task as. - - Will default to the current user under an interactive token if not - specified during creation. - - The user account specified must have the C(SeBatchLogonRight) logon right - which can be added with M(win_user_right). - type: str - aliases: [ user ] - password: - description: - - The password for the user account to run the scheduled task as. - - This is required when running a task without the user being logged in, - excluding the builtin service accounts and Group Managed Service Accounts (gMSA). - - If set, will always result in a change unless C(update_password) is set - to C(no) and no other changes are required for the service. - type: str - version_added: '2.4' - update_password: - description: - - Whether to update the password even when not other changes have occurred. - - When C(yes) will always result in a change when executing the module. - type: bool - default: yes - version_added: '2.5' - - # RegistrationInfo options - author: - description: - - The author of the task. - type: str - version_added: '2.5' - date: - description: - - The date when the task was registered. - type: str - version_added: '2.5' - description: - description: - - The description of the task. - type: str - version_added: '2.5' - source: - description: - - The source of the task. - type: str - version_added: '2.5' - version: - description: - - The version number of the task. - type: str - version_added: '2.5' - - # Settings options - allow_demand_start: - description: - - Whether the task can be started by using either the Run command or the - Context menu. - type: bool - version_added: '2.5' - allow_hard_terminate: - description: - - Whether the task can be terminated by using TerminateProcess. - type: bool - version_added: '2.5' - compatibility: - description: - - The integer value with indicates which version of Task Scheduler a task - is compatible with. - - C(0) means the task is compatible with the AT command. - - C(1) means the task is compatible with Task Scheduler 1.0. - - C(2) means the task is compatible with Task Scheduler 2.0. - type: int - choices: [ 0, 1, 2 ] - version_added: '2.5' - delete_expired_task_after: - description: - - The amount of time that the Task Scheduler will wait before deleting the - task after it expires. - - A task expires after the end_boundary has been exceeded for all triggers - associated with the task. - - This is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - version_added: '2.5' - disallow_start_if_on_batteries: - description: - - Whether the task will not be started if the computer is running on - battery power. - type: bool - version_added: '2.5' - enabled: - description: - - Whether the task is enabled, the task can only run when C(yes). - type: bool - version_added: '2.5' - execution_time_limit: - description: - - The amount of time allowed to complete the task. - - When set to `PT0S`, the time limit is infinite. - - When omitted, the default time limit is 72 hours. - - This is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - version_added: '2.5' - hidden: - description: - - Whether the task will be hidden in the UI. - type: bool - version_added: '2.5' - multiple_instances: - description: - - An integer that indicates the behaviour when starting a task that is - already running. - - C(0) will start a new instance in parallel with existing instances of - that task. - - C(1) will wait until other instances of that task to finish running - before starting itself. - - C(2) will not start a new instance if another is running. - - C(3) will stop other instances of the task and start the new one. - type: int - choices: [ 0, 1, 2, 3 ] - version_added: '2.5' - priority: - description: - - The priority level (0-10) of the task. - - When creating a new task the default is C(7). - - See U(https://msdn.microsoft.com/en-us/library/windows/desktop/aa383512.aspx) - for details on the priority levels. - type: int - version_added: '2.5' - restart_count: - description: - - The number of times that the Task Scheduler will attempt to restart the - task. - type: int - version_added: '2.5' - restart_interval: - description: - - How long the Task Scheduler will attempt to restart the task. - - If this is set then C(restart_count) must also be set. - - The maximum allowed time is 31 days. - - The minimum allowed time is 1 minute. - - This is in the ISO 8601 Duration format C(P[n]Y[n]M[n]DT[n]H[n]M[n]S). - type: str - version_added: '2.5' - run_only_if_idle: - description: - - Whether the task will run the task only if the computer is in an idle - state. - type: bool - version_added: '2.5' - run_only_if_network_available: - description: - - Whether the task will run only when a network is available. - type: bool - version_added: '2.5' - start_when_available: - description: - - Whether the task can start at any time after its scheduled time has - passed. - type: bool - version_added: '2.5' - stop_if_going_on_batteries: - description: - - Whether the task will be stopped if the computer begins to run on battery - power. - type: bool - version_added: '2.5' - wake_to_run: - description: - - Whether the task will wake the computer when it is time to run the task. - type: bool - version_added: '2.5' -notes: -- In Ansible 2.4 and earlier, this could only be run on Server 2012/Windows 8 - or newer. Since Ansible 2.5 this restriction has been lifted. -- The option names and structure for actions and triggers of a service follow - the C(RegisteredTask) naming standard and requirements, it would be useful to - read up on this guide if coming across any issues U(https://msdn.microsoft.com/en-us/library/windows/desktop/aa382542.aspx). -- A Group Managed Service Account (gMSA) can be used by setting C(logon_type) to C(password) - and omitting the password parameter. For more information on gMSAs, - see U(https://techcommunity.microsoft.com/t5/Core-Infrastructure-and-Security/Windows-Server-2012-Group-Managed-Service-Accounts/ba-p/255910) -seealso: -- module: win_scheduled_task_stat -- module: win_user_right -author: -- Peter Mounce (@petemounce) -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Create a task to open 2 command prompts as SYSTEM - win_scheduled_task: - name: TaskName - description: open command prompt - actions: - - path: cmd.exe - arguments: /c hostname - - path: cmd.exe - arguments: /c whoami - triggers: - - type: daily - start_boundary: '2017-10-09T09:00:00' - username: SYSTEM - state: present - enabled: yes - -- name: Create task to run a PS script as NETWORK service on boot - win_scheduled_task: - name: TaskName2 - description: Run a PowerShell script - actions: - - path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe - arguments: -ExecutionPolicy Unrestricted -NonInteractive -File C:\TestDir\Test.ps1 - triggers: - - type: boot - username: NETWORK SERVICE - run_level: highest - state: present - -- name: Update Local Security Policy to allow users to run scheduled tasks - win_user_right: - name: SeBatchLogonRight - users: - - LocalUser - - DOMAIN\NetworkUser - action: add - -- name: Change above task to run under a domain user account, storing the passwords - win_scheduled_task: - name: TaskName2 - username: DOMAIN\User - password: Password - logon_type: password - -- name: Change the above task again, choosing not to store the password - win_scheduled_task: - name: TaskName2 - username: DOMAIN\User - logon_type: s4u - -- name: Change above task to use a gMSA, where the password is managed automatically - win_scheduled_task: - name: TaskName2 - username: DOMAIN\gMsaSvcAcct$ - logon_type: password - -- name: Create task with multiple triggers - win_scheduled_task: - name: TriggerTask - path: \Custom - actions: - - path: cmd.exe - triggers: - - type: daily - - type: monthlydow - username: SYSTEM - -- name: Set logon type to password but don't force update the password - win_scheduled_task: - name: TriggerTask - path: \Custom - actions: - - path: cmd.exe - username: Administrator - password: password - update_password: no - -- name: Disable a task that already exists - win_scheduled_task: - name: TaskToDisable - enabled: no - -- name: Create a task that will be repeated every minute for five minutes - win_scheduled_task: - name: RepeatedTask - description: open command prompt - actions: - - path: cmd.exe - arguments: /c hostname - triggers: - - type: registration - repetition: - interval: PT1M - duration: PT5M - stop_at_duration_end: yes -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 b/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 deleted file mode 100644 index 37bc78a281..0000000000 --- a/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 +++ /dev/null @@ -1,330 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.CamelConversion -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.SID - -$params = Parse-Args -arguments $args -$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP - -$path = Get-AnsibleParam -obj $params -name "path" -type "str" -default "\" -$name = Get-AnsibleParam -obj $params -name "name" -type "str" - -$result = @{ - changed = $false -} - -$task_enums = @" -public enum TASK_ACTION_TYPE -{ - TASK_ACTION_EXEC = 0, - // The below are not supported and are only kept for documentation purposes - TASK_ACTION_COM_HANDLER = 5, - TASK_ACTION_SEND_EMAIL = 6, - TASK_ACTION_SHOW_MESSAGE = 7 -} - -public enum TASK_LOGON_TYPE -{ - TASK_LOGON_NONE = 0, - TASK_LOGON_PASSWORD = 1, - TASK_LOGON_S4U = 2, - TASK_LOGON_INTERACTIVE_TOKEN = 3, - TASK_LOGON_GROUP = 4, - TASK_LOGON_SERVICE_ACCOUNT = 5, - TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6 -} - -public enum TASK_RUN_LEVEL -{ - TASK_RUNLEVEL_LUA = 0, - TASK_RUNLEVEL_HIGHEST = 1 -} - -public enum TASK_STATE -{ - TASK_STATE_UNKNOWN = 0, - TASK_STATE_DISABLED = 1, - TASK_STATE_QUEUED = 2, - TASK_STATE_READY = 3, - TASK_STATE_RUNNING = 4 -} - -public enum TASK_TRIGGER_TYPE2 -{ - TASK_TRIGGER_EVENT = 0, - TASK_TRIGGER_TIME = 1, - TASK_TRIGGER_DAILY = 2, - TASK_TRIGGER_WEEKLY = 3, - TASK_TRIGGER_MONTHLY = 4, - TASK_TRIGGER_MONTHLYDOW = 5, - TASK_TRIGGER_IDLE = 6, - TASK_TRIGGER_REGISTRATION = 7, - TASK_TRIGGER_BOOT = 8, - TASK_TRIGGER_LOGON = 9, - TASK_TRIGGER_SESSION_STATE_CHANGE = 11 -} -"@ - -$original_tmp = $env:TMP -$env:TMP = $_remote_tmp -Add-Type -TypeDefinition $task_enums -$env:TMP = $original_tmp - -Function Get-PropertyValue($task_property, $com, $property) { - $raw_value = $com.$property - - if ($null -eq $raw_value) { - return $null - } elseif ($raw_value.GetType().Name -eq "__ComObject") { - $com_values = @{} - Get-Member -InputObject $raw_value -MemberType Property | ForEach-Object { - $com_value = Get-PropertyValue -task_property $property -com $raw_value -property $_.Name - $com_values.$($_.Name) = $com_value - } - - return ,$com_values - } - - switch ($property) { - DaysOfWeek { - $value_list = @() - $map = @( - @{ day = "sunday"; bitwise = 0x01 } - @{ day = "monday"; bitwise = 0x02 } - @{ day = "tuesday"; bitwise = 0x04 } - @{ day = "wednesday"; bitwise = 0x08 } - @{ day = "thursday"; bitwise = 0x10 } - @{ day = "friday"; bitwise = 0x20 } - @{ day = "saturday"; bitwise = 0x40 } - ) - foreach ($entry in $map) { - $day = $entry.day - $bitwise = $entry.bitwise - if ($raw_value -band $bitwise) { - $value_list += $day - } - } - - $value = $value_list -join "," - break - } - DaysOfMonth { - $value_list = @() - $map = @( - @{ day = "1"; bitwise = 0x01 } - @{ day = "2"; bitwise = 0x02 } - @{ day = "3"; bitwise = 0x04 } - @{ day = "4"; bitwise = 0x08 } - @{ day = "5"; bitwise = 0x10 } - @{ day = "6"; bitwise = 0x20 } - @{ day = "7"; bitwise = 0x40 } - @{ day = "8"; bitwise = 0x80 } - @{ day = "9"; bitwise = 0x100 } - @{ day = "10"; bitwise = 0x200 } - @{ day = "11"; bitwise = 0x400 } - @{ day = "12"; bitwise = 0x800 } - @{ day = "13"; bitwise = 0x1000 } - @{ day = "14"; bitwise = 0x2000 } - @{ day = "15"; bitwise = 0x4000 } - @{ day = "16"; bitwise = 0x8000 } - @{ day = "17"; bitwise = 0x10000 } - @{ day = "18"; bitwise = 0x20000 } - @{ day = "19"; bitwise = 0x40000 } - @{ day = "20"; bitwise = 0x80000 } - @{ day = "21"; bitwise = 0x100000 } - @{ day = "22"; bitwise = 0x200000 } - @{ day = "23"; bitwise = 0x400000 } - @{ day = "24"; bitwise = 0x800000 } - @{ day = "25"; bitwise = 0x1000000 } - @{ day = "26"; bitwise = 0x2000000 } - @{ day = "27"; bitwise = 0x4000000 } - @{ day = "28"; bitwise = 0x8000000 } - @{ day = "29"; bitwise = 0x10000000 } - @{ day = "30"; bitwise = 0x20000000 } - @{ day = "31"; bitwise = 0x40000000 } - ) - - foreach ($entry in $map) { - $day = $entry.day - $bitwise = $entry.bitwise - if ($raw_value -band $bitwise) { - $value_list += $day - } - } - - $value = $value_list -join "," - break - } - WeeksOfMonth { - $value_list = @() - $map = @( - @{ week = "1"; bitwise = 0x01 } - @{ week = "2"; bitwise = 0x02 } - @{ week = "3"; bitwise = 0x04 } - @{ week = "4"; bitwise = 0x04 } - ) - - foreach ($entry in $map) { - $week = $entry.week - $bitwise = $entry.bitwise - if ($raw_value -band $bitwise) { - $value_list += $week - } - } - - $value = $value_list -join "," - break - } - MonthsOfYear { - $value_list = @() - $map = @( - @{ month = "january"; bitwise = 0x01 } - @{ month = "february"; bitwise = 0x02 } - @{ month = "march"; bitwise = 0x04 } - @{ month = "april"; bitwise = 0x08 } - @{ month = "may"; bitwise = 0x10 } - @{ month = "june"; bitwise = 0x20 } - @{ month = "july"; bitwise = 0x40 } - @{ month = "august"; bitwise = 0x80 } - @{ month = "september"; bitwise = 0x100 } - @{ month = "october"; bitwise = 0x200 } - @{ month = "november"; bitwise = 0x400 } - @{ month = "december"; bitwise = 0x800 } - ) - - foreach ($entry in $map) { - $month = $entry.month - $bitwise = $entry.bitwise - if ($raw_value -band $bitwise) { - $value_list += $month - } - } - - $value = $value_list -join "," - break - } - Type { - if ($task_property -eq "actions") { - $value = [Enum]::ToObject([TASK_ACTION_TYPE], $raw_value).ToString() - } elseif ($task_property -eq "triggers") { - $value = [Enum]::ToObject([TASK_TRIGGER_TYPE2], $raw_value).ToString() - } - break - } - RunLevel { - $value = [Enum]::ToObject([TASK_RUN_LEVEL], $raw_value).ToString() - break - } - LogonType { - $value = [Enum]::ToObject([TASK_LOGON_TYPE], $raw_value).ToString() - break - } - UserId { - $sid = Convert-ToSID -account_name $raw_value - $value = Convert-FromSid -sid $sid - } - GroupId { - $sid = Convert-ToSID -account_name $raw_value - $value = Convert-FromSid -sid $sid - } - default { - $value = $raw_value - break - } - } - - return ,$value -} - -$service = New-Object -ComObject Schedule.Service -try { - $service.Connect() -} catch { - Fail-Json -obj $result -message "failed to connect to the task scheduler service: $($_.Exception.Message)" -} - -try { - $task_folder = $service.GetFolder($path) - $result.folder_exists = $true -} catch { - $result.folder_exists = $false - if ($null -ne $name) { - $result.task_exists = $false - } - Exit-Json -obj $result -} - -$folder_tasks = $task_folder.GetTasks(1) -$folder_task_names = @() -$folder_task_count = 0 -$task = $null -for ($i = 1; $i -le $folder_tasks.Count; $i++) { - $task_name = $folder_tasks.Item($i).Name - $folder_task_names += $task_name - $folder_task_count += 1 - - if ($null -ne $name -and $task_name -eq $name) { - $task = $folder_tasks.Item($i) - } -} -$result.folder_task_names = $folder_task_names -$result.folder_task_count = $folder_task_count - -if ($null -ne $name) { - if ($null -ne $task) { - $result.task_exists = $true - - # task state - $result.state = @{ - last_run_time = (Get-Date $task.LastRunTime -Format s) - last_task_result = $task.LastTaskResult - next_run_time = (Get-Date $task.NextRunTime -Format s) - number_of_missed_runs = $task.NumberOfMissedRuns - status = [Enum]::ToObject([TASK_STATE], $task.State).ToString() - } - - # task definition - $task_definition = $task.Definition - $ignored_properties = @("XmlText") - $properties = @("principal", "registration_info", "settings") - $collection_properties = @("actions", "triggers") - - foreach ($property in $properties) { - $property_name = $property -replace "_" - $result.$property = @{} - $values = $task_definition.$property_name - Get-Member -InputObject $values -MemberType Property | ForEach-Object { - if ($_.Name -notin $ignored_properties) { - $result.$property.$($_.Name) = (Get-PropertyValue -task_property $property -com $values -property $_.Name) - } - } - } - - foreach ($property in $collection_properties) { - $result.$property = @() - $collection = $task_definition.$property - $collection_count = $collection.Count - for ($i = 1; $i -le $collection_count; $i++) { - $item = $collection.Item($i) - $item_info = @{} - - Get-Member -InputObject $item -MemberType Property | ForEach-Object { - if ($_.Name -notin $ignored_properties) { - $item_info.$($_.Name) = (Get-PropertyValue -task_property $property -com $item -property $_.Name) - } - } - $result.$property += $item_info - } - } - } else { - $result.task_exists = $false - } -} - -$result = Convert-DictToSnakeCase -dict $result - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_scheduled_task_stat.py b/lib/ansible/modules/windows/win_scheduled_task_stat.py deleted file mode 100644 index 1c450c4040..0000000000 --- a/lib/ansible/modules/windows/win_scheduled_task_stat.py +++ /dev/null @@ -1,379 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_scheduled_task_stat -version_added: "2.5" -short_description: Get information about Windows Scheduled Tasks -description: -- Will return whether the folder and task exists. -- Returns the names of tasks in the folder specified. -- Use M(win_scheduled_task) to configure a scheduled task. -options: - path: - description: The folder path where the task lives. - type: str - default: \ - name: - description: - - The name of the scheduled task to get information for. - - If C(name) is set and exists, will return information on the task itself. - type: str -seealso: -- module: win_scheduled_task -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Get information about a folder - win_scheduled_task_stat: - path: \folder name - register: task_folder_stat - -- name: Get information about a task in the root folder - win_scheduled_task_stat: - name: task name - register: task_stat - -- name: Get information about a task in a custom folder - win_scheduled_task_stat: - path: \folder name - name: task name - register: task_stat -''' - -RETURN = r''' -actions: - description: A list of actions. - returned: name is specified and task exists - type: list - sample: [ - { - "Arguments": "/c echo hi", - "Id": null, - "Path": "cmd.exe", - "Type": "TASK_ACTION_EXEC", - "WorkingDirectory": null - } - ] -folder_exists: - description: Whether the folder set at path exists. - returned: always - type: bool - sample: true -folder_task_count: - description: The number of tasks that exist in the folder. - returned: always - type: int - sample: 2 -folder_task_names: - description: A list of tasks that exist in the folder. - returned: always - type: list - sample: [ 'Task 1', 'Task 2' ] -principal: - description: Details on the principal configured to run the task. - returned: name is specified and task exists - type: complex - contains: - display_name: - description: The name of the user/group that is displayed in the Task - Scheduler UI. - returned: '' - type: str - sample: Administrator - group_id: - description: The group that will run the task. - returned: '' - type: str - sample: BUILTIN\Administrators - id: - description: The ID for the principal. - returned: '' - type: str - sample: Author - logon_type: - description: The logon method that the task will run with. - returned: '' - type: str - sample: TASK_LOGON_INTERACTIVE_TOKEN - run_level: - description: The level of user rights used to run the task. - returned: '' - type: str - sample: TASK_RUNLEVEL_LUA - user_id: - description: The user that will run the task. - returned: '' - type: str - sample: SERVER\Administrator -registration_info: - description: Details on the task registration info. - returned: name is specified and task exists - type: complex - contains: - author: - description: The author os the task. - returned: '' - type: str - sample: SERVER\Administrator - date: - description: The date when the task was register. - returned: '' - type: str - sample: '2017-01-01T10:00:00' - description: - description: The description of the task. - returned: '' - type: str - sample: task description - documentation: - description: The documentation of the task. - returned: '' - type: str - sample: task documentation - security_descriptor: - description: The security descriptor of the task. - returned: '' - type: str - sample: security descriptor - source: - description: The source of the task. - returned: '' - type: str - sample: source - uri: - description: The URI/path of the task. - returned: '' - type: str - sample: \task\task name - version: - description: The version of the task. - returned: '' - type: str - sample: 1.0 -settings: - description: Details on the task settings. - returned: name is specified and task exists - type: complex - contains: - allow_demand_start: - description: Whether the task can be started by using either the Run - command of the Context menu. - returned: '' - type: bool - sample: true - allow_hard_terminate: - description: Whether the task can terminated by using TerminateProcess. - returned: '' - type: bool - sample: true - compatibility: - description: The compatibility level of the task - returned: '' - type: int - sample: 2 - delete_expired_task_after: - description: The amount of time the Task Scheduler will wait before - deleting the task after it expires. - returned: '' - type: str - sample: PT10M - disallow_start_if_on_batteries: - description: Whether the task will not be started if the computer is - running on battery power. - returned: '' - type: bool - sample: false - disallow_start_on_remote_app_session: - description: Whether the task will not be started when in a remote app - session. - returned: '' - type: bool - sample: true - enabled: - description: Whether the task is enabled. - returned: '' - type: bool - sample: true - execution_time_limit: - description: The amount of time allowed to complete the task. - returned: '' - type: str - sample: PT72H - hidden: - description: Whether the task is hidden in the UI. - returned: '' - type: bool - sample: false - idle_settings: - description: The idle settings of the task. - returned: '' - type: dict - sample: { - "idle_duration": "PT10M", - "restart_on_idle": false, - "stop_on_idle_end": true, - "wait_timeout": "PT1H" - } - maintenance_settings: - description: The maintenance settings of the task. - returned: '' - type: str - sample: null - mulitple_instances: - description: Indicates the behaviour when starting a task that is already - running. - returned: '' - type: int - sample: 2 - network_settings: - description: The network settings of the task. - returned: '' - type: dict - sample: { - "id": null, - "name": null - } - priority: - description: The priority level of the task. - returned: '' - type: int - sample: 7 - restart_count: - description: The number of times that the task will attempt to restart - on failures. - returned: '' - type: int - sample: 0 - restart_interval: - description: How long the Task Scheduler will attempt to restart the - task. - returned: '' - type: str - sample: PT15M - run_only_id_idle: - description: Whether the task will run if the computer is in an idle - state. - returned: '' - type: bool - sample: true - run_only_if_network_available: - description: Whether the task will run only when a network is available. - returned: '' - type: bool - sample: false - start_when_available: - description: Whether the task can start at any time after its scheduled - time has passed. - returned: '' - type: bool - sample: false - stop_if_going_on_batteries: - description: Whether the task will be stopped if the computer begins to - run on battery power. - returned: '' - type: bool - sample: true - use_unified_scheduling_engine: - description: Whether the task will use the unified scheduling engine. - returned: '' - type: bool - sample: false - volatile: - description: Whether the task is volatile. - returned: '' - type: bool - sample: false - wake_to_run: - description: Whether the task will wake the computer when it is time to - run the task. - returned: '' - type: bool - sample: false -state: - description: Details on the state of the task - returned: name is specified and task exists - type: complex - contains: - last_run_time: - description: The time the registered task was last run. - returned: '' - type: str - sample: '2017-09-20T20:50:00' - last_task_result: - description: The results that were returned the last time the task was - run. - returned: '' - type: int - sample: 267009 - next_run_time: - description: The time when the task is next scheduled to run. - returned: '' - type: str - sample: '2017-09-20T22:50:00' - number_of_missed_runs: - description: The number of times a task has missed a scheduled run. - returned: '' - type: int - sample: 1 - status: - description: The status of the task, whether it is running, stopped, etc. - returned: '' - type: str - sample: TASK_STATE_RUNNING -task_exists: - description: Whether the task at the folder exists. - returned: name is specified - type: bool - sample: true -triggers: - description: A list of triggers. - returned: name is specified and task exists - type: list - sample: [ - { - "delay": "PT15M", - "enabled": true, - "end_boundary": null, - "execution_time_limit": null, - "id": null, - "repetition": { - "duration": null, - "interval": null, - "stop_at_duration_end": false - }, - "start_boundary": null, - "type": "TASK_TRIGGER_BOOT" - }, - { - "days_of_month": "5,15,30", - "enabled": true, - "end_boundary": null, - "execution_time_limit": null, - "id": null, - "months_of_year": "june,december", - "random_delay": null, - "repetition": { - "duration": null, - "interval": null, - "stop_at_duration_end": false - }, - "run_on_last_day_of_month": true, - "start_boundary": "2017-09-20T03:44:38", - "type": "TASK_TRIGGER_MONTHLY" - } - ] -''' diff --git a/lib/ansible/modules/windows/win_security_policy.ps1 b/lib/ansible/modules/windows/win_security_policy.ps1 deleted file mode 100644 index 274204b6aa..0000000000 --- a/lib/ansible/modules/windows/win_security_policy.ps1 +++ /dev/null @@ -1,196 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Jordan Borean <jborean93@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = 'Stop' - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_mode = Get-AnsibleParam -obj $Params -name "_ansible_diff" -type "bool" -default $false - -$section = Get-AnsibleParam -obj $params -name "section" -type "str" -failifempty $true -$key = Get-AnsibleParam -obj $params -name "key" -type "str" -failifempty $true -$value = Get-AnsibleParam -obj $params -name "value" -failifempty $true - -$result = @{ - changed = $false - section = $section - key = $key - value = $value -} - -if ($diff_mode) { - $result.diff = @{} -} - -Function Run-SecEdit($arguments) { - $stdout = $null - $stderr = $null - $log_path = [IO.Path]::GetTempFileName() - $arguments = $arguments + @("/log", $log_path) - - try { - $stdout = &SecEdit.exe $arguments | Out-String - } catch { - $stderr = $_.Exception.Message - } - $log = Get-Content -Path $log_path - Remove-Item -Path $log_path -Force - - $return = @{ - log = ($log -join "`n").Trim() - stdout = $stdout - stderr = $stderr - rc = $LASTEXITCODE - } - - return $return -} - -Function Export-SecEdit() { - $secedit_ini_path = [IO.Path]::GetTempFileName() - # while this will technically make a change to the system in check mode by - # creating a new file, we need these values to be able to do anything - # substantial in check mode - $export_result = Run-SecEdit -arguments @("/export", "/cfg", $secedit_ini_path, "/quiet") - - # check the return code and if the file has been populated, otherwise error out - if (($export_result.rc -ne 0) -or ((Get-Item -Path $secedit_ini_path).Length -eq 0)) { - Remove-Item -Path $secedit_ini_path -Force - $result.rc = $export_result.rc - $result.stdout = $export_result.stdout - $result.stderr = $export_result.stderr - Fail-Json $result "Failed to export secedit.ini file to $($secedit_ini_path)" - } - $secedit_ini = ConvertFrom-Ini -file_path $secedit_ini_path - - return $secedit_ini -} - -Function Import-SecEdit($ini) { - $secedit_ini_path = [IO.Path]::GetTempFileName() - $secedit_db_path = [IO.Path]::GetTempFileName() - Remove-Item -Path $secedit_db_path -Force # needs to be deleted for SecEdit.exe /import to work - - $ini_contents = ConvertTo-Ini -ini $ini - Set-Content -Path $secedit_ini_path -Value $ini_contents - $result.changed = $true - - $import_result = Run-SecEdit -arguments @("/configure", "/db", $secedit_db_path, "/cfg", $secedit_ini_path, "/quiet") - $result.import_log = $import_result.log - Remove-Item -Path $secedit_ini_path -Force - if ($import_result.rc -ne 0) { - $result.rc = $import_result.rc - $result.stdout = $import_result.stdout - $result.stderr = $import_result.stderr - Fail-Json $result "Failed to import secedit.ini file from $($secedit_ini_path)" - } -} - -Function ConvertTo-Ini($ini) { - $content = @() - foreach ($key in $ini.GetEnumerator()) { - $section = $key.Name - $values = $key.Value - - $content += "[$section]" - foreach ($value in $values.GetEnumerator()) { - $value_key = $value.Name - $value_value = $value.Value - - if ($null -ne $value_value) { - $content += "$value_key = $value_value" - } - } - } - - return $content -join "`r`n" -} - -Function ConvertFrom-Ini($file_path) { - $ini = @{} - switch -Regex -File $file_path { - "^\[(.+)\]" { - $section = $matches[1] - $ini.$section = @{} - } - "(.+?)\s*=(.*)" { - $name = $matches[1].Trim() - $value = $matches[2].Trim() - if ($value -match "^\d+$") { - $value = [int]$value - } elseif ($value.StartsWith('"') -and $value.EndsWith('"')) { - $value = $value.Substring(1, $value.Length - 2) - } - - $ini.$section.$name = $value - } - } - - return $ini -} - -if ($section -eq "Privilege Rights") { - Add-Warning -obj $result -message "Using this module to edit rights and privileges is error-prone, use the win_user_right module instead" -} - -$will_change = $false -$secedit_ini = Export-SecEdit -if (-not ($secedit_ini.ContainsKey($section))) { - Fail-Json $result "The section '$section' does not exist in SecEdit.exe output ini" -} - -if ($secedit_ini.$section.ContainsKey($key)) { - $current_value = $secedit_ini.$section.$key - - if ($current_value -cne $value) { - if ($diff_mode) { - $result.diff.prepared = @" -[$section] --$key = $current_value -+$key = $value -"@ - } - - $secedit_ini.$section.$key = $value - $will_change = $true - } -} elseif ([string]$value -eq "") { - # Value is requested to be removed, and has already been removed, do nothing -} else { - if ($diff_mode) { - $result.diff.prepared = @" -[$section] -+$key = $value -"@ - } - $secedit_ini.$section.$key = $value - $will_change = $true -} - -if ($will_change -eq $true) { - $result.changed = $true - if (-not $check_mode) { - Import-SecEdit -ini $secedit_ini - - # secedit doesn't error out on improper entries, re-export and verify - # the changes occurred - $verification_ini = Export-SecEdit - $new_section_values = $verification_ini.$section - if ($new_section_values.ContainsKey($key)) { - $new_value = $new_section_values.$key - if ($new_value -cne $value) { - Fail-Json $result "Failed to change the value for key '$key' in section '$section', the value is still $new_value" - } - } elseif ([string]$value -eq "") { - # Value was empty, so OK if no longer in the result - } else { - Fail-Json $result "The key '$key' in section '$section' is not a valid key, cannot set this value" - } - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_security_policy.py b/lib/ansible/modules/windows/win_security_policy.py deleted file mode 100644 index d582a53231..0000000000 --- a/lib/ansible/modules/windows/win_security_policy.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub, actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_security_policy -version_added: '2.4' -short_description: Change local security policy settings -description: -- Allows you to set the local security policies that are configured by - SecEdit.exe. -options: - section: - description: - - The ini section the key exists in. - - If the section does not exist then the module will return an error. - - Example sections to use are 'Account Policies', 'Local Policies', - 'Event Log', 'Restricted Groups', 'System Services', 'Registry' and - 'File System' - - If wanting to edit the C(Privilege Rights) section, use the - M(win_user_right) module instead. - type: str - required: yes - key: - description: - - The ini key of the section or policy name to modify. - - The module will return an error if this key is invalid. - type: str - required: yes - value: - description: - - The value for the ini key or policy name. - - If the key takes in a boolean value then 0 = False and 1 = True. - type: str - required: yes -notes: -- This module uses the SecEdit.exe tool to configure the values, more details - of the areas and keys that can be configured can be found here - U(https://msdn.microsoft.com/en-us/library/bb742512.aspx). -- If you are in a domain environment these policies may be set by a GPO policy, - this module can temporarily change these values but the GPO will override - it if the value differs. -- You can also run C(SecEdit.exe /export /cfg C:\temp\output.ini) to view the - current policies set on your system. -- When assigning user rights, use the M(win_user_right) module instead. -seealso: -- module: win_user_right -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Change the guest account name - win_security_policy: - section: System Access - key: NewGuestName - value: Guest Account - -- name: Set the maximum password age - win_security_policy: - section: System Access - key: MaximumPasswordAge - value: 15 - -- name: Do not store passwords using reversible encryption - win_security_policy: - section: System Access - key: ClearTextPassword - value: 0 - -- name: Enable system events - win_security_policy: - section: Event Audit - key: AuditSystemEvents - value: 1 -''' - -RETURN = r''' -rc: - description: The return code after a failure when running SecEdit.exe. - returned: failure with secedit calls - type: int - sample: -1 -stdout: - description: The output of the STDOUT buffer after a failure when running - SecEdit.exe. - returned: failure with secedit calls - type: str - sample: check log for error details -stderr: - description: The output of the STDERR buffer after a failure when running - SecEdit.exe. - returned: failure with secedit calls - type: str - sample: failed to import security policy -import_log: - description: The log of the SecEdit.exe /configure job that configured the - local policies. This is used for debugging purposes on failures. - returned: secedit.exe /import run and change occurred - type: str - sample: Completed 6 percent (0/15) \tProcess Privilege Rights area. -key: - description: The key in the section passed to the module to modify. - returned: success - type: str - sample: NewGuestName -section: - description: The section passed to the module to modify. - returned: success - type: str - sample: System Access -value: - description: The value passed to the module to modify to. - returned: success - type: str - sample: Guest Account -''' diff --git a/lib/ansible/modules/windows/win_shortcut.ps1 b/lib/ansible/modules/windows/win_shortcut.ps1 deleted file mode 100644 index 291f13cc80..0000000000 --- a/lib/ansible/modules/windows/win_shortcut.ps1 +++ /dev/null @@ -1,374 +0,0 @@ -#!powershell - -# Copyright: (c) 2016, Dag Wieers (@dagwieers) <dag@wieers.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# Based on: http://powershellblogger.com/2016/01/create-shortcuts-lnk-or-url-files-with-powershell/ - -#AnsibleRequires -CSharpUtil Ansible.Basic -#Requires -Module Ansible.ModuleUtils.AddType - -$spec = @{ - options = @{ - src = @{ type='str' } - dest = @{ type='path'; required=$true } - state = @{ type='str'; default='present'; choices=@( 'absent', 'present' ) } - arguments = @{ type='str'; aliases=@( 'args' ) } - directory = @{ type='path' } - hotkey = @{ type='str' } - icon = @{ type='path' } - description = @{ type='str' } - windowstyle = @{ type='str'; choices=@( 'maximized', 'minimized', 'normal' ) } - run_as_admin = @{ type='bool'; default=$false } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$src = $module.Params.src -$dest = $module.Params.dest -$state = $module.Params.state -$arguments = $module.Params.arguments # NOTE: Variable $args is a special variable -$directory = $module.Params.directory -$hotkey = $module.Params.hotkey -$icon = $module.Params.icon -$description = $module.Params.description -$windowstyle = $module.Params.windowstyle -$run_as_admin = $module.Params.run_as_admin - -# Expand environment variables on non-path types -if ($null -ne $src) { - $src = [System.Environment]::ExpandEnvironmentVariables($src) -} -if ($null -ne $arguments) { - $arguments = [System.Environment]::ExpandEnvironmentVariables($arguments) -} -if ($null -ne $description) { - $description = [System.Environment]::ExpandEnvironmentVariables($description) -} - -$module.Result.changed = $false -$module.Result.dest = $dest -$module.Result.state = $state - -# TODO: look at consolidating other COM actions into the C# class for future compatibility -Add-CSharpType -AnsibleModule $module -References @' -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using System.Text; - -namespace Ansible.Shortcut -{ - [ComImport()] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("000214F9-0000-0000-C000-000000000046")] - internal interface IShellLinkW - { - // We only care about GetPath and GetIDList, omit the other methods for now - void GetPath(StringBuilder pszFile, int cch, IntPtr pfd, UInt32 fFlags); - void GetIDList(out IntPtr ppidl); - } - - [ComImport()] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("45E2b4AE-B1C3-11D0-B92F-00A0C90312E1")] - internal interface IShellLinkDataList - { - void AddDataBlock(IntPtr pDataBlock); - void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock); - void RemoveDataBlock(uint dwSig); - void GetFlags(out ShellLinkFlags dwFlags); - void SetFlags(ShellLinkFlags dwFlags); - } - - internal class NativeHelpers - { - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct SHFILEINFO - { - public IntPtr hIcon; - public int iIcon; - public UInt32 dwAttributes; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] public char[] szDisplayName; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] public char[] szTypeName; - } - } - - internal class NativeMethods - { - [DllImport("shell32.dll")] - public static extern void ILFree( - IntPtr pidl); - - [DllImport("shell32.dll")] - public static extern IntPtr SHGetFileInfoW( - IntPtr pszPath, - UInt32 dwFileAttributes, - ref NativeHelpers.SHFILEINFO psfi, - int sbFileInfo, - UInt32 uFlags); - - [DllImport("shell32.dll")] - public static extern int SHParseDisplayName( - [MarshalAs(UnmanagedType.LPWStr)] string pszName, - IntPtr pbc, - out IntPtr ppidl, - UInt32 sfagoIn, - out UInt32 psfgaoOut); - } - - [System.Flags] - public enum ShellLinkFlags : uint - { - Default = 0x00000000, - HasIdList = 0x00000001, - HasLinkInfo = 0x00000002, - HasName = 0x00000004, - HasRelPath = 0x00000008, - HasWorkingDir = 0x00000010, - HasArgs = 0x00000020, - HasIconLocation = 0x00000040, - Unicode = 0x00000080, - ForceNoLinkInfo = 0x00000100, - HasExpSz = 0x00000200, - RunInSeparate = 0x00000400, - HasLogo3Id = 0x00000800, - HasDarwinId = 0x00001000, - RunAsUser = 0x00002000, - HasExpIconSz = 0x00004000, - NoPidlAlias = 0x00008000, - ForceUncName = 0x00010000, - RunWithShimLayer = 0x00020000, - ForceNoLinkTrack = 0x00040000, - EnableTargetMetadata = 0x00080000, - DisableLinkPathTracking = 0x00100000, - DisableKnownFolderRelativeTracking = 0x00200000, - NoKfAlias = 0x00400000, - AllowLinkToLink = 0x00800000, - UnAliasOnSave = 0x01000000, - PreferEnvironmentPath = 0x02000000, - KeepLocalIdListForUncTarget = 0x04000000, - PersistVolumeIdToRelative = 0x08000000, - Valid = 0x0FFFF7FF, - Reserved = 0x80000000 - } - - public class ShellLink - { - private static Guid CLSID_ShellLink = new Guid("00021401-0000-0000-C000-000000000046"); - - public static ShellLinkFlags GetFlags(string path) - { - IShellLinkW link = InitialiseObj(path); - ShellLinkFlags dwFlags; - ((IShellLinkDataList)link).GetFlags(out dwFlags); - return dwFlags; - } - - public static void SetFlags(string path, ShellLinkFlags flags) - { - IShellLinkW link = InitialiseObj(path); - ((IShellLinkDataList)link).SetFlags(flags); - ((IPersistFile)link).Save(null, false); - } - - public static string GetTargetPath(string path) - { - IShellLinkW link = InitialiseObj(path); - - StringBuilder pathSb = new StringBuilder(260); - link.GetPath(pathSb, pathSb.Capacity, IntPtr.Zero, 0); - string linkPath = pathSb.ToString(); - - // If the path wasn't set, try and get the path from the ItemIDList - ShellLinkFlags flags = GetFlags(path); - if (String.IsNullOrEmpty(linkPath) && ((uint)flags & (uint)ShellLinkFlags.HasIdList) == (uint)ShellLinkFlags.HasIdList) - { - IntPtr idList = IntPtr.Zero; - try - { - link.GetIDList(out idList); - linkPath = GetDisplayNameFromPidl(idList); - } - finally - { - NativeMethods.ILFree(idList); - } - } - return linkPath; - } - - public static string GetDisplayNameFromPath(string path) - { - UInt32 sfgaoOut; - IntPtr pidl = IntPtr.Zero; - try - { - int res = NativeMethods.SHParseDisplayName(path, IntPtr.Zero, out pidl, 0, out sfgaoOut); - Marshal.ThrowExceptionForHR(res); - return GetDisplayNameFromPidl(pidl); - } - finally - { - NativeMethods.ILFree(pidl); - } - } - - private static string GetDisplayNameFromPidl(IntPtr pidl) - { - NativeHelpers.SHFILEINFO shFileInfo = new NativeHelpers.SHFILEINFO(); - UInt32 uFlags = 0x000000208; // SHGFI_DISPLAYNAME | SHGFI_PIDL - NativeMethods.SHGetFileInfoW(pidl, 0, ref shFileInfo, Marshal.SizeOf(typeof(NativeHelpers.SHFILEINFO)), uFlags); - return new string(shFileInfo.szDisplayName).TrimEnd('\0'); - } - - private static IShellLinkW InitialiseObj(string path) - { - IShellLinkW link = Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_ShellLink)) as IShellLinkW; - ((IPersistFile)link).Load(path, 0); - return link; - } - } -} -'@ - -# Convert from window style name to window style id -$windowstyles = @{ - normal = 1 - maximized = 3 - minimized = 7 -} - -# Convert from window style id to window style name -$windowstyleids = @( "", "normal", "", "maximized", "", "", "", "minimized" ) - -If ($state -eq "absent") { - If (Test-Path -Path $dest) { - # If the shortcut exists, try to remove it - Try { - Remove-Item -Path $dest -WhatIf:$module.CheckMode - } Catch { - # Report removal failure - $module.FailJson("Failed to remove shortcut '$dest'. ($($_.Exception.Message))", $_) - } - # Report removal success - $module.Result.changed = $true - } Else { - # Nothing to report, everything is fine already - } -} ElseIf ($state -eq "present") { - # Create an in-memory object based on the existing shortcut (if any) - $Shell = New-Object -ComObject ("WScript.Shell") - $ShortCut = $Shell.CreateShortcut($dest) - - # Compare existing values with new values, report as changed if required - - If ($null -ne $src) { - # Windows translates executables to absolute path, so do we - If (Get-Command -Name $src -Type Application -ErrorAction SilentlyContinue) { - $src = (Get-Command -Name $src -Type Application).Definition - } - If (-not (Test-Path -Path $src -IsValid)) { - If (-not (Split-Path -Path $src -IsAbsolute)) { - $module.FailJson("Source '$src' is not found in PATH and not a valid or absolute path.") - } - } - } - - # Determine if we have a WshShortcut or WshUrlShortcut by checking the Arguments property - # A WshUrlShortcut objects only consists of a TargetPath property - - $file_shortcut = $false - If (Get-Member -InputObject $ShortCut -Name Arguments) { - # File ShortCut, compare multiple properties - $file_shortcut = $true - - $target_path = $ShortCut.TargetPath - If (($null -ne $src) -and ($ShortCut.TargetPath -ne $src)) { - if ((Test-Path -Path $dest) -and (-not $ShortCut.TargetPath)) { - # If the shortcut already exists but not on the COM object, we - # are dealing with a shell path like 'shell:RecycleBinFolder'. - $expanded_src = [Ansible.Shortcut.ShellLink]::GetDisplayNameFromPath($src) - $actual_src = [Ansible.Shortcut.ShellLink]::GetTargetPath($dest) - if ($expanded_src -ne $actual_src) { - $module.Result.changed = $true - $ShortCut.TargetPath = $src - } - } else { - $module.Result.changed = $true - $ShortCut.TargetPath = $src - } - $target_path = $src - } - - # This is a full-featured application shortcut ! - If (($null -ne $arguments) -and ($ShortCut.Arguments -ne $arguments)) { - $module.Result.changed = $true - $ShortCut.Arguments = $arguments - } - $module.Result.args = $ShortCut.Arguments - - If (($null -ne $directory) -and ($ShortCut.WorkingDirectory -ne $directory)) { - $module.Result.changed = $true - $ShortCut.WorkingDirectory = $directory - } - $module.Result.directory = $ShortCut.WorkingDirectory - - # FIXME: Not all values are accepted here ! Improve docs too. - If (($null -ne $hotkey) -and ($ShortCut.Hotkey -ne $hotkey)) { - $module.Result.changed = $true - $ShortCut.Hotkey = $hotkey - } - $module.Result.hotkey = $ShortCut.Hotkey - - If (($null -ne $icon) -and ($ShortCut.IconLocation -ne $icon)) { - $module.Result.changed = $true - $ShortCut.IconLocation = $icon - } - $module.Result.icon = $ShortCut.IconLocation - - If (($null -ne $description) -and ($ShortCut.Description -ne $description)) { - $module.Result.changed = $true - $ShortCut.Description = $description - } - $module.Result.description = $ShortCut.Description - - If (($null -ne $windowstyle) -and ($ShortCut.WindowStyle -ne $windowstyles.$windowstyle)) { - $module.Result.changed = $true - $ShortCut.WindowStyle = $windowstyles.$windowstyle - } - $module.Result.windowstyle = $windowstyleids[$ShortCut.WindowStyle] - } else { - # URL Shortcut, just compare the TargetPath - if (($null -ne $src) -and ($ShortCut.TargetPath -ne $src)) { - $module.Result.changed = $true - $ShortCut.TargetPath = $src - } - $target_path = $ShortCut.TargetPath - } - $module.Result.src = $target_path - - If (($module.Result.changed -eq $true) -and ($module.CheckMode -ne $true)) { - Try { - $ShortCut.Save() - } Catch { - $module.FailJson("Failed to create shortcut '$dest'. ($($_.Exception.Message))", $_) - } - } - - if ((Test-Path -Path $dest) -and $file_shortcut) { - # Only control the run_as_admin flag if using a File Shortcut - $flags = [Ansible.Shortcut.ShellLink]::GetFlags($dest) - if ($run_as_admin -and (-not $flags.HasFlag([Ansible.Shortcut.ShellLinkFlags]::RunAsUser))) { - [Ansible.Shortcut.ShellLink]::SetFlags($dest, ($flags -bor [Ansible.Shortcut.ShellLinkFlags]::RunAsUser)) - $module.Result.changed = $true - } elseif (-not $run_as_admin -and ($flags.HasFlag([Ansible.Shortcut.ShellLinkFlags]::RunAsUser))) { - [Ansible.Shortcut.ShellLink]::SetFlags($dest, ($flags -bxor [Ansible.Shortcut.ShellLinkFlags]::RunAsUser)) - $module.Result.changed = $true - } - } -} - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_shortcut.py b/lib/ansible/modules/windows/win_shortcut.py deleted file mode 100644 index 2fe2ff4292..0000000000 --- a/lib/ansible/modules/windows/win_shortcut.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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_shortcut -version_added: '2.3' -short_description: Manage shortcuts on Windows -description: -- Create, manage and delete Windows shortcuts -options: - src: - description: - - Executable or URL the shortcut points to. - - The executable needs to be in your PATH, or has to be an absolute - path to the executable. - type: str - description: - description: - - Description for the shortcut. - - This is usually shown when hoovering the icon. - type: str - dest: - description: - - Destination file for the shortcuting file. - - File name should have a C(.lnk) or C(.url) extension. - type: path - required: yes - arguments: - description: - - Additional arguments for the executable defined in C(src). - - Was originally just C(args) but renamed in Ansible 2.8. - type: str - aliases: [ args ] - directory: - description: - - Working directory for executable defined in C(src). - type: path - icon: - description: - - Icon used for the shortcut. - - File name should have a C(.ico) extension. - - The file name is followed by a comma and the number in the library file (.dll) or use 0 for an image file. - type: path - hotkey: - description: - - Key combination for the shortcut. - - This is a combination of one or more modifiers and a key. - - Possible modifiers are Alt, Ctrl, Shift, Ext. - - Possible keys are [A-Z] and [0-9]. - type: str - windowstyle: - description: - - Influences how the application is displayed when it is launched. - type: str - choices: [ maximized, minimized, normal ] - state: - description: - - When C(absent), removes the shortcut if it exists. - - When C(present), creates or updates the shortcut. - type: str - choices: [ absent, present ] - default: present - run_as_admin: - description: - - When C(src) is an executable, this can control whether the shortcut will be opened as an administrator or not. - type: bool - default: no - version_added: '2.8' -notes: -- 'The following options can include Windows environment variables: C(dest), C(args), C(description), C(dest), C(directory), C(icon) C(src)' -- 'Windows has two types of shortcuts: Application and URL shortcuts. URL shortcuts only consists of C(dest) and C(src)' -seealso: -- module: win_file -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- name: Create an application shortcut on the desktop - win_shortcut: - src: C:\Program Files\Mozilla Firefox\Firefox.exe - dest: C:\Users\Public\Desktop\Mozilla Firefox.lnk - icon: C:\Program Files\Mozilla Firefox\Firefox.exe,0 - -- name: Create the same shortcut using environment variables - win_shortcut: - description: The Mozilla Firefox web browser - src: '%ProgramFiles%\Mozilla Firefox\Firefox.exe' - dest: '%Public%\Desktop\Mozilla Firefox.lnk' - icon: '%ProgramFiles\Mozilla Firefox\Firefox.exe,0' - directory: '%ProgramFiles%\Mozilla Firefox' - hotkey: Ctrl+Alt+F - -- name: Create an application shortcut for an executable in PATH to your desktop - win_shortcut: - src: cmd.exe - dest: Desktop\Command prompt.lnk - -- name: Create an application shortcut for the Ansible website - win_shortcut: - src: '%ProgramFiles%\Google\Chrome\Application\chrome.exe' - dest: '%UserProfile%\Desktop\Ansible website.lnk' - arguments: --new-window https://ansible.com/ - directory: '%ProgramFiles%\Google\Chrome\Application' - icon: '%ProgramFiles%\Google\Chrome\Application\chrome.exe,0' - hotkey: Ctrl+Alt+A - -- name: Create a URL shortcut for the Ansible website - win_shortcut: - src: https://ansible.com/ - dest: '%Public%\Desktop\Ansible website.url' -''' - -RETURN = r''' -''' diff --git a/lib/ansible/modules/windows/win_snmp.ps1 b/lib/ansible/modules/windows/win_snmp.ps1 deleted file mode 100644 index 48e03253fe..0000000000 --- a/lib/ansible/modules/windows/win_snmp.ps1 +++ /dev/null @@ -1,126 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args -arguments $args -supports_check_mode $true; -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$managers = Get-AnsibleParam -obj $params -name "permitted_managers" -type "list" -default $null -$communities = Get-AnsibleParam -obj $params -name "community_strings" -type "list" -default $null -$action_in = Get-AnsibleParam -obj $params -name "action" -type "str" -default "set" -ValidateSet @("set", "add", "remove") -$action = $action_in.ToLower() - -$result = @{ - failed = $False - changed = $False - community_strings = [System.Collections.ArrayList]@() - permitted_managers = [System.Collections.ArrayList]@() -} - -# Make sure lists are modifyable -[System.Collections.ArrayList]$managers = $managers -[System.Collections.ArrayList]$communities = $communities -[System.Collections.ArrayList]$indexes = @() - -# Type checking -# You would think that "$null -ne $managers" would work, but it doesn't. -# A proper type check is required. If a user provides an empty list then $managers -# is still of the correct type. If a user provides no option then $managers is $null. -If ($managers -Is [System.Collections.ArrayList] -And $managers.Count -gt 0 -And $managers[0] -IsNot [String]) { - Fail-Json $result "Permitted managers must be an array of strings" -} - -If ($communities -Is [System.Collections.ArrayList] -And $communities.Count -gt 0 -And $communities[0] -IsNot [String]) { - Fail-Json $result "SNMP communities must be an array of strings" -} - -$Managers_reg_key = "HKLM:\System\CurrentControlSet\services\SNMP\Parameters\PermittedManagers" -$Communities_reg_key = "HKLM:\System\CurrentControlSet\services\SNMP\Parameters\ValidCommunities" - -ForEach ($idx in (Get-Item $Managers_reg_key).Property) { - $manager = (Get-ItemProperty $Managers_reg_key).$idx - If ($idx.ToLower() -eq '(default)') { - continue - } - - $remove = $False - If ($managers -Is [System.Collections.ArrayList] -And $managers.Contains($manager)) { - If ($action -eq "remove") { - $remove = $True - } Else { - # Remove manager from list to add since it already exists - $managers.Remove($manager) - } - } ElseIf ($action -eq "set" -And $managers -Is [System.Collections.ArrayList]) { - # Will remove this manager since it is not in the set list - $remove = $True - } - - If ($remove) { - $result.changed = $True - Remove-ItemProperty -Path $Managers_reg_key -Name $idx -WhatIf:$check_mode - } Else { - # Remember that this index is in use - $indexes.Add([int]$idx) | Out-Null - $result.permitted_managers.Add($manager) | Out-Null - } -} - -ForEach ($community in (Get-Item $Communities_reg_key).Property) { - If ($community.ToLower() -eq '(default)') { - continue - } - - $remove = $False - If ($communities -Is [System.Collections.ArrayList] -And $communities.Contains($community)) { - If ($action -eq "remove") { - $remove = $True - } Else { - # Remove community from list to add since it already exists - $communities.Remove($community) - } - } ElseIf ($action -eq "set" -And $communities -Is [System.Collections.ArrayList]) { - # Will remove this community since it is not in the set list - $remove = $True - } - - If ($remove) { - $result.changed = $True - Remove-ItemProperty -Path $Communities_reg_key -Name $community -WhatIf:$check_mode - } Else { - $result.community_strings.Add($community) | Out-Null - } -} - -If ($action -eq "remove") { - Exit-Json $result -} - -# Add managers that don't already exist -$next_index = 0 -If ($managers -Is [System.Collections.ArrayList]) { - ForEach ($manager in $managers) { - While ($True) { - $next_index = $next_index + 1 - If (-Not $indexes.Contains($next_index)) { - $result.changed = $True - New-ItemProperty -Path $Managers_reg_key -Name $next_index -Value "$manager" -WhatIf:$check_mode | Out-Null - $result.permitted_managers.Add($manager) | Out-Null - break - } - } - } -} - -# Add communities that don't already exist -If ($communities -Is [System.Collections.ArrayList]) { - ForEach ($community in $communities) { - $result.changed = $True - New-ItemProperty -Path $Communities_reg_key -Name $community -PropertyType DWord -Value 4 -WhatIf:$check_mode | Out-Null - $result.community_strings.Add($community) | Out-Null - } -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_snmp.py b/lib/ansible/modules/windows/win_snmp.py deleted file mode 100644 index df9a577082..0000000000 --- a/lib/ansible/modules/windows/win_snmp.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Ansible, inc -# 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_snmp -version_added: '2.8' -short_description: Configures the Windows SNMP service -description: - - This module configures the Windows SNMP service. -options: - permitted_managers: - description: - - The list of permitted SNMP managers. - type: list - community_strings: - description: - - The list of read-only SNMP community strings. - type: list - action: - description: - - C(add) will add new SNMP community strings and/or SNMP managers - - C(set) will replace SNMP community strings and/or SNMP managers. An - empty list for either C(community_strings) or C(permitted_managers) - will result in the respective lists being removed entirely. - - C(remove) will remove SNMP community strings and/or SNMP managers - type: str - choices: [ add, set, remove ] - default: set -author: - - Michael Cassaniti (@mcassaniti) -''' - -EXAMPLES = r''' ---- - - hosts: Windows - tasks: - - name: Replace SNMP communities and managers - win_snmp: - community_strings: - - public - permitted_managers: - - 192.168.1.2 - action: set - - - hosts: Windows - tasks: - - name: Replace SNMP communities and clear managers - win_snmp: - community_strings: - - public - permitted_managers: [] - action: set -''' - -RETURN = r''' -community_strings: - description: The list of community strings for this machine. - type: list - returned: always - sample: - - public - - snmp-ro -permitted_managers: - description: The list of permitted managers for this machine. - type: list - returned: always - sample: - - 192.168.1.1 - - 192.168.1.2 -''' diff --git a/lib/ansible/modules/windows/win_timezone.ps1 b/lib/ansible/modules/windows/win_timezone.ps1 deleted file mode 100644 index f46a11c3ad..0000000000 --- a/lib/ansible/modules/windows/win_timezone.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Phil Schwartz <schwartzmx@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false - -$timezone = Get-AnsibleParam -obj $params -name "timezone" -type "str" -failifempty $true - -$result = @{ - changed = $false - previous_timezone = $timezone - timezone = $timezone -} - -Try { - # Get the current timezone set - $result.previous_timezone = $(tzutil.exe /g) - If ($LASTEXITCODE -ne 0) { - Throw "An error occurred when getting the current machine's timezone setting." - } - - if ( $result.previous_timezone -eq $timezone ) { - Exit-Json $result "Timezone '$timezone' is already set on this machine" - } Else { - # Check that timezone is listed as an available timezone to the machine - $tzList = $(tzutil.exe /l).ToLower() - If ($LASTEXITCODE -ne 0) { - Throw "An error occurred when listing the available timezones." - } - - $tzExists = $tzList.Contains(($timezone -Replace '_dstoff').ToLower()) - if (-not $tzExists) { - Fail-Json $result "The specified timezone: $timezone isn't supported on the machine." - } - - if ($check_mode) { - $result.changed = $true - } else { - tzutil.exe /s "$timezone" - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred when setting the specified timezone with tzutil." - } - - $new_timezone = $(tzutil.exe /g) - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred when getting the current machine's timezone setting." - } - - if ($timezone -eq $new_timezone) { - $result.changed = $true - } - } - - if ($diff_support) { - $result.diff = @{ - before = "$($result.previous_timezone)`n" - after = "$timezone`n" - } - } - } -} Catch { - Fail-Json $result "Error setting timezone to: $timezone." -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_timezone.py b/lib/ansible/modules/windows/win_timezone.py deleted file mode 100644 index a3b66c9e56..0000000000 --- a/lib/ansible/modules/windows/win_timezone.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Phil Schwartz <schwartzmx@gmail.com> -# 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_timezone -version_added: '2.1' -short_description: Sets Windows machine timezone -description: -- Sets machine time to the specified timezone. -options: - timezone: - description: - - Timezone to set to. - - 'Example: Central Standard Time' - - To disable Daylight Saving time, add the suffix C(_dstoff) on timezones that support this. - type: str - required: yes -notes: -- The module will check if the provided timezone is supported on the machine. -- A list of possible timezones is available from C(tzutil.exe /l) and from - U(https://msdn.microsoft.com/en-us/library/ms912391.aspx) -- If running on Server 2008 the hotfix - U(https://support.microsoft.com/en-us/help/2556308/tzutil-command-line-tool-is-added-to-windows-vista-and-to-windows-server-2008) - needs to be installed to be able to run this module. -seealso: -- module: win_region -author: -- Phil Schwartz (@schwartzmx) -''' - -EXAMPLES = r''' -- name: Set timezone to 'Romance Standard Time' (GMT+01:00) - win_timezone: - timezone: Romance Standard Time - -- name: Set timezone to 'GMT Standard Time' (GMT) - win_timezone: - timezone: GMT Standard Time - -- name: Set timezone to 'Central Standard Time' (GMT-06:00) - win_timezone: - timezone: Central Standard Time - -- name: Set timezime to Pacific Standard time and disable Daylight Saving time adjustments - win_timezone: - timezone: Pacific Standard Time_dstoff -''' - -RETURN = r''' -previous_timezone: - description: The previous timezone if it was changed, otherwise the existing timezone. - returned: success - type: str - sample: Central Standard Time -timezone: - description: The current timezone (possibly changed). - returned: success - type: str - sample: Central Standard Time -''' diff --git a/lib/ansible/modules/windows/win_toast.ps1 b/lib/ansible/modules/windows/win_toast.ps1 deleted file mode 100644 index e993651521..0000000000 --- a/lib/ansible/modules/windows/win_toast.ps1 +++ /dev/null @@ -1,90 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -# version check -$osversion = [Environment]::OSVersion -$lowest_version = 10 -if ($osversion.Version.Major -lt $lowest_version ) { - Fail-Json -obj $result -message "Sorry, this version of windows, $osversion, does not support Toast notifications. Toast notifications are available from version $lowest_version" -} - -$stopwatch = [system.diagnostics.stopwatch]::startNew() -$now = [DateTime]::Now -$default_title = "Notification: " + $now.ToShortTimeString() - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$expire_seconds = Get-AnsibleParam -obj $params -name "expire" -type "int" -default 45 -$group = Get-AnsibleParam -obj $params -name "group" -type "str" -default "Powershell" -$msg = Get-AnsibleParam -obj $params -name "msg" -type "str" -default "Hello world!" -$popup = Get-AnsibleParam -obj $params -name "popup" -type "bool" -default $true -$tag = Get-AnsibleParam -obj $params -name "tag" -type "str" -default "Ansible" -$title = Get-AnsibleParam -obj $params -name "title" -type "str" -default $default_title - -$timespan = New-TimeSpan -Seconds $expire_seconds -$expire_at = $now + $timespan -$expire_at_utc = $($expire_at.ToUniversalTime()|Out-String).Trim() - -$result = @{ - changed = $false - expire_at = $expire_at.ToString() - expire_at_utc = $expire_at_utc - toast_sent = $false -} - -# If no logged in users, there is no notifications service, -# and no-one to read the message, so exit but do not fail -# if there are no logged in users to notify. - -if ((Get-Process -Name explorer -ErrorAction SilentlyContinue).Count -gt 0){ - - [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null - $template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText01) - - #Convert to .NET type for XML manipulation - $toastXml = [xml] $template.GetXml() - $toastXml.GetElementsByTagName("text").AppendChild($toastXml.CreateTextNode($title)) > $null - # TODO add subtitle - - #Convert back to WinRT type - $xml = New-Object Windows.Data.Xml.Dom.XmlDocument - $xml.LoadXml($toastXml.OuterXml) - - $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) - $toast.Tag = $tag - $toast.Group = $group - $toast.ExpirationTime = $expire_at - $toast.SuppressPopup = -not $popup - - try { - $notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($msg) - if (-not $check_mode) { - $notifier.Show($toast) - $result.toast_sent = $true - Start-Sleep -Seconds $expire_seconds - } - } catch { - $excep = $_ - $result.exception = $excep.ScriptStackTrace - Fail-Json -obj $result -message "Failed to create toast notifier: $($excep.Exception.Message)" - } -} else { - $result.toast_sent = $false - $result.no_toast_sent_reason = 'No logged in users to notify' -} - -$endsend_at = Get-Date | Out-String -$stopwatch.Stop() - -$result.time_taken = $stopwatch.Elapsed.TotalSeconds -$result.sent_localtime = $endsend_at.Trim() - -Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_toast.py b/lib/ansible/modules/windows/win_toast.py deleted file mode 100644 index 5bb28c8ebb..0000000000 --- a/lib/ansible/modules/windows/win_toast.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Jon Hawkesworth (@jhawkesworth) <figs@unity.demon.co.uk> -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_toast -version_added: "2.4" -short_description: Sends Toast windows notification to logged in users on Windows 10 or later hosts -description: - - Sends alerts which appear in the Action Center area of the windows desktop. -options: - expire: - description: - - How long in seconds before the notification expires. - type: int - default: 45 - group: - description: - - Which notification group to add the notification to. - type: str - default: Powershell - msg: - description: - - The message to appear inside the notification. - - May include \n to format the message to appear within the Action Center. - type: str - default: Hello, World! - popup: - description: - - If C(no), the notification will not pop up and will only appear in the Action Center. - type: bool - default: yes - tag: - description: - - The tag to add to the notification. - type: str - default: Ansible - title: - description: - - The notification title, which appears in the pop up.. - type: str - default: Notification HH:mm -notes: - - This module must run on a windows 10 or Server 2016 host, so ensure your play targets windows hosts, or delegates to a windows host. - - The module does not fail if there are no logged in users to notify. - - Messages are only sent to the local host where the module is run. - - You must run this module with async, otherwise it will hang until the expire period has passed. -seealso: -- module: win_msg -- module: win_say -author: -- Jon Hawkesworth (@jhawkesworth) -''' - -EXAMPLES = r''' -- name: Warn logged in users of impending upgrade (note use of async to stop the module from waiting until notification expires). - win_toast: - expire: 60 - title: System Upgrade Notification - msg: Automated upgrade about to start. Please save your work and log off before {{ deployment_start_time }} - async: 60 - poll: 0 -''' - -RETURN = r''' -expire_at_utc: - description: Calculated utc date time when the notification expires. - returned: always - type: str - sample: 07 July 2017 04:50:54 -no_toast_sent_reason: - description: Text containing the reason why a notification was not sent. - returned: when no logged in users are detected - type: str - sample: No logged in users to notify -sent_localtime: - description: local date time when the notification was sent. - returned: always - type: str - sample: 07 July 2017 05:45:54 -time_taken: - description: How long the module took to run on the remote windows host in seconds. - returned: always - type: float - sample: 0.3706631999999997 -toast_sent: - description: Whether the module was able to send a toast notification or not. - returned: always - type: bool - sample: false -''' diff --git a/lib/ansible/modules/windows/win_unzip.ps1 b/lib/ansible/modules/windows/win_unzip.ps1 deleted file mode 100644 index b49e808845..0000000000 --- a/lib/ansible/modules/windows/win_unzip.ps1 +++ /dev/null @@ -1,178 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Phil Schwartz <schwartzmx@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -# TODO: This module is not idempotent (it will always unzip and report change) - -$ErrorActionPreference = "Stop" - -$pcx_extensions = @('.bz2', '.gz', '.msu', '.tar', '.zip') - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$src = Get-AnsibleParam -obj $params -name "src" -type "path" -failifempty $true -$dest = Get-AnsibleParam -obj $params -name "dest" -type "path" -failifempty $true -$creates = Get-AnsibleParam -obj $params -name "creates" -type "path" -$recurse = Get-AnsibleParam -obj $params -name "recurse" -type "bool" -default $false -$delete_archive = Get-AnsibleParam -obj $params -name "delete_archive" -type "bool" -default $false -aliases 'rm' - -# Fixes a fail error message (when the task actually succeeds) for a -# "Convert-ToJson: The converted JSON string is in bad format" -# This happens when JSON is parsing a string that ends with a "\", -# which is possible when specifying a directory to download to. -# This catches that possible error, before assigning the JSON $result -$result = @{ - changed = $false - dest = $dest -replace '\$','' - removed = $false - src = $src -replace '\$','' -} - -Function Extract-Zip($src, $dest) { - $archive = [System.IO.Compression.ZipFile]::Open($src, [System.IO.Compression.ZipArchiveMode]::Read, [System.Text.Encoding]::UTF8) - foreach ($entry in $archive.Entries) { - $archive_name = $entry.FullName - - $entry_target_path = [System.IO.Path]::Combine($dest, $archive_name) - $entry_dir = [System.IO.Path]::GetDirectoryName($entry_target_path) - - # Normalize paths for further evaluation - $full_target_path = [System.IO.Path]::GetFullPath($entry_target_path) - $full_dest_path = [System.IO.Path]::GetFullPath($dest + [System.IO.Path]::DirectorySeparatorChar) - - # Ensure file in the archive does not escape the extraction path - if (-not $full_target_path.StartsWith($full_dest_path)) { - Fail-Json -obj $result -message "Error unzipping '$src' to '$dest'! Filename contains relative paths which would extract outside the destination: $entry_target_path" - } - - if (-not (Test-Path -LiteralPath $entry_dir)) { - New-Item -Path $entry_dir -ItemType Directory -WhatIf:$check_mode | Out-Null - $result.changed = $true - } - - if ((-not ($entry_target_path.EndsWith("\") -or $entry_target_path.EndsWith("/"))) -and (-not $check_mode)) { - [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $entry_target_path, $true) - } - $result.changed = $true - } - $archive.Dispose() -} - -Function Extract-ZipLegacy($src, $dest) { - # [System.IO.Compression.ZipFile] was only added in .net 4.5, this is used - # when .net is older than that. - $shell = New-Object -ComObject Shell.Application - $zip = $shell.NameSpace([IO.Path]::GetFullPath($src)) - $dest_path = $shell.NameSpace([IO.Path]::GetFullPath($dest)) - - $shell = New-Object -ComObject Shell.Application - - if (-not $check_mode) { - # https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866.aspx - # From Folder.CopyHere documentation, 1044 means: - # - 1024: do not display a user interface if an error occurs - # - 16: respond with "yes to all" for any dialog box that is displayed - # - 4: do not display a progress dialog box - $dest_path.CopyHere($zip.Items(), 1044) - } - $result.changed = $true -} - -If ($creates -and (Test-Path -LiteralPath $creates)) { - $result.skipped = $true - $result.msg = "The file or directory '$creates' already exists." - Exit-Json -obj $result -} - -If (-Not (Test-Path -LiteralPath $src)) { - Fail-Json -obj $result -message "File '$src' does not exist." -} - -$ext = [System.IO.Path]::GetExtension($src) - -If (-Not (Test-Path -LiteralPath $dest -PathType Container)){ - Try{ - New-Item -ItemType "directory" -path $dest -WhatIf:$check_mode | out-null - } Catch { - Fail-Json -obj $result -message "Error creating '$dest' directory! Msg: $($_.Exception.Message)" - } -} - -If ($ext -eq ".zip" -And $recurse -eq $false) { - # TODO: PS v5 supports zip extraction, use that if available - $use_legacy = $false - try { - # determines if .net 4.5 is available, if this fails we need to fall - # back to the legacy COM Shell.Application to extract the zip - Add-Type -AssemblyName System.IO.Compression.FileSystem | Out-Null - Add-Type -AssemblyName System.IO.Compression | Out-Null - } catch { - $use_legacy = $true - } - - if ($use_legacy) { - try { - Extract-ZipLegacy -src $src -dest $dest - } catch { - Fail-Json -obj $result -message "Error unzipping '$src' to '$dest'!. Method: COM Shell.Application, Exception: $($_.Exception.Message)" - } - } else { - try { - Extract-Zip -src $src -dest $dest - } catch { - Fail-Json -obj $result -message "Error unzipping '$src' to '$dest'!. Method: System.IO.Compression.ZipFile, Exception: $($_.Exception.Message)" - } - } -} Else { - # Check if PSCX is installed - $list = Get-Module -ListAvailable - - If (-Not ($list -match "PSCX")) { - Fail-Json -obj $result -message "PowerShellCommunityExtensions PowerShell Module (PSCX) is required for non-'.zip' compressed archive types." - } Else { - $result.pscx_status = "present" - } - - Try { - Import-Module PSCX - } - Catch { - Fail-Json $result "Error importing module PSCX" - } - - Try { - Expand-Archive -Path $src -OutputPath $dest -Force -WhatIf:$check_mode - } Catch { - Fail-Json -obj $result -message "Error expanding '$src' to '$dest'! Msg: $($_.Exception.Message)" - } - - If ($recurse) { - Get-ChildItem -LiteralPath $dest -recurse | Where-Object {$pcx_extensions -contains $_.extension} | ForEach-Object { - Try { - Expand-Archive $_.FullName -OutputPath $dest -Force -WhatIf:$check_mode - } Catch { - Fail-Json -obj $result -message "Error recursively expanding '$src' to '$dest'! Msg: $($_.Exception.Message)" - } - If ($delete_archive) { - Remove-Item -LiteralPath $_.FullName -Force -WhatIf:$check_mode - $result.removed = $true - } - } - } - - $result.changed = $true -} - -If ($delete_archive){ - try { - Remove-Item -LiteralPath $src -Recurse -Force -WhatIf:$check_mode - } catch { - Fail-Json -obj $result -message "failed to delete archive at '$src': $($_.Exception.Message)" - } - $result.removed = $true -} -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_unzip.py b/lib/ansible/modules/windows/win_unzip.py deleted file mode 100644 index 5830c61cd8..0000000000 --- a/lib/ansible/modules/windows/win_unzip.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Phil Schwartz <schwartzmx@gmail.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_unzip -version_added: "2.0" -short_description: Unzips compressed files and archives on the Windows node -description: -- Unzips compressed files and archives. -- Supports .zip files natively. -- Supports other formats supported by the Powershell Community Extensions (PSCX) module (basically everything 7zip supports). -- For non-Windows targets, use the M(unarchive) module instead. -requirements: -- PSCX -options: - src: - description: - - File to be unzipped (provide absolute path). - type: path - required: yes - dest: - description: - - Destination of zip file (provide absolute path of directory). If it does not exist, the directory will be created. - type: path - required: yes - delete_archive: - description: - - Remove the zip file, after unzipping. - type: bool - default: no - aliases: [ rm ] - recurse: - description: - - Recursively expand zipped files within the src file. - - Setting to a value of C(yes) requires the PSCX module to be installed. - type: bool - default: no - creates: - description: - - If this file or directory exists the specified src will not be extracted. - type: path -notes: -- This module is not really idempotent, it will extract the archive every time, and report a change. -- For extracting any compression types other than .zip, the PowerShellCommunityExtensions (PSCX) Module is required. This module (in conjunction with PSCX) - has the ability to recursively unzip files within the src zip file provided and also functionality for many other compression types. If the destination - directory does not exist, it will be created before unzipping the file. Specifying rm parameter will force removal of the src file after extraction. -seealso: -- module: unarchive -author: -- Phil Schwartz (@schwartzmx) -''' - -EXAMPLES = r''' -# This unzips a library that was downloaded with win_get_url, and removes the file after extraction -# $ ansible -i hosts -m win_unzip -a "src=C:\LibraryToUnzip.zip dest=C:\Lib remove=yes" all - -- name: Unzip a bz2 (BZip) file - win_unzip: - src: C:\Users\Phil\Logs.bz2 - dest: C:\Users\Phil\OldLogs - creates: C:\Users\Phil\OldLogs - -- name: Unzip gz log - win_unzip: - src: C:\Logs\application-error-logs.gz - dest: C:\ExtractedLogs\application-error-logs - -# Unzip .zip file, recursively decompresses the contained .gz files and removes all unneeded compressed files after completion. -- name: Unzip ApplicationLogs.zip and decompress all GZipped log files - hosts: all - gather_facts: no - tasks: - - name: Recursively decompress GZ files in ApplicationLogs.zip - win_unzip: - src: C:\Downloads\ApplicationLogs.zip - dest: C:\Application\Logs - recurse: yes - delete_archive: yes - -- name: Install PSCX - win_psmodule: - name: Pscx - state: present -''' - -RETURN = r''' -dest: - description: The provided destination path - returned: always - type: str - sample: C:\ExtractedLogs\application-error-logs -removed: - description: Whether the module did remove any files during task run - returned: always - type: bool - sample: true -src: - description: The provided source path - returned: always - type: str - sample: C:\Logs\application-error-logs.gz -''' diff --git a/lib/ansible/modules/windows/win_user_profile.ps1 b/lib/ansible/modules/windows/win_user_profile.ps1 deleted file mode 100644 index 111abf10ef..0000000000 --- a/lib/ansible/modules/windows/win_user_profile.ps1 +++ /dev/null @@ -1,163 +0,0 @@ -#!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 - -$spec = @{ - options = @{ - name = @{ type = "str" } - remove_multiple = @{ type = "bool"; default = $false } - state = @{ type = "str"; default = "present"; choices = @("absent", "present") } - username = @{ type = "sid"; } - } - required_if = @( - @("state", "present", @("username")), - @("state", "absent", @("name", "username"), $true) - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) -$module.Result.path = $null - -$name = $module.Params.name -$remove_multiple = $module.Params.remove_multiple -$state = $module.Params.state -$username = $module.Params.username - -Add-CSharpType -AnsibleModule $module -References @' -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace Ansible.WinUserProfile -{ - public class NativeMethods - { - [DllImport("Userenv.dll", CharSet = CharSet.Unicode)] - public static extern int CreateProfile( - [MarshalAs(UnmanagedType.LPWStr)] string pszUserSid, - [MarshalAs(UnmanagedType.LPWStr)] string pszUserName, - [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath, - UInt32 cchProfilePath); - - [DllImport("Userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool DeleteProfileW( - [MarshalAs(UnmanagedType.LPWStr)] string lpSidString, - IntPtr lpProfile, - IntPtr lpComputerName); - - [DllImport("Userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool GetProfilesDirectoryW( - [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpProfileDir, - ref UInt32 lpcchSize); - } -} -'@ - -Function Get-LastWin32ExceptionMessage { - param([int]$ErrorCode) - $exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode - $exp_msg = "{0} (Win32 ErrorCode {1} - 0x{1:X8})" -f $exp.Message, $ErrorCode - return $exp_msg -} - -Function Get-ExpectedProfilePath { - param([String]$BaseName) - - # Environment.GetFolderPath does not have an enumeration to get the base profile dir, use PInvoke instead - # and combine with the base name to return back to the user - best efforts - $profile_path_length = 0 - [Ansible.WinUserProfile.NativeMethods]::GetProfilesDirectoryW($null, - [ref]$profile_path_length) > $null - - $raw_profile_path = New-Object -TypeName System.Text.StringBuilder -ArgumentList $profile_path_length - $res = [Ansible.WinUserProfile.NativeMethods]::GetProfilesDirectoryW($raw_profile_path, - [ref]$profile_path_length) - - if ($res -eq $false) { - $msg = Get-LastWin32ExceptionMessage -Error ([System.Runtime.InteropServices.Marshal]::GetLastWin32Error()) - $module.FailJson("Failed to determine profile path with the base name '$BaseName': $msg") - } - $profile_path = Join-Path -Path $raw_profile_path.ToString() -ChildPath $BaseName - - return $profile_path -} - -$profiles = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" - -if ($state -eq "absent") { - if ($null -ne $username) { - $user_profiles = $profiles | Where-Object { $_.PSChildName -eq $username.Value } - } else { - # If the username was not provided, or we are removing a profile for a deleted user, we need to try and find - # the correct SID to delete. We just verify that the path matches based on the name passed in - $expected_profile_path = Get-ExpectedProfilePath -BaseName $name - - $user_profiles = $profiles | Where-Object { - $profile_path = (Get-ItemProperty -Path $_.PSPath -Name ProfileImagePath).ProfileImagePath - $profile_path -eq $expected_profile_path - } - - if ($user_profiles.Length -gt 1 -and -not $remove_multiple) { - $module.FailJson("Found multiple profiles matching the path '$expected_profile_path', set 'remove_multiple=True' to remove all the profiles for this match") - } - } - - foreach ($user_profile in $user_profiles) { - $profile_path = (Get-ItemProperty -Path $user_profile.PSPath -Name ProfileImagePath).ProfileImagePath - if (-not $module.CheckMode) { - $res = [Ansible.WinUserProfile.NativeMethods]::DeleteProfileW($user_profile.PSChildName, [IntPtr]::Zero, - [IntPtr]::Zero) - if ($res -eq $false) { - $msg = Get-LastWin32ExceptionMessage -Error ([System.Runtime.InteropServices.Marshal]::GetLastWin32Error()) - $module.FailJson("Failed to delete the profile for $($user_profile.PSChildName): $msg") - } - } - - # While we may have multiple profiles when the name option was used, it will always be the same path due to - # how we match name to a profile so setting it mutliple time sis fine - $module.Result.path = $profile_path - $module.Result.changed = $true - } -} elseif ($state -eq "present") { - # Now we know the SID, see if the profile already exists - $user_profile = $profiles | Where-Object { $_.PSChildName -eq $username.Value } - if ($null -eq $user_profile) { - # In case a SID was set as the username we still need to make sure the SID is mapped to a valid local account - try { - $account_name = $username.Translate([System.Security.Principal.NTAccount]) - } catch [System.Security.Principal.IdentityNotMappedException] { - $module.FailJson("Fail to map the account '$($username.Value)' to a valid user") - } - - # If the basename was not provided, determine it from the actual username - if ($null -eq $name) { - $name = $account_name.Value.Split('\', 2)[-1] - } - - if ($module.CheckMode) { - $profile_path = Get-ExpectedProfilePath -BaseName $name - } else { - $raw_profile_path = New-Object -TypeName System.Text.StringBuilder -ArgumentList 260 - $res = [Ansible.WinUserProfile.NativeMethods]::CreateProfile($username.Value, $name, $raw_profile_path, - $raw_profile_path.Capacity) - - if ($res -ne 0) { - $exp = [System.Runtime.InteropServices.Marshal]::GetExceptionForHR($res) - $module.FailJson("Failed to create profile for user '$username': $($exp.Message)") - } - $profile_path = $raw_profile_path.ToString() - } - - $module.Result.changed = $true - $module.Result.path = $profile_path - } else { - $module.Result.path = (Get-ItemProperty -Path $user_profile.PSPath -Name ProfileImagePath).ProfileImagePath - } -} - -$module.ExitJson() - diff --git a/lib/ansible/modules/windows/win_user_profile.py b/lib/ansible/modules/windows/win_user_profile.py deleted file mode 100644 index 264392488c..0000000000 --- a/lib/ansible/modules/windows/win_user_profile.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/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_user_profile -version_added: '2.8' -short_description: Manages the Windows user profiles. -description: -- Used to create or remove user profiles on a Windows host. -- This can be used to create a profile before a user logs on or delete a - profile when removing a user account. -- A profile can be created for both a local or domain account. -options: - name: - description: - - Specifies the base name for the profile path. - - When I(state) is C(present) this is used to create the profile for - I(username) at a specific path within the profile directory. - - This cannot be used to specify a path outside of the profile directory - but rather it specifies a folder(s) within this directory. - - If a profile for another user already exists at the same path, then a 3 - digit incremental number is appended by Windows automatically. - - When I(state) is C(absent) and I(username) is not set, then the module - will remove all profiles that point to the profile path derived by this - value. - - This is useful if the account no longer exists but the profile still - remains. - type: str - remove_multiple: - description: - - When I(state) is C(absent) and the value for I(name) matches multiple - profiles the module will fail. - - Set this value to C(yes) to force the module to delete all the profiles - found. - default: no - type: bool - state: - description: - - Will ensure the profile exists when set to C(present). - - When creating a profile the I(username) option must be set to a valid - account. - - Will remove the profile(s) when set to C(absent). - - When removing a profile either I(username) must be set to a valid - account, or I(name) is set to the profile's base name. - default: present - choices: - - absent - - present - type: str - username: - description: - - The account name of security identifier (SID) for the profile. - - This must be set when I(state) is C(present) and must be a valid account - or the SID of a valid account. - - When I(state) is C(absent) then this must still be a valid account number - but the SID can be a deleted user's SID. -seealso: -- module: win_user -- module: win_domain_user -author: -- Jordan Borean (@jborean93) -''' - -EXAMPLES = r''' -- name: Create a profile for an account - win_user_profile: - username: ansible-account - state: present - -- name: Create a profile for an account at C:\Users\ansible - win_user_profile: - username: ansible-account - name: ansible - state: present - -- name: Remove a profile for a still valid account - win_user_profile: - username: ansible-account - state: absent - -- name: Remove a profile for a deleted account - win_user_profile: - name: ansible - state: absent - -- name: Remove a profile for a deleted account based on the SID - win_user_profile: - username: S-1-5-21-3233007181-2234767541-1895602582-1305 - state: absent - -- name: Remove multiple profiles that exist at the basename path - win_user_profile: - name: ansible - state: absent - remove_multiple: yes -''' - -RETURN = r''' -path: - description: The full path to the profile for the account. This will be null - if C(state=absent) and no profile was deleted. - returned: always - type: str - sample: C:\Users\ansible -''' diff --git a/lib/ansible/modules/windows/win_wait_for_process.ps1 b/lib/ansible/modules/windows/win_wait_for_process.ps1 deleted file mode 100644 index 70ff4bbba4..0000000000 --- a/lib/ansible/modules/windows/win_wait_for_process.ps1 +++ /dev/null @@ -1,176 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Ansible Project -# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> -# 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.SID - -$spec = @{ - options = @{ - process_name_exact = @{ type='list' } - process_name_pattern = @{ type='str' } - pid = @{ type='int'; default=0 } - owner = @{ type='str' } - sleep = @{ type='int'; default=1 } - pre_wait_delay = @{ type='int'; default=0 } - post_wait_delay = @{ type='int'; default=0 } - process_min_count = @{ type='int'; default=1 } - state = @{ type='str'; default='present'; choices=@( 'absent', 'present' ) } - timeout = @{ type='int'; default=300 } - } - mutually_exclusive = @( - @( 'pid', 'process_name_exact' ), - @( 'pid', 'process_name_pattern' ), - @( 'process_name_exact', 'process_name_pattern' ) - ) - required_one_of = @( - ,@( 'owner', 'pid', 'process_name_exact', 'process_name_pattern' ) - ) - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$process_name_exact = $module.Params.process_name_exact -$process_name_pattern = $module.Params.process_name_pattern -$process_id = $module.Params.pid # pid is a reserved variable in PowerShell, using process_id instead -$owner = $module.Params.owner -$sleep = $module.Params.sleep -$pre_wait_delay = $module.Params.pre_wait_delay -$post_wait_delay = $module.Params.post_wait_delay -$process_min_count = $module.Params.process_min_count -$state = $module.Params.state -$timeout = $module.Params.timeout - -$module.Result.changed = $false -$module.Result.elapsed = 0 -$module.Result.matched_processes = @() - -# Validate the input -if ($state -eq "absent" -and $sleep -ne 1) { - $module.Warn("Parameter 'sleep' has no effect when waiting for a process to stop.") -} - -if ($state -eq "absent" -and $process_min_count -ne 1) { - $module.Warn("Parameter 'process_min_count' has no effect when waiting for a process to stop.") -} - -if ($owner -and ("IncludeUserName" -notin (Get-Command -Name Get-Process).Parameters.Keys)) { - $module.FailJson("This version of Powershell does not support filtering processes by 'owner'.") -} - -Function Get-FilteredProcesses { - [cmdletbinding()] - Param( - [String] - $Owner, - $ProcessNameExact, - $ProcessNamePattern, - [int] - $ProcessId - ) - - $FilteredProcesses = @() - - try { - $Processes = Get-Process -IncludeUserName - $SupportsUserNames = $true - } catch [System.Management.Automation.ParameterBindingException] { - $Processes = Get-Process - $SupportsUserNames = $false - } - - foreach ($Process in $Processes) { - - # If a process name was specified in the filter, validate that here. - if ($ProcessNamePattern) { - if ($Process.ProcessName -notmatch $ProcessNamePattern) { - continue - } - } - - # If a process name was specified in the filter, validate that here. - if ($ProcessNameExact -is [Array]) { - if ($ProcessNameExact -notcontains $Process.ProcessName) { - continue - } - } elseif ($ProcessNameExact) { - if ($ProcessNameExact -ne $Process.ProcessName) { - continue - } - } - - # If a PID was specified in the filter, validate that here. - if ($ProcessId -and $ProcessId -ne 0) { - if ($ProcessId -ne $Process.Id) { - continue - } - } - - # If an owner was specified in the filter, validate that here. - if ($Owner) { - if (-not $Process.UserName) { - continue - } elseif ((Convert-ToSID($Owner)) -ne (Convert-ToSID($Process.UserName))) { # NOTE: This is rather expensive - continue - } - } - - if ($SupportsUserNames -eq $true) { - $FilteredProcesses += @{ name = $Process.ProcessName; pid = $Process.Id; owner = $Process.UserName } - } else { - $FilteredProcesses += @{ name = $Process.ProcessName; pid = $Process.Id } - } - } - - return ,$FilteredProcesses -} - -$module_start = Get-Date -Start-Sleep -Seconds $pre_wait_delay - -if ($state -eq "present" ) { - - # Wait for a process to start - do { - - $Processes = Get-FilteredProcesses -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id - $module.Result.matched_processes = $Processes - - if ($Processes.count -ge $process_min_count) { - break - } - - if (((Get-Date) - $module_start).TotalSeconds -gt $timeout) { - $module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds - $module.FailJson("Timed out while waiting for process(es) to start") - } - - Start-Sleep -Seconds $sleep - - } while ($true) - -} elseif ($state -eq "absent") { - - # Wait for a process to stop - $Processes = Get-FilteredProcesses -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id - $module.Result.matched_processes = $Processes - - if ($Processes.count -gt 0 ) { - try { - # This may randomly fail when used on specially protected processes (think: svchost) - Wait-Process -Id $Processes.pid -Timeout $timeout - } catch [System.TimeoutException] { - $module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds - $module.FailJson("Timeout while waiting for process(es) to stop") - } - } - -} - -Start-Sleep -Seconds $post_wait_delay -$module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_wait_for_process.py b/lib/ansible/modules/windows/win_wait_for_process.py deleted file mode 100644 index a26e2be5fe..0000000000 --- a/lib/ansible/modules/windows/win_wait_for_process.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub, actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_wait_for_process -version_added: '2.7' -short_description: Waits for a process to exist or not exist before continuing. -description: -- Waiting for a process to start or stop. -- This is useful when Windows services behave poorly and do not enumerate external dependencies in their manifest. -options: - process_name_exact: - description: - - The name of the process(es) for which to wait. The name of the process(es) should not include the file extension suffix. - type: list - process_name_pattern: - description: - - RegEx pattern matching desired process(es). - type: str - sleep: - description: - - Number of seconds to sleep between checks. - - Only applies when waiting for a process to start. Waiting for a process to start - does not have a native non-polling mechanism. Waiting for a stop uses native PowerShell - and does not require polling. - type: int - default: 1 - process_min_count: - description: - - Minimum number of process matching the supplied pattern to satisfy C(present) condition. - - Only applies to C(present). - type: int - default: 1 - pid: - description: - - The PID of the process. - type: int - owner: - description: - - The owner of the process. - - Requires PowerShell version 4.0 or newer. - type: str - pre_wait_delay: - description: - - Seconds to wait before checking processes. - type: int - default: 0 - post_wait_delay: - description: - - Seconds to wait after checking for processes. - type: int - default: 0 - state: - description: - - When checking for a running process C(present) will block execution - until the process exists, or until the timeout has been reached. - C(absent) will block execution until the process no longer exists, - or until the timeout has been reached. - - When waiting for C(present), the module will return changed only if - the process was not present on the initial check but became present on - subsequent checks. - - If, while waiting for C(absent), new processes matching the supplied - pattern are started, these new processes will not be included in the - action. - type: str - default: present - choices: [ absent, present ] - timeout: - description: - - The maximum number of seconds to wait for a for a process to start or stop - before erroring out. - type: int - default: 300 -seealso: -- module: wait_for -- module: win_wait_for -author: -- Charles Crossan (@crossan007) -''' - -EXAMPLES = r''' -- name: Wait 300 seconds for all Oracle VirtualBox processes to stop. (VBoxHeadless, VirtualBox, VBoxSVC) - win_wait_for_process: - process_name_pattern: 'v(irtual)?box(headless|svc)?' - state: absent - timeout: 500 - -- name: Wait 300 seconds for 3 instances of cmd to start, waiting 5 seconds between each check - win_wait_for_process: - process_name_exact: cmd - state: present - timeout: 500 - sleep: 5 - process_min_count: 3 -''' - -RETURN = r''' -elapsed: - description: The elapsed seconds between the start of poll and the end of the module. - returned: always - type: float - sample: 3.14159265 -matched_processes: - description: List of matched processes (either stopped or started). - returned: always - type: complex - contains: - name: - description: The name of the matched process. - returned: always - type: str - sample: svchost - owner: - description: The owner of the matched process. - returned: when supported by PowerShell - type: str - sample: NT AUTHORITY\SYSTEM - pid: - description: The PID of the matched process. - returned: always - type: int - sample: 7908 -''' diff --git a/lib/ansible/modules/windows/win_wakeonlan.ps1 b/lib/ansible/modules/windows/win_wakeonlan.ps1 deleted file mode 100644 index f14889534d..0000000000 --- a/lib/ansible/modules/windows/win_wakeonlan.ps1 +++ /dev/null @@ -1,52 +0,0 @@ -#!powershell - -# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#AnsibleRequires -CSharpUtil Ansible.Basic - -$spec = @{ - options = @{ - mac = @{ type='str'; required=$true } - broadcast = @{ type='str'; default='255.255.255.255' } - port = @{ type='int'; default=7 } - } - supports_check_mode = $true -} - -$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) - -$module.Result.changed = $false - -$mac = $module.Params.mac -$mac_orig = $module.Params.mac -$broadcast = $module.Params.broadcast -$port = $module.Params.port - -$broadcast = [Net.IPAddress]::Parse($broadcast) - -# Remove possible separator from MAC address -if ($mac.Length -eq (12 + 5)) { - $mac = $mac.Replace($mac.Substring(2, 1), "") -} - -# If we don't end up with 12 hexadecimal characters, fail -if ($mac.Length -ne 12) { - $module.FailJson("Incorrect MAC address: $mac_orig") -} - -# Create payload for magic packet -# TODO: Catch possible conversion errors -$target = 0,2,4,6,8,10 | ForEach-Object { [convert]::ToByte($mac.Substring($_, 2), 16) } -$data = (,[byte]255 * 6) + ($target * 20) - -# Broadcast payload to network -$udpclient = new-Object System.Net.Sockets.UdpClient -if (-not $module.CheckMode) { - $udpclient.Connect($broadcast, $port) - [void] $udpclient.Send($data, 102) -} - -$module.Result.changed = $true - -$module.ExitJson() diff --git a/lib/ansible/modules/windows/win_wakeonlan.py b/lib/ansible/modules/windows/win_wakeonlan.py deleted file mode 100644 index 60439624af..0000000000 --- a/lib/ansible/modules/windows/win_wakeonlan.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Dag Wieers <dag@wieers.com> -# 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_wakeonlan -version_added: '2.4' -short_description: Send a magic Wake-on-LAN (WoL) broadcast packet -description: -- The C(win_wakeonlan) module sends magic Wake-on-LAN (WoL) broadcast packets. -- For non-Windows targets, use the M(wakeonlan) module instead. -options: - mac: - description: - - MAC address to send Wake-on-LAN broadcast packet for. - type: str - required: yes - broadcast: - description: - - Network broadcast address to use for broadcasting magic Wake-on-LAN packet. - type: str - default: 255.255.255.255 - port: - description: - - UDP port to use for magic Wake-on-LAN packet. - type: int - default: 7 -todo: -- Does not have SecureOn password support -notes: -- This module sends a magic packet, without knowing whether it worked. It always report a change. -- Only works if the target system was properly configured for Wake-on-LAN (in the BIOS and/or the OS). -- Some BIOSes have a different (configurable) Wake-on-LAN boot order (i.e. PXE first). -seealso: -- module: wakeonlan -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- name: Send a magic Wake-on-LAN packet to 00:00:5E:00:53:66 - win_wakeonlan: - mac: 00:00:5E:00:53:66 - broadcast: 192.0.2.23 - -- name: Send a magic Wake-On-LAN packet on port 9 to 00-00-5E-00-53-66 - win_wakeonlan: - mac: 00-00-5E-00-53-66 - port: 9 - delegate_to: remote_system -''' - -RETURN = r''' -# Default return values -''' diff --git a/lib/ansible/modules/windows/win_webpicmd.ps1 b/lib/ansible/modules/windows/win_webpicmd.ps1 deleted file mode 100644 index c2d1b7ca71..0000000000 --- a/lib/ansible/modules/windows/win_webpicmd.ps1 +++ /dev/null @@ -1,116 +0,0 @@ -#!powershell - -# Copyright: (c) 2015, Peter Mounce <public@neverrunwithscissors.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = "Stop" - -Function Find-Command -{ - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0)] [string] $command - ) - $installed = get-command $command -erroraction Ignore - write-verbose "$installed" - if ($installed) - { - return $installed - } - return $null -} - -Function Find-WebPiCmd -{ - [CmdletBinding()] - param() - $p = Find-Command "webpicmd.exe" - if ($null -ne $p) - { - return $p - } - $a = Find-Command "c:\programdata\chocolatey\bin\webpicmd.exe" - if ($null -ne $a) - { - return $a - } - Throw "webpicmd.exe is not installed. It must be installed (use chocolatey)" -} - -Function Test-IsInstalledFromWebPI -{ - [CmdletBinding()] - - param( - [Parameter(Mandatory=$true, Position=0)] - [string]$package - ) - - $cmd = "$executable /list /listoption:installed" - $results = invoke-expression $cmd - - if ($LastExitCode -ne 0) - { - $result.webpicmd_error_cmd = $cmd - $result.webpicmd_error_log = "$results" - - Throw "Error checking installation status for $package" - } - Write-Verbose "$results" - - if ($results -match "^$package\s+") - { - return $true - } - - return $false -} - -Function Install-WithWebPICmd -{ - [CmdletBinding()] - - param( - [Parameter(Mandatory=$true, Position=0)] - [string]$package - ) - - $cmd = "$executable /install /products:$package /accepteula /suppressreboot" - - $results = invoke-expression $cmd - - if ($LastExitCode -ne 0) - { - $result.webpicmd_error_cmd = $cmd - $result.webpicmd_error_log = "$results" - Throw "Error installing $package" - } - - write-verbose "$results" - - if ($results -match "Install of Products: SUCCESS") - { - $result.changed = $true - } -} - -$result = @{ - changed = $false -} - -$params = Parse-Args $args - -$package = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true - -Try { - $script:executable = Find-WebPiCmd - if ((Test-IsInstalledFromWebPI -package $package) -eq $false) { - Install-WithWebPICmd -package $package - } - - Exit-Json $result -} Catch { - Fail-Json $result $_.Exception.Message -} diff --git a/lib/ansible/modules/windows/win_webpicmd.py b/lib/ansible/modules/windows/win_webpicmd.py deleted file mode 100644 index fc0663b304..0000000000 --- a/lib/ansible/modules/windows/win_webpicmd.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Peter Mounce <public@neverrunwithscissors.com> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_webpicmd -version_added: "2.0" -short_description: Installs packages using Web Platform Installer command-line -description: - - Installs packages using Web Platform Installer command-line - (U(http://www.iis.net/learn/install/web-platform-installer/web-platform-installer-v4-command-line-webpicmdexe-rtw-release)). - - Must be installed and present in PATH (see M(win_chocolatey) module; 'webpicmd' is the package name, and you must install 'lessmsi' first too)? - - Install IIS first (see M(win_feature) module). -notes: - - Accepts EULAs and suppresses reboot - you will need to check manage reboots yourself (see M(win_reboot) module) -options: - name: - description: - - Name of the package to be installed. - type: str - required: yes -seealso: -- module: win_package -author: -- Peter Mounce (@petemounce) -''' - -EXAMPLES = r''' -- name: Install URLRewrite2. - win_webpicmd: - name: URLRewrite2 -''' diff --git a/lib/ansible/modules/windows/win_xml.ps1 b/lib/ansible/modules/windows/win_xml.ps1 deleted file mode 100644 index d051a3f67d..0000000000 --- a/lib/ansible/modules/windows/win_xml.ps1 +++ /dev/null @@ -1,265 +0,0 @@ -#!powershell - -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -#Requires -Module Ansible.ModuleUtils.Legacy -#Requires -Module Ansible.ModuleUtils.Backup - -Set-StrictMode -Version 2 - -function Copy-Xml($dest, $src, $xmlorig) { - if ($src.get_NodeType() -eq "Text") { - $dest.set_InnerText($src.get_InnerText()) - } - - if ($src.get_HasAttributes()) { - foreach ($attr in $src.get_Attributes()) { - $dest.SetAttribute($attr.get_Name(), $attr.get_Value()) - } - } - - if ($src.get_HasChildNodes()) { - foreach ($childnode in $src.get_ChildNodes()) { - if ($childnode.get_NodeType() -eq "Element") { - $newnode = $xmlorig.CreateElement($childnode.get_Name(), $xmlorig.get_DocumentElement().get_NamespaceURI()) - Copy-Xml -dest $newnode -src $childnode -xmlorig $xmlorig - $dest.AppendChild($newnode) | Out-Null - } elseif ($childnode.get_NodeType() -eq "Text") { - $dest.set_InnerText($childnode.get_InnerText()) - } - } - } -} - -function Compare-XmlDocs($actual, $expected) { - if ($actual.get_Name() -ne $expected.get_Name()) { - throw "Actual name not same as expected: actual=" + $actual.get_Name() + ", expected=" + $expected.get_Name() - } - ##attributes... - - if (($actual.get_NodeType() -eq "Element") -and ($expected.get_NodeType() -eq "Element")) { - if ($actual.get_HasAttributes() -and $expected.get_HasAttributes()) { - if ($actual.get_Attributes().Count -ne $expected.get_Attributes().Count) { - throw "attribute mismatch for actual=" + $actual.get_Name() - } - for ($i=0;$i -lt $expected.get_Attributes().Count; $i =$i+1) { - if ($expected.get_Attributes()[$i].get_Name() -ne $actual.get_Attributes()[$i].get_Name()) { - throw "attribute name mismatch for actual=" + $actual.get_Name() - } - if ($expected.get_Attributes()[$i].get_Value() -ne $actual.get_Attributes()[$i].get_Value()) { - throw "attribute value mismatch for actual=" + $actual.get_Name() - } - } - } - - if (($actual.get_HasAttributes() -and !$expected.get_HasAttributes()) -or (!$actual.get_HasAttributes() -and $expected.get_HasAttributes())) { - throw "attribute presence mismatch for actual=" + $actual.get_Name() - } - } - - ##children - if ($expected.get_ChildNodes().Count -ne $actual.get_ChildNodes().Count) { - throw "child node mismatch. for actual=" + $actual.get_Name() - } - - for ($i=0;$i -lt $expected.get_ChildNodes().Count; $i =$i+1) { - if (-not $actual.get_ChildNodes()[$i]) { - throw "actual missing child nodes. for actual=" + $actual.get_Name() - } - Compare-XmlDocs $expected.get_ChildNodes()[$i] $actual.get_ChildNodes()[$i] - } - - if ($expected.get_InnerText()) { - if ($expected.get_InnerText() -ne $actual.get_InnerText()) { - throw "inner text mismatch for actual=" + $actual.get_Name() - } - } - elseif ($actual.get_InnerText()) { - throw "actual has inner text but expected does not for actual=" + $actual.get_Name() - } -} - - -function Save-ChangedXml($xmlorig, $result, $message, $check_mode, $backup) { - $result.changed = $true - if (-Not $check_mode) { - if ($backup) { - $result.backup_file = Backup-File -path $dest -WhatIf:$check_mode - # Ensure backward compatibility (deprecate in future) - $result.backup = $result.backup_file - } - $xmlorig.Save($dest) - $result.msg = $message - } else { - $result.msg += " check mode" - } -} - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - -$debug_level = Get-AnsibleParam -obj $params -name "_ansible_verbosity" -type "int" -$debug = $debug_level -gt 2 - -$dest = Get-AnsibleParam $params "path" -type "path" -FailIfEmpty $true -aliases "dest", "file" -$fragment = Get-AnsibleParam $params "fragment" -type "str" -aliases "xmlstring" -$xpath = Get-AnsibleParam $params "xpath" -type "str" -FailIfEmpty $true -$backup = Get-AnsibleParam $params "backup" -type "bool" -Default $false -$type = Get-AnsibleParam $params "type" -type "str" -Default "element" -ValidateSet "element", "attribute", "text" -$attribute = Get-AnsibleParam $params "attribute" -type "str" -FailIfEmpty ($type -eq "attribute") -$state = Get-AnsibleParam $params "state" -type "str" -Default "present" -$count = Get-AnsibleParam $params "count" -type "bool" -Default $false - -$result = @{ - changed = $false -} - -If (-Not (Test-Path -Path $dest -PathType Leaf)){ - Fail-Json $result "Specified path $dest does not exist or is not a file." -} - -$xmlorig = New-Object -TypeName System.Xml.XmlDocument -$xmlorig.XmlResolver = $null -Try { - $xmlorig.Load($dest) -} -Catch { - Fail-Json $result "Failed to parse file at '$dest' as an XML document: $($_.Exception.Message)" -} - -$namespaceMgr = New-Object System.Xml.XmlNamespaceManager $xmlorig.NameTable -$namespace = $xmlorig.DocumentElement.NamespaceURI -$localname = $xmlorig.DocumentElement.LocalName - -$namespaceMgr.AddNamespace($xmlorig.$localname.SchemaInfo.Prefix, $namespace) - -$nodeList = $xmlorig.SelectNodes($xpath, $namespaceMgr) -$nodeListCount = $nodeList.get_Count() -if ($count) { - $result.count = $nodeListCount - if (-not $fragment) { - Exit-Json $result - } -} -## Exit early if xpath did not match any nodes -if ($nodeListCount -eq 0) { - $result.msg = "The supplied xpath did not match any nodes. If this is unexpected, check your xpath is valid for the xml file at supplied path." - Exit-Json $result -} - -$changed = $false -$result.msg = "not changed" - -if ($type -eq "element") { - if ($state -eq "absent") { - foreach ($node in $nodeList) { - # there are some nodes that match xpath, delete without comparing them to fragment - if (-Not $check_mode) { - $removedNode = $node.get_ParentNode().RemoveChild($node) - $changed = $true - if ($debug) { - $result.removed += $result.removed + $removedNode.get_OuterXml() - } - } - } - } else { # state -eq 'present' - $xmlfragment = $null - Try { - $xmlfragment = [xml]$fragment - } Catch { - Fail-Json $result "Failed to parse fragment as XML: $($_.Exception.Message)" - } - - foreach ($node in $nodeList) { - $candidate = $xmlorig.CreateElement($xmlfragment.get_DocumentElement().get_Name(), $xmlorig.get_DocumentElement().get_NamespaceURI()) - Copy-Xml -dest $candidate -src $xmlfragment.DocumentElement -xmlorig $xmlorig - - if ($node.get_NodeType() -eq "Document") { - $node = $node.get_DocumentElement() - } - $elements = $node.get_ChildNodes() - [bool]$present = $false - [bool]$changed = $false - $element_count = $elements.get_Count() - $nstatus = "node: " + $node.get_Value() + " element: " + $elements.get_OuterXml() + " Element count is $element_count" - Add-Warning $result $nstatus - if ($elements.get_Count()) { - if ($debug) { - $err = @() - $result.err = {$err}.Invoke() - } - foreach ($element in $elements) { - $estatus = "element is " + $element.get_OuterXml() - Add-Warning $result $estatus - try { - Compare-XmlDocs $candidate $element - $present = $true - break - } catch { - if ($debug) { - $result.err.Add($_.Exception.ToString()) - } - } - } - if (-Not $present -and ($state -eq "present")) { - [void]$node.AppendChild($candidate) - $result.msg = $result.msg + "xml added " - $changed = $true - } - } - } - } -} elseif ($type -eq "text") { - foreach ($node in $nodeList) { - if ($node.get_InnerText() -ne $fragment) { - $node.set_InnerText($fragment) - $changed = $true - } - } -} elseif ($type -eq "attribute") { - foreach ($node in $nodeList) { - if ($state -eq 'present') { - if ($node.NodeType -eq 'Attribute') { - if ($node.Name -eq $attribute -and $node.Value -ne $fragment ) { - # this is already the attribute with the right name, so just set its value to match fragment - $node.Value = $fragment - $changed = $true - } - } else { # assume NodeType is Element - if ($node.$attribute -ne $fragment) { - if (!$node.HasAttribute($attribute)) { # add attribute to Element if missing - $node.SetAttributeNode($attribute, $xmlorig.get_DocumentElement().get_NamespaceURI()) - } - #set the attribute into the element - $node.SetAttribute($attribute, $fragment) - $changed = $true - } - } - } elseif ($state -eq 'absent') { - if ($node.NodeType -eq 'Attribute') { - $attrNode = [System.Xml.XmlAttribute]$node - $parent = $attrNode.OwnerElement - $parent.RemoveAttribute($attribute) - $changed = $true - } else { # element node processing - if ($node.Name -eq $attribute ) { # note not caring about the state of 'fragment' at this point - $node.RemoveAttribute($attribute) - $changed = $true - } - } - } else { - Add-Warning $result "Unexpected state when processing attribute $($attribute), add was $add, state was $state" - } - } -} -if ($changed) { - if ($state -eq "absent") { - $summary = "$type removed" - } else { - $summary = "$type changed" - } - Save-ChangedXml -xmlorig $xmlorig -result $result -message $summary -check_mode $check_mode -backup $backup -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_xml.py b/lib/ansible/modules/windows/win_xml.py deleted file mode 100644 index a503df81d8..0000000000 --- a/lib/ansible/modules/windows/win_xml.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: win_xml -version_added: "2.7" -short_description: Manages XML file content on Windows hosts -description: - - Manages XML nodes, attributes and text, using xpath to select which xml nodes need to be managed. - - XML fragments, formatted as strings, are used to specify the desired state of a part or parts of XML files on remote Windows servers. - - For non-Windows targets, use the M(xml) module instead. -options: - attribute: - description: - - The attribute name if the type is 'attribute'. - - Required if C(type=attribute). - type: str - count: - description: - - When set to C(yes), return the number of nodes matched by I(xpath). - type: bool - default: false - version_added: 2.9 - backup: - description: - - Determine whether a backup should be created. - - When set to C(yes), create a backup file including the timestamp information - so you can get the original file back if you somehow clobbered it incorrectly. - type: bool - default: no - fragment: - description: - - The string representation of the XML fragment expected at xpath. Since ansible 2.9 not required when I(state=absent), or when I(count=yes). - type: str - required: false - aliases: [ xmlstring ] - path: - description: - - Path to the file to operate on. - type: path - required: true - aliases: [ dest, file ] - state: - description: - - Set or remove the nodes (or attributes) matched by I(xpath). - type: str - default: present - choices: [ present, absent ] - version_added: 2.9 - type: - description: - - The type of XML node you are working with. - type: str - required: yes - default: element - choices: [ attribute, element, text ] - xpath: - description: - - Xpath to select the node or nodes to operate on. - type: str - required: true -author: - - Richard Levenberg (@richardcs) - - Jon Hawkesworth (@jhawkesworth) -notes: - - Only supports operating on xml elements, attributes and text. - - Namespace, processing-instruction, command and document node types cannot be modified with this module. -seealso: - - module: xml - description: XML manipulation for Posix hosts. - - name: w3shools XPath tutorial - description: A useful tutorial on XPath - link: https://www.w3schools.com/xml/xpath_intro.asp -''' - -EXAMPLES = r''' -- name: Apply our filter to Tomcat web.xml - win_xml: - path: C:\apache-tomcat\webapps\myapp\WEB-INF\web.xml - fragment: '<filter><filter-name>MyFilter</filter-name><filter-class>com.example.MyFilter</filter-class></filter>' - xpath: '/*' - -- name: Apply sslEnabledProtocols to Tomcat's server.xml - win_xml: - path: C:\Tomcat\conf\server.xml - xpath: '//Server/Service[@name="Catalina"]/Connector[@port="9443"]' - attribute: 'sslEnabledProtocols' - fragment: 'TLSv1,TLSv1.1,TLSv1.2' - type: attribute - -- name: remove debug configuration nodes from nlog.conf - win_xml: - path: C:\IISApplication\nlog.conf - xpath: /nlog/rules/logger[@name="debug"]/descendant::* - state: absent - -- name: count configured connectors in Tomcat's server.xml - win_xml: - path: C:\Tomcat\conf\server.xml - xpath: //Server/Service/Connector - count: yes - register: connector_count - -- name: show connector count - debug: - msg="Connector count is {{connector_count.count}}" - -- name: ensure all lang=en attributes to lang=nl - win_xml: - path: C:\Data\Books.xml - xpath: //@[lang="en"] - attribute: lang - fragment: nl - type: attribute - -''' - -RETURN = r''' -backup_file: - description: Name of the backup file that was created. - returned: if backup=yes - type: str - sample: C:\Path\To\File.txt.11540.20150212-220915.bak -count: - description: Number of nodes matched by xpath. - returned: if count=yes - type: int - sample: 33 -msg: - description: What was done. - returned: always - type: str - sample: "xml added" -err: - description: XML comparison exceptions. - returned: always, for type element and -vvv or more - type: list - sample: attribute mismatch for actual=string -''' |