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

microsoft.ad.object_info cant get user_cannot_change_password and password_never_expires attributes #169

Open
roumano opened this issue Dec 10, 2024 · 3 comments

Comments

@roumano
Copy link

roumano commented Dec 10, 2024

SUMMARY

The module microsoft.ad.user permit to set these "security" attribut :

enabled
user_cannot_change_password
password_never_expires

But it's no way to get/see these informations from microsoft.ad.object_info, even with properties: *

With the previous module (deprecated) it's was possible, as exemple with :

win_domain_user:
  state: query  
  search_base: "OU=People"
  ldap_filter: "CN={{ uid }}"

It's important for us as it's permit to make a listing of active account/locked account

ISSUE TYPE
  • Feature Idea
COMPONENT NAME

object_info

ADDITIONAL INFORMATION

It's permit to improve the security of our AD as we can have a listing of enable/disable accounts

  • previous module deprecated :
   - name: "Look of a the account in Active Directory"
     win_domain_user:
       domain_server: "{{ AD_Domain }}"
       domain_username: "{{ AD_username }}"
       domain_password: "{{ AD_password }}"
       path: "OU=People,{{ AD_DC }}"
       name: "{{ uid }}"
       state: query
     register: ad_before
  • new module where the information is not available :
    - name: "Look of the account in Active Directory"
      microsoft.ad.object_info:
        domain_server: "{{ AD_Domain }}"
        domain_username: "{{ AD_username }}"
        domain_password: "{{ AD_password }}"
        search_base: "OU=People,{{ AD_DC }}"
        ldap_filter: "CN={{ uid }}"
        properties: '*'
      register: ad_before
@jborean93
Copy link
Collaborator

The object_info module is generic for AD objects using Get-ADObject so any special properties returned by Get-ADUser won't be there. In this case the user_cannot_change_password and password_never_expires attribute is actually stored as a bitmask value inside userAccountControl. By requesting the userAccountControl property you'll get two values back; userAccountControl and userAccountControl_AnsibleFlags with the former being the raw integer value and the latter being a list of bit flags that are set.

As an example

ok: [SERVER2022] =>
    changed: false
    invocation:
        module_args:
            domain_password: VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
            domain_server: null
            domain_username: [email protected]
            filter: null
            identity: null
            include_deleted: false
            ldap_filter: sAMAccountName=vagrant-domain
            properties:
            - userAccountControl
            search_base: null
            search_scope: null
    objects:
    -   DistinguishedName: CN=vagrant-domain,CN=Users,DC=domain,DC=test
        Name: vagrant-domain
        ObjectClass: user
        ObjectGUID: 6c708e25-10b7-4d3d-bfee-ca9b19e09413
        userAccountControl: 66048
        userAccountControl_AnsibleFlags:
        - ADS_UF_NORMAL_ACCOUNT
        - ADS_UF_DONT_EXPIRE_PASSWD

You can see the list of flag values at https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_user_flag_enum with user_cannot_change_password and password_never_expires being ADS_UF_PASSWD_CANT_CHANGE and ADS_UF_DONT_EXPIRE_PASSWD respectively.

To add that in as a check you could do something like

- name: get account information
  microsoft.ad.object_info:
    ...
    properties:
    - userAccountControl
  register: account_info

- name: verify account cannot change password and is set to not expire
  assert:
    that:
    - account_info.objects | length == 1
    - '"ADS_UF_PASSWD_CANT_CHANGE" in account_info.objects[0].userAccountControl_AnsibleFlags'
    - '"ADS_UF_DONT_EXPIRE_PASSWD" in account_info.objects[0].userAccountControl_AnsibleFlags'

In the future we may add a user_info module that reflects the input options for the user module but for now we've kept object_info agnostic of the type and thus are limited to what Get-ADObject returns.

@roumano
Copy link
Author

roumano commented Dec 11, 2024

Thanks for these useful information !

on our AD server, i able to get ADS_UF_ACCOUNTDISABLE and ADS_UF_DONT_EXPIRE_PASSWD but not the ADS_UF_PASSWD_CANT_CHANGE.
Do you think , it's only available since a recent version of the Active Directory ?

@jborean93
Copy link
Collaborator

Sorry I misspoke, the -CannotChangePassword parameter that user_cannot_change_password is exposing isn't actually done through the userAccountControl flags. It's actually setting the security descriptor and adding in a DENY rule for change password on SELF. Unfortunately exposing that information is not very human friendly. You have two options available to you right now

  • Use a win_shell or win_powershell task to call Get-ADUser and have that return the information you want
- ansible.windows.win_powershell:
    script: |
      [CmdletBinding()]
      param($Identity)

      $Ansible.Changed = $false
      Get-ADUser -Identity $Identity -Properties CannotChangePassword, PasswordNeverExpires
    parameters:
      Identity: '{{ user.distinguished_name }}'
  register: user_info

- debug:
    msg: |
      CannotChangePassword: {{ user_info.output[0].CannotChangePassword }}
      PasswordNeverExpires: {{ user_info.output[0].PasswordNeverExpires }}
  • Request the ntSecurityDescriptor property and see if the deny entry is present

The SDDL ACE entry for controlling user_cannot_change_password is (OD;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS). I believe the first ;CR; flag may be optional and dependent on the domain controller version but breaking it down this is what the SDDL means

  • OD - SDDL_OBJECT_ACCESS_DENIED
  • CR - SDDL_CRITICAL
  • ab721a53-1e2f-11d0-9819-00aa0040529b - User-Change-Password right
  • PS - SDDL_PERSONAL_SELF

So to turn that into a check you can use regex to search for that SDDL entry which looks something like:

- microsoft.ad.object_info:
    identity: '{{ user.distinguished_name }}'
    properties:
    - userAccountControl
    - ntSecurityDescriptor
  register: user_info

- debug:
    msg: |
      CannotChangePassword: {{ user_info.objects[0].ntSecurityDescriptor is search("\(OD;;(?:CR)?;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS\)") }}
      PasswordNeverExpires: {{ "ADS_UF_DONT_EXPIRE_PASSWD" in user_info.objects[0].userAccountControl_AnsibleFlags }}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants