Solved

Deleting ADAM (LDAP) objects based on attributes with vbscript.

Posted on 2008-06-19
20
3,045 Views
Last Modified: 2012-06-27
I'm currently trying to create a vb script that will search through an OU in ADAM (Active directory application mode) and delete user accounts based on the ipPhone attribute.

Basically, if the field is empty, I want the account removed. All of our ipPhone numbers begin with "1"... Not sure if that helps.

I'm not sure how different dealing with ADAM through vbs is than dealing with AD, but the following script will show me what the ipPhone attribute is for a specified user.


On Error Resume Next
strUser = "CN=Generic User,CN=Users,DC=ADAM,DC=COM"
Set objUser = GetObject("LDAP://localhost:389/" & strUser)
WScript.Echo "Attributes for " & strUser
WScript.Echo "Phone: " & objUser.ipPhone

And this script will supposedly delete a specified user account:

'Delete user account in ADAM.
On Error Resume Next
strUser = "cn=Generic User"
strContainer = "cn=users,dc=ADAM,dc=com"
Set objContainer = GetObject("LDAP://localhost:389/" & strContainer)
objContainer.Delete "user", strUser

I'm just not very familiar with VB scripting and I'm not sure how to tie them together in order to achieve what I need to.
0
Comment
Question by:jfaraone
  • 13
  • 7
20 Comments
 

Author Comment

by:jfaraone
ID: 21825917
Updated point value to 300.
0
 

Author Comment

by:jfaraone
ID: 21826477
500...
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 21829830
Hi, normally something like this would work for LDAP queries, but like you, I'm not sure about the ADAM interface....

This will only report on the users with no ipPhone value.  Once you have checked them, if you want to delete them, just uncomment the objContainer.Delete line.

Regards,

Rob.
Const ADS_SCOPE_SUBTREE = 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") = 1000

objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
 

Set objRootDSE = GetObject("LDAP://RootDSE")

objCommand.CommandText = _

    "SELECT cn,dn FROM 'LDAP://" & objRootDSE.Get("defaultNamingContext") & "' WHERE objectCategory='user' " & _

        "AND ipPhone = ''"

Set objRecordSet = objCommand.Execute
 

objRecordSet.MoveFirst

Do Until objRecordSet.EOF

	strUser = objRecordSet.Fields("cn").Value

	strContainer = Replace(objRecordSet.Fields("cn").Value, strUser, "")

    Set objContainer = GetObject("LDAP://localhost:389/" & strContainer)

	WScript.Echo "Deleting " & strUser & " from " & objContainer.adsPath

	'objContainer.Delete "user", strUser

    objRecordSet.MoveNext

Loop

Open in new window

0
 

Author Comment

by:jfaraone
ID: 21845722
I get a Windows Script Host error on line 16 char 1 - "Unspecified error" Code = 80004005 Source = Provider.
0
 

Author Comment

by:jfaraone
ID: 21847615
Another comment; ADAM is designed so that you can run multiple instances on a single machine. It looks like you're connecting to localhost; it may need to specify "CN=Users,DC=ADAM,DC=COM" specifically, as this instance is named "ADAM". Does that make sense?
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 21852223
OK, so try modifying this bit:
objCommand.CommandText = _
    "SELECT cn,dn FROM 'LDAP://" & objRootDSE.Get("defaultNamingContext") & "' WHERE objectCategory='user' " & _
        "AND ipPhone = ''"


to something like
objCommand.CommandText = _
    "SELECT cn,dn FROM 'LDAP://CN=Users,DC=ADAM,DC=COM' WHERE objectCategory='user' " & _
        "AND ipPhone = ''"


to see if you can connect to that instance.

Regards,

Rob.
0
 

Author Comment

by:jfaraone
ID: 21855622
I think we're really close. It seems that we're just stuck at binding to my LDAP instance. The following script was included in a zip of example scripts MS offers for download. The following script is able to bind to my ADAM instance without problem.

Does this help?

'Bind to ADAM object.

strServer = "localhost" 'Server running ADAM instance.

strPort = "389" 'Optional

strObject = "CN=Users,DC=ADAMPAULO,DC=com" 'Optional

strPath = "LDAP://" & strServer & ":" & strPort & "/" & strObject

WScript.Echo "Attempting to bind to: " & strPath & VbCrLf

Set objADAM = GetObject(strPath)

If Err.Number = vbEmpty Then

    WScript.Echo "Bind succeeded."

    WScript.Echo "Name:    " & objADAM.Name

    WScript.Echo "Parent:  " & objADAM.Parent

    WScript.Echo "DN:      " & objADAM.distinguishedName

    WScript.Echo "Created: " & objADAM.whenCreated

Else

    WScript.Echo "ERROR: Bind failed."

End If

Open in new window

0
 
LVL 65

Expert Comment

by:RobSampson
ID: 21861448
OK, so that script you posted doesn't execute a query, which should be able to be done, but I'll need to look for an example, unless the below works.....the script you provided does, however, give more clues on how to connect to your instance...

Regards,

Rob.
Const ADS_SCOPE_SUBTREE = 2
 

strServer = "localhost" 'Server running ADAM instance.

strPort = "389" 'Optional

strObject = "CN=Users,DC=ADAMPAULO,DC=com" 'Optional

strPath = "LDAP://" & strServer & ":" & strPort & "/" & strObject
 

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") = 1000

objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 

 

Set objRootDSE = GetObject("LDAP://RootDSE")

objCommand.CommandText = _

	"SELECT cn,dn FROM '" & strPath & "' WHERE objectCategory='user' AND ipPhone = ''"

Set objRecordSet = objCommand.Execute

 

objRecordSet.MoveFirst

Do Until objRecordSet.EOF

	strUser = objRecordSet.Fields("cn").Value

	strContainer = Replace(objRecordSet.Fields("cn").Value, strUser, "")

    Set objContainer = GetObject("LDAP://" & strServer & ":" & strPort & "/" & strContainer)

	WScript.Echo "Deleting " & strUser & " from " & objContainer.adsPath

	'objContainer.Delete "user", strUser

    objRecordSet.MoveNext

Loop

Open in new window

0
 

Author Comment

by:jfaraone
ID: 21864995
I get an error...


Line:20
Char:1
Error: Unspecified Error
Code: 80004005
Source: Provider

Is there any way I can set up a MeetingPlace session so we can just troubleshoot a few things?
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 21872411
Hi, I have been looking at some code from the Active Directory Cookbook, 2nd Edition, and it doesn't seem like this should be any different to the LDAP methods....

Rob.
' ------ SCRIPT CONFIGURATION ------

strServer = "localhost"

strPort = "389" 'Optional

'strObject = "CN=Users,DC=ADAMPAULO,DC=com" 'Optional

strPath = "LDAP://" & strServer & ":" & strPort

' ------ END CONFIGURATION --------
 

Set objRootDSE = GetObject("LDAP://" & strServer & ":" & strPort & "/RootDSE")

Set objLDAP = GetObject("LDAP://" & strServer & ":" & strPort & "/" & objRootDSE.Get("rootDomainNamingContext"))
 

strBase = "<LDAP://cn=USERS," & objRootDSE.Get("rootDomainNamingContext") & ">;"

strFilter = "(&(objectcategory=user)(ipPhone=));"

strAttrs  = "cn,distinguishedName;"

strScope  = "onelevel"

strQuery = strBase & strFilter & strAttrs & strScope

	

MsgBox "About to execute " & VbCrLf & strQuery

	

Set objConn = CreateObject("ADODB.Connection")

objConn.Provider = "ADsDSOObject"

objConn.Open "Active Directory Provider"

Set objRecordSet = objConn.Execute(strQuery)
 

Do Until objRecordSet.EOF

	strUser = objRecordSet.Fields("cn").Value

	strContainer = Replace(objRecordSet.Fields("cn").Value, strUser, "")

	Set objContainer = GetObject("LDAP://" & strServer & ":" & strPort & "/" & strContainer)

	WScript.Echo "Deleting " & strUser & " from " & objContainer.adsPath

	'objContainer.Delete "user", strUser

	objRecordSet.MoveNext

Loop

Open in new window

0
 

Author Comment

by:jfaraone
ID: 21909235
New error... this one seems promising.

Line: 9
Char: 1
Error: The directory property cannot be found in the cache.

Code: 8000500D
Source: Active directory

A couple of quick searches turned up this

http://www.microsoft.com/technet/scriptcenter/guide/sas_usr_zbco.mspx?mfr=true

which says that we're trying to query an attribute that doesn't contain a value. Which makes sense, I think.

Just to make sure I'm not leaving anything out, our ADAM server is not a complete copy of our AD. Only certain attributes were imported to keep our ADAM server as lightweight as possible.

Here is a list of the imported attributes, just so we can verify that we aren't querying an attribute that doesn't actually exist...

    <include>objectSID</include>    
    <include>sourceObjectGuid</include>
    <include>lastAgedChange</include>
    <include>sAMAccountName</include>
    <include>middleName</include>
    <include>ipPhone</include>
    <include>mail</include>
    <include>department</include>
    <include>givenName</include>
    <include>manager</include>
    <include>sn</include>

0
 

Author Comment

by:jfaraone
ID: 21909488
As suggested by the MS article, I added

On Error Resume Next

and the script ran at 99% for a good while with no noticeable changes to our ADAM directory, so I'm thinking it got hung up somewhere. One other note, when running the script the popup displays the following:

About to execute
(&(objectcategory=user)(ipPhone=));cn,distinguishedName;onelevel

Just wanted to verify that this was the expected contents of the pop-up window.
vbserror.jpg
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 21912504
Oh great! That's progress!  OK, so from here:
http://www.microsoft.com/technet/scriptcenter/guide/sas_usr_hqdo.mspx?mfr=true

it looks like the part of the query for the empty ipPhone:
strFilter = "(&(objectcategory=user)(ipPhone=));"

is not correct.  It should be
strFilter = "(&(objectcategory=user)(!ipPhone=*));"

but if that doesn't work, you could also try the IsNull method described in that article too.

Oh, and by the way, currently it won't delete account, it will just tell you whether it *would* or not.  This line is commented out:
      'objContainer.Delete "user", strUser

so once you're happy with the results, remove the apostrophe, and accounts will start to be deleted.

Regards,

Rob.
0
 

Author Comment

by:jfaraone
ID: 21948166
I receive a windows script host error:

Line: 10
Char: 1
Error: The directory property cannot be found in the cache.

Code: 8000500D
Source: Active Directory
0
 
LVL 65

Accepted Solution

by:
RobSampson earned 500 total points
ID: 21949404
OK, so maybe the IPPhone attribute does not exist for some users.  So we'll take IPPhone out of the filter, and test for it for every user instead.

Regards,

Rob.
' ------ SCRIPT CONFIGURATION ------

strServer = "localhost"

strPort = "389" 'Optional

'strObject = "CN=Users,DC=ADAMPAULO,DC=com" 'Optional

strPath = "LDAP://" & strServer & ":" & strPort

' ------ END CONFIGURATION --------

 

Set objRootDSE = GetObject("LDAP://" & strServer & ":" & strPort & "/RootDSE")

Set objLDAP = GetObject("LDAP://" & strServer & ":" & strPort & "/" & objRootDSE.Get("rootDomainNamingContext"))

 

strBase = "<LDAP://cn=USERS," & objRootDSE.Get("rootDomainNamingContext") & ">;"

strFilter = "(&(objectcategory=user));"

strAttrs  = "cn,adsPath;"

strScope  = "onelevel"

strQuery = strBase & strFilter & strAttrs & strScope

	

MsgBox "About to execute " & VbCrLf & strQuery

	

Set objConn = CreateObject("ADODB.Connection")

objConn.Provider = "ADsDSOObject"

objConn.Open "Active Directory Provider"

Set objRecordSet = objConn.Execute(strQuery)

 

Do Until objRecordSet.EOF

	strUser = objRecordSet.Fields("cn").Value

	Set objUser = GetObject(objRecordSet.Fields("adsPath").Value)

	On Error Resume Next

	strIPPhone = objUser.IPPhone

	If Err.Number = 0 Then

		On Error GoTo 0

		If strIPPhone = "" Then

			strContainer = Replace(objRecordSet.Fields("cn").Value, strUser, "")

			Set objContainer = GetObject("LDAP://" & strServer & ":" & strPort & "/" & strContainer)

			WScript.Echo strUser & " has an empty IPPhone attribute."

			WScript.Echo "Deleting " & strUser & " from " & objContainer.adsPath

			WScript.Echo ""

			'objContainer.Delete "user", strUser

		End If

	Else

		Err.Clear

		On Error GoTo 0

		WScript.Echo strUser & " does not have an IPPhone attribute."

		strContainer = Replace(objRecordSet.Fields("cn").Value, strUser, "")

		Set objContainer = GetObject("LDAP://" & strServer & ":" & strPort & "/" & strContainer)

		WScript.Echo "Deleting " & strUser & " from " & objContainer.adsPath

		WScript.Echo ""

		'objContainer.Delete "user", strUser

	End If

	objRecordSet.MoveNext

Loop

Open in new window

0
 

Author Comment

by:jfaraone
ID: 21953556
Same error, but this time it occurs on line 9. I pasted your recent post verbatim for a line number reference...
0
 

Author Comment

by:jfaraone
ID: 22038892
Well, I wasn't able to resolve this via vbs, but I found a way to accomplish this via ADAM itself.

I used the  parameter in ADAM to a) filter users on import, and b) delete users that don't exist in Active directory. In case anyone else has the same problem, here is the object-filter I used...

(&#124;(&amp;(objectCategory=Person)(ipPhone=*))(&amp;(objectClass=user)(isDeleted=TRUE)))

0
 

Author Closing Comment

by:jfaraone
ID: 31468894
Didn't solve my problem, but A for effort. Thanks!
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 22047427
Thanks for the grade.  Very strange that such a filter works directly, but not via VBS....

Rob.
0
 

Author Comment

by:jfaraone
ID: 22050146
I should also point out that the whole purpose of this ordeal was to filter users in a custom LDAP directory for our Unified Communications Manager (formerly CallManager) phone system. The system itself as of version 6.x only supports filtering based on OU's, and I didn't want to restructure my OU's into IP phone and non-IP phone containers...

For some reason, LDAP import filtering was removed after CM 5.x... hopefully this will allow me to keep a current list of users with valid internal phone numbers.

At any rate, hope this helps anyone else with the same issue.
0

Join & Write a Comment

Not long ago I saw a question in the VB Script forum that I thought would not take much time. You can read that question (Question ID  (http://www.experts-exchange.com/Programming/Languages/Visual_Basic/VB_Script/Q_28455246.html)28455246) Here (http…
Resolve DNS query failed errors for Exchange
This tutorial will walk an individual through the steps necessary to join and promote the first Windows Server 2012 domain controller into an Active Directory environment running on Windows Server 2008. Determine the location of the FSMO roles by lo…
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles from a Windows Server 2008 domain controller to a Windows Server 2012 domain controlle…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now