ansible-collections / community.windows

Windows community collection for Ansible
https://galaxy.ansible.com/community/windows
GNU General Public License v3.0
199 stars 153 forks source link

win_domain_user: Errors when user already exists #431

Open fiskhest opened 2 years ago

fiskhest commented 2 years ago
SUMMARY

If the user already exists and you try to create them, the ps1 errors out if the name attribute is not set to the same as DN, GUID, SID or SAM account name.

ISSUE TYPE
COMPONENT NAME

community.windows.win_domain_user

ANSIBLE VERSION
ansible [core 2.12.1]
  config file = None
  configured module search path = ['/home/fiskhest/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/fiskhest/.pyenv/versions/3.10.4/envs/customer/lib/python3.10/site-packages/ansible
  ansible collection location = /home/fiskhest/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/fiskhest/.pyenv/versions/customer/bin/ansible
  python version = 3.10.4 (main, May 16 2022, 10:19:33) [GCC 12.1.0]
  jinja version = 3.0.1
  libyaml = True
COLLECTION VERSION
# /home/fiskhest/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.windows 1.11.0
CONFIGURATION
DEFAULT_HOST_LIST(/home/fiskhest/scratch/customer/project/ansible/ansible.cfg) = ['/home/fiskhest/scratch/customer/project/ansible/inventory/tf_inventory.py']
DEFAULT_JINJA2_NATIVE(/home/fiskhest/scratch/customer/project/ansible/ansible.cfg) = True
DEFAULT_VAULT_PASSWORD_FILE(/home/fiskhest/scratch/customer/project/ansible/ansible.cfg) = /home/fiskhest/scratch/customer/project/ansible/.vault-pw
OS / ENVIRONMENT

Controller: Arch Linux (also encountering the same inside a debian buster runner image) Target: Windows Server 2019

STEPS TO REPRODUCE

If an identity: is not set and the value supplied to the name: parameter is not one of distinguished name (DN), GUID, security identifier (SID), or Security Account Manager (SAM) account name, the module crashes with an error: Unhandled exception while executing module: The specified account already exists when running a second time.

- name: Create users in the users OU
  community.windows.win_domain_user:
    name: "hestfisk"
    firstname: "fisk"
    surname: "hest"
    upn: "fiskhest@somedomain.local"
    password: "1a2B3c4D5%"
    state: present
    path: "ou=Users,ou=somedomain,dc=somedomain,dc=local"
    groups:
      - Domain Admins
EXPECTED RESULTS

When running a second time, the above should result in an "OK" instead of an unhandled exception.

ACTUAL RESULTS
TASK [some/role : Setup users] ******************************************************************************************************
task path: /home/path/to/playbook/tasks/main.yml:22
META: noop
META: noop
included: /home/path/to/playbook/tasks/user.yml for <redacted>

TASK [some/role : Create users in the users OU] *************************************************************************************
task path: /home/path/to/playbook/tasks/user.yml:2
META: noop
META: noop
redirecting filter ansible.builtin.json_query to community.general.json_query
Loading collection community.general from /home/fiskhest/.ansible/collections/ansible_collections/community/general
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
url lookup connecting to https://<redacted>/api/v4/projects/<redacted>
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
url lookup connecting to https://<redacted>/api/v4/projects/<redacted>
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
url lookup connecting to https://<redacted>/api/v4/projects/<redacted>
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
url lookup connecting to https://<redacted>/api/v4/projects/<redacted>
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
redirecting filter ansible.builtin.json_query to community.general.json_query
url lookup connecting to https://<redacted>/api/v4/projects/<redacted>
redirecting filter ansible.builtin.json_query to community.general.json_query
Using module file /home/fiskhest/.ansible/collections/ansible_collections/community/windows/plugins/modules/win_domain_user.ps1
Pipelining is enabled.
<<redacted>> ESTABLISH WINRM CONNECTION FOR USER: <redacted>.local\<redacted> on PORT 5986 TO <redacted>
EXEC (via pipeline wrapper)
redirecting filter ansible.builtin.json_query to community.general.json_query
The full traceback is:
The specified account already exists
At line:252 char:21
+ ... $user_obj = New-ADUser @create_args -WhatIf:$check_mode -PassThru @ex ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceExists: (CN=<redacted>,ou=<redacted>,dc=local:String) [New-ADUser], ADIdentityAlreadyExistsException
    + FullyQualifiedErrorId : ActiveDirectoryServer:1316,Microsoft.ActiveDirectory.Management.Commands.NewADUser

ScriptStackTrace:
at <ScriptBlock>, <No file>: line 252

Microsoft.ActiveDirectory.Management.ADIdentityAlreadyExistsException: The specified account already exists ---> System.ServiceModel.FaultException: The supplied entry already exists.
   --- End of inner exception stack trace ---
   at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForExtendedError(String extendedErrorMessage, Exception innerException)
   at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForErrorCode(String message, String errorCode, String extendedErrorMessage, Exception innerException)
   at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForFaultDetail(FaultDetail faultDetail, FaultException faultException)
   at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowException(AdwsFault adwsFault, FaultException faultException)
   at Microsoft.ActiveDirectory.Management.AdwsConnection.Create(ADAddRequest request)
   at Microsoft.ActiveDirectory.Management.ADWebServiceStoreAccess.Microsoft.ActiveDirectory.Management.IADSyncOperations.Add(ADSessionHandle handle, ADAddRequest request)
   at Microsoft.ActiveDirectory.Management.ADActiveObject.Create()
   at Microsoft.ActiveDirectory.Management.Commands.ADNewCmdletBase`3.ADNewCmdletBaseProcessCSRoutine()
   at Microsoft.ActiveDirectory.Management.CmdletSubroutinePipeline.Invoke()
   at Microsoft.ActiveDirectory.Management.Commands.ADCmdletBase`1.ProcessRecord()
failed: [<redacted>] (item={<redacted>,
    "msg": "Unhandled exception while executing module: The specified account already exists",
    <redacted>
}
Steps to resolve

I debugged the actual module code and there seems to be a misunderstanding about what can be supplied to the -identity-parameter for Get-ADUser. The module currently takes a name or identity param, and if identity is null it will be set the value of name.

# Module code win_domain_user.ps1 line 165
if ($null -eq $identity) {
    $identity = $name
}

Later inside the module that variable is used to find a user object inside AD.

# Module code win_domain_user.ps1 line 223
try {
    $user_obj = Get-ADUser `
        -Identity $identity `
        -Properties ('*', 'msDS-PrincipalName') @extra_args
    $user_guid = $user_obj.ObjectGUID
}

However, the actual documentation for Get-ADUser says:

The Identity parameter specifies the Active Directory user to get. You can identify a user by its distinguished name (DN), GUID, security identifier (SID), or Security Account Manager (SAM) account name.

If the value supplied to name: does not match one of the above, this issue is reproducible 100%.

My suggestion is to either update the documentation to reflect that the name should match DN/SAM image much like what is already done for the identity param: image

or update the code so that identity defaults to SamAccountName or $upn.Split('@')[0] (though, I notice now that is not a required param).

I helped a customer resolve this by asking them to provide the first part of the upn as a name, but only after having to read source code to understand what was wrong.

jsathler commented 1 year ago

Hi, I'm getting same error message "The specified account already exists" if I execute the playbook multiple times. If I delete the user, it is created successfully.

Code

tasks:

Error message

The full traceback is: The specified account already exists At line:252 char:21

ScriptStackTrace: at , : line 252

Microsoft.ActiveDirectory.Management.ADIdentityAlreadyExistsException: The specified account already exists ---> System.ServiceModel.FaultException: The supplied entry already exists. --- End of inner exception stack trace --- at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForExtendedError(String extendedErrorMessage, Exception innerException) at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForErrorCode(String message, String errorCode, String extendedErrorMessage, Exception innerException) at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForFaultDetail(FaultDetail faultDetail, FaultException faultException) at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowException(AdwsFault adwsFault, FaultException faultException) at Microsoft.ActiveDirectory.Management.AdwsConnection.Create(ADAddRequest request) at Microsoft.ActiveDirectory.Management.ADWebServiceStoreAccess.Microsoft.ActiveDirectory.Management.IADSyncOperations.Add(ADSessionHandle handle, ADAddRequest request) at Microsoft.ActiveDirectory.Management.ADActiveObject.Create() at Microsoft.ActiveDirectory.Management.Commands.ADNewCmdletBase3.ADNewCmdletBaseProcessCSRoutine() at Microsoft.ActiveDirectory.Management.CmdletSubroutinePipeline.Invoke() at Microsoft.ActiveDirectory.Management.Commands.ADCmdletBase1.ProcessRecord() fatal: [74.234.93.189]: FAILED! => { "changed": false, "msg": "Unhandled exception while executing module: The specified account already exists" }