Friday, April 27, 2007

Reporting on the Top 10 largest emails in each users mailbox in Exchange

Someone asked for a script last week to show the top 5 largest emails by size in each user’s mailbox being a Letterman fan (yep we get it here in Aus just no all the jokes) I tough I’d post it as a Top 10 script. You can expand on this concept to really look at the Top 10 of anything in your user's mailbox's (drum roll at meeting not included). The script is basically the mailbox content age script I posted a while ago modified to instead create a disconnected record-set of the largest 10 items in each folder in a mailbox and then basically sort this list at the end of the mailbox scan and write the top 10 largest items for each user to a separate htm file in the reports directory. To limit the query to only return the top 10 largest items in each folder the query uses the order by SQL verb to sort the result by the item size and to limit the number of results returned the range header is used in WebDAV eg

req.setRequestHeader "Range", "rows=0-9"

This means only the first 10 rows of the result is returned which cuts down the processing of the script greatly and means the execution time and load on the server is greatly reduced. The script uses the admin virtual root so it will run with delegated Exchange Admin Rights. The script requires 1 command-line parameter which in the servername of the server you wish to run it against eg something like

Cscript shotop10.vbs servername

I’ve put a downloadable copy of the script here the script itself looks like.

On Error Resume next
Servername = wscript.arguments(0)
set shell = createobject("")
strValueName = "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias"
minTimeOffset = shell.regread(strValueName)
toffset = datediff("h",DateAdd("n", minTimeOffset, now()),now())
treport = "<table border=""1"" width=""100%"">" & vbcrlf
treport = treport & " <tr>" & vbcrlf
treport = treport & "<td align=""center"" bgcolor=""#000080""><b><font color=""#FFFFFF"">Folder</font></b></td>"
& vbcrlf
treport = treport & "<td align=""center"" bgcolor=""#000080""><b><font color=""#FFFFFF"">Date
Recieved</font></b></td>" & vbcrlf
treport = treport & "<td align=""center"" bgcolor=""#000080""><b><font color=""#FFFFFF"">Mail
From</font></b></td>" & vbcrlf
treport = treport & "<td align=""center"" bgcolor=""#000080""><b><font color=""#FFFFFF"">Subject</font></b></td>"
& vbcrlf
treport = treport & "<td align=""center"" bgcolor=""#000080""><b><font color=""#FFFFFF"">Size
KB</font></b></td>" & vbcrlf
treport = treport & "</tr>" & vbcrlf
set req = createobject("microsoft.xmlhttp")
set com = createobject("ADODB.Command")
set conn = createobject("ADODB.Connection")
Set iAdRootDSE = GetObject("LDAP://RootDSE")
strNameingContext = iAdRootDSE.Get("configurationNamingContext")
strDefaultNamingContext = iAdRootDSE.Get("defaultNamingContext")
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
polQuery = "<LDAP://" & strNameingContext &amp; ">;(&(objectCategory=msExchRecipientPolicy)(cn=Default
svcQuery = "<LDAP://" & strNameingContext &amp; ">;(&(objectCategory=msExchExchangeServer)(cn="
& Servername & "));cn,name,legacyExchangeDN;subtree"
Com.ActiveConnection = Conn
Com.CommandText = polQuery
Set plRs = Com.Execute
while not plRs.eof
for each adrobj in plrs.fields("gatewayProxy").value
if instr(adrobj,"SMTP:") then dpDefaultpolicy =
wscript.echo dpDefaultpolicy
Com.CommandText = svcQuery
Set Rs = Com.Execute
while not rs.eof
GALQueryFilter = "(&(&(&(& (mailnickname=*)(!msExchHideFromAddressLists=TRUE)(|
(&(objectCategory=person)(objectClass=user)(msExchHomeServerName=" &
rs.fields("legacyExchangeDN") &amp; ")) )))))"
strQuery = "<LDAP://" & strDefaultNamingContext &amp; ">;" & GALQueryFilter &
com.Properties("Page Size") = 100
Com.CommandText = strQuery
Set Rs1 = Com.Execute
while not Rs1.eof
set conn1 = createobject("ADODB.Connection")
strConnString = "Data Provider=NONE; Provider=MSDataShape"
conn1.Open strConnString
set objParentRS = createobject("adodb.recordset")
" NEW adVarChar(255) AS MailDate, " & _
" NEW adVarChar(255) AS FolderName, " & _
" NEW adVarChar(255) AS MailFrom, " & _
" NEW adVarChar(255) AS Subject, " & _
" NEW adBigInt AS Size"
objParentRS.LockType = 3
objParentRS.Open strSQL, conn1
falias = "http://" & servername &amp; "/exadmin/admin/" & dpDefaultpolicy & "/mbx/"
for each paddress in rs1.fields("proxyaddresses").value
if instr(paddress,"SMTP:") then falias = falias & replace(paddress,"SMTP:","")

wscript.echo falias
call RecurseFolder(falias)
objParentRS.Sort = "Size DESC"
Set fso = CreateObject("Scripting.FileSystemObject")
set wfile = fso.opentextfile("c:\temp\" & rs1.fields("mail").value &
report = ""
For mrep = 1 To 10
report = report & "<tr>" & vbcrlf
report = report & "<td align=""center"">" & objParentRS.fields("FolderName") &amp;
" </td>" & vbcrlf
report = report & "<td align=""center"">" & objParentRS.fields("MailDate") &amp;
" </td>" & vbcrlf
report = report & "<td align=""center"">" & objParentRS.fields("MailFrom") &amp;
" </td>" & vbcrlf
report = report & "<td align=""center"">" & objParentRS.fields("Subject") &amp;
" </td>" & vbcrlf
report = report & "<td align=""center"">" &
formatnumber(cdbl(objParentRS.fields("Size"))/1024,2) &amp; " </td>" & vbcrlf
report = report & "</tr>" & vbcrlf
Set objParentRS = nothing
wfile.writeline treport
wfile.writeline report
wfile.writeline "</table>"
set wfile = Nothing
set conn = nothing
set com = nothing

Public Sub RecurseFolder(sUrl) "SEARCH", sUrl, False, "", ""
sQuery = "<?xml version=""1.0""?>"
sQuery = sQuery & "<g:searchrequest xmlns:g=""DAV:"">"
sQuery = sQuery & "<g:sql>SELECT """
sQuery = sQuery & "mapi/proptag/x0e080003"", ""DAV:hassubs"" FROM SCOPE "
sQuery = sQuery & "('SHALLOW TRAVERSAL OF """ & sUrl &amp; """') "
sQuery = sQuery & "WHERE ""DAV:isfolder"" = true and ""DAV:ishidden"" = false
and """" = 1"
sQuery = sQuery & "</g:sql>"
sQuery = sQuery & "</g:searchrequest>"
req.setRequestHeader "Content-Type", "text/xml"
req.setRequestHeader "Translate", "f"
req.setRequestHeader "Depth", "0"
req.setRequestHeader "Content-Length", "" & Len(sQuery)
req.send sQuery
Set oXMLDoc = req.responseXML
Set oXMLSizeNodes = oXMLDoc.getElementsByTagName("d:x0e080003")
Set oXMLHREFNodes = oXMLDoc.getElementsByTagName("a:href")
Set oXMLHasSubsNodes = oXMLDoc.getElementsByTagName("a:hassubs")
For i = 0 to oXMLSizeNodes.length - 1
call procfolder(oXMLHREFNodes.Item(i).nodeTypedValue,sUrl)
wscript.echo oXMLHREFNodes.Item(i).nodeTypedValue
If oXMLHasSubsNodes.Item(i).nodeTypedValue = True Then
call RecurseFolder(oXMLHREFNodes.Item(i).nodeTypedValue)
End If
End Sub

sub procfolder(strURL,pfname)
wscript.echo strURL
strQuery = "<?xml version=""1.0""?><D:searchrequest xmlns:D = ""DAV:""
strQuery = strQuery & "<D:sql>SELECT ""DAV:displayname"",
""urn:schemas:httpmail:subject"", "
strQuery = strQuery & """DAV:creationdate"", ""DAV:getcontentlength"", "
strQuery = strQuery & """urn:schemas:httpmail:fromemail"",
strQuery = strQuery & " FROM scope('shallow traversal of """
strQuery = strQuery & strURL &amp; """') Where ""DAV:ishidden"" = False AND
""DAV:isfolder"" = False Order by ""DAV:getcontentlength""
DESC</D:sql></D:searchrequest>" "SEARCH", strURL, false
req.setrequestheader "Content-Type", "text/xml"
req.setRequestHeader "Translate","f"
req.setRequestHeader "Range", "rows=0-9"
req.send strQuery
If req.status >= 500 Then
ElseIf req.status = 207 Then
set oResponseDoc = req.responseXML
set oNodeList = oResponseDoc.getElementsByTagName("a:displayname")
set oNodeList1 = oResponseDoc.getElementsByTagName("a:href")
set oSize = oResponseDoc.getElementsByTagName("a:getcontentlength")
set odatereceived = oResponseDoc.getElementsByTagName("d:datereceived")
set fromEmail = oResponseDoc.getElementsByTagName("d:fromemail")
set subject = oResponseDoc.getElementsByTagName("d:subject")
For i = 0 To (oNodeList.length -1)
set oNode = oNodeList.nextNode
set oNode1 = oNodeList1.nextNode
set oNode2 = oSize.nextNode
set oNode3 = odatereceived.nextNode
set onode4 = fromEmail.nextNode
set onode5 = subject.nextNode
wscript.echo onode4.text & " " & onode5.text
objParentRS("MailDate") =
&amp; " " & Mid(oNode3.text,12,8))
objParentRS("FolderName") = unescape(Replace(strURL,falias,""))
objParentRS("MailFrom") = onode4.text
objParentRS("Subject") = Right(onode5.text,255)
objParentRS("Size") = oNode2.Text
End If
end sub


Todd said...

The script appears to run fine, but where would I find the report folder and htm files?

All I did was use your script and wrote a batch file to launch it as you suggested.

Are there other things I should be doing? Should I be running it from a certain folder?


Todd said...

OK, so I created C:\Temp and it is putting files there for each user.

The problem now is that all of the pages are empty of data. The webpage has header fields but there is not data for anyone, even administrator, under which context the script was run.

Glen said...

You might want to try remming out the on error resume next line this will allow you to see any errors the script is generating. Let us know what the errors are your getting. The other thing to try is maybe you are running only https on the admin virtual share. So you will need to change the following line

falias = "http://" & servername & "/exadmin/admin/" & dpDefaultpolicy & "/mbx/"


falias = "https://" & servername & "/exadmin/admin/" & dpDefaultpolicy & "/mbx/"


Superz said...

C:\zshowtop10.vbs(106, 4) msxml3.dll: The system cannot locate the resource specified

Do you need to install the ExAdmin tool as well before running this script.