Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change default path handling for existing AD objs #59

Merged
merged 1 commit into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelogs/fragments/default-path.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
minor_changes:
- >-
AD objects will no longer be moved to the default AD path for their type if no ``path`` was specified. Use the value
``microsoft.ad.default_path`` to explicitly set the path to the default path if that behaviour is desired.
10 changes: 8 additions & 2 deletions plugins/doc_fragments/ad_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,17 @@ class ModuleDocFragment:
path:
description:
- The path of the OU or the container where the new object should exist in.
- If no path is specified, the default is the C(defaultNamingContext) of
domain for most objects.
- If creating a new object, the new object will be created at the path
specified. If no path is specified then the C(defaultNamingContext) of
the domain will be used as the path for most object types.
- If managing an existing object found by I(identity), the path of the
found object will be moved to the one specified by this option. If no
path is specified, the object will not be moved.
- The modules M(microsoft.ad.computer), M(microsoft.ad.user), and
M(microsoft.ad.group) have their own default path that is
configured on the Active Directory domain controller.
- This can be set to C(microsoft.ad.default_path) which will equal the
default value used when creating a new object.
type: str
protect_from_deletion:
description:
Expand Down
14 changes: 10 additions & 4 deletions plugins/module_utils/_ADObject.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,8 @@ Function Invoke-AnsibleADObject {
$PostAction
)

$defaultPathSentinel = 'microsoft.ad.default_path'

$spec = @{
options = @{
attributes = @{
Expand Down Expand Up @@ -828,7 +830,7 @@ Function Invoke-AnsibleADObject {
}
else {
$ouPath = $defaultObjectPath
if ($module.Params.path) {
if ($module.Params.path -and $module.Params.path -ne $defaultPathSentinel) {
$ouPath = $module.Params.path
}
"$namePrefix=$($Module.Params.name -replace ',', '\,'),$ouPath"
Expand Down Expand Up @@ -911,7 +913,7 @@ Function Invoke-AnsibleADObject {
}

$objectPath = $null
if ($module.Params.path) {
if ($module.Params.path -and $module.Params.path -ne $defaultPathSentinel) {
$objectPath = $path
$newParams.Path = $module.Params.path
}
Expand Down Expand Up @@ -1081,8 +1083,12 @@ Function Invoke-AnsibleADObject {
$module.Result.changed = $true
}

if ($module.Params.path -and $module.Params.path -ne $objectPath) {
$objectPath = $module.Params.path
$desiredPath = $module.Params.path
if ($desiredPath -eq $defaultPathSentinel) {
$desiredPath = $defaultObjectPath
}
if ($desiredPath -and $desiredPath -ne $objectPath) {
$objectPath = $desiredPath
$module.Diff.after.path = $objectPath

$addProtection = $false
Expand Down
107 changes: 107 additions & 0 deletions tests/integration/targets/object/tasks/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,59 @@
- move_ou_actual.objects[0].DistinguishedName == 'OU=TestOU 2,' ~ sub_ous.results[0].distinguished_name
- move_ou_actual.objects[0].ProtectedFromAccidentalDeletion == true

- name: do not move object in non default path without path - check
object:
name: TestOU 2
identity: '{{ sub_ous.results[1].object_guid }}'
type: organizationalUnit
attributes:
set:
description: Test comment
register: dont_move_no_path_check
check_mode: true

- name: get result of do not move object in non default path without path - check
object_info:
identity: '{{ sub_ous.results[1].object_guid }}'
properties:
- description
register: dont_move_no_path_check_actual

- name: assert do not move object in non default path without path - check
assert:
that:
- dont_move_no_path_check is changed
- dont_move_no_path_check.distinguished_name == 'OU=TestOU 2,' ~ sub_ous.results[0].distinguished_name
- dont_move_no_path_check_actual.objects[0].Name == 'TestOU 2'
- dont_move_no_path_check_actual.objects[0].DistinguishedName == 'OU=TestOU 2,' ~ sub_ous.results[0].distinguished_name
- dont_move_no_path_check_actual.objects[0].Description == None

- name: do not move object in non default path without path
object:
name: TestOU 2
identity: '{{ sub_ous.results[1].object_guid }}'
type: organizationalUnit
attributes:
set:
description: Test comment
register: dont_move_no_path

- name: get result of do not move object in non default path without path
object_info:
identity: '{{ sub_ous.results[1].object_guid }}'
properties:
- description
register: dont_move_no_path_actual

- name: assert do not move object in non default path without path - check
assert:
that:
- dont_move_no_path is changed
- dont_move_no_path.distinguished_name == 'OU=TestOU 2,' ~ sub_ous.results[0].distinguished_name
- dont_move_no_path_actual.objects[0].Name == 'TestOU 2'
- dont_move_no_path_actual.objects[0].DistinguishedName == 'OU=TestOU 2,' ~ sub_ous.results[0].distinguished_name
- dont_move_no_path_actual.objects[0].Description == 'Test comment'

- name: remove object that is protected from deletion - check
object:
name: My, Container
Expand Down Expand Up @@ -1444,3 +1497,57 @@
assert:
that:
- not unset_normal_again is changed

- name: move object back into the default path - check
object:
name: My, Container
identity: '{{ object_identity }}'
type: container
path: microsoft.ad.default_path
register: move_into_default_check
check_mode: true

- name: get result of move object back into the default path - check
object_info:
identity: '{{ object_identity }}'
register: move_into_default_check_actual

- name: assert move object back into the default path - check
assert:
that:
- move_into_default_check is changed
- move_into_default_check.distinguished_name == 'CN=My\, Container,' ~ setup_domain_info.output[0].defaultNamingContext
- move_into_default_check_actual.objects[0].DistinguishedName == 'CN=My\, Container,CN=Users,' ~ setup_domain_info.output[0].defaultNamingContext

- name: move object back into the default path
object:
name: My, Container
identity: '{{ object_identity }}'
type: container
path: microsoft.ad.default_path
register: move_into_default

- name: get result of move object back into the default path
object_info:
identity: '{{ object_identity }}'
register: move_into_default_actual

- name: assert move object back into the default path
assert:
that:
- move_into_default is changed
- move_into_default.distinguished_name == 'CN=My\, Container,' ~ setup_domain_info.output[0].defaultNamingContext
- move_into_default_actual.objects[0].DistinguishedName == 'CN=My\, Container,' ~ setup_domain_info.output[0].defaultNamingContext

- name: move object back into the default path - idempotent
object:
name: My, Container
identity: '{{ object_identity }}'
type: container
path: microsoft.ad.default_path
register: move_into_default_again

- name: assert move object back into the default path - idempotent
assert:
that:
- not move_into_default_again is changed
108 changes: 106 additions & 2 deletions tests/integration/targets/user/tasks/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,115 @@
that:
- not move_user_again is changed

- name: update user not in default path by identity - check
user:
name: MyUser2
identity: '{{ object_sid }}'
firstname: first name
register: dont_move_no_path_check
check_mode: true

- name: get result of update user not in default path by identity - check
object_info:
identity: '{{ object_identity }}'
properties:
- givenName
register: dont_move_no_path_check_actual
check_mode: true

- name: assert update user not in default path by identity - check
assert:
that:
- dont_move_no_path_check is changed
- dont_move_no_path_check.distinguished_name == 'CN=MyUser2,' ~ setup_domain_info.output[0].defaultNamingContext
- dont_move_no_path_check_actual.objects[0].DistinguishedName == 'CN=MyUser2,' ~ setup_domain_info.output[0].defaultNamingContext
- dont_move_no_path_check_actual.objects[0].Name == 'MyUser2'
- dont_move_no_path_check_actual.objects[0].givenName == None

- name: update user not in default path by identity
user:
name: MyUser2
identity: '{{ object_sid }}'
firstname: first name
register: dont_move_no_path

- name: get result of update user not in default path by identity
object_info:
identity: '{{ object_identity }}'
properties:
- givenName
register: dont_move_no_path_actual
check_mode: true

- name: assert update user not in default path by identity - check
assert:
that:
- dont_move_no_path is changed
- dont_move_no_path.distinguished_name == 'CN=MyUser2,' ~ setup_domain_info.output[0].defaultNamingContext
- dont_move_no_path_actual.objects[0].DistinguishedName == 'CN=MyUser2,' ~ setup_domain_info.output[0].defaultNamingContext
- dont_move_no_path_actual.objects[0].Name == 'MyUser2'
- dont_move_no_path_actual.objects[0].givenName == 'first name'

- name: move user back - check
user:
name: MyUser
identity: MyUser
path: microsoft.ad.default_path
register: move_with_path_sentinel_check
check_mode: true

- name: get result of move user back - check
object_info:
identity: '{{ object_identity }}'
properties:
- sAMAccountName
register: move_with_path_sentinel_check_actual

- name: assert move user back - check
assert:
that:
- move_with_path_sentinel_check is changed
- move_with_path_sentinel_check.distinguished_name == 'CN=MyUser,CN=Users,' ~ setup_domain_info.output[0].defaultNamingContext
- move_with_path_sentinel_check_actual.objects[0].DistinguishedName == 'CN=MyUser2,' ~ setup_domain_info.output[0].defaultNamingContext
- move_with_path_sentinel_check_actual.objects[0].Name == 'MyUser2'
- move_with_path_sentinel_check_actual.objects[0].sAMAccountName == 'MyUser'

- name: move user back
user:
name: MyUser
identity: MyUser # By sAMAccountName
path: CN=Users,{{ setup_domain_info.output[0].defaultNamingContext }}
identity: MyUser
path: microsoft.ad.default_path
register: move_with_path_sentinel

- name: get result of move user back
object_info:
identity: '{{ object_identity }}'
properties:
- sAMAccountName
register: move_with_path_sentinel_actual

- name: assert move user back
assert:
that:
- move_with_path_sentinel is changed
- move_with_path_sentinel.distinguished_name == 'CN=MyUser,CN=Users,' ~ setup_domain_info.output[0].defaultNamingContext
- move_with_path_sentinel_actual.objects[0].DistinguishedName == 'CN=MyUser,CN=Users,' ~ setup_domain_info.output[0].defaultNamingContext
- move_with_path_sentinel_actual.objects[0].Name == 'MyUser'
- move_with_path_sentinel_actual.objects[0].sAMAccountName == 'MyUser'

- name: move user back - idempotent
user:
name: MyUser
identity: MyUser
path: microsoft.ad.default_path
register: move_with_path_sentinel_again

- name: assert move user back - idempotent
assert:
that:
- not move_with_path_sentinel_again is changed

# CN=Users,{{ setup_domain_info.output[0].defaultNamingContext }}

- name: update password from blank - skip for on_create
user:
Expand Down