Announcement

Collapse
No announcement yet.

Error coming up when no groups found.

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Error coming up when no groups found.

    Where do i start.

    We are having a clear out of our AD domain, believe it or not but we have 500 users and 450 groups, so i was writing, well stealing really, a script whereby i can get a list of all the domain users, dump that to a file and then use the file to then enter the distinguished name so i can check for groups.

    Bit long winded i know but it works for me and i can tweak it at a later date.

    So what happens is that when i come across a user the script bombs out because the memberOf protion in AD is empty.

    I have some code that sort of works around it a little bit but it will only do the first hit before failing on me.

    The error code is 424.

    The code is

    Code:
    'open the data file
    Set objTextStream = objFSO.OpenTextFile(".\user.txt")
    'make an array from the data file
    arrUser = Split(objTextStream.ReadAll, vbNewLine)
    'close the data file
    objTextStream.Close
    
    For i = LBound(arrUser) To UBound(arrUser)
    	'Connect to AD as user
    	strLDAP = "LDAP://" & arrUser(i)
    	Set objUser = GetObject(strLDAP)
    	objmemberOf  = objUser.GetEx("memberOf")
    	Do while err.number <> 424
    		If err.number = vbEmpty then
    			For Each objGroup in objmemberOf 
    				objGroup = Mid(objGroup, 4, 330) 
    				arrGroup = Split(objGroup, "," )
    				strList = strList & arrGroup(0) & vbCr
    			Next
    		End If
    	Loop
    	wscript.echo strList
    Next
    I'm still working on this so it may change and any changes i make i will post up new code.

    This is the part that i can't get to work and everything else works perfectly.

  • #2
    Re: Error coming up when no groups found.

    This is a common problem when dealing with attributes that can be multi-valued.
    http://www.rlmueller.net/MemberOf.htm

    If such attribute is multi-valued (contains more that two values) you must tread it like an array, you can use Join() or For-Next. But when it contains just one or no values then you have to tread it like a string, then you are not allowed to use Join() and For-Next. (Note: the Primary Group is not counted in this attribute)

    That is why you use the GetEx method to retrieve memberOf. This method will return also a "Variant()"-type value instead of a "String" when there is only one group.
    But (!) this method still is returning an error when the memberOf is "Empty".
    You must trap the raised err.number to handle this.
    But you already knew all of that, you just forgot to enable Error trapping (On Error Resume Next) first.

    Try something like this:
    Code:
    
    'open the data file
    Set objTextStream = objFSO.OpenTextFile(".\user.txt")
    'make an array from the data file
    arrUser = Split(objTextStream.ReadAll, vbNewLine)
    'close the data file
    objTextStream.Close
    
    For i = LBound(arrUser) To UBound(arrUser)
                    strUserPath = "LDAP://" & arrUser(i)
                    'Connect to AD as user
                    Set objUser = GetObject(strUserPath)
                    strUserName = objUser.cn   ' or:  Mid(Split(arrUser(i), "," )(0), 4 )
    
                    On Error Resume Next
                    colMemberof  = objUser.GetEx("memberOf")
                                 If Not CBool(Err.Number) Then
                                   On Error GoTo 0
                                   For Each objGroup in colMemberof
                                      strGroup = Mid(Split(objGroup, "," )(0), 4 ) 
                                      strList = strList & strGroup & vbCr
                                   Next
                                 End If
                    On Error GoTo 0
                    call CustomBox(strList, strUserName)
                    strList = Empty
    Next
    
    Wscript.Quit
    
    
    
    'open InternetExplorer and create a window:
    Function CustomBox(sResults, sTitle) 
      '---------------------------------- 
       Set colItems = GetObject("Winmgmts:\\.\root\cimv2").ExecQuery _ 
          ("Select ScreenWidth,ScreenHeight From Win32_DesktopMonitor") 
       For Each objItem in colItems 
          intHorizontal = objItem.ScreenWidth 
          intVertical = objItem.ScreenHeight 
       Next 
       Set colItems = Nothing 
      '---------------------------------- 
      'Convert input string to a valid HTML string 
       sResults = Replace(sResults,"&","&",1,-1,vbTextCompare) 
       sResults = Replace(sResults,Chr(34),"""",1,-1,vbTextCompare) 
       sResults = Replace(sResults,">",">",1,-1,vbTextCompare) 
       sResults = Replace(sResults,"<","<",1,-1,vbTextCompare) 
       sResults = Replace(sResults, vbNewLine, "<br>") 
      '---------------------------------- 
      set oIE = CreateObject("InternetExplorer.Application") 
      'http://msdn2.microsoft.com/en-us/library/aa768398.aspx 
      With oIE 
       .RegisterAsDropTarget = False 
       .Resizable = True : .FullScreen = False 
       .AddressBar = False 
       .MenuBar = False 
       .ToolBar = False 
       .StatusBar = False 
       .width = 600 : .height = 800 
       .Left = (intHorizontal - .width) / 2 
       .Top = (intVertical - .height) / 2 
       .Navigate "about:blank" 
       Do Until .ReadyState = 4 : WScript.Sleep 100 : Loop 
       .document.open 
       .document.write _ 
         "<html><head><" & "script>bboxwait=true;</" _ 
         & "script><title>" & sTitle & "</title></head>"_ 
         & "<body bgColor=Silver" _ 
         & " style='border-Style:outset;border-" _ 
         & "Width:3px'>" _ 
         & "<pre>"& sResults &"</pre>" 
      .document.close 
      '---------------------------------- 
       'set window focus 
       .Visible = True 
       Do Until .ReadyState = 4 : WScript.Sleep 100 : Loop
       WScript.CreateObject("WSCript.shell") _
        .AppActivate "http:/// - " & .document.Title  ' IE version 7.0
      End With '---(oIE)--- 
    End Function
    
    The line(s) On Error GoTo 0 disables error handling again. It is recommended not having Error trapping enabled when you don't expect errors in the script, to make a script more reliable, and is also better for trouble shouting purpose.

    If Not CBool(Err.Number) is the same as If (Err.Number = 0)

    Instead of the line: wscript.echo strList, I called a function 'CustomBox' in the example. This will cause the script to continue after each msgbox and leave all the msgboxes open. And when using this function you'll be able to copy the content of the msgboxes is needed.


    \Rems
    Last edited by Rems; 11th October 2007, 22:32. Reason: Replaced: Do While (.Busy) : Wscript.Sleep 100 : Loop

    This posting is provided "AS IS" with no warranties, and confers no rights.

    __________________

    ** Remember to give credit where credit's due **
    and leave Reputation Points for meaningful posts

    Comment


    • #3
      Re: Error coming up when no groups found.

      Originally posted by Rems View Post
      This is a common problem when dealing with attributes that can be multi-valued.
      http://www.rlmueller.net/MemberOf.htm

      If such attribute is multi-valued (contains more that two values) you must tread it like an array, you can use Join() or For-Next. But when it contains just one or no values then you have to tread it like a string, then you are not allowed to use Join() and For-Next. (Note: the Primary Group is not counted in this attribute)

      That is why you use the GetEx method to retrieve memberOf. This method will return also a "Variant()"-type value instead of a "String" when there is only one group.
      But (!) this method still is returning an error when the memberOf is "Empty".
      You must trap the raised err.number to handle this.
      But you already knew all of that, you just forgot to enable Error trapping (On Error Resume Next) first.

      Try something like this:
      Code:
      
      'open the data file
      Set objTextStream = objFSO.OpenTextFile(".\user.txt")
      'make an array from the data file
      arrUser = Split(objTextStream.ReadAll, vbNewLine)
      'close the data file
      objTextStream.Close
      
      For i = LBound(arrUser) To UBound(arrUser)
                      strUserPath = "LDAP://" & arrUser(i)
                      'Connect to AD as user
                      Set objUser = GetObject(strUserPath)
                      strUserName = objUser.cn   ' or:  Mid(Split(arrUser(i), "," )(0), 4 )
      
                      On Error Resume Next
                      colMemberof  = objUser.GetEx("memberOf")
                                   If Not CBool(Err.Number) Then
                                     On Error GoTo 0
                                     For Each objGroup in colMemberof
                                        strGroup = Mid(Split(objGroup, "," )(0), 4 ) 
                                        strList = strList & strGroup & vbCr
                                     Next
                                   End If
                      On Error GoTo 0
                      call CustomBox(strList, strUserName)
                      strList = Empty
      Next
      
      Wscript.Quit
      
      
      
      'open InternetExplorer and create a window:
      Function CustomBox(sResults, sTitle) 
        '---------------------------------- 
         Set colItems = GetObject("Winmgmts:\\.\root\cimv2").ExecQuery _ 
            ("Select ScreenWidth,ScreenHeight From Win32_DesktopMonitor") 
         For Each objItem in colItems 
            intHorizontal = objItem.ScreenWidth 
            intVertical = objItem.ScreenHeight 
         Next 
         Set colItems = Nothing 
        '---------------------------------- 
        'Convert input string to a valid HTML string 
         sResults = Replace(sResults,"&","&",1,-1,vbTextCompare) 
         sResults = Replace(sResults,Chr(34),"""",1,-1,vbTextCompare) 
         sResults = Replace(sResults,">",">",1,-1,vbTextCompare) 
         sResults = Replace(sResults,"<","<",1,-1,vbTextCompare) 
         sResults = Replace(sResults, vbNewLine, "<br>") 
        '---------------------------------- 
        set oIE = CreateObject("InternetExplorer.Application") 
        'http://msdn2.microsoft.com/en-us/library/aa768398.aspx 
        With oIE 
         .RegisterAsDropTarget = False 
         .Resizable = True : .FullScreen = False 
         .AddressBar = False 
         .MenuBar = False 
         .ToolBar = False 
         .StatusBar = False 
         .width = 600 : .height = 800 
         .Left = (intHorizontal - .width) / 2 
         .Top = (intVertical - .height) / 2 
         .Navigate "about:blank" 
         Do Until .ReadyState = 4 : WScript.Sleep 100 : Loop 
         .document.open 
         .document.write _ 
           "<html><head><" & "script>bboxwait=true;</" _ 
           & "script><title>" & sTitle & "</title></head>"_ 
           & "<body bgColor=Silver" _ 
           & " style='border-Style:outset;border-" _ 
           & "Width:3px'>" _ 
           & "<pre>"& sResults &"</pre>" 
        .document.close 
        '---------------------------------- 
         'set window focus 
         .Visible = True 
         Do Until .ReadyState = 4 : WScript.Sleep 100 : Loop
         WScript.CreateObject("WSCript.shell") _
          .AppActivate "http:/// - " & .document.Title  ' IE version 7.0
        End With '---(oIE)--- 
      End Function
      
      The line(s) On Error GoTo 0 disables error handling again. It is recommended not having Error trapping enabled when you don't expect errors in the script, to make a script more reliable, and is also better for trouble shouting purpose.

      If Not CBool(Err.Number) is the same as If (Err.Number = 0)

      Instead of the line: wscript.echo strList, I called a function 'CustomBox' in the example. This will cause the script to continue after each msgbox and leave all the msgboxes open. And when using this function you'll be able to copy the content of the msgboxes is needed.


      \Rems
      Thanks Rems.

      I'm now away on a little bit of a different tangent but i have used yours and it works perfectly, after a couple of slight changes to some of the code.

      Comment


      • #4
        Re: Error coming up when no groups found.

        Ok now back on with this.

        I've managed to go off on a completely different tangent with this and have stolen, changed and made my own some script that uses ADO to do what i want.

        Now the problem i face is that when i run the script and get all the members of the groups i am missing some.

        We are part of an large corporation that has 2 child domains to it that are effectively run independantly. I am in one domain but i can have users from the other child domain as members of our groups. What is not happening is that it will not get me the entire list of users from the group because it cannot check the users from the other domain. (I hope that makes sense)

        Is there anyway round this??

        Code:
        Option Explicit
        
        Dim adoConnection, adoCommand, objRootDSE, strDNSDomain, strQuery
        Dim adoRecordset, strDN, objGroup
        Dim objFso, objFile, objMember
        
        ' Use ADO to search Active Directory.
        Set adoConnection = CreateObject("ADODB.Connection")
        Set adoCommand = CreateObject("ADODB.Command")
        adoConnection.Provider = "ADsDSOObject"
        adoConnection.Open "Active Directory Provider"
        adoCommand.ActiveConnection = adoConnection
        
        ' Determine the DNS domain from the RootDSE object.
        Set objRootDSE = GetObject("LDAP://RootDSE")
        strDNSDomain = objRootDSE.Get("defaultNamingContext")
        
        ' Search for all groups, return the Distinguished Name of each.
        strQuery = "<LDAP://" & strDNSDomain & ">;(objectClass=group);distinguishedName;subtree"
        adoCommand.CommandText = strQuery
        adoCommand.Properties("Page Size") = 100
        adoCommand.Properties("Timeout") = 30
        adoCommand.Properties("Cache Results") = False
        
        Set adoRecordset = adoCommand.Execute
        If (adoRecordset.EOF = True) Then
            Wscript.Echo "No groups found"
            adoRecordset.Close
            adoConnection.Close
            Set objRootDSE = Nothing
            Set adoConnection = Nothing
            Set adoCommand = Nothing
            Set adoRecordset = Nothing
            Wscript.Quit
        End If
        
        ' Enumerate all groups, bind to each, and document group members.
        Do Until adoRecordset.EOF
            strDN = adoRecordset.Fields("distinguishedName").Value
            ' Escape any forward slash characters with backslash.
            strDN = Replace(strDN, "/", "\/")
            Set objGroup = GetObject("LDAP://" & strDN)
            'Wscript.Echo "|" & objGroup.sAMAccountName
        	Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
        	Set objFile = objFSO.CreateTextFile(".\TXTFiles\" & objGroup.sAMAccountName & ".txt", True)
        	For Each objMember In objGroup.Members
        		objFile.WriteLine objMember.cn
            Next
            adoRecordset.MoveNext
        	objFile.Close
        Loop
        adoRecordset.Close
        
        ' Clean up.
        adoConnection.Close
        Set objRootDSE = Nothing
        Set objGroup = Nothing
        Set adoConnection = Nothing
        Set adoCommand = Nothing
        Set adoRecordset = Nothing

        Comment


        • #5
          Re: Error coming up when no groups found.

          We don't have multiple domains here so I can not test it my self.

          In stead of binding to the root with the LDAP:// provider,
          you must now use the Global Catalog: GC://
          http://msdn2.microsoft.com/en-us/library/ms675564.aspx


          Create a 'For Each domain'-Loop around your script:
          (skip the lines:
          Set objRootDSE = GetObject("LDAP://RootDSE")
          strDNSDomain = objRootDSE.Get("defaultNamingContext")
          )

          Code:
          Set oGC = GetObject("GC:")
          For Each oDomainEnum In oGC
            Set oDomainBind = GetObject("LDAP://" & oDomainEnum.Name)
            strDNSDomain = oDomainBind.distinguishedName
             wscript.echo strDNSDomain  'checkpoint
            '<your script here>
          
          Next
          Or, take a look here:
          Get Operating System for all Computers in the Forest


          \Rems
          Last edited by Rems; 26th October 2007, 07:40.

          This posting is provided "AS IS" with no warranties, and confers no rights.

          __________________

          ** Remember to give credit where credit's due **
          and leave Reputation Points for meaningful posts

          Comment


          • #6
            Re: Error coming up when no groups found.

            Originally posted by Rems View Post
            We don't have multiple domains here so I can not test it my self.

            In stead of binding to the root with the LDAP:// provider,
            you must now use the Global Catalog: GC://
            http://msdn2.microsoft.com/en-us/library/ms675564.aspx


            Create a 'For Each domain'-Loop around your script:
            (skip the lines:
            Set objRootDSE = GetObject("LDAP://RootDSE")
            strDNSDomain = objRootDSE.Get("defaultNamingContext")
            )

            Code:
            Set oGC = GetObject("GC:")
            For Each oDomainEnum In oGC
              Set oDomainBind = GetObject("LDAP://" & oDomainEnum.Name)
              strDNSDomain = oDomainBind.distinguishedName
               wscript.echo strDNSDomain  'checkpoint
              '<your script here>
            
            Next
            Or, take a look here:
            Get Operating System for all Computers in the Forest
            Ok, ok, you asked for child-domains;
            Code:
            Set oGC = GetObject("GC:")
            
            For Each oDomainEnum In oGC
                ' Bind to the domain.
                Set oDomainBind = GetObject("LDAP://" + oDomainEnum.Name)
            
                strDNSDomain = oDomainBind.distinguishedName
                wscript.echo strDNSDomain
                '<...>
            
                ' Enumerate the child objects of the domain.
                For Each oChild In oDomainBind
                  strDNSDomain = oChild.distinguishedName
                  wscript.echo strDNSDomain
                  '<...>
                Next
            
            Next
            If you only want to search in child domains it won't be nessesary to bind to the GC:

            Just Enumerate the child objects of the domain, and do your query once again within in the 'For-Next' loop.
            Or, if there is only one child domain, you could manually type the dn of the domain;
            strDNSDomain = "dc=NA,dc=fabrikam,dc=com"

            But what if the child domain have child domain of their own, and those might also have child domains ..and so on.
            To enumerate all child levels from the domain you must create a subroutine for the 'For-Next' loop.Then make another call to the subroutine from within the subroutine. http://www.microsoft.com/technet/scr....mspx?mfr=true


            - - -
            This is a nice script though;
            http://www.rlmueller.net/Programs/DocumentForest.txt


            \Rems

            This posting is provided "AS IS" with no warranties, and confers no rights.

            __________________

            ** Remember to give credit where credit's due **
            and leave Reputation Points for meaningful posts

            Comment

            Working...
            X