none
Searching Active Directory for Enabled/Disabled accounts using VB Script

    質問

  • Hi Guys,

    I'm having a bit of an issue while trying to query users in Active Directory, so I'm hoping someone here might be able to lend me a bit of a hand. I am using a combination of PowerShell and VB Script to do an extraction of users from AD, and then quering the accounts in specific OUs to see if the account exists, and if the account is enabled or disabled as part of an automation process for accounts.

    The PowerShell extraction works great - we're basing the extraction off the employeeID and the samAccountName values. PowerShell extracts all accounts and places the information into a flat file to be read in by the VB Script. The VB Script looks at a SQL Database (extracted from PeopleSoft) which contains all the employee information, including employment status, etc. and then checks to see if these accounts exists by parsing the PowerShell extraction.

    The PowerShell script works great, but we haven't upgraded our DCs to Windows 2008, so some of the functionality of PowerShell is limited, so we're trying to fall back on VB to help compare the users in AD to complete this process. For the most part, the script works as expected, unless the account actually exists outside the OUs we have it check for. The Domain is called USR (for User). Here's an example of what we're looking at:

    OUs to be scanned:

    USR-Users

    USR-Disabled

    USR-Disabled-2011

    The script uses an array with the Domain OUs listed in them, where the last OU uses Year(Date) - 1 to capture the previous year's disabled accounts. All this works as expected. What I am having problems with is that when the function it calls to actually scan the specific OUs for existing accounts, it comes back with accounts that reside OUTSIDE the OUs specified by the query. We have an OU for new accounts that need to be completed before being issued to the user called USR-New. The script is finding accounts in that OU when it is not specified in the search OUs:

    Actual Account OU: ou=USR-New,dc=usr,dc=net,dc=icc UserID:RF849

    Where it says it finds it: ou=USR-Users,dc=usr,dc=net,dc=icc UserID:RF849

    Log report:

    Match: 1331463 -> 1331463 = RF849
    Searching: Domain:ou=USR-Users,dc=usr,dc=net,dc=icc UserID:RF849 EmpID:1331463
    Query: SELECT ADsPath FROM 'LDAP://ou=USR-Users,dc=usr,dc=net,dc=icc' WHERE objectCategory='user' AND name='RF849'
    User: RF849 EmplID: 1331463 RM: False - Account is already active!
    Searching Domain:ou=USR-Disabled,dc=usr,dc=net,dc=icc UserID:RF849 EmpID:1331463
    Query: SELECT ADsPath FROM 'LDAP://ou=USR-Disabled,dc=usr,dc=net,dc=icc' WHERE objectCategory='user' AND name='RF849'
    User: RF849 EmplID: 1331463 RM: True - Account not found in this OU!
    Searching Domain:ou=USR-Disabled-2011,dc=usr,dc=net,dc=icc UserID:RF849 EmpID:1331463
    Query: SELECT ADsPath FROM 'LDAP://ou=USR-Disabled-2011,dc=usr,dc=net,dc=icc' WHERE objectCategory='user' AND name='RF849'
    User: RF849 EmplID: 1331463 RM: True - Account not found in this OU!

    The function we use is:

    '################################################### Function accountStatus( strDomain, strUser ) Const ADS_SCOPE_SUBTREE = 0 Const ADS_UF_ACCOUNTDISABLE = 2 Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.Properties("Page Size") = 10000 objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE strDomain = domainOU(x):strUser = lineParse(1) If strDomain = "" Or strUser = "" Then logOut.WriteLine "Invalid Domain/User - Domain: " & strDomain & " User: " & strUser End If objCommand.CommandText = "SELECT ADsPath FROM 'LDAP://" & strDomain & "' WHERE objectCategory='user' AND name='"_

    & strUser & "'" logOut.WriteLine "Query: " & objCommand.CommandText Set objRecordSet = objCommand.Execute Do Until objRecordSet.EOF logOut.WriteLine "SELECT ADsPath FROM 'LDAP://" & strDomain & "' WHERE objectCategory='user' AND name='" & strUser _

    & "' RV:" & objRecordSet.Fields("ADsPath").Value UserVar = objRecordSet.Fields("ADsPath").Value 'On Error Resume Next Dim objUSer Set objUser = GetObject(Uservar) If objUser.AccountDisabled = False Then logOut.WriteLine = "Account Enabled!" Else logOut.WriteLine = "Account Disabled!" End If accountStatus = objUser.AccountDisabled objRecordSet.MoveNext Loop objRecordSet.Close objConnection.Close End Function

    I have extra statements that write data out to a test output log file so I can track how each portion of the script is running for troubleshooting. None of the information inside the do-loop shows up in the report, and for some reason, it is still finding the accounts in the USR-New OU when it should be limiting the report to the 3 OUs specified in the script. Any ideas as to why it would be looking outside the OUs we listed and finding the account anyway? Thanks for your help on this!

    2012年2月17日 18:56

回答

  • As a test case use this code in PowerShell to prove that the OUs and AD is working correctly.

    # reports user status and rreturns user objkect for further reporting. function Get-AccountStatus{ [CmdLetBinding()] Param( [string]$domain='', [string]$samaccountname='Guest' ) $se=[adsisearcher]([adsi]$domain) $se.Filter="samaccountname=$samaccountname" $user=$se.FindOne() if($user){ $user=$user.GetDirectoryEntry() Write-Verbose "Account disabled=$($user.AccountDisabled) Parent=$($user.Parent)" $user }else{ Write-Verbose "User not found $domain - $samaccountname" } } Get-AccountStatus -sam user01 -dom 'LDAP://ou=some ou,dc=dom,dc=local' -v

    The above will take an out wher it say -dom.  The -v gets messages.  The code returns the user if founf in the OU.  It can be modified to do almost anything.  We can even send an array of OUs and test the users parent against the dn of the ou array.

    Run this at a PowerShell prompt to prove that you are getting correct values.  Using samacaount name is what you need as 'Name' and 'CN" are not unique.


    ¯\_(ツ)_/¯

    • 回答としてマーク Jim Sprague 2012年2月17日 20:52
    2012年2月17日 20:31

すべての返信

  • I'm having a hard time following the explanation of what you're trying to do but if the core of the problem is related to an OU that is being scanned that isn't supposed to be, from what you posted of your script, I would be looking at the contents of "strDomain" each time the function was invoked.

    It sounds like you're either passing one too many OUs to the function itself OR you're passing an OU that is hierarchically higher in your AD structure and because your LDAP search scope is set to "subtree", the query will grab all users from all OUs under the DN of the OU that gets passed.

    2012年2月17日 19:29
  • THe log you posted does not match teh code you posted.  It is similar bit seem to be missing something.

    Why don't you just clearly state what it is you are trying to do?  It looks like way too much code for the results.

    Where are you using PowerShell?  I don't see an PowerShell code.

    Did you know that you can filter a query for disabled accounts so you can retrieve only accounts that are disable of enable as needed.

    You are recreating the recordswet and connection for every call to the function.  This is going to be  slow and error prone.

    You can just change the name and re-execute the query.  No need to rebuild the whole connection each time.

    I keep rereading your explanation.  Each time I read it I get more confused.

    Maybe you should start by askingone question instead of a battery of questions.  Remember that you are the only one who knows what you want.  It is up to you to communicate that clearly.


    ¯\_(ツ)_/¯

    2012年2月17日 19:47
  • Another though in keeping with thepip3r's concerns.

    You can only search on one item at a time or in one location.

    If you choose user then you cannot easily use OU.

    To search on multiple OUs will not work in this version of LDAP so you are better off searching on user. If you are searching on a a user then check the OU against targets. The OU is the parent object of the user object.

    All of this would be much easier in PowerShell.


    ¯\_(ツ)_/¯

    2012年2月17日 19:51
  • Thanks for the reply. I'm just listing the function rather than the entire script because it's the focal point of the problem at this point. All of the OUs sit at the same level. The function calls to the specific OU; the subtree allows for it to look at sub OUs for specialty groups, etc. that may exist under the one being called. I did list the specific OUs in my initial post:

    Actual Account OU: ou=USR-New,dc=usr,dc=net,dc=icc UserID:RF849
    Where it says it finds it: ou=USR-Users,dc=usr,dc=net,dc=icc UserID:RF849
    So all of the OUs sit at the same level, but are called upon specifically by the function so that it scans that specific OU and its sub OUs. You said that I have my LDAP Search Scope set to subtree - I've tried restricting it by changing ADS_SCOPE_SUBTREE = 0 so that it only looks at the root folder specified and I still get the same results. Do I need to set this somewhere else to restrict it? Thanks again!


    - Jim

    2012年2月17日 19:54
  • so in your function, I'd actually WScript.Echo out your strDomain -- don't rely on your log file. Verify the location of the account in ADUC to see the right answer.  And for the log entries you keep posting, I don't see any area of that function that would generate those lines.  It appears that you're dealing with a different part of the script.  The part of the script you posted above appears to be irrelevant.  I say that because even though you call it a "function", in vbs terms, it's really a sub-routine because you're not returning any values.  As such, I'd say that we'd need to see the code surrounding the area that generates the log entries for the lines that you posted in your reply. 

    At this point, I would blindly wager that you have a variable scope issue.  I imagine that you're running this against a number of users.  I would also imagine this is happening against multiple users as well.  If this is the case, verify that if you're using a global variable, you're not writing the value of the previous iteration of your loop of users to the log file.  As in, your script might be erroring out and continuing (with on error resume next) but using a previous value that's in a variable that wasn't properly wiped before writing to the log file. 

    Again though, this is all speculation because I'm with jrv, it doesn't seem that we're looking at the right piece of code per the logs entries you're posting.

    2012年2月17日 20:21
  • As a test case use this code in PowerShell to prove that the OUs and AD is working correctly.

    # reports user status and rreturns user objkect for further reporting. function Get-AccountStatus{ [CmdLetBinding()] Param( [string]$domain='', [string]$samaccountname='Guest' ) $se=[adsisearcher]([adsi]$domain) $se.Filter="samaccountname=$samaccountname" $user=$se.FindOne() if($user){ $user=$user.GetDirectoryEntry() Write-Verbose "Account disabled=$($user.AccountDisabled) Parent=$($user.Parent)" $user }else{ Write-Verbose "User not found $domain - $samaccountname" } } Get-AccountStatus -sam user01 -dom 'LDAP://ou=some ou,dc=dom,dc=local' -v

    The above will take an out wher it say -dom.  The -v gets messages.  The code returns the user if founf in the OU.  It can be modified to do almost anything.  We can even send an array of OUs and test the users parent against the dn of the ou array.

    Run this at a PowerShell prompt to prove that you are getting correct values.  Using samacaount name is what you need as 'Name' and 'CN" are not unique.


    ¯\_(ツ)_/¯

    • 回答としてマーク Jim Sprague 2012年2月17日 20:52
    2012年2月17日 20:31
  • Thanks, jrv!

    I believe I can work with this as a solution without having to rely on VB to do what we need. I didn't think this functionality was available without the 2008 functionality on the domain! Thank you both for your help!


    - Jim

    2012年2月17日 20:54
  • Thanks, jrv!

    I believe I can work with this as a solution without having to rely on VB to do what we need. I didn't think this functionality was available without the 2008 functionality on the domain! Thank you both for your help!


    - Jim

    You will find that all things are easier in PowerShell. 

    If you have VBscript code that works as needed then use it but when in POwerSHell it is usually esier to recreate the code than it is to fix old scripts.

    In WS2003 domains use Quest CmdLets if you re not a wiz at AD as they make D mamangement much easier.  If in AD2008 then use the MS CMdLets as they are a better match although the Quest CmdLets still work and work well.


    ¯\_(ツ)_/¯

    2012年2月17日 21:04