win_domain_object_info: new module (#67450)
* win_domain_object_info: new module * Added basic integration tests
This commit is contained in:
parent
be26f4916f
commit
38f26ffcc7
5 changed files with 564 additions and 0 deletions
271
lib/ansible/modules/windows/win_domain_object_info.ps1
Normal file
271
lib/ansible/modules/windows/win_domain_object_info.ps1
Normal file
|
@ -0,0 +1,271 @@
|
|||
#!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()
|
162
lib/ansible/modules/windows/win_domain_object_info.py
Normal file
162
lib/ansible/modules/windows/win_domain_object_info.py
Normal file
|
@ -0,0 +1,162 @@
|
|||
#!/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
test/integration/targets/win_domain_object_info/aliases
Normal file
1
test/integration/targets/win_domain_object_info/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
unsupported
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: remove test domain user
|
||||
win_domain_user:
|
||||
name: '{{ test_user.distinguished_name }}'
|
||||
state: absent
|
125
test/integration/targets/win_domain_object_info/tasks/main.yml
Normal file
125
test/integration/targets/win_domain_object_info/tasks/main.yml
Normal file
|
@ -0,0 +1,125 @@
|
|||
# These tests can't run in CI, this is really just a basic smoke tests for local runs.
|
||||
---
|
||||
- name: assert better error message on auth failure
|
||||
win_domain_object_info:
|
||||
identity: id
|
||||
register: fail_auth
|
||||
failed_when: '"Failed to contact the AD server, this could be caused by the double hop problem" not in fail_auth.msg'
|
||||
vars:
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_psrp_auth: ntlm
|
||||
|
||||
- name: create test ad user
|
||||
win_domain_user:
|
||||
name: Ansible Test
|
||||
firstname: Ansible
|
||||
surname: Test
|
||||
company: Contoso R Us
|
||||
password: Password01
|
||||
state: present
|
||||
password_never_expires: yes
|
||||
groups:
|
||||
- Domain Users
|
||||
enabled: false
|
||||
register: test_user
|
||||
notify: remove test domain user
|
||||
|
||||
- name: set a binary attribute and return other useful info missing from above
|
||||
win_shell: |
|
||||
Set-ADUser -Identity '{{ test_user.sid }}' -Replace @{ audio = @([byte[]]@(1, 2, 3, 4), [byte[]]@(5, 6, 7, 8)) }
|
||||
|
||||
$user = Get-ADUser -Identity '{{ test_user.sid }}' -Properties modifyTimestamp, ObjectGUID
|
||||
|
||||
[TimeZoneInfo]::ConvertTimeToUtc($user.modifyTimestamp).ToString('o')
|
||||
$user.ObjectGUID.ToString()
|
||||
([System.Security.Principal.SecurityIdentifier]'{{ test_user.sid }}').Translate([System.Security.Principal.NTAccount]).Value
|
||||
register: test_user_extras
|
||||
|
||||
- name: set other test info for easier access
|
||||
set_fact:
|
||||
test_user_mod_date: '{{ test_user_extras.stdout_lines[0] }}'
|
||||
test_user_id: '{{ test_user_extras.stdout_lines[1] }}'
|
||||
test_user_name: '{{ test_user_extras.stdout_lines[2] }}'
|
||||
|
||||
- name: get properties for single user by DN
|
||||
win_domain_object_info:
|
||||
identity: '{{ test_user.distinguished_name }}'
|
||||
register: by_identity
|
||||
check_mode: yes # Just verifies it runs in check mode
|
||||
|
||||
- name: assert get properties for single user by DN
|
||||
assert:
|
||||
that:
|
||||
- not by_identity is changed
|
||||
- by_identity.objects | length == 1
|
||||
- by_identity.objects[0].keys() | list | length == 4
|
||||
- by_identity.objects[0].DistinguishedName == test_user.distinguished_name
|
||||
- by_identity.objects[0].Name == 'Ansible Test'
|
||||
- by_identity.objects[0].ObjectClass == 'user'
|
||||
- by_identity.objects[0].ObjectGUID == test_user_id
|
||||
|
||||
- name: get specific properties by GUID
|
||||
win_domain_object_info:
|
||||
identity: '{{ test_user_id }}'
|
||||
properties:
|
||||
- audio # byte[]
|
||||
- company # string
|
||||
- department # not set
|
||||
- logonCount # int
|
||||
- modifyTimestamp # DateTime
|
||||
- nTSecurityDescriptor # SecurityDescriptor as SDDL
|
||||
- objectSID # SID
|
||||
- ProtectedFromAccidentalDeletion # bool
|
||||
- sAMAccountType # Test out the enum string attribute that we add
|
||||
- userAccountControl # Test ou the enum string attribute that we add
|
||||
register: by_guid_custom_props
|
||||
|
||||
- name: assert get specific properties by GUID
|
||||
assert:
|
||||
that:
|
||||
- not by_guid_custom_props is changed
|
||||
- by_guid_custom_props.objects | length == 1
|
||||
- by_guid_custom_props.objects[0].DistinguishedName == test_user.distinguished_name
|
||||
- by_guid_custom_props.objects[0].Name == 'Ansible Test'
|
||||
- by_guid_custom_props.objects[0].ObjectClass == 'user'
|
||||
- by_guid_custom_props.objects[0].ObjectGUID == test_user_id
|
||||
- not by_guid_custom_props.objects[0].ProtectedFromAccidentalDeletion
|
||||
- by_guid_custom_props.objects[0].audio == ['BQYHCA==', 'AQIDBA==']
|
||||
- by_guid_custom_props.objects[0].company == 'Contoso R Us'
|
||||
- by_guid_custom_props.objects[0].department == None
|
||||
- by_guid_custom_props.objects[0].logonCount == 0
|
||||
- by_guid_custom_props.objects[0].modifyTimestamp == test_user_mod_date
|
||||
- by_guid_custom_props.objects[0].nTSecurityDescriptor.startswith('O:DAG:DAD:AI(')
|
||||
- by_guid_custom_props.objects[0].objectSID.Name == test_user_name
|
||||
- by_guid_custom_props.objects[0].objectSID.Sid == test_user.sid
|
||||
- by_guid_custom_props.objects[0].sAMAccountType == 805306368
|
||||
- by_guid_custom_props.objects[0].sAMAccountType_AnsibleFlags == ['SAM_USER_OBJECT']
|
||||
- by_guid_custom_props.objects[0].userAccountControl == 66050
|
||||
- by_guid_custom_props.objects[0].userAccountControl_AnsibleFlags == ['ADS_UF_ACCOUNTDISABLE', 'ADS_UF_NORMAL_ACCOUNT', 'ADS_UF_DONT_EXPIRE_PASSWD']
|
||||
|
||||
- name: get invalid property
|
||||
win_domain_object_info:
|
||||
filter: sAMAccountName -eq 'Ansible Test'
|
||||
properties:
|
||||
- FakeProperty
|
||||
register: invalid_prop_warning
|
||||
|
||||
- name: assert get invalid property
|
||||
assert:
|
||||
that:
|
||||
- not invalid_prop_warning is changed
|
||||
- invalid_prop_warning.objects | length == 0
|
||||
- invalid_prop_warning.warnings | length == 1
|
||||
- '"Failed to retrieve properties for AD object" not in invalid_prop_warning.warnings[0]'
|
||||
|
||||
- name: get by ldap filter returning multiple
|
||||
win_domain_object_info:
|
||||
ldap_filter: (&(objectClass=computer)(objectCategory=computer))
|
||||
properties: '*'
|
||||
register: multiple_ldap
|
||||
|
||||
- name: assert get by ldap filter returning multiple
|
||||
assert:
|
||||
that:
|
||||
- not multiple_ldap is changed
|
||||
- multiple_ldap.objects | length > 1
|
Loading…
Reference in a new issue