Issue with VBA Script in Excel to pull LDAP Query - excel

I am currently having an issue with VBA Script that I have written to pull some LDAP information from Active Directory. However, I have came to a break in the code at GetAdsProp.
There is a listing to refernce "o" in LDAP as Business Unit. I need to pull that said name from LDAP Query "o".
Here is the code that I am working with:
Sub Update_List()
Set a = Application.Selection
For Each b In a.Rows
Number = b.Row
letter = b.Column
If Range("A" & Number).Value <> "" Then
corpID = Range("A" & Number).Value
Cells.Range("E" & Number).Value = GetAdsProp("samAccountName", corpID, "Name")
Cells.Range("F" & Number).Value = GetCNName(GetAdsProp("samAccountName", corpID, "Manager"))
Cells.Range("G" & Number).Value = GetCNName(GetAdsProp("samAccountName", corpID, "o"))
End If
Next
End Sub
Function GetAdsProp(ByVal SearchField As String, ByVal SearchString As String, ByVal ReturnField As String) As String
' Get the domain string ("dc=domain, dc=local")
Dim strDomain As String
strDomain = GetObject("LDAP://rootDSE").Get("defaultNamingContext")
' ADODB Connection to AD
Dim objConnection As ADODB.Connection
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
' Connection
Dim objCommand As ADODB.Command
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
' Search the AD recursively, starting at root of the domain
objCommand.CommandText = _
"<LDAP://" & strDomain & ">;(&(objectCategory=User)" & _
"(" & SearchField & "=" & SearchString & "));" & SearchField & "," & ReturnField & ";subtree"
' RecordSet
Dim objRecordSet As ADODB.Recordset
Set objRecordSet = objCommand.Execute
If objRecordSet.RecordCount = 0 Then
GetAdsProp = "not found" ' no records returned
Else
GetAdsProp = objRecordSet.Fields(ReturnField) ' return value
End If
' Close connection
objConnection.Close
' Cleanup
Set objRecordSet = Nothing
Set objCommand = Nothing
Set objConnection = Nothing
End Function
Function GetCNName(ByVal CNInput As String) As String
' GetCNName = Regex.Replace(CNInput, "CN=[^,]*,* *", "")
Set RE = CreateObject("vbscript.regexp")
' Get the first part of the CN string up to the second comma
RE.Pattern = "^CN=([^,]*,){2}"
Set foo = RE.Execute(CNInput)
' Get the matched part
CN = ""
For Each Match In foo
If CN = "" Then
CN = Match.Value
End If
Next
' Replace the first bit of text
RE.Pattern = "^CN="
CN = RE.Replace(CN, "")
' Replace the last comma
RE.Pattern = ",$"
CN = RE.Replace(CN, "")
' Replace the slash
RE.Pattern = "\\"
CN = RE.Replace(CN, "")
GetCNName = CN
End Function
Here is the error that I am getting:
Visual Basic Error
Get AdsProp Line issue
Any help would be greatly appreciated.
Thank You.

Related

Import from closed workbooks using ADODB

After so many hours in that field, I could be able to get data from all the worksheets in closed workbook and could get data from specific columns using ADODB.
#Siddharth Rout helped me to be able to get the sheet names in the order of tab.
The following code works fine for only one closed workbook. But in fact I am trying to do the same and get all the data from the specific column (Reference - Ref No - Number ..) from several workbooks
Sub ImportFromClosedWorkbook()
Dim e, ws As Worksheet, cn As ADODB.Connection, rs As ADODB.Recordset, rsHeaders As ADODB.Recordset, b As Boolean, sFile As String, shName As String, strSQL As String, iCol As Long
sFile = ThisWorkbook.Path & "\Sample.xlsx"
Dim con As Object
Set con = CreateObject("DAO.DBEngine.120")
Dim rsData As ADODB.Recordset
Set cn = New ADODB.Connection
cn.Open ConnectionString:="Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & sFile & "';" & "Extended Properties=""Excel 12.0;HDR=YES;IMEX=1;"";"
Set ws = ThisWorkbook.ActiveSheet
Dim db As Object, i As Long
Set db = con.OpenDatabase(sFile, False, True, "Excel 12.0 XMl;")
For i = 0 To db.TableDefs.Count - 1
sName = db.TableDefs(i).Name
b = False
strSQL = "SELECT * FROM [" & sName & "]"
Set rsHeaders = New ADODB.Recordset
rsHeaders.Open Source:=strSQL, ActiveConnection:=cn, Options:=1
For iCol = 0 To rsHeaders.Fields.Count - 1
For Each e In Array("Ref No", "Reference", "Number")
If e = rsHeaders.Fields(iCol).Name Then
b = True: Exit For
End If
Next e
If b Then Exit For
Next iCol
If b Then
strSQL = "SELECT [" & e & "] FROM [" & sName & "]"
Set rsData = New ADODB.Recordset
Set rsData = cn.Execute(strSQL)
ws.Range("A" & ws.Cells(Rows.Count, 1).End(xlUp).Row + 1).CopyFromRecordset rsData
rsData.Close
End If
Next i
db.Close: Set db = Nothing
Set con = Nothing
cn.Close: Set cn = Nothing
End Sub
Is it suitable to build a public procedure or what's the best approach in that case and how can I release the objects in correct way?
I would break out your code even more - there are distinct activities which could be factored out into reusable methods.
FYI your tableDefs objects already contains the field names, so there's no need to separately query for those.
Eg:
Sub ImportFromClosedWorkbook()
Dim sFile As String, sheetName As String, colName As String, rs As ADODB.Recordset
Dim cols As Collection, col
sFile = ThisWorkbook.FullName
Set cols = FindColumns(sFile, Array("Ref", "Reference", "RefNo"))
'loop found columns
For Each col In cols
sheetName = col(0)
colName = col(1)
Debug.Print "##", sheetName, colName
Set rs = WorkBookQuery(sFile, "Select [" & colName & "] from [" & sheetName & "]")
If Not rs.EOF Then
' ActiveSheet.Cells(Rows.Count, "A").End(xlUp).CopyFromRecordset rs
End If
Next col
End Sub
'given a workbook path, find all column headings matching andname in arrNames
'returns a collections of [sheetName, columnName] arrays
Function FindColumns(wbFullPath As String, arrNames) As Collection
Dim tabledefs As Object, td As Object, f As Object, rv As New Collection
Set tabledefs = CreateObject("DAO.DBEngine.120") _
.OpenDatabase(wbFullPath, False, True, "Excel 12.0 XMl;").tabledefs
For Each td In tabledefs
For Each f In td.Fields
'Debug.Print td.Name, f.Name
If Not IsError(Application.Match(f.Name, arrNames, 0)) Then
rv.Add Array(td.Name, f.Name)
End If
Next f
Next td
Set FindColumns = rv
End Function
'run a SQL query against a workbook
Function WorkBookQuery(wbFullPath As String, SQL As String) As ADODB.Recordset
Dim rs As ADODB.Recordset
With New ADODB.Connection
.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & wbFullPath & "';" & _
"Extended Properties=""Excel 12.0;HDR=YES;IMEX=1;"";"
Set WorkBookQuery = .Execute(SQL, Options:=1)
End With
End Function
There seems to be a logical error in the process of cycling through the fields. It would be nice to use a user-defined function that checks if the field name exists.
Sub ImportFromClosedWorkbook()
Dim e, ws As Worksheet, cn As ADODB.Connection, rs As ADODB.Recordset, rsHeaders As ADODB.Recordset, b As Boolean, sFile As String, shName As String, strSQL As String, iCol As Long
Dim sField As String
sFile = ThisWorkbook.Path & "\Sample.xlsx"
Dim con As Object
Set con = CreateObject("DAO.DBEngine.120")
Dim rsData As ADODB.Recordset
Set cn = New ADODB.Connection
cn.Open ConnectionString:="Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & sFile & "';" & "Extended Properties=""Excel 12.0;HDR=YES;IMEX=1;"";"
Set ws = ThisWorkbook.ActiveSheet
Dim db As Object, i As Long
Set db = con.OpenDatabase(sFile, False, True, "Excel 12.0 XMl;")
For i = 0 To db.TableDefs.Count - 1
sName = db.TableDefs(i).Name
b = False
strSQL = "SELECT * FROM [" & sName & "]"
Set rsHeaders = New ADODB.Recordset
rsHeaders.Open Source:=strSQL, ActiveConnection:=cn, Options:=1
For iCol = 0 To rsHeaders.Fields.Count - 1
' For Each e In Array("Ref No", "Reference", "Number")
' If e = rsHeaders.Fields(iCol).Name Then
' b = True: Exit For
' End If
' Next e
' If b Then Exit For
' Next iCol
' If b Then
sField = rsHeaders.Fields(iCol).Name
If isField(sField) Then
strSQL = "SELECT [" & sField & "] FROM [" & sName & "]"
Set rsData = New ADODB.Recordset
Set rsData = cn.Execute(strSQL)
ws.Range("A" & ws.Cells(Rows.Count, 1).End(xlUp).Row + 1).CopyFromRecordset rsData
rsData.Close
End If
Next iCol
Next i
db.Close: Set db = Nothing
Set con = Nothing
cn.Close: Set cn = Nothing
End Sub
Function isField(sField As String) As Boolean
Dim vName As Variant, e As Variant
vName = Array("Ref No", "Reference", "Number")
For Each e In vName
If e = sField Then
isField = True
Exit Function
End If
Next e
End Function
If all the files have the same structure and are in a folder, you could use the FileSystemObject reference as below:
"https://stackoverflow.com/questions/10380312/loop-through-files-in-a-folder-using-vba"
and you could run the existing code in a loop in the file system code, hope that works

Getting "Automation error" while updating a column through VBA

I have written a VBA code to update a column but getting Automation Error while running the program at line 41 which is Set rsf = cmd.Execute. Is the way of writing update statement incorrect in my code? Not getting what is the issue here. I'd appreciate any help towards a solution for my problem.
Private Sub Update_Visibility_Flag_Click()
Dim fldrpath As String
Dim currDate As String
Dim mePrgTrck As String
Dim wkb1 As Workbook
Dim sht1 As Worksheet
Dim cnf As ADODB.Connection
Dim rsf As ADODB.Recordset
Dim sqlstr As String
fldrpath = "\\lp99dfd\groups$\Record Extracts\New folder\New folder\" & Format(Date, "yyyymm")
currDate = "PI_202008"
mePrgTrck = fldrpath & "\LE\Progress_Tracker_" & Format(Date, "yyyymm") & "_LE.xlsx"
Set wkb1 = Workbooks.Open(mePrgTrck)
Set sht1 = wkb1.Sheets(currDate)
Set cnf = New ADODB.Connection
Set rsf = New ADODB.Recordset
cnf.Open ( _
"User ID=AI_ZK_DTA" & _
";Password=aizkdta" & _
";Data Source=POIUY" & _
";Provider=OraOLEDB.Oracle")
For Each cell In sht1.Range("A2:A28")
If cell.Offset(0, 3).Value = "Success" Then
sqlstr = "UPDATE AI_" & cell.Value & "_DTA SET VISIBLE = 'Y'"
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
Set cmd.ActiveConnection = cnf
cmd.CommandType = adCmdText
cmd.Properties("PLSQLRSet") = True
cmd.CommandText = sqlstr
Set rsf = cmd.Execute
cmd.Properties("PLSQLRSet") = False
cell.Offset(0, 8).Value = cell.Offset(0, 8).Value & "| Done"
End If
Next cell
wkb1.Close True
Set rsf = Nothing
Set cnf = Nothing
End Sub

Excel function with ADODB connection string to Access database

I've created below Excel function which connects to an access database with ADODB (approx 10k lines).
It generally works but there are two main issues:
It is unreliable: often it returns 0 while the result should be different
It is definitely slow
Any suggestion on how to improve?
Public Function TotaleSQL(Cat As String, SubCat As String, Anno As Integer) As Long
On Error Resume Next
Dim cn As Object, rs As Object, output As String, sql As String
Dim src As String
Dim Total As Long
Dim CatLong As String
src = "Z:\Report.accdb"
'---Connecting to the Data Source---
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.connectionstring = "Data Source=" & src & ";Persist Security Info=False"
.Open
End With
'---Run the SQL SELECT Query---
CatLong = "'" & Cat & ":" & SubCat & "'"
sql = "SELECT Report.Withdrawal, Report.Deposit, Report.Category, Report.Date FROM Report WHERE (((Report.Category)=" & CatLong & ") AND ((Year([date]))=" & Anno & "));"
'sql = "SELECT * FROM [Sheet1$]"
Set rs = cn.Execute(sql)
Total = 0
Do
Total = Total + Val(rs(1) & "") - Val(rs(0) & "")
rs.Movenext
Loop Until rs.EOF
'---Clean up---
rs.Close
cn.Close
Set cn = Nothing
Set rs = Nothing
TotaleSQL = Total
End Function
If Cat, SubCat or Anno are user inputs it is more secure to use parameters in your query. For example
Public Function TotaleSQL(Cat As String, SubCat As String, Anno As Integer)
Const DATABASE = "Z:\Report.accdb"
Const TABLE_NAME = "Report"
Const SQL = " SELECT SUM(iif(Deposit is null,0,Deposit) " & _
" - iif(Withdrawal is null,0,Withdrawal)) " & _
" FROM " & TABLE_NAME & _
" WHERE Category = ? " & _
" AND YEAR(ddate)= ? "
Dim cn As Object, cmd As Object, rs As Object
'---Connecting to the Data Source---
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.connectionstring = "Data Source=" & DATABASE & ";Persist Security Info=False"
.Open
End With
' create command
Set cmd = CreateObject("ADODB.Command")
With cmd
.ActiveConnection = cn
.CommandText = SQL
.CommandType = 1 'adCmdText
.Parameters.Append .CreateParameter("P1", 200, 1, 50) ' 1=adParamInput 200=adVarChar
.Parameters.Append .CreateParameter("P2", 3, 1) ' 3=adInteger
End With
' execute with parameters
With cmd
.Parameters(0).Value = Cat & ":" & SubCat
.Parameters(1).Value = Anno
Set rs = .Execute
End With
TotaleSQL = rs(0)
rs.Close
cn.Close
Set cn = Nothing
Set rs = Nothing
Set cmd = Nothing
End Function
Sub test()
Debug.Print TotaleSQL("Cat", "SubCat", 2020)
End Sub

Returning Manager Name from AD using Excel VBA

I am trying to obtain the manager of the user found in AD using Excel VBA. The code is as follows:
Sub ADQuery()
MsgBox GetDepartment("Simpson", "Homer")
End Sub
Function GetDepartment(strLastName As String, strFirstName As String) As String
Dim objRoot As Object
Dim strDomain As String
Dim objConn As Object
Dim objComm As Object
Dim objRecordset As Object
Dim sFilter As String
Dim sAttribs As String
Dim sDepth As String
Dim sBase As String
Dim sQuery As String
Set objRoot = GetObject("LDAP://RootDSE")
strDomain = objRoot.Get("DefaultNamingContext")
Set objConn = CreateObject("ADODB.Connection")
Set objComm = CreateObject("ADODB.Command")
strLastName = Replace(strLastName, Space(1), "")
strFirstName = Replace(strFirstName, Space(1), "")
sFilter = "(&(objectClass=person)(objectCategory=user)(givenName=" & strFirstName & ")" & "(sn=" & strLastName & "*)" & ")"
sAttribs = "manager,sAMAccountName,givenName,sn"
sDepth = "SubTree"
sBase = "<LDAP://" & strDomain & ">"
sQuery = sBase & ";" & sFilter & ";" & sAttribs & ";" & sDepth
objConn.Open "Data Source=Active Directory Provider;Provider=ADsDSOObject"
Set objComm.ActiveConnection = objConn
objComm.Properties("Page Size") = 40000
objComm.CommandText = sQuery
Set objRecordset = objComm.Execute
Do Until objRecordset.EOF
GetDepartment = objRecordset("department")
Exit Function
objRecordset.MoveNext
Loop
End Function
But I get the following error message:
Run-time error '3265': Item cannot be found in the collection corresponding to the requested name or ordinal.
Looking in AD itself, it seems the 'manager' attribute listed in the Attribute Editor is listed with a value:
CN=Burns\, Montgomery,OU=Users,OU=Springfield,OU=Nuclear Power Plant,DC=powerplantnet,DC=com
And the syntax is Distinguished Name.
My function returns a string and I can see that the manager attribute is not a string. How do I get the information back as a string?

Importing data from Access to Excel using SQL SELECT WHERE <DATE>

I am trying to import specific records from Access Tables to Excel spreadsheets based on a particular Date criteria. The code fails when it tries to execute the sql statement and the error message says
"DataType mismatch"
I have explored as many ways as I could think of or what I could find online to set the data type for the date but nothing seems to work. After modifying the code as it is now, I was able to get rid off the error message but code does not recognize the data in the access table. Any help will be greatly appreciated. Apologies in advance if my question does not make any sense. Trying to get my head around this being a newbie developer... Thanks for your patience.
Public Sub ImportData()
Application.ScreenUpdating = False
'
' Initialize shtArray (Public Array)
'
With ThisWorkbook
shtArray = Array(.Sheets("shtDom"))
End With
'
' Initialize tblArray (Public Array)
'
tblArray = Array("tbl_DOM")
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Dim dbPath As String
Dim SQL As String
Dim i As Integer
Dim sht As Worksheet, lastRow As Long
Dim dtDom As String, dtObk As String
If Weekday(frmInterface.dtPickDomestic, vbMonday) = 1 Then
dtDom = Format(frmInterface.dtPickDomestic - 3, "dd/mm/yyyy")
Else
dtDom = Format(frmInterface.dtPickDomestic - 1, "dd/mm/yyyy")
End If
If Weekday(frmInterface.dtPickOtherBanks, vbMonday) = 1 Then
dtObk = Format(frmInterface.dtPickOtherBanks - 3, "dd/mm/yyyy")
Else
dtObk = Format(frmInterface.dtPickOtherBanks - 1, "dd/mm/yyyy")
End If
dbPath = ThisWorkbook.Path & "\DOMESTIC SETTLEMENTS.mdb"
Set con = New ADODB.Connection
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath
For i = LBound(tblArray) To UBound(tblArray)
Select Case i
Case 0, 8, 9
SQL = "SELECT * FROM " & tblArray(i) & " WHERE [BAL_DATE] = #" & dtDom & "#"
Case 10, 11, 12, 13
'SQL = "SELECT * FROM " & tblArray(i) & " WHERE [BAL_DATE] = #" & dtObk & "#"
Case Else
GoTo continue
End Select
Set sht = shtArray(i)
lastRow = sht.Cells(Rows.count, 1).End(xlUp).Offset(1, 0).Row
Set rs = New ADODB.Recordset
rs.Open SQL, con
If rs.EOF And rs.BOF Then
rs.Close
con.Close
Set rs = Nothing
Set con = Nothing
MsgBox "No records!!!", vbCritical
Exit Sub
End If
shtArray(i).Range("A" & lastRow).CopyFromRecordset rs
rs.Close
Set rs = Nothing
continue:
Next i
con.Close
Set con = Nothing
Set sht = Nothing
On Error GoTo 0
Exit Sub
errHandler:
Set rs = Nothing
Set con = Nothing
End Sub

Resources