ansible-collections / microsoft.ad

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

Remove-ADObject: The object class of the instance object must contain 'top'. when trying to delete a computer account #147

Closed murrahjm closed 1 month ago

murrahjm commented 2 months ago
SUMMARY

When trying to delete a computer account with the microsoft.ad.computer module and it has leaf objects (bitlocker keys I think), it fails with the error Remove-ADObject: The object class of the instance object must contain 'top'. This is reproduceable in native powershell when trying to delete the leaf objects, not the computer object itself.

ISSUE TYPE
COMPONENT NAME

microsoft.ad.computer

ANSIBLE VERSION
ansible [core 2.15.0]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/runner/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /runner/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.18 (main, Sep 22 2023, 17:58:34) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20)] (/usr/bin/python3.9)
  jinja version = 3.1.2
  libyaml = True
COLLECTION VERSION
Collection   Version
------------ -------
microsoft.ad 1.6.0
CONFIGURATION
CONFIG_FILE() = /etc/ansible/ansible.cfg
OS / ENVIRONMENT

Windows Server 2019 Standard

STEPS TO REPRODUCE

Create a playbook that attempts to delete a computer object with bitlocker keys.

    - name: delete computers in delete list
      microsoft.ad.computer:
        identity: "{{computer_distinguishedName }}"
        state: absent
        domain_password: "{{ password }}"
        domain_username: "{{ username }}"
EXPECTED RESULTS

Expected results would be for the computer account to be deleted

ACTUAL RESULTS

Task fails with a stack trace:

"exception": "The object class of the instance object must contain 'top'.\r\nAt line:1143 char:26\r\n+                     $_ | Remove-ADObject @removeParams @adParams\r\n+                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n    + CategoryInfo          : InvalidArgument: (CN=2018-04-05T1...DC=eprod,DC=com:ADObject) [Remove-ADObject], ArgumentException\r\n    + FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.RemoveADObject\r\n\r\nScriptStackTrace:\r\nat <ScriptBlock>, <No file>: line 1143\r\nat Invoke-AnsibleADObject, <No file>: line 1137\r\nat <ScriptBlock>, <No file>: line 150\r\n\r\nSystem.ArgumentException: The object class of the instance object must contain 'top'.\r\n   at Microsoft.ActiveDirectory.Management.Commands.ADFactory`1.ValidateObjectClass(T identityObj)\r\n   at Microsoft.ActiveDirectory.Management.Commands.ADFactory`1.GetDirectoryObjectFromIdentity(T identityObj, String searchRoot, Boolean showDeleted)\r\n   at Microsoft.ActiveDirectory.Management.Commands.ADRemoveCmdletBase`3.ADRemoveCmdletBaseProcessCSRoutine()\r\n   at Microsoft.ActiveDirectory.Management.CmdletSubroutinePipeline.Invoke()\r\n   at Microsoft.ActiveDirectory.Management.Commands.ADCmdletBase`1.ProcessRecord()",

This appears to be an issue with this line: https://github.com/ansible-collections/microsoft.ad/blob/b8a7a231d4e82fac8e385dc8bf3c3a3fded3c243/plugins/module_utils/_ADObject.psm1#L1170

Reproducing this in powershell shows that this is an issue with bitlocker leaf objects:

get-adobject -filter * -searchbase $objectdn | ?{$null -eq $_.objectclass}

DistinguishedName                                                                                                    Name ObjectClass ObjectGUID
-----------------                                                                                                    ---- ----------- ----------
CN=2020-01-20T18:11:08-06:00{1DDFDEBA-4427-43D1-877F-429BFD600937},CN=redacted,OU=Disabled Computers,DC=eprod,DC=com
CN=2023-03-15T10:12:07-06:00{D0FED8B6-8F82-4831-B0D7-DF6CEA26F731},CN=redacted,OU=Disabled Computers,DC=eprod,DC=com
CN=2021-04-30T10:02:41-06:00{F1FC2B25-10B3-4285-8E88-D02A3895CCCD},CN=redacted,OU=Disabled Computers,DC=eprod,DC=com

get-adobject -filter * -searchbase $objectdn | ?{$null -eq $_.objectclass} | remove-adobject -Credential $adcred
Remove-ADObject: The object class of the instance object must contain 'top'.
Remove-ADObject: The object class of the instance object must contain 'top'.
Remove-ADObject: The object class of the instance object must contain 'top'.

Note that in powershell, specifying the identity parameter instead of using the pipeline seems to work:

remove-adobject -Identity 'CN=2023-05-11T17:10:54-06:00{5291E9ED-211C-4C4F-970F-BEA048590D3C},CN=redacted,OU=Disabled Computers,DC=eprod,DC=com'
jborean93 commented 2 months ago

Thanks for the bug report, I'll have to find a way to set these bitlocker keys for testing. Appreciate the excellent information you've provided that covers this issue.

jborean93 commented 2 months ago

I've tried testing this out by creating a mock bitlocker key object to a dummy computer object and it works fine.

- name: create computer object
  microsoft.ad.computer:
    name: TestComputer
    state: present
  register: create_comp

- name: add recovery leaf object
  ansible.windows.win_powershell:
    parameters:
      Path: '{{ create_comp.distinguished_name }}'
    script: |
      param([string]$Path)

      $ErrorActionPreference = 'Stop'

      $keyGuid = [Guid]::NewGuid()
      $recoveryParams = @{
          Name = "$((Get-Date).ToString('yyyy-MM-ddThh:mm:sszzz')){$($keyGuid.ToString().ToUpperInvariant())}"
          Path = $Path
          Type = 'msFVE-RecoveryInformation'
          OtherAttributes = @{
              'msFVE-RecoveryGuid' = $keyGuid.ToByteArray()
              'msFVE-RecoveryPassword' = '1234'
          }
      }
      New-ADObject @recoveryParams

- name: remove computer
  microsoft.ad.computer:
    name: TestComputer
    state: absent

The error you are receiving somewhat indicates to me that the object might be potentially malformed. If you open up the attribute editor for the key object do you see top in objectClass?

image

Note: I had to do View -> User, Contacts, Groups, and Computers as containers to see it.

murrahjm commented 2 months ago

ok I think this might be a permissions issue. When I check the objects I see no objectclass at all. but I went and had our domain admins check it and they look right to him.

image

I'm going to do some tests with different permissions and will report back.

murrahjm commented 1 month ago

hey sorry for the delay, but I can confirm that this was an issue with my permissions. I had full control of the objects only, and nothing to the child objects, which is why I couldn't read the objectClass or any other property.

jborean93 commented 1 month ago

No worries, thanks for confirming and sharing the info.