January 2011 - Posts

Long ago and far away (way back in 2006) I wrote an article for finding the disk space used by Exchange 2000 and Exchange 2003, Finding disk space used by Exchange. While this worked through a few Exchange 2007 betas, by Exchange 2007 RTM, the STM file had been removed, and the original script would bomb, making the script only useful for Exchange 2000 and Exchange 2003.

Astute VBscript users would determine how to comment out the few lines causing the issue, but for most folks, that was too much.

So, I finally updated that script for Exchange 2007 and Exchange 2010 (tested on both) and updated it to PowerShell.

This is the script:

function build-object( [string]$server, [string]$edbfilepath, [string]$displayName )
{
	write-debug "build-object enter"
	write-debug "build-object server: $server"
	write-debug "build-object edbfilepath: $edbfilepath type $($edbfilepath.gettype().ToString())"
	write-debug "build-object displayName: $displayName"
	write-debug " "

	$o = "" | Select Name, Display, Length
	$o.display = $displayName

#	if( $server -eq $env:ComputerName )
	if( 0 )
	{
		$r = dir -ea 0 $EdbFilePath
	}
	else
	{
		$filename = "\\" + $server + "\" + $edbfilepath.SubString( 0, 1 ) + 
			"$" + $edbfilepath.SubString( 2 )
		write-debug "Calculated filename: $filename"
		$r = dir -ea 0 $filename
	}

	$o.Name = $r.Name
	$o.Length = $r.Length

	write-debug "build-object exit"
	return $o
}

Get-Command Get-ExchangeServer -ea 0 | out-null
if( ! $? )
{
	write-error "You must run this script within an Exchange Management Shell"
	return
}

$legacy = Get-ExchangeServer $env:Computername -ea 0
if( ! $? )
{
	write-error "You must run this script on an Exchange server"
	return
}

$org = $legacy.ExchangeLegacyDN.SubString( 3 )
$org = $org.SubString( 0, $org.IndexOf( '/' ))
"Exchange Organization Name: $org"

$default = ( Get-AcceptedDomain |? { $_.Default -eq $true } ).DomainName
"Default SMTP Domain: $default"

$forest = $legacy.DistinguishedName.SubString( $legacy.DistinguishedName.IndexOf( 'DC=' ) )
"Active Directory Forest: $forest"
" "
"All Exchange Servers in forest"
$servers = Get-ExchangeServer | select Name
foreach( $server in $servers )
{
	"`tName: $($server.Name)"
}
" "
"All Mailbox Servers in forest"
$mailbox = Get-ExchangeServer |? { $_.ServerRole -match "Mailbox" } | Select Name, IsMemberOfCluster
foreach( $server in $mailbox )
{
	"`tName: $($server.Name)"
}
" "

"Acquiring size of databases..."
" "
[int64]$totalSize = 0
foreach( $server in $mailbox )
{
	"Server name: $($server.Name)"

	[int64]$serverSize = 0
	$serverArray = @()

	if( $server.IsMemberOfCluster -eq 'Yes' )
	{
		$mailboxServer = Get-MailboxServer $server.Name -ea 0
		$myServer = $mailboxServer.RedundantMachines[0]

		$serverArray += (get-mailboxdatabase      -server $server.Name -ea 0) |% {
			build-object $myServer $_.EdbFilePath $_.AdminDisplayName;
		}

	}
	else
	{
		$serverArray += (get-mailboxdatabase      -server $server.Name -ea 0) |% {
			build-object $_.Server $_.EdbFilePath $_.AdminDisplayName;
		}
	}

	$serverArray += (get-publicfolderdatabase -server $server.Name -ea 0) |% {
		build-object $_.Server $_.EdbFilePath $_.AdminDisplayName;
	}

	$serverArray |% { $serverSize += $_.Length }

	foreach( $element in $serverArray )
	{
		if( $element )
		{
			"`tDatabase name: $($element.Display)"
			"`t`tEDB File: $($element.Name)"
			"`t`tEDB size: {0} bytes, {1} GB" -f $element.Length.ToString("N0"), ($element.Length / 1GB).ToString("N3")
			#$element
		}
	}
	"Total size of databases on server {0} bytes, {1} GB" -f  $serverSize.ToString("N0"), ($serverSize / 1GB).ToString("N3")
	" "
	$totalSize += $serverSize
}

"Total size of all databases in organization {0} bytes, {1} GB" -f  $totalSize.ToString("N0"), ($totalSize / 1GB).ToString("N3")

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!

Posted by michael | with no comments
Filed under: , ,

Each January, April, July, and October Microsofts recognizes certain individuals who - at no payment - assist in providing support to other Microsoft customers in various online forums, mailing lists, news groups, etc. Microsoft calls these people MVPs - Most Valuable Professionals. These people are "Independent Experts providing Real World Answers." For more information about the MVP program, click here.

I've been privileged to be awarded MVP for Exchange Server seven times now. My award cycle is in July - so every year, the week before July 1, I start getting just a little bit nervous as to whether I'll be awarded for the coming year. So far - so good. It is truly a privilege to be an MVP. In Exchange, there are a number of very notable individuals with whom I share the honor: Ed Crowley, Chris Scharff, Paul Robichaux, Tony Redmond, Jim McBee, David Elfassy - names that are likely on your bookshelf if you own any Exchange books. There are, of course, over a hundred other good people I didn't name. Many Exchange MVPs have gone on to work for Microsofton the Exchange Team or on the Lync (OCS) Team.

As of today, there are 88 types of MVPs, totalling 3,799 individuals, world-wide. Of those, there are 113 MVPs for Exchange Server. There are also 49 MVPs for PowerShell.

Now, an MVP doesn't have to make themselves publicly known. They can also choose to make their information only available to other MVPs. (This is somewhat common on the Microsoft forums, where people do not want to their company associations to be known, for whatever reason.)

The question arose regarding how to find this information out programmatically on a PowerShell MVP list. A new PowerShell MVP, Tome Tanasovski, was the first person to post a solution, soon followed by Claus Nielsen, another new PowerShell MVP. Claus' solution improved on Tome's by allowing an MVP to connect the secured MVP site in order to count those MVPs whose information was not public. It also switched from using System.Web .NET classes to using Internet Explorer automation.

I ran into some challenges using their versions, and wanted to add error handling and cleanup to their implementations. Thus, mine was born.

While you personally may not care about the number of MVPs and MVP specialities that exist on a day to day basis, you probably can find some PowerShell techniques here that can come in useful! Including being able to sign into a protected site...

Here is the script:

##
## Count-MVPs.ps1
##
## Michael B. Smith
## January, 2011
##
## Based on an original work by
## Tome Tanasovski, PowerShell MVP
## which was enhanced by
## Claus Nielsen, PowerShell MVP
##
## But this version has more features. :-)
##
## Note: Kirk Munro has yet another version of this script that is
## very developer-ish.
##

Param(
	[switch]$gridview,
	[string]$username,
	[string]$password,
	[int]$timeout = 15
)

[System.Reflection.Assembly]::LoadWithPartialName( "System.Web" ) | out-null

$ie = $null

try
{
	if( $username )
	{
		$URI = "https://login.live.com/ppsecure/secure.srf?lc=1033&sf=1&id=259214&ru=https://mvp.support.microsoft.com/communities/mvp.aspx&tw=0&fs=0&kv=0&cb=wizid%3df4502d34-3b8f-4a04-b741-289e08aa1782%26lcid%3d1033%26returnurl%3dhttps%253a%252f%252fmvp.support.microsoft.com%252fcommunities%252fmvp.aspx%26brand%3dMicrosoft&cbcxt=&wp=MCMBI&wa=wsignin1.0&wreply=https://mvp.support.microsoft.com/communities/mvp.aspx&wlsu=1"
	}
	else
	{
		$URI = "https://mvp.support.microsoft.com/communities/mvp.aspx"
	}

	$ie = new-object -com "InternetExplorer.Application"
	$ie.visible = $false
	$ie.navigate( $URI )

	$count = 0
	while( $ie.Busy -eq $true )
	{
		Start-Sleep -Milliseconds 1000
		$count++

		if( $count -gt $timeout )
		{
			write-error "Timeout ($timeout seconds) waiting for content from the MVP Community website"
			return
		}
	}

	$doc = $ie.Document
	#$ie.Document.Body.InnerText.ToString()

	if( $username )
	{
		## log in

		$user = $doc.getElementById( "i0116" ) ## login
		if( $user )
		{
			$user.value = $username
		}
		else
		{
			"Document element named 'Login' not found"
		}

		$pass = $doc.getElementById( "i0118" ) ## passwd
		if( $pass )
		{
			$pass.value = $password
		}
		else
		{
			"Document element named 'Passwd' not found"
		}

		$Logon = $doc.getElementById( "i0011" )  ## SI
		if( $Logon )
		{
			$Logon.click() 
		}
		else
		{
			"Document element named 'SI' not found"
		}

		$count = 0
		while( $ie.LocationURL -ne "https://mvp.support.microsoft.com/communities/mvp.aspx" )
		{
			if( $ie.LocationURL -eq "https://mvp.support.microsoft.com/mvpinvalidsignin.aspx" )
			{
				write-error "Invalid username / password combination"
				return
			}

			Start-Sleep -Milliseconds 1000
			$count++

			if( $count -gt $timeout )
			{
				"Location URL is $($ie.LocationURL)"
				write-error "Timeout ($timeout seconds) waiting to connect to the MVP Community website"
				return
			}
		}

		$count = 0
		while( $ie.Busy -eq $true )
		{
			Start-Sleep -Milliseconds 1000
			$count++

			if( $count -gt $timeout )
			{
				"IE still busy after $count seconds"
				write-error "Timeout ($timeout seconds) waiting for content from the MVP Community website"
				return
			}

		}

		#$ie.Document.Body.InnerHTML.ToString()
	}

	$regex = [regex]'(?m)<A href=\"(\/communities\/mvp.aspx\?product=1&amp;competency=(\S+))\"'
	$objects = @()
	$totMVPs = 0

	$return = $ie.Document.Body.InnerHTML.ToString()
	$matches = $regex.Matches( $return )
	" "
	"There were $($matches.Count) specialties found"
	" "
	$matches |% {
		$specialty = [System.Web.HttpUtility]::UrlDecode( $_.Groups[2].Value )
		"Scanning MVPs for $specialty..."

		$obj = New-Object psobject 
		$obj |Add-Member NoteProperty -Name 'Group' -Value ($specialty)

		$URI = "https://mvp.support.microsoft.com$($_.Groups[1].Value -replace 'amp;','')"
		while( $ie.Busy -eq $true ) { Start-Sleep -Milliseconds 1000; }
		$ie.navigate( $URI )

		while( $ie.Busy -eq $true ) { Start-Sleep -Milliseconds 1000; }
		if( ($ie.Document.Body.InnerText.ToString()) -match 'Results 1 \- \d+ of (\d+)' ) 
		{
			$MVPs=""
			$MVPs = [int]$matches[1] 
			"...$MVPs found"
			$obj |Add-Member NoteProperty -Name 'Count' -Value ($MVPs)
			$totMVPs += $MVPs
		}
		$objects += $obj
	}

	" "
	"Total MVP Competencies $($objects.Count)"
	"Total MVPs listed $totMVPs"
	if( $gridview )
	{
		$objects |sort Count -Descending |ogv
	}
	else
	{
		$objects |sort Count -Descending
	}

}
finally
{
	if( $ie )
	{
		$ie.Quit()
		$ie = $null
	}
}

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!

Posted by michael | with no comments
Filed under: , ,