 |
[August 25th, 2008] Check the home page regarding PowerShell related news from a brand new sponsor: Idera!
|
|
Disaster Recovery Enumerate E2K7 Servers
Last Post 18 Dec 2007 04:41 PM by DonJ. 11 Replies.
|
Sort:
|
|
Prev Next |
You are not authorized to post a reply. |
|
KarlMitschke
 Basic Member Posts:164

 |
| 26 Nov 2007 07:54 PM |
|
This script will find all Exchange 2007 servers in your organization and output information to a text file. - useful for Disaster Recovery planning. Requires remote WMI on the exchange servers.
Set-PSDebug -Strict $snapins = Get-PSSnapin |select name $snapincount=0; $found = $false do { $foundname = $snapins[$snapincount].name if ($foundname -eq "Microsoft.Exchange.Management.PowerShell.Admin") #Exchange Shell already loaded { $found = $True break } $snapincount++} while ($snapincount -lt $snapins.Count)
if ($found -ne $True) { Add-PSSnapin "Microsoft.Exchange.Management.PowerShell.Admin" } $exchangeservers = Get-ExchangeServer |where-object {$_.admindisplayversion.major -gt 7} #(Now we have all the servers) $date = (get-date).toshortdatestring() $role = $null foreach($exchangeserver in $exchangeservers) { Write-Host "Processing $exchangeserver ...." $version = $exchangeserver.admindisplayversion.major $server = $exchangeserver.name.tostring() $role = $exchangeserver.serverrole $binaries = $exchangeserver.datapath $binaries = $binaries.PathName.ToString() $binaries = $binaries.replace("\Mailbox","") $edition = $exchangeserver.edition $file = New-Item -type file "$Server-ConfigReport.txt" -force add-content $file "*********************** $Server ************************************" add-content $file "Report Generated: $date" add-content $file "" add-content $file "This is an Exchange 2007 server, with the $role role(s) installed." add-content $file "This server is running Exchange $edition edition." add-content $file "" add-content $file "*********Exchange Installation Directory*********" add-content $file "Exchange binaries are installed in $binaries" add-content $file "" add-content $file "********MEMORY********" $memory = @(Get-WmiObject Win32_PhysicalMemory -computer $Server |select capacity) $a = 0 [int64]$total=0 do { $total = $total + $memory[$a].capacity; $a++ } while ($a -lt $memory.Count) $total = [math]::round($total/1024/1024,0) add-content $file "Total memory in system is $total (MB)" $total = "" $memory = "" add-content $file "" add-content $file "*********PROCESSOR*********" add-content $file "!!!!!WARNING!!!!!" add-content $file "Processors listed may be physical or virtual (hyperthreading) - WMI can't distinguish the two" add-content $file "" $processors = get-wmiobject win32_processor -computer $Server | select AddressWidth,CurrentClockSpeed,Description,DeviceID,L2CacheSize,Name,ProcessorID $a = 0 if ($processors.count -ne $null) { do { $processor= $processors[$a] |format-list |Out-String $processor= $processor.trim() $loop = $a $loop++ add-content $file "------------Processor $loop ----------------------"; add-content $file $processor; add-content $file "" $a++ } while ($a -lt $processors.Count) } else { $processor= $processors |format-list |Out-String $processor= $processor.trim() add-content $file "------------Processor 1----------------------"; add-content $file $processor; add-content $file "" } $processor = "" $processors = "" $a = "" add-content $file "" add-content $file "*********Network Adapter*********" $network = @(get-wmiobject Win32_NetworkAdapterconfiguration -computer $Server -filter IPEnabled=TRUE |select ipaddress,dnshostname,dnsserversearchorder,DNSDomainSuffixSearchOrder,WINSPrimaryServer,WINSSecondaryServer,TcpipNetbiosOptions) foreach ($objItem in $network) { $ipaddress = $objItem.ipaddress $dnshostname = $objItem.dnshostname add-content $file "IP Address: $ipaddress" add-content $file "DNS Host Name: $dnshostname" foreach ($dnsserversearchorder in $objItem.dnsserversearchorder) { add-content $file "DNS Server Search Order: $dnsserversearchorder" } foreach ($DNSDomainSuffixSearchOrder in $objItem.DNSDomainSuffixSearchOrder) { add-content $file "DNS Domain Suffix Search Order: $DNSDomainSuffixSearchOrder" } $wins = $objItem.WINSPrimaryServer add-content $file "WINS Primary Server: $wins" $wins = $objItem.WINSSecondaryServer add-content $file "WINS Secondary Server: $wins" $options = $objItem.TcpipNetbiosOptions add-content $file "TCPIP Netbios Options: $options" add-content $file "" } $network = "" $objItem = "" $wins = "" $options = "" add-content $file "*********Physical Disks*********" $disks = @(Get-WmiObject Win32_DiskDrive -computer $Server |select size,deviceid,partitions) foreach ($disk in $disks) { $size= [math]::round($disk.size / 1073741824 +0.5, 0) $id = $disk.deviceid $partitions = $disk.partitions add-content $file "DeviceID: $id" add-content $file "Size: $size (GB)" add-content $file "Partitions: $partitions" add-content $file "" } $disks="" $id = "" $partitions = "" $partitions = @(Get-WmiObject Win32_DiskPartition -computer $Server |select BootPartition,Description,DeviceID,Name,Size) add-content $file "" add-content $file "*********Partition Information*********" foreach ($partition in $partitions) { $boot = $partition.BootPartition $DeviceID = $partition.DeviceID $description = $partition.Description $name = $partition.Name $size= [math]::round($partition.size / 1073741824 +0.5, 0) add-content $file "Name: $name" add-content $file "Description: $description" add-content $file "Device ID: $DeviceID" add-content $file "Size:$size (GB)" add-content $file "Boot Partition: $Boot" add-content $file "" } $boot = "" $DeviceID = "" $description = "" $name = "" $size = "" $logicalDisks = @(Get-WmiObject Win32_LogicalDisk -computer $Server |select Name,Description,DeviceID,Size) add-content $file "*********Logical Disk Information*********" foreach ($logicalDisk in $logicalDisks) { $name = $logicalDisk.Name $DeviceID = $logicalDisk.DeviceID $description = $logicalDisk.Description $size= [math]::round($logicalDisk.size / 1073741824 +0.5, 0) add-content $file "Name: $name" add-content $file "Description: $description" add-content $file "Device ID: $DeviceID" add-content $file "Size: $size (GB)" add-content $file "" } $DeviceID = "" $description = "" $name = "" $size = "" add-content $file "" if ($role -like "*mailbox*") { add-content $file "*********Database Information*********" add-content $file "Mailbox Databases on $exchangeserver" $storagegroups = Get-StorageGroup -server $exchangeserver |select logfolderpath,systemfolderpath,name,distinguishedname |sort name foreach ($storagegroup in $storagegroups) { $sgname = $storagegroup.name $dn = $storagegroup.distinguishedname $logfolder = $storagegroup.logfolderpath $sysfolder = $storagegroup.systemfolderpath add-content $file "Storage Group: $sgname" add-content $file "Storage Group DN: $dn" add-content $file "Storage Group Log Folder: $logfolder" add-content $file "Storage Group System Folder: $sysfolder" add-content $file "" $dn = "" $logfolder = "" $sysfolder = "" $databases = Get-MailboxDatabase -StorageGroup $storagegroup.distinguishedname |select name, distinguishedname,edbfilepath |Sort name foreach ($database in $databases) { $dbname = $database.name $dbdn = $database.distinguishedname $edbpath = $database.edbfilepath add-content $file "Database: $dbname" add-content $file "Database DN: $dbdn" add-content $file "Database Path: $edbpath" add-content $file "" $dbname = "" $dbdn = "" $edbpath = "" } $publicfolders = get-publicfolderdatabase -server $exchangeserver |select name, distinguishedname,edbfilepath |Sort name if ($publicfolders -ne $null) { foreach ($publicfolder in $publicfolders) { $pfname = $publicfolder.name $pfdn = $publicfolder.distinguishedname $pfedbpath = $publicfolder.edbfilepath Add-Content $file "Public Folder: $pfname " Add-Content $file "Public Folder DN: $pfdn " Add-Content $file "Public Folder Path: $pfedbpath" Add-Content $file "" $pfname = "" $pfdn = "" $pfedbpath = "" } } } } else { add-content $file "This server does not have the mailbox role installed." }
$version = $null $server = $null $role = $null $binaries = $null $edition = $null
}
|
|
|
|
|
DonJ PowerShell MVP
 Basic Member Posts:134

 |
| 26 Nov 2007 11:15 PM |
|
Suggestions: Add a #REQUIRES to document the need for the Exchange snap-in. PSH will bomb automatically if the snap-in isn't loaded, and it's more self-documenting than your initial loop. You might add some logic to your processor check. As of Vista/2008, WMI distinguishes cores/processors/hyperthreads somewhat differently. The Scripting Guys did an article on this for TechNet, if you're interested. Apart from that, here's the only major thing I'd do: Break each major section (Processor, Logical Disk, etc) into standalone functions (Get-XSrvProcessor, Get-XSrvLogicalDisk, etc.). Rather than outputting text, have each one output custom objects (see previous thread in this forum), or just output the original WMI (and other) objects. Then your script can just call the functions sequentially, perhaps outputting header and warning information before doing so. That'll make your actual inventory-gathering code more flexible and modular, and it would allow someone else to more easily customize the information they get. If your functions are outputting the full WMI objects, for example, someone could modify your script to call the function and just select the properties THEY want, rather than the ones you've pre-determined. Then, just have your script output using Write-Output instead of Write-Host (and don't use Add-Content at all). That way, someone could run your script and select their output: ./MyScript | Out-File ./MyScript | Out-Printer Etc. The idea is to keep everything as flexible as possible for easier modification and re-use. Hope that's useful! You've done a great job so far; now it's time to "PowerShell-ize" things to increase its future flexibility. |
|
- Don Jones www.ConcentratedTech.com Subscribe (RSS) or visit for weekly PowerShell tips and lessons |
|
|
KarlMitschke
 Basic Member Posts:164

 |
| 27 Nov 2007 03:10 PM |
|
Don, for the #REQUIRES, do you mean something as simple as: #REQUIRES Microsoft.Exchange.Management.PowerShell.Admin ???? I got you on the functions, that's usually a given with my code, I guess I shouldn't let powershell let me be lazy! Good idea on the Write-Output and allowing people to choose where they want the output. I will look-up the Scripting Guys on Vista / 2008 WMI - that brings up a point, for real Disaster Recovery, most places would like to know OS version information, patch levels, etcetera. Thanks for the suggestions Karl |
|
|
|
|
RichS
 New Member Posts:41

 |
| 28 Nov 2007 07:06 PM |
|
If you are doing this for DR purposes I would also want to output all of the configuration information on the Exchange Server itself, the databases and various rules e.g. mailbox quotas, message size limits, transport rules etc etc. This would definitely require a function driven approach otherwise the code will become unwieldy |
|
Richard Siddaway Microsoft MVP - PowerShell UK PowerShell User group Leader www.get-psuguk.org.uk |
|
|
DonJ PowerShell MVP
 Basic Member Posts:134

 |
| 28 Nov 2007 08:39 PM |
|
Actually the #REQUIRES syntax isn't quite that - I think it's #REQUIRES -snapin (snap-in name here) But I don't have the shell handy at the moment to check the syntax. |
|
- Don Jones www.ConcentratedTech.com Subscribe (RSS) or visit for weekly PowerShell tips and lessons |
|
|
KarlMitschke
 Basic Member Posts:164

 |
| 28 Nov 2007 09:34 PM |
|
Don; I found it by break-testing The "#requires" statement must be in one of the following formats: "#requires -shellid " "#requires -version " "#requires -pssnapin [-version ]" |
|
|
|
|
KarlMitschke
 Basic Member Posts:164

 |
| 28 Nov 2007 09:35 PM |
|
Excellent ideas! This is exactly what I am looking for - more stuff to add 
I am modularizing it now, and once I have that done I will add more to it, and i will repost it if that's allowed? |
|
|
|
|
KarlMitschke
 Basic Member Posts:164

 |
| 28 Nov 2007 11:03 PM |
|
Posted By DonJ on 11/26/2007 3:15 PM Suggestions:
Apart from that, here's the only major thing I'd do: Break each major section (Processor, Logical Disk, etc) into standalone functions (Get-XSrvProcessor, Get-XSrvLogicalDisk, etc.). Rather than outputting text, have each one output custom objects (see previous thread in this forum), or just output the original WMI (and other) objects. Then your script can just call the functions sequentially, perhaps outputting header and warning information before doing so. That'll make your actual inventory-gathering code more flexible and modular, and it would allow someone else to more easily customize the information they get. If your functions are outputting the full WMI objects, for example, someone could modify your script to call the function and just select the properties THEY want, rather than the ones you've pre-determined.
Don; I'm finally at a point where I can start to work on this some more Would you suggest putting each of the major functions in their own files and then dot-sourcing them, or just having all the functions in the get-dridata.ps1 script? If I do save them as seperate ps1 files, would you suggest the files be nameg get-* or that they be named something like procinfo.ps1 with a function get-XSrvProcessor so I can dot-source procinfo.ps1 and then use get-XSrvProcessor in my get-drdata script? Thanks for all the help and suggestions! Karl |
|
|
|
|
KarlMitschke
 Basic Member Posts:164

 |
| 28 Nov 2007 11:05 PM |
|
Posted By RichS on 11/28/2007 11:06 AM If you are doing this for DR purposes I would also want to output all of the configuration information on the Exchange Server itself, the databases and various rules e.g. mailbox quotas, message size limits, transport rules etc etc. This would definitely require a function driven approach otherwise the code will become unwieldy
Well, I finally learned to quote  I responded to this earlier, but it looked like I was replying to Don. Thanks, Richard - this is just what I am looking for, MORE STUFF to add  I will work on that after I get it all powershell-ish  Karl |
|
|
|
|
RichS
 New Member Posts:41

 |
| 30 Nov 2007 09:16 AM |
|
If you are modularising the script - I would think about using separate files. I've been doing something similar with AD and I made the modules such that I could run each one separately with a file that calls all of them. It gives maximum flexibility. |
|
Richard Siddaway Microsoft MVP - PowerShell UK PowerShell User group Leader www.get-psuguk.org.uk |
|
|
KarlMitschke
 Basic Member Posts:164

 |
| 18 Dec 2007 02:42 PM |
|
Posted By DonJ on 11/26/2007 3:15 PM Suggestions: Add a #REQUIRES to document the need for the Exchange snap-in. PSH will bomb automatically if the snap-in isn't loaded, and it's more self-documenting than your initial loop.
Don; The trouble with using the #REQUIRES is that it doesn't load the snap-in, it just bombs, as you mentioned. My loop loads the snap-in, so the script always runs. I have thought about this for some time, and I like my solution better that the #REQUIRES statement. I use my loop in scheduled scripts where there is no console output. Karl |
|
|
|
|
DonJ PowerShell MVP
 Basic Member Posts:134

 |
| 18 Dec 2007 04:41 PM |
|
Don; I'm finally at a point where I can start to work on this some more  " align="absmiddle" border="0"> Would you suggest putting each of the major functions in their own files and then dot-sourcing them, or just having all the functions in the get-dridata.ps1 script? If I do save them as seperate ps1 files, would you suggest the files be nameg get-* or that they be named something like procinfo.ps1 with a function get-XSrvProcessor so I can dot-source procinfo.ps1 and then use get-XSrvProcessor in my get-drdata script? Thanks for all the help and suggestions! Karl If the functions would get used elsewhere, I'd put them in a "library" file at dot-source it into the script. If they're pretty unique to this script, I'd just leave them in it. No sense creating complexity if you don't need it. If you're going to put them into seperate files, those filenames should pretty explicitly cover what the file contains: ExchangeDRLibrary.ps1, for example. "Library" implies it's not a single-purpose script, and it meant for inclusion (dot sourcing) inside another.
|
|
- Don Jones www.ConcentratedTech.com Subscribe (RSS) or visit for weekly PowerShell tips and lessons |
|
|
| You are not authorized to post a reply. |
|
Active Forums 4.1
|
|
 |