header1   header
header
header Register : : Login header
header
connector   connector
menuleft menuright
submenu   submenu
left
Disaster Recovery Enumerate E2K7 Servers
Last Post 07 Jul 2011 12:12 PM by AlexK. 12 Replies.
Printer Friendly
  •  
  •  
  •  
  •  
  •  
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
Karl MitschkeUser is Offline
Basic Member
Basic Member
Posts:457
Avatar

--
26 Nov 2007 10:54 AM

    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

    }

    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Don JonesUser is Offline
    PowerShell MVP
    Basic Member
    Basic Member
    Posts:134
    Avatar

    --
    26 Nov 2007 02: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
    Karl MitschkeUser is Offline
    Basic Member
    Basic Member
    Posts:457
    Avatar

    --
    27 Nov 2007 06:10 AM
    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
    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Richard SiddawayUser is Offline
    New Member
    New Member
    Posts:50
    Avatar

    --
    28 Nov 2007 10: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
    Richard Siddaway
    Microsoft MVP - PowerShell
    UK PowerShell User group Leader
    www.get-psuguk.org.uk
    Don JonesUser is Offline
    PowerShell MVP
    Basic Member
    Basic Member
    Posts:134
    Avatar

    --
    28 Nov 2007 11:39 AM
    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
    Karl MitschkeUser is Offline
    Basic Member
    Basic Member
    Posts:457
    Avatar

    --
    28 Nov 2007 12: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 ]"
    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Karl MitschkeUser is Offline
    Basic Member
    Basic Member
    Posts:457
    Avatar

    --
    28 Nov 2007 12: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?

    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Karl MitschkeUser is Offline
    Basic Member
    Basic Member
    Posts:457
    Avatar

    --
    28 Nov 2007 02: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
    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Karl MitschkeUser is Offline
    Basic Member
    Basic Member
    Posts:457
    Avatar

    --
    28 Nov 2007 02: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
    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Richard SiddawayUser is Offline
    New Member
    New Member
    Posts:50
    Avatar

    --
    30 Nov 2007 12: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
    Karl MitschkeUser is Offline
    Basic Member
    Basic Member
    Posts:457
    Avatar

    --
    18 Dec 2007 05:42 AM
    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


    http://unlockpowershell.wordpress.com
    Co-Author, Windows PowerShell 2.0 Bible
    -join("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Don JonesUser is Offline
    PowerShell MVP
    Basic Member
    Basic Member
    Posts:134
    Avatar

    --
    18 Dec 2007 07:41 AM
    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.
    - Don Jones
    www.ConcentratedTech.com
    Subscribe (RSS) or visit for weekly PowerShell tips and lessons
    AlexKUser is Offline
    New Member
    New Member
    Posts:5
    Avatar

    --
    07 Jul 2011 12:12 PM
    EDB recover use contemporary methods of restoring edb files. It works under all Windows OS. The application can work with big edb files due to all its wide probabilities such as viewing the results of repairing, working with all versions of MS Exchange Server.
    You are not authorized to post a reply.


    Active Forums 4.3
    right
    footer   footer
    footer Sponsored by Quest Software • SAPIEN Technologies • Compellent • Microsoft Windows Server 2008 R2 footer
    footer   footer