August 2008 - Posts

It was pointed out to me, by a party that shall remain nameless, that I depended in today's earlier post Finding Disk Space Used By Exchange Version 2, that I ASSumed that msExchCurrentServerRoles would never be higher than 64 and that that was a bad assumption on my part, for reasons that shall also remain nameless.

So, here is a "fixed" version of just the ShowServerInfo() subroutine. Just rip the old one out, and add the new one in:

Const Exch_Role_Edge         = 64   ' 0x40
Const Exch_Role_Hub          = 32   ' 0x20
Const Exch_Role_UM           = 16   ' 0x10
Const Exch_Role_ClientAccess = 4    ' 0x04
Const Exch_Role_Mailbox      = 2    ' 0x02

Sub ShowServerInfo (ByVal spaces, ByVal strServer)
	Dim objServer, obj
	Dim str, i, msExchCurrentRoles

	On Error Resume Next
	Err.Clear

	Set objServer = GetObject ("LDAP://" & strServer)
	If Err = 0 Then
		msExchCurrentRoles = objServer.Get ("msExchCurrentServerRoles")
		If Err = 0 Then
			i = msExchCurrentRoles
			str = ""
			'' got a exchange 2007 or higher box
			If (msExchCurrentRoles and Exch_Role_Mailbox) = Exch_Role_Mailbox Then
				str = str & "Mailbox "
				i = i - Exch_Role_Mailbox
			End If

			If (msExchCurrentRoles and Exch_Role_ClientAccess) = Exch_Role_ClientAccess Then
				str = str & "ClientAccess "
				i = i - Exch_Role_ClientAccess
			End If

			If (msExchCurrentRoles and Exch_Role_UM) = Exch_Role_UM Then
				str = str & "UnifiedMessaging "
				i = i - Exch_Role_UM
			End If

			If (msExchCurrentRoles and Exch_Role_Hub) = Exch_Role_Hub Then
				str = str & "HubTransport "
				i = i - Exch_Role_Hub
			End If

			If (msExchCurrentRoles and Exch_Role_Edge) = Exch_Role_Edge Then
				str = str & "EdgeTransport "
				i = i - Exch_Role_Edge
			End If

			If i <> 0 Then
				str = str & vbCRLF & Space (spaces) & _
					"Unknown bits set in msExchCurrentRoles = 0x" & Hex(i)
			End If

			e Space (spaces) & "Exchange roles installed: " & str
		Else
			i = objServer.Get ("serverRole")
			If i = 1 Then
				e Space (spaces) & "Exchange role: Front-end server"
			Else
				e Space (spaces) & "Exchange role: Back-end server"
			End If
		End If
		Err.Clear

		obj = objServer.Get ("serialNumber")
		If Err = 0 Then
			If IsNull (obj) Then
				e Space (spaces) & "No version found"
			ElseIf IsEmpty (obj) Then
				e Space (spaces) & "Version string is empty"
			ElseIf IsArray (obj) Then
				For Each str in obj
					e Space (spaces) & str
				Next
			Else
				e Space (spaces) & obj
			End If
		End If
		Err.Clear

	End If
	e " "
	Set objServer = Nothing

	On Error Goto 0
End Sub
Until next time...

As always, if there are items you would like me to talk about, please drop me a line and let me know!

In July of 2006, I had a blog entry named Finding Disk Space Used By Exchange, and it was quite well received and it worked well for most people.

However, it had a few issues:

  1. It doesn't work with Exchange Server 2007 because the streaming file was removed from Exchange
  2. If a Recovery Storage Group was present, but the files were removed, it would fail
  3. If an Exchange Server was not reachable via UNC path, it would fail

There was also one out-and-out bug: Per-server totals were not zeroed after each server was evaluated.

And from a feature perspective, lots of folks wanted some information about the storage groups (circular logging, log prefix, etc.) and the servers (version and server roles).

There is a Microsoft script that also contains much of this information, but it is based on CDOEXM, which means that it pretty much must run on an Exchange 2003 server; and it also has the same three issues mentioned above (plus a couple of others - for example, if the EDB and STM files were separated on different volumes or path, it would also fail). James Chong, another Microsoft Exchange MVP, pointed out that script to me. It does have a feature of reporting the current sizes of the logs being used by a storage group.

So, in this version 2, I've fixed the three issues above, fixed the out-and-out bug, and now I provide a fair bit of information about each storage group. I think I also now check for all possible errors that could occur. As before, this script does depend on my Utility Libraries for Exchange Scripting.

I have tested this script in an environment containing Exchange 2000, Exchange 2003, and Exchange 2007. It should work for you with all of those too!

Some sample output from my small test environment:

Exchange Organization Name: First Organization
Default SMTP address for organization: essential.local

All Exchange Servers in forest DC=essential,DC=local
	Server Name: WIN2003-EXCH
	Server Name: WIN2008-EXCH
 
Server name: WIN2003-EXCH
  Exchange role: Back-end server
  Version Version 6.5 (Build 7638.2: Service Pack 2)
 
  Storage group: First Storage Group
    Circular Logging is False
    Zero out deleted databse pages is False
    Log file prefix is E00
    Transaction log location is C:\Program Files\Exchsrvr\mdbdata
    System path location is C:\Program Files\Exchsrvr\mdbdata
 
    Store: Public Folder Store (WIN2003-EXCH)
      EDB file: C:\Program Files\Exchsrvr\mdbdata\pub1.edb
      Size: 18 megabytes
      SLV file: C:\Program Files\Exchsrvr\mdbdata\pub1.stm
      Size: 4 megabytes
      Store Size Total: 22 megabytes
    Store: Mailbox Store (WIN2003-EXCH)
      EDB file: C:\Program Files\Exchsrvr\mdbdata\priv1.edb
      Size: 6 megabytes
      SLV file: C:\Program Files\Exchsrvr\mdbdata\priv1.stm
      Size: 8 megabytes
      Store Size Total: 14 megabytes
    Storage Group total: 36 megabytes
  Server total: 36 megabytes
 
Server name: WIN2008-EXCH
  Exchange roles installed: HubTransport ClientAccess Mailbox
  Version Version 8.1 (Build 30240.6)
 
  Storage group: First Storage Group
    Circular Logging is False
    Zero out deleted databse pages is False
    Log file prefix is E00
    Transaction log location is C:\Program Files\Microsoft\Exchange Server\Mailbox\First Storage Group
    System path location is C:\Program Files\Microsoft\Exchange Server\Mailbox\First Storage Group
 
    Store: Mailbox Database
      EDB file: C:\Program Files\Microsoft\Exchange Server\Mailbox\First Storage Group\Mailbox Database.edb
      ** Could not open file: \\WIN2008-EXCH\C$\Program Files\Microsoft\Exchange Server\Mailbox\First Storage Group\Mailbox Database.edb
      Could not find streaming filename in active directory
      Store Size Total: 0 megabytes
    Storage Group total: 0 megabytes
  Storage group: Recovery Storage Group
    Circular Logging is False
    Zero out deleted databse pages is False
    Log file prefix is R00
    Transaction log location is C:\rsg
    System path location is C:\rsg
 
    Store: Mailbox Database
      EDB file: C:\rsg\Mailbox Database.edb
      ** Could not open file: \\WIN2008-EXCH\C$\rsg\Mailbox Database.edb
      Could not find streaming filename in active directory
      Store Size Total: 0 megabytes
    Storage Group total: 0 megabytes
  Storage group: Second Storage Group
    Circular Logging is False
    Zero out deleted databse pages is False
    Log file prefix is E01
    Transaction log location is C:\Program Files\Microsoft\Exchange Server\Mailbox\Second Storage Group
    System path location is C:\Program Files\Microsoft\Exchange Server\Mailbox\Second Storage Group
 
    Store: Public Folder Database
      EDB file: C:\Program Files\Microsoft\Exchange Server\Mailbox\Second Storage Group\Public Folder Database.edb
      ** Could not open file: \\WIN2008-EXCH\C$\Program Files\Microsoft\Exchange Server\Mailbox\Second Storage Group\Public Folder Database.edb
      Could not find streaming filename in active directory
      Store Size Total: 0 megabytes
    Storage Group total: 0 megabytes
  Server total: 0 megabytes
 
Organization total: 36 megabytes

And here is the code for your enjoyment:

<JOB>
<SCRIPT language="VBScript">
Option Explicit
</SCRIPT>
<SCRIPT language="VBScript" src="lib/constants.vbs" mce_src="lib/constants.vbs">
<script language="VBScript" src="lib/ado.vbs" mce_src="lib/ado.vbs"           >
<script language="VBScript" src="lib/report.vbs" mce_src="lib/report.vbs"        >
<script language="VBScript" src="lib/systeminfo.vbs" mce_src="lib/systeminfo.vbs"    >
<script language="VBScript" src="lib/queries.vbs" mce_src="lib/queries.vbs"       >
<script language="VBScript">
'
' exch-store-space.wsf
'
' This program reports on the space utilization of the EDB and STM
' files for all stores for all storage groups for all servers in the
' Exchange organization.
'
' Updated on August 8, 2008 to deal with missing files
' Also now works with Exchange 2007 (no msExchSLVFile attribute on 2007 servers)
' Also dumps some data about each individual storage group
' Also won't abort on missing Recovery Storage Group
' Also total per-server correctly
'
Dim arr, arrSG, arrStore
Dim s, s1, strServer
Dim i, j, k, orgTotal
Dim objFSO

Call GetSystemInfo
Call GetAllServers

Set objFSO = CreateObject ("Scripting.FileSystemObject")

arr = Split (strServerListDN, ";")

For i = LBound (arr) To UBound (arr)
	Dim serverTotal

	serverTotal = 0
	s = arr (i)
	strServer = Mid (Left (s, Instr (s, ",") - 1), 4)
	e "Server name: " & strServer

	Call ShowServerInfo (2, s)

	Call GetStorageGroupsForServer (arr (i))

	arrSG = Split (strServerSGDN, ";")

	For j = LBound (arrSG) To UBound (arrSG)
		Dim sgTotal

		sgTotal = 0

		s = arrSG (j)
		s1 = Mid (Left (s, Instr (s, ",") - 1), 4)
		e Space (2) & "Storage group: " & s1

		Call ShowStorageGroupInfo (4, s)

		Call GetStoresForStorageGroupLDAP (s)

		arrStore = Split (strStoreDN, ";")

		For k = LBound (arrStore) To UBound (arrStore)
			Dim obj, objFile, strFile, storeTotal

			storeTotal = 0

			s = arrStore (k)
			s1 = Mid (Left (s, Instr (s, ",") - 1), 4)
			e Space (4) & "Store: " & s1

			On Error Resume Next
			Err.Clear

			Set obj = GetObject ("LDAP://" & s)

			If Err Then
				e Space (6) & "** Could not open store in active directory"
			Else
				s = obj.Get ("msExchEDBFile")
				If Err Then
					e Space (6) & "** Could not find EDB filename in active directory"
				Else
				
					e Space (6) & "EDB file: " & s
					' must convert the file to a UNC path
					strFile = "\\" & strServer & "\" & _
						Left (s, 1) & "$" & Mid (s, 3)
					Set objFile = objFSO.GetFile (strFile)
					If Err Then
						e Space (6) & "** Could not open file: " & strFile
					Else
						e Space (6) & "Size: " & _
							FormatNumber (objFile.Size / (1024 * 1024), 0) & _
							" megabytes"
						storeTotal = storeTotal + (objFile.Size / (1024 * 1024))
					End If
				End If
				Err.Clear

				s = obj.Get ("msExchSLVFile")
				If Err Then
					e Space (6) & "Could not find streaming filename in active directory"
				Else
					e Space (6) & "SLV file: " & s
					strFile = "\\" & strServer & "\" & _
						Left (s, 1) & "$" & Mid (s, 3)
					Set objFile = objFSO.GetFile (strFile)
					If Err Then
						e Space (6) & "** Could not open file: " & strFile
					Else
						e Space (6) & "Size: " & _
							FormatNumber (objFile.Size / (1024 * 1024), 0) & _
							" megabytes"
						storeTotal = storeTotal + (objFile.Size / (1024 * 1024))
					End If
				End If
				Err.Clear

				e Space (6) & "Store Size Total: " & _
					FormatNumber (storeTotal, 0) & _
					" megabytes"
				sgTotal = sgTotal + storeTotal
			End If

			On Error Goto 0
		Next

		e Space (4) & "Storage Group total: " & FormatNumber (sgTotal, 0) & _
			" megabytes"
		serverTotal = serverTotal + sgTotal
	Next

	e Space (2) & "Server total: " & FormatNumber (serverTotal, 0) & " megabytes"
	e " "
	orgTotal = orgTotal + serverTotal
Next
e "Organization total: " & FormatNumber (orgTotal, 0) & " megabytes"

Call ClearSystemInfo

Sub ShowServerInfo (ByVal spaces, ByVal strServer)
	Dim objServer, obj
	Dim str, i

	On Error Resume Next
	Err.Clear

	Set objServer = GetObject ("LDAP://" & strServer)
	If Err = 0 Then
		i = objServer.Get ("msExchCurrentServerRoles")
		If Err = 0 Then
			str = ""
			'' got a exchange 2007 or higher box
			If i >= 64 Then
				str = str & "EdgeTransport "
				i = i - 64
			End If
			If i >= 32 Then
				str = str & "HubTransport "
				i = i - 32
			End If
			If i >= 16 Then
				str = str & "UnifiedMessaging "
				i = i - 16
			End If
			If i >= 4 Then
				str = str & "ClientAccess "
				i = i - 4
			End If
			If i >= 2 Then
				str = str & "Mailbox"
				i = i - 2
			End If
			e Space (spaces) & "Exchange roles installed: " & str
		Else
			i = objServer.Get ("serverRole")
			If i = 1 Then
				e Space (spaces) & "Exchange role: Front-end server"
			Else
				e Space (spaces) & "Exchange role: Back-end server"
			End If
		End If
		Err.Clear

		obj = objServer.Get ("serialNumber")
		If Err = 0 Then
			If IsNull (obj) Then
				e Space (spaces) & "No version found"
			ElseIf IsEmpty (obj) Then
				e Space (spaces) & "Version string is empty"
			ElseIf IsArray (obj) Then
				For Each str in obj
					e Space (spaces) & "Version " & str
				Next
			Else
				e Space (spaces) & "Version " & obj
			End If
		End If
		Err.Clear

	End If
	e " "
	Set objServer = Nothing

	On Error Goto 0
End Sub

Sub ShowStorageGroupInfo (ByVal spaces, ByVal strSG)
	Dim objSG
	Dim str, i
	Dim prefix

	prefix = "msExchESEParam"   '' all storage group attributes start with that prefix

	On Error Resume Next
	Err.Clear

	Set objSG = GetObject ("LDAP://" & strSG)
	If Err = 0 Then
		i = objSG.Get (prefix & "CircularLog")
		If Err = 0 Then
			e Space (spaces) & "Circular Logging is " & CBool(i)
		End If
		Err.Clear

		i = objSG.Get (prefix & "ZeroDatabaseDuringBackup")
		If Err = 0 Then
			e Space (spaces) & "Zero out deleted databse pages is " & CBool (i)
		End If
		Err.Clear

		str = objSG.Get (prefix & "BaseName")
		If Err = 0 Then
			e Space (spaces) & "Log file prefix is " & str
		End If
		Err.Clear

		str = objSG.Get (prefix & "LogFilePath")
		If Err = 0 Then
			e Space (spaces) & "Transaction log location is " & str
		End If
		Err.Clear

		str = objSG.Get (prefix & "SystemPath")
		If Err = 0 Then
			e Space (spaces) & "System path location is " & str
		End If
		Err.Clear

		e " "
	Else
		e Space (spaces) & "** Couldn't open " & strSG
	End If
	Set objSG = Nothing

	On Error Goto 0
End Sub
</SCRIPT>
</JOB>

Until next time...

As always, if there are items you would like me to talk about, please drop me a line and let me know!