Getting a List of Storagegroups in a PowerShell Script

In Getting Our Computername in a PowerShell Script, you learned how to do just that - store the running computer name (the short NetBIOS name) into a PowerShell variable. At the same time, you learned WHY and HOW it worked. From this point forward, I'll presume that you've executed this particular PowerShell statement:

$global:computername = (gc env:computername)

Note that this causes the storage scope of the $computername variable to be global. It can be interrogated from any function as just $computername (as long as there are no variables that have the same name in a closer scope [such as local scope or function scope]), but for the global value to be updated requires the inclusion of the "global:" specifier.

Thankfully, when we install the Exchange Tools on a workstation or server, one of the Exchange cmdlets is get-storagegroup. To see a list of all the storage groups on all the (Exchange 2007+) servers in your Exchange organization, you just enter get-storagegroup at the prompt in an Exchange Management Shell (EMS).

So, are we done? Nope.

While that list is probably all we need if we are working from a command prompt, if we are writing a script, we are probably interested in more than just the displayed information. For example, consider these requirements of the things we may want to know:

  • where the system files for the storage groups are placed
  • where the log files for the storage groups are placed
  • the log file prefix for the storage groups
  • a list of all the disk volumes used by the storage groups

Using get-storagegroup plus a feature of PowerShell makes all of this pretty easy. The feature we will use is called an associative array. Also known as a hash array, for those of you with a Perl background. An associative array is basically a two-dimensional array that is mapped to a single dimensional array based on the index element. Unlike a normal array, an associative array can be indexed by almost anything - a string, a regular expression, an integer - anything you may want to use. In order to pull off this "magic", an associative array actually consists of two parallel arrays - the keys array and the values array. Now, these arrays get indexed as you are used to - by integers, starting at zero. However, their contents may consist of any object.

Note: in Perl, associative arrays aren't quite as flexible. In PowerShell, you can literally have an object as a key and an object as a value. This allows you the flexibility of storing arbitrarily complex values into an associative array. However, the search through the keys array is linear. Thus, you should probably keep the arrays fairly small.

While in the EMS, you may get the impression that get-storagegroup returns text, that would be incorrect. Instead, get-storagegroup returns a collection (which is a fancy word for an enumerable array) of objects of type Microsoft.Exchange.Data.Directory.SystemConfiguration.StorageGroup. That's a mouthful. Let's just say that it returns an array containing all of the storage group objects.

So, to process that array, you would do something like this:

$collection = get-storagegroup
foreach ($entry in $collection)
        ... do something with $entry ...

However, you are more likely to want to look at the storage groups on a particular server. So, that leads you to a construct like this:

$collection = get-storagegroup -server $computername
foreach ($entry in $collection)
        ... do something with $entry ...

Note the tie-in back to $computername! This will access the global $computername variable which has the name of the running computer stored within it.

Now, what does a storage group consist of? It really has only two things:

  • log files
  • system files

And each of those two things also has a prefix associated with them (which allows multiple storage groups to share a single directory). All log files have a particular format:

        <prefix> <log identifier> .LOG

All system files (there is only a single system file per storage group) have a particular format:

        <prefix> .CHK

A prefix follows this format:

        [E <two-digit-storage-group-number>] | [R00]

In words, a prefix is either R00, which is exclusively for the Recovery Storage Group, of which there may be only one per server; or the letter 'E' followed by a two-digit number. The number represents the index of the storage group on the server, where the first storage group created is '00' the second is '01', etc. So the first storage group created on a server will have a prefix of E00.

For that same first storage group, the log files have a format of E00*.log and the system file is named E00.chk.

Note: the number of digits contained in <log identifier> went from five hexidecimal digits in Exchange 2003 (and all earlier versions) to eight hexidecimal digits in Exchange 2007+. Even with the change in log file size from 5 MB to 1 MB, this means that you get 80+ times as many log files before rollover in Exchange 2007+ as you did in earlier versions of Exchange Server.

Let's see...what else? Oh yes - you want to ignore recovery storage groups. Except for recoveries from backup, you are not supposed to touch them.

Now, given all those above details, what can we do with them? This!

## $volumes will contain the volume letters used by all named
## files and directories.

$global:volumes = @{}

## any storage group will contain:
## a] a system file directory
## b] a log file directory
## c] a filename for each database within the SG
## $pathPattern contains the dos patterns of files in the storage group

$global:pathpattern = @{}		### Exx.chk, Exx*.log, *.edb

function getStorageGroups
        $count = 0
	# locate the storage groups and their log files and system files
	$colSG = get-StorageGroup -server $computername
	if ($colSG.Count -lt 1)
		write-host "No storage groups found on server $computername"
		return 1

	## parse the pathnames for each SG to determine what
	## volumes it stores data upon and what directories are used

	foreach ($sg in $colSG)
		if ($sg.Recovery)
			write-host ("Skipping RECOVERY STORAGE GROUP " + $sg.Name)


		$prefix  = $sg.LogFilePrefix
		$logpath = $sg.LogFolderPath.ToString()
		$syspath = $sg.SystemFolderPath.ToString()

		write-host $sg.Name.ToString() "`t" $sg.Guid.ToString()
		write-host "`tLog prefix:      $prefix"
		write-host "`tLog file path:   $logpath"
		write-host "`tSystem path:     $syspath"

		## E00*.log
		$pathpattern.(join-path $logpath ($prefix + "*.log")) = 1

		$vol = $logpath.SubString(0, 1)
		$volumes.$vol += 1

		## E00.chk
		$pathpattern.(join-path $syspath ($prefix + ".chk")) = 1

		$vol = $syspath.SubString(0, 1)
		$volumes.$vol += 1

		write-host " "

	if ($count -lt 1)
		write-host "No storage groups found on server $computername"
		return 1

	return 0

This routine stores, for each storage group, the files that are contained within that storage group. It also stores away the disk volumes used by that storage group. For the write-host output of the function, you could surround the blocks by $debug conditional statements to minimize the output of the routine (or just remove them entirely).

So, what is contained within storage groups? Databases! In our next post in this series, you'll learn how to deal with them programatically too.

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!

Published Thursday, December 11, 2008 2:12 PM by michael
Filed under: , ,


Friday, December 12, 2008 11:41 AM by subject: exchange

# Weekend reading

Monitoring Exchange 2007 With System Center Operations Manager 2007 (Part 2) Exchange 2007 Install and

Tuesday, December 23, 2008 12:05 PM by Michael's meanderings...

# Getting a List of Stores in a PowerShell Script

In my last post, Getting a List of Storage Groups in a PowerShell Script , you saw how to use the information

Tuesday, January 06, 2009 7:17 AM by Michael's meanderings...

# Exchange 2007 and Windows 2008: Offline Exchange Backup

In my article Getting a List of Stores in a PowerShell Script you learned how to obtain a list of all

Friday, January 23, 2009 1:56 PM by Michael's meanderings...

# Exchange 2007 and Windows 2008: Using Diskshadow for Online Exchange Backup (part 5 of 7)

If you hadn&#39;t noticed, I&#39;ve been following a theme the last couple of months with some of the

Monday, January 26, 2009 11:38 AM by Michael's meanderings...

# Exchange 2007 and Windows 2008: Online Exchange Backup (part 6 of 7)

In the first five parts of this series, I&#39;ve given you the background to understand how Exchange

Tuesday, January 27, 2009 1:00 AM by billig computer

# billig computer

Tolle Geschenke zu Top-Preisen! Bestellen Sie beim Technik-Experten

Sunday, April 26, 2009 4:08 AM by Data file recovery expert

# Data file recovery expert

Hopefully file recovery is a breeze for you. Here's the however word. yes if you can't do it yourself you'll need help if your data is vital to your business. First try out the software and if that falls short of your expectation service is standing by

Tuesday, September 29, 2009 6:35 AM by serverhousing

# serverhousing

Das neue Rechenzentrum wird über Wärmetauscher via Grundwasser gekühlt und mit Strom durch ein neues Biomasse- Kraftwerk vor Ort versorgt werden. Zudem wird das komplette Gebäude viele weitere„ Öko“- Komponenten umfassen, wie z. B. die Integration von