none
How can I get a list of Active Directory user accounts where "The user cannot change the password" attribute is NOT set?

    Question

  • I'm trying to pull a list of all our service accounts that do not have the user cannot change the password box checked.

    ug3j

    Wednesday, May 30, 2012 4:03 PM

Answers

  • Sorry, I just noticed that you are only interested in service accounts. If you can filter on just service accounts, this should reduce the work load considerably. If all of the service accounts you are interested in are user accounts, and they all have an SPN, you can replace the filter with the following:

    ' Filter on all user service accounts.
    strFilter = "(&(objectCategory=person)(objectClass=user)(servicePrincipalName=*))"

    -----

    Since computer accounts can also be service accounts, you may want to remove the first two clauses in the filter that restrict the script to user objects. Or, perhaps you have some other way to identify the service accounts of interest. Also, this only finds domain accounts, not local accounts. Many service accounts are local objects. Querying for local service accounts would be very time consuming, unless you only work with one computer.


    Richard Mueller - MVP Directory Services

    • Marked as answer by ug3j Wednesday, May 30, 2012 6:14 PM
    Wednesday, May 30, 2012 4:55 PM
    Moderator

All replies

  • What command are you trying, and/or what script language are you using?

    Bill

    Wednesday, May 30, 2012 4:39 PM
    Moderator
  • Whether or not a user can change their password is saved in the ntSecurityDescriptor attribute of the user object in AD. You cannot query for the setting. To find all users in the domain that can or cannot change their password, you must bind to every user object and their corresponding ntSecurityDescriptor attribute, then check all ACE's in the DACL for the possible deny ACE's for this permission. The following VBScript example displays the distinguished names of all users and either True (they can change their password) or False. This will be slow if there are many users in the domain. Testing for True or False and only outputing in one case will not make the script faster, but will reduce the output.

    Option Explicit

    Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
    Dim objRootDSE, strDNSDomain, strQuery, adoRecordset, strDN
    Dim objUser

    ' Setup ADO objects.
    Set adoCommand = CreateObject("ADODB.Command")
    Set adoConnection = CreateObject("ADODB.Connection")
    adoConnection.Provider = "ADsDSOObject"
    adoConnection.Open "Active Directory Provider"
    Set adoCommand.ActiveConnection = adoConnection

    ' Search entire Active Directory forest.
    Set objRootDSE = GetObject("LDAP://RootDSE")
    strDNSDomain = objRootDSE.Get("rootDomainNamingContext")
    strBase = "<LDAP://" & strDNSDomain & ">"

    ' Filter on all users.
    strFilter = "(&(objectCategory=person)(objectClass=user))"

    ' Comma delimited list of attribute values to retrieve.
    strAttributes = "distinguishedName"

    ' Construct the LDAP syntax query.
    strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
    adoCommand.CommandText = strQuery
    adoCommand.Properties("Page Size") = 200
    adoCommand.Properties("Timeout") = 30
    adoCommand.Properties("Cache Results") = False

    ' Run the query.
    Set adoRecordset = adoCommand.Execute

    ' Enumerate the resulting recordset.
    Do Until adoRecordset.EOF
        ' Retrieve values.
        strDN = adoRecordset.Fields("distinguishedName").Value
        strDN = Replace(strDN, "/", "\/")
        ' Bind to the object.
        Set objUser = GetObject("LDAP://" & strDN)
        ' Display user and whether they can change their password.
        Wscript.Echo objUser.distinguishedName & ";" & CanChgPwd(objUser)
        ' Move to the next record in the recordset.
        adoRecordset.MoveNext
    Loop

    ' Clean up.
    adoRecordset.Close
    adoConnection.Close

    Function CanChgPwd(ByVal objUser)
        ' Function to determine if a user can change their password.
        ' Returns True if the user can change their password, False othewise.

       Dim objSecDescriptor, objDACL, objACE

        Const CHANGE_PASSWORD_GUID = "{AB721A53-1E2F-11D0-9819-00AA0040529B}"
        Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100
        Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = &H6
        Const ADS_ACEFLAG_OBJECT_TYPE_PRESENT = &H1

        ' Bind to the user security objects.
        Set objSecDescriptor = objUser.Get("ntSecurityDescriptor")
        Set objDACL = objSecDescriptor.discretionaryAcl

        ' Search for ACE's for Change Password.
        CanChgPwd = True
        For Each objACE In objDACL
            If (UCase(objACE.Trustee) = "NT AUTHORITY\SELF") _
                    And (UCase(objACE.objectType) = CHANGE_PASSWORD_GUID) _
                    And (objACE.AceFlags = 0) _
                    And (objACE.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS) _
                    And (objACE.Flags =  ADS_ACEFLAG_OBJECT_TYPE_PRESENT) Then
                If (objACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT) Then
                    CanChgPwd = False
                    Exit For
                End If
            End If
            If (UCase(objACE.Trustee) = "EVERYONE") _
                    And (UCase(objACE.objectType) = CHANGE_PASSWORD_GUID) _
                    And (objACE.AceFlags = 0) _
                    And (objACE.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS) _
                    And (objACE.Flags =  ADS_ACEFLAG_OBJECT_TYPE_PRESENT) Then
                If (objACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT) Then
                    CanChgPwd = False
                    Exit For
                End If
            End If
        Next
    End Function

    -----

    As with most administrative scripts, this should be run at a command prompt using the cscript host program, so the output can be redirected to a text file.


    Richard Mueller - MVP Directory Services

    Wednesday, May 30, 2012 4:41 PM
    Moderator
  • ADO commands and vbscript.

    ug3j

    Wednesday, May 30, 2012 4:44 PM
  • Sorry, I just noticed that you are only interested in service accounts. If you can filter on just service accounts, this should reduce the work load considerably. If all of the service accounts you are interested in are user accounts, and they all have an SPN, you can replace the filter with the following:

    ' Filter on all user service accounts.
    strFilter = "(&(objectCategory=person)(objectClass=user)(servicePrincipalName=*))"

    -----

    Since computer accounts can also be service accounts, you may want to remove the first two clauses in the filter that restrict the script to user objects. Or, perhaps you have some other way to identify the service accounts of interest. Also, this only finds domain accounts, not local accounts. Many service accounts are local objects. Querying for local service accounts would be very time consuming, unless you only work with one computer.


    Richard Mueller - MVP Directory Services

    • Marked as answer by ug3j Wednesday, May 30, 2012 6:14 PM
    Wednesday, May 30, 2012 4:55 PM
    Moderator
  • Richard,

    It worked, thank you very much!!


    ug3j

    Wednesday, May 30, 2012 6:16 PM