Migrated to community.windows

pull/67091/head
Ansible Core Team 5 years ago committed by Matt Martz
parent 40218535ee
commit bb3494356a

@ -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()

@ -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

@ -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"
}
'''

@ -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

@ -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
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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
'''

@ -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()

@ -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.
'''

@ -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

@ -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'''
'''

@ -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

@ -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'
'''

@ -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

@ -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'''
'''

@ -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

@ -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'''
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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
'''

@ -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

@ -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
'''

@ -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

@ -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:\' ]
'''

@ -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()

@ -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'''
'''

@ -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

@ -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'''
'''

@ -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

@ -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
'''

@ -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

@ -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"]
'''

@ -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()

@ -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",
}]
'''

@ -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

@ -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
'''

@ -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

@ -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
'''

@ -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

@ -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"]
'''

@ -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

@ -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
'''

@ -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()

@ -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
'''

@ -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;

@ -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
'''

@ -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

@ -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
'''

@ -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

@ -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: '*'
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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'''
'''

@ -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

@ -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
'''

@ -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()

@ -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'''
#
'''

@ -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

@ -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
'''

@ -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

@ -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
'''

@ -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

@ -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"
'''

@ -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
}

@ -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"
'''

@ -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

@ -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
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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 = '''
#
'''

@ -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;
}

@ -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
'''

@ -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()

@ -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'''
'''

@ -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

@ -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
'''

@ -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()

@ -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
'''

@ -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

@ -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
'''

@ -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

@ -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"}]
'''

@ -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()

@ -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'''
#
'''

@ -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()

@ -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
'''

@ -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()

@ -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
}
'''

@ -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()

@ -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
'''

@ -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()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save