A need has arisen to implement asset tagging on Linux objects in Active Directory. I have been testing with the module named in the subject line, but I keep getting this error:
“New-ADObject failed: An attempt was made to add an object to the directory with a name that is already in use.”
This network is isolated, so getting the full error output (-vvv) is a little difficult. The documentation suggests that editing an existing Active Directory computer object is possible.
microsoft.ad.object module – Manage Active Directory objects — Ansible Documentation
Does anyone have any experience with this particular module, or any suggestions to try for getting past this error?
Probably going to need more information. Can you share a snippet of the code you’re using? Are you running this module against a linux or a windows target (not the AD object)? Have you tried using microsoft.ad.computer module – Manage Active Directory computer objects — Ansible Documentation instead?
1 Like
Below is the code from my test playbook, with some fields sanitized. Previously we had a requirement to update just the description field, so we were using win_domain_computer, but that module is very limited in what it can do. The vars at the bottom are required for that module to work, so I assume they are needed for most microsoft.ad modules, which is why I left them in place. I did try using microsoft.ad.computer, but was not able to get it to work either. The hosts are Linux, which use adcli and sssd to domain-join and authenticate users against an Active Directory infrastructure.
**Please ignore any format issues, since I hand-typed this into this message.
- hosts: all
gather_facts: true
tasks:
- name: Test microsoft.ad module
microsoft.ad.object:
domain_server: ‘{{ ad_preferred_dc }}’
domain_username: ‘{{ domain_username }}’
domain_password: ‘{{ domain_password }}’
name: ‘{{ inventory_hostname }}’
identity: ‘{{ inventory_hostname }}’
path: ‘{{ ad_computer_ou }}’
attributes:
set:
ENVIRONMENT: ‘[env_name]’
PLATFORM: ‘Rocky8]’
BRANCH: ‘[branch_name]’
type: computer
state: present
delegate_to: ‘{{ ad_preferred_dc }}’
vars:
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
ansible_winrm_transport: ntlm
ansible_port: [port]
ansible_become: no
ansible_become_method: runas
ansible_user: ‘{{ domain_username }}’
ansible_password: ‘{{ domain_password }}’
ansible_become_flags: logon_type=interactive logon_flags=with_profile
ignore_errors: yes
First, I don’t see any module argument that you’re using for microsoft.ad.object that isn’t in microsoft.ad.computer. I do however see that using both the name and identity arguments may attempt to name, rename, or move an object if the name does not match the property found by identity (if one is found at all, then we would try to create a new object by that name). The ‘name’ argument appends ‘$’ to the value to determine the sAMAccountName. Some other arguments, such as identity, don’t appear to do this. So, your name and identity will never both match “{{ inventory_hostname }}”, and thus trigger creating a New-ADObject using a name that already exists.
Assuming inventory_hostname is the short hostname and not the fqdn, try something like this:
- hosts: all
gather_facts: true
tasks:
- name: Test microsoft.ad module
microsoft.ad.object:
domain_server: "{{ ad_preferred_dc }}"
domain_username: "{{ domain_username }}"
domain_password: "{{ domain_password }}"
name: "{{ inventory_hostname }}"
identity: "{{ inventory_hostname }}$" # appending the '$' should force searching for sAMAccountName's
sam_account_name: "{{ inventory_hostname }}" # sam_account_name appends missing '$' automatically
path: "{{ ad_computer_ou }}"
attributes:
set:
ENVIRONMENT: "[env_name]"
PLATFORM: "[Rocky8]"
BRANCH: "[branch_name]"
type: computer
state: present
delegate_to: "{{ ad_preferred_dc }}"
vars:
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
ansible_winrm_transport: ntlm
ansible_port: [port]
ansible_become: no
ansible_become_method: runas
ansible_user: "{{ domain_username }}"
ansible_password: "{{ domain_password }}"
ansible_become_flags: logon_type=interactive logon_flags=with_profile
ignore_errors: yes
The changes you suggested worked when I tested them, so thanks for that. However, when I changed the hard-coded key-pair values for variables it complains about undefined variables. For example:
POC-EMAIL: group-name@outlook.com
vs
POC-EMAIL: ‘{{ cm_poc_email }}’
That and other key-pair variables are defined in the group_vars for this particular set of workstations, so they are defined somewhere. Does this particular module require hard-coded key-pair values?
I think you might be using single quotes when you should be using double-quotes. Single quotes tell Ansible to use literal strings, so ‘{{ cm_poc_email }}’ is literally {{ cm_poc_email }}
, while “{{ cm_poc_email }}” expands to group-name@outlook.com
.
1 Like
You might also be running into issues with how delegate_to
handles variables relating to the host. The vars you’re using may be undefined in this context. In this case, you may need to use "{{ hostvars[inventory_hostname]['cm_poc_email'] }}"
.
1 Like
I wanted to revisit this topic for one more question. Currently the service account that I/Ansible uses to create and join VM’s to the Windows domain sits in the ‘Domain Admins’ Active Directory group. The lead engineer for the Windows team wants to remove that service account from that group, but doing so prevents object attributes from being edited inside the computer object. With the account out of the Domain Admins group, builds will complete if I comment out the Microsoft.ad.object task within the role, so it seems that module is looking for a specific set of permissions.
The error on the Ansible-side, when the service account is out of the Domain Admins group, is: ntlm: the specified credentials were rejected by the server.
Are there changes that need to be made, on either the Linux or Windows side, to allow this service account the ability to create/edit object attributes (without adding it back to the AD group)?
I’ve seen this recommended. I checked with our Windows team and this registry key doesn’t currently exist on our domain controllers. Does anyone have any direct experience with this?
The error you’re seeing is coming from WinRM denying your remote session. The service account needs to be a local administrator of the Windows computer (not necessarily a “domain admin”). Since the Domain Admins are local admins of the DC’s, your delegate_to: "{{ ad_preferred_dc }}"
worked as expected while your service account was in the DA’s group.
An ideal solution would be to use a non-dc Windows server to delegate these tasks to. By default, you would need to add the service account as a local admin to this server, but if you want to take “best practices” even further, you could configure custom session configurations/endpoints for powershell to use. The custom sessions would allow you to restrict what powershell commands/modules are available, as well as what users/groups are allowed to connect to said session configs.
I would start with delegating the task to a non-dc server with the service account in the local administrators group of said server, and make sure things work. Something to keep in mind is the second hop scenario, which the microsoft.ad.object module may workaround for you since it does ask for domain username/password. If everything works as expected, then entertain the custom session configurations. (you would also need to switch Ansible connection plugins from ansible.builtin.winrm
to ansible.builtin.psrp
to be able to specify which configuration to use via ansible_psrp_configuration_name
)
1 Like