ansible-collections / microsoft.ad

Ansible collection for Active Directory management
GNU General Public License v3.0
36 stars 19 forks source link

Moving computer object fails #124

Open jengstrom440 opened 3 weeks ago

jengstrom440 commented 3 weeks ago
SUMMARY

Moving a computer object does not work per documentation, ansible attempts use New-ADComputer instead of Move-ADObject.

ISSUE TYPE

Bug

COMPONENT NAME

microsoft.ad.computer

ANSIBLE VERSION

ansible [core 2.16.3] config file = None configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3/dist-packages/ansible ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections executable location = /usr/bin/ansible python version = 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0] (/usr/bin/python3) jinja version = 3.1.2 libyaml = True

COLLECTION VERSION

ansible-galaxy collection list microsoft.ad

/home/ansible/.ansible/collections/ansible_collections Collection Version microsoft.ad 1.6.0

/usr/lib/python3/dist-packages/ansible_collections Collection Version microsoft.ad 1.4.1

CONFIGURATION

CONFIG_FILE() = None

OS / ENVIRONMENT

Ubuntu 24.04 Server install on VMware ESXi 8

STEPS TO REPRODUCE

I have a simple playbook to move a computer account in AD to a specified OU in the 'path'. Happens each time it is run.

- name: Move server to Domain Servers OU
  microsoft.ad.computer:
    identity: "{{ ansible_hostname }}"
    path: OU=Domain Servers,OU=Site-Location,DC=ad,DC=cityofgotham,DC=com
EXPECTED RESULTS

I expect the existing computer account to be moved and not recreated. Documentation states that:

identity: This must be specified if; name is not set, when trying to rename the object with a new name, or when trying to move the object into a different path.

path: If managing an existing object found by 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.

ACTUAL RESULTS

Since it is using "New-ADComputer" in the verbose error output instead of "Move-ADObject", this will not achieve desired results. This needs to be adjusted for "Move-ADObject" instead:

1:53:18 PM The full traceback is: 1:53:18 PM The specified account already exists 1:53:18 PM At line:984 char:29 1:53:18 PM $adObject = & $newCommand @newParams @adParams 1:53:18 PM

1:53:18 PM CategoryInfo : ResourceExists: (CN=batman,O...ofgotham,DC=com:String) [New-ADComputer]

david-sieg commented 3 weeks ago

Hi,

we solved it with two tasks:

- name: "Search computer object in ad to fetch guid [ad_manage_computer_add]"
  microsoft.ad.object_info:
    ldap_filter: "(&(objectClass=computer)(cn={{ inventory_hostname_short }}*))"
    properties: '*'
    domain_server: ADCONTROLLER
    domain_username: ADMIN
    domain_password: PASSWORD
  delegate_to: ADCONTROLLER
  register: reg_computer_obj

- name: "Move computer object to the right Active Directory OU [ad_manage_computer_add]"
  microsoft.ad.object:
    identity: "{{ reg_computer_obj.objects.0.ObjectGUID }}"
    name: "{{ inventory_hostname_short }}"
    path: "CN=Computers,DC=domain,DC=com"
    type: computer
    domain_server: ADCONTROLLER
    domain_username: ADMIN
    domain_password: PASSWORD
    state: present
  delegate_to: ADCONTROLLER

Hope that helps...

jengstrom440 commented 3 weeks ago

Hi,

we solved it with two tasks:

- name: "Search computer object in ad to fetch guid [ad_manage_computer_add]"
  microsoft.ad.object_info:
    ldap_filter: "(&(objectClass=computer)(cn={{ inventory_hostname_short }}*))"
    properties: '*'
    domain_server: ADCONTROLLER
    domain_username: ADMIN
    domain_password: PASSWORD
  delegate_to: ADCONTROLLER
  register: reg_computer_obj

- name: "Move computer object to the right Active Directory OU [ad_manage_computer_add]"
  microsoft.ad.object:
    identity: "{{ reg_computer_obj.objects.0.ObjectGUID }}"
    name: "{{ inventory_hostname_short }}"
    path: "CN=Computers,DC=domain,DC=com"
    type: computer
    domain_server: ADCONTROLLER
    domain_username: ADMIN
    domain_password: PASSWORD
    state: present
  delegate_to: ADCONTROLLER

Hope that helps...

Thank you for posting this. That has got me thinking of a few ideas to test and I believe I have figured it out. Since identity is looking for a matching SamAccountName for the computer object, it must end with a $. I assumed that the code in ansible would append the $ to the end of the entry as every computer object in Active Directory must end in $ for it's SamAccountName. That appears not to be the case. After some trial and error along with the above inspiration, I have tested this solution to work. I also recommend the documentation be appended as well.

  - name: Move server to Servers OU
    microsoft.ad.object:
      identity: "{{ ansible_hostname }}$"
      name: "{{ ansible_hostname }}"
      path: OU=Domain Servers,OU=Site-Location,DC=ad,DC=cityofgotham,DC=com
      type: computer

All of these entries are needed at a minimum to get it to work. Oddly enough sometimes during my testing, it would create completely identical named computer objects that were disabled. That is less than desirable. Hopefully this will help someone.

jborean93 commented 3 weeks ago

You are correct in that using identity will just use the filter (sAMAccountName=$identity) and not add the $ to the end. I think the logic should be amended so that for resources that are known to end with $ it should automatically added to the filter string so you don't have to.

Thanks for looking into this and providing all that great detail!

jengstrom440 commented 3 weeks ago

So is the thought to change that so it appends a $ to the identity to see if it finds a match? Also could add the example to the documentation afterwards? Please let me know if I can do anything else to help, thanks!

jborean93 commented 3 weeks ago

Yea I think it makes sense that when we are dealing with an object type that is known to always end with $ it will automatically append $ if it's not already there when searching by sAMAccountName.

Documentation is also a great idea!