?
Solved

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

Posted on 2008-06-19
20
Medium Priority
?
3,144 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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 2000 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

Featured Post

Transaction-level recovery for Oracle database

Veeam Explore for Oracle delivers low RTOs and RPOs with agentless transaction log backup and transaction-level recovery of Oracle databases. You can restore the database to a precise point in time, even to a specific transaction.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A hard and fast method for reducing Active Directory Administrators members.
Here's a look at newsworthy articles and community happenings during the last month.
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…
Sometimes it takes a new vantage point, apart from our everyday security practices, to truly see our Active Directory (AD) vulnerabilities. We get used to implementing the same techniques and checking the same areas for a breach. This pattern can re…

840 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