header
header Register : : Login header
header
divider
menuleft
menuright
submenu
left

[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.
Printer Friendly
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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

}

DonJUser is Offline
PowerShell MVP
Basic Member
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.
KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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
RichSUser is Offline
New Member
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
DonJUser is Offline
PowerShell MVP
Basic Member
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.
KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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 ]"
KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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?

KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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
KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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
RichSUser is Offline
New Member
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.
KarlMitschkeUser is Offline
Basic Member
Basic Member
Posts:161

--
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


DonJUser is Offline
PowerShell MVP
Basic Member
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 <img src=" 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.
You are not authorized to post a reply.

Active Forums 4.1
right
   
footer Sponsored by Quest Software • SAPIEN Technologies • ShellTools, LLC • Microsoft Windows Server 2008 footer
footer