Gary's $this and that about PowerShell and Exchange

I am a Microsoft Senior Premier Field Engineer based out of Atlanta, GA. My focus is on Exchange Server and Powershell. This blog is mainly to share interesting Powershell script samples and tidbits about Exchange. I am a Microsoft Certified Master for
Blog - About

About $this blog:

I am a Microsoft Senior Premier Field Engineer based out of Atlanta, GA.  My focus is on Exchange Server and Powershell.  This blog is mainly to share interesting Powershell script samples and tidbits about Exchange.  I am a Microsoft Certified Master for Exchange Server 2007.

  • Gary's $this and that about PowerShell and Exchange

    Example for Identifying Domain Controller Subnet Membership with PowerShell

    • 0 Comments

    First of all let me thank Rich Doyle (a fellow PFE) for allowing me to post this script.  Rich writes some pretty kick-butt stuff and I know I speak for many of us that appreciate his help and his scripts!

    Now Rich claims he hacked this together and didn't get around to polishing it up with optimization and full error trapping and such.  That is okay as my posts are all meant to be just conceptual demos and are not meant at all to be production level scripts.  That said, this “unpolished” script is longer and way more thorough then what I usually write.  You go Rich!

     

    Here is Rich’s write-up on this script:

     

    The customer I am at this week wanted an easy way for admins to find DC subnet membership if the subnet of the DC did not match what was defined for the local AD Site in which the DC was a member.  The customer wanted finding the subnet in AD to be actionable by someone without extensive IP subnet knowledge - especially as there are a lot of defined subnets to review in AD.

    Attached is a script that performs this search, and the results.

    I am sending it out, as it provides some really useful functions for IPv4 Address, Network and Subnet calculations (IPv6 equivalent is coming soon).  These are ported and slightly modified from some great c# examples on MSDN Blogs, referenced within the script.  It also demonstrates some usage of the System.DirectoryService.ActiveDirectory.Forest class (and before anyone says anything, yes, I need to cache domains, domain controllers, sites and subnets data rather than re-search…but I haven’t gotten to optimization of the script yet J)

    The script requires Powershell v1.0 (or 2.0, but not tested on the RTM release yet), and uses the following syntax:

    .\Process-SubnetDefinitions.ps1 -serverlist:"server1","server2","server3"

    The script checks the IP Address and subnet of the DC against the local site for an explicit network match, then all other sites for explicit network match.  If there is no explicit network match it proceeds to check all subnets as "catch-alls", starting with 32-bit/4th octect subnets and working up to 8-bit/1st octet subnets until a match (if there is one) is found.

    It will provide the following output:

    PS C:\Documents and Settings\xxxx\My Documents\Conflicting Subnet Definitions> .\Process-SubnetDefinitions.ps1 -serverlist:"xxxx","xxxx"

    *****************************
    Process-SubnetDefinitions.ps1
    *****************************

    Retrieving IP Address details and Subnet defintions for listed servers

    Processing DC Local Sites First for explicit match....

    Working on Server: xxxx

    Working on Server: xxxx

    Processing All Other Sites for explicit match....

    Working on Server: xxxx
      Checking Site: xxxx
       Match Found

    Working on Server: xxxx
      Checking Site: xxxx
       Match Found

    DCName                 : xxxx
    IPAddress              : 10.100.139.74
    SubnetMask             : 255.255.255.0
    Site                   : xxxx
    SiteSubnets            : {10.100.8.0/24, 172.28.22.0/24, 10.104.8.8/32, 10.100.139.33/32...}
    SiteWithMatchingSubnet : xxxx
    MatchingSiteSubnet     : 10.100.139.0/24
    MatchFound             : True
    CriticalError          : False
    ErrorDetails           :

    DCName                 : xxxx
    IPAddress              : 10.104.225.1
    SubnetMask             : 255.255.255.0
    Site                   : xxxx
    SiteSubnets            : {10.104.225.36/32, 10.104.144.69/32, 10.179.2.20/32, 10.100.144.69/32...}
    SiteWithMatchingSubnet : xxxx
    MatchingSiteSubnet     : 10.104.225.0/24
    MatchFound             : True
    CriticalError          : False
    ErrorDetails           :

     

    Here is the script demo itself:

    param([array]$serverlist=$null)
    
    # This script solved a specific problem for me - Identification of a Domain Controller's subnet membership in Active Directory (even if not in the same site,
    # or a "catch-all" subnet - where the IPV4 Network cannot be used, and IP Address Ranges must be used to determine).
    
    # It checks the local AD Site for network match, then all other AD Sites for network match, then all AD Sites for "catch-all" match
    
    # After some research, I found helpful articles on MSDN, and MSDN Blogs
    
    # This script demonstrates IPv4 Address calculations, enumerating Domains, DCs and Sites within Active Directory (and still needs to be optimized to not
    # re-query AD Data as it runs).
    
    # References
    
    # MSDN - IPAddress Class - http://msdn.microsoft.com/en-us/library/system.net.ipaddress(VS.80).aspx
    # MSDN - Forest Class - http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.forest(VS.80).aspx
    # MSDN Blogs - Knom's Developer Corner - http://blogs.msdn.com/knom/archive/2008/12/31/ip-address-calculations-with-c-subnetmasks-networks.aspx
    
    # This script is provided "AS IS" with no warranties, and confers no rights.
    # Use of any portion or all of this script are subject to the terms specified at
    # http://www.microsoft.com/info/cpyright.htm. 
    
    Write-Host "";
    Write-Host "*****************************";
    Write-Host "Process-SubnetDefinitions.ps1";
    Write-Host "*****************************";
    Write-Host "";
    
    If($serverlist -eq $null) { Write-Host "You must specify the target dc using the serverlist switch. e.g., .\Process-SubnetDefinitions.ps1 -serverlist:'servername'"; break; }
    
    Write-Host "Retrieving IP Address details and Subnet defintions for listed servers";
    
    $erroractionpreference = "SilentlyContinue";
    $error.Clear();
    
    # For use with [System.Net.IpAddress]::Parse which cannot read the native powershell byte array, so we Parse and create a string (returns a string, e.g. "255.255.255.0")
    function FormatBinaryMask
    {
        param([byte[]]$binaryMask)
        $string = [string]::Empty;
        for([int]$i = 0; $i -lt 4; $i++)
        {
            if($i -eq 0)
            {
                $string = $string + $binaryMask[$i].ToString();
            }
            else
            {
                $string = $string + "." + $binaryMask[$i].ToString();
            };
        };
        return $string;
    }
    
    # Specify the number of bits for the Host to return the subnet mask (returns an IPAddress object)
    function SubnetMaskByHostBitLength
    {
        param([int]$hostPartLength)
        
        $netPartLength = 32 - $hostPartLength;
        
        if ($netPartLength -lt 2) { Write-Error "Too many hosts for IPv4"; break; };
        
        $binaryMask = New-Object byte[] 4;
        
        for([int]$i = 0; $i -lt 4; $i++)
        {
            if(($i * 8 + 8) -le $netPartLength) { $binaryMask[$i] = [byte]255; }
            elseif(($i * 8) -gt $netPartLength) { $binaryMask[$i] = [byte]0; }
            else
            {
                $oneLength = $netPartLength - $i * 8
                $binaryDigit = [String]::Empty.PadLeft($oneLength, '1').PadRight(8, '0')
                $binaryMask[$i] = [System.Convert]::ToByte($binaryDigit, 2)
            };
        };
        return [System.Net.IPAddress]::Parse((FormatBinaryMask $binaryMask));
    }
    
    # Specify the number of bits for the Network to return the subnet mask (returns an IPAddress object)
    function SubnetMaskByNetBitLength
    {
        param([int]$netPartLength)
        $hostPartLength = 32 - $netPartLength;
        return SubnetMaskByHostBitLength($hostPartLength);
    
    }
    
    # Performs a boolean AND between the IP Address and the subnet mask to return the Network Address, e.g. 192.168.1.0 (returns an IP Address object)
    function GetNetworkAddress
    {
        param([System.Net.IPAddress]$address, [System.Net.IPAddress]$subnetMask)
        [byte[]]$ipAdressBytes = $address.GetAddressBytes();
        [byte[]]$subnetMaskBytes = $subnetMask.GetAddressBytes();
    
        if ($ipAdressBytes.Length -ne $subnetMaskBytes.Length) { Write-Error "Lengths of IP address and subnet mask do not match"; break; };
    
        $broadcastAddress = New-Object byte[] $ipAdressBytes.Length;
        
        for ([int]$i = 0; $i -lt $broadcastAddress.Length; $i++)
        {
            $broadcastAddress[$i] = [byte]($ipAdressBytes[$i] -band ($subnetMaskBytes[$i]));
        };
        return [System.Net.IPAddress]::Parse((FormatBinaryMask $broadcastAddress));
    }
    
    # Performs a boolean AND between the the subnet mask and 255.255.255.255 to return a filter for determining the number of usable addresses and those 
    # limits within a network, e.g. would return 0.0.0.255 for the subnet mask 255.255.255.0 (returns an IP Address object)
    function GetNetworkUsableAddressRangeFilter
    {
        param([System.Net.IPAddress]$subnetMask)
    
        $topend = [System.Net.IPAddress]::Parse("255.255.255.255");
    
        [byte[]]$subnetMaskBytes = $subnetMask.GetAddressBytes();
        [byte[]]$topendBytes = $topend.GetAddressBytes();
    
        $broadcastAddress = New-Object byte[] $subnetMaskBytes.Length;
        
        for ([int]$i = 0; $i -lt $broadcastAddress.Length; $i++)
        {
            $broadcastAddress[$i] = [byte]($topendBytes[$i] -bxor ($subnetMaskBytes[$i]));
        };
        return [System.Net.IPAddress]::Parse((FormatBinaryMask $broadcastAddress));
    }
    
    # Compares the networks of 2 sets of IP Address and subnet masks to determine if they are on the same network
    function  
    {
        param([string]$address1, [string]$subnetMask1, [string]$address2, [string]$subnetMask2)
        $network1 = GetNetworkAddress ([System.Net.IPAddress]::Parse($address1)) ([System.Net.IPAddress]::Parse($subnetMask1));
        $network2 = GetNetworkAddress ([System.Net.IPAddress]::Parse($address2)) ([System.Net.IPAddress]::Parse($subnetMask2));
    
        return $network1.Equals($network2);
    }
    
    $results = @();
    
    $forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest();
    
    foreach($server in $serverlist)
    {
        $error.Clear();
        $result = New-Object PSObject | Select-Object "DCName","IPAddress","SubnetMask","Site","SiteSubnets",
    "SiteWithMatchingSubnet","MatchingSiteSubnet","MatchFound","CriticalError","ErrorDetails"; $result.DCName = $server; $wmi = Get-WmiObject -computer $server Win32_NetworkAdapterConfiguration -filter "IPEnabled='true'"; If ($error) { $result.CriticalError = $true; $result.ErrorDetails = $error[0]; $error.Clear(); } else { $result.CriticalError = $false; $result.ErrorDetails = $null; $result.IPAddress = $wmi.IPAddress[0]; $result.SubnetMask = $wmi.IPSubnet[0]; }; foreach($domain in $forest.Domains) { foreach($dc in $domain.DomainControllers) { if($dc.Name.ToLower().Contains($server.ToLower())) { $result.Site = $dc.SiteName; }; }; }; foreach($site in $forest.Sites) { if($site.Name -eq $result.Site) { $result.SiteSubnets = $site.Subnets; }; }; $result.SiteWithMatchingSubnet = $null; $result.MatchingSiteSubnet = $null; $result.MatchFound = $false; $results += $result; }; # Search Local Site Subnets for explicit match Write-Host ""; Write-Host "Processing DC Local Sites First for explicit match...."; foreach($result in $results) { If($result.CriticalError -eq $false) { Write-Host ""; Write-Host (" Working on Server: {0}" -f $result.DCName); foreach($subnet in $result.SiteSubnets) { if($result.MatchFound -eq $false) { $res = $false; $netaddress = $subnet.Name.Split("/"); $res = IsInSameSubnet $result.IPAddress $result.SubnetMask $netaddress[0] (SubnetMaskByNetBitLength $netaddress[1]); If($res) { Write-Host " Match Found"; $result.SiteWithMatchingSubnet = $result.Site; $result.MatchingSiteSubnet = $subnet.Name; $result.MatchFound = $true; }; }; }; }; } $continue = $false; foreach($result in $results) { if($result.MatchFound -eq $false) { $continue = $true; }; } if($continue -eq $false) { $results; break; }; # Search All other Sites for explicit match Write-Host ""; Write-Host "Processing All Other Sites for explicit match...."; foreach($result in $results) { if($result.CriticalError -eq $false) { Write-Host ""; Write-Host (" Working on Server: {0}" -f $result.DCName); foreach($site in $forest.Sites) { $found = $false; if ($site.Name -ne $result.Site) { Write-Host (" Checking Site: {0}" -f $site.Name); foreach($subnet in $site.Subnets) { $res = $false; $netaddress = $subnet.Name.Split("/"); $res = IsInSameSubnet $result.IPAddress $result.SubnetMask $netaddress[0] (SubnetMaskByNetBitLength $netaddress[1]); If($res) { Write-Host " Match Found"; $result.SiteWithMatchingSubnet = $site.Name; $result.MatchingSiteSubnet = $subnet.Name; $result.MatchFound = $true; }; }; }; if($result.MatchFound) { break; }; }; }; }; $continue = $false; foreach($result in $results) { if($result.MatchFound -eq $false) { $continue = $true; }; } if($continue -eq $false) { $results; break; }; # Search all subnets for "catch-all" match Write-Host ""; Write-Host "Processing All Other Sites for Catch-All subnet match....if required...."; foreach($server in $results) { If($server.CriticalError -eq $false) { Write-Host ""; write-host (" Working on Server: {0}" -f $server.DCName); while($server.MatchFound -eq $false) { foreach($site in $forest.Sites) { if($server.MatchFound -eq $false) { Write-Host (" Checking Site: {0}" -f $site.Name); foreach($subnet in $site.Subnets) { if($server.MatchFound -eq $false) { $lastaddress = new-object byte[] 4; $netaddress = $subnet.Name.Split("/"); If($netaddress[0] -le 8) { $netmask = SubnetMaskByNetBitLength $netaddress[1]; $filter = GetNetworkUsableAddressRangeFilter $netmask; $i = 0; foreach($byte in $filter.GetAddressBytes()) { if ($byte -eq 0) { $lastaddress[$i] = [System.Net.IPAddress]::Parse($netaddress[0]).GetAddressBytes()[$i]; } else { $lastaddress[$i] = ([System.Net.IPAddress]::Parse($netaddress[0])).GetAddressBytes()[$i] + $byte; }; $i++; }; $serverip = ([System.Net.IPAddress]::Parse($server.IPAddress)).GetAddressBytes(); If(($serverip[0] -ge $([System.Net.IPAddress]::Parse($netaddress[0])).GetAddressBytes()[0]) -and ($serverip[0] -le $lastaddress[0])) { If(($serverip[1] -ge $([System.Net.IPAddress]::Parse($netaddress[0])).GetAddressBytes()[1]) -and ($serverip[1] -le $lastaddress[1])) { If(($serverip[2] -ge $([System.Net.IPAddress]::Parse($netaddress[0])).GetAddressBytes()[2]) -and ($serverip[2] -le $lastaddress[2])) { If(($serverip[3] -ge $([System.Net.IPAddress]::Parse($netaddress[0])).GetAddressBytes()[3]) -and ($serverip[3] -le $lastaddress[3])) { Write-Host " Match Found"; $server.MatchingSiteSubnet = ("{0}/{1}" -f ($netaddress[0], $netaddress[1])); $server.SiteWithMatchingSubnet = $subnet.Site; $server.MatchFound = $true; }; }; }; }; }; }; }; }; }; }; }; }; $results; $erroractionpreference = "Continue";

     

    Again Thanks a ton Rich!!!!!  You really rock man!

     

    -Gary

    This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.

  • Gary's $this and that about PowerShell and Exchange

    Counting Public Folder SubFolders

    • 0 Comments

    This sample script was written by a brilliant fellow PFE named Chris Schrimsher.  Chris gave me permission to post this script out there to the two of you reading my posts…you know who you are :)

    In this script there is a concept that he is using that I like.  He uses a hash table (dictionary object), to build up a list of folders and their count of subfolders.  As he works through the entire tree of public folders, he just checks to see if the parent is in the list.  If its there, he just adds to the number count, if not he adds it with a 1.  Its a very simple but brilliant use of a hash table.

    I saw some of this script when he was writing it, and I must confess, I haven't actually run this version I am posting here.  He prettied it up a bit and is also using a progress bar here.

    Well, enough of me wasting time, here is the sample code that you can pull lots of neat scripting concepts from:

    # Name:   Get-PFSubFolderCount.ps1
    # Author: Chris Schrimsher
    # Date:   August 6, 2009
    #
    # Description:
    # Enumerates all public folders, and generates a count of the number of subfolders that each folder has.
    # Warns about any folders that have more than the maximum specified below, and makes full report available.
    
    # This script is provided "AS IS" with no warranties, and confers no rights.
    # Use of any portion or all of this script are subject to the terms specified at
    # http://www.microsoft.com/info/cpyright.htm. 
    
    $MaxSubFolders = 250
    
    Clear-Host
    
    # Populate the initial progress bar (real time progress updating is not available while retrieving public folder information)
    Write-Progress -Activity "Public Folder Subfolder Count Tool" `
                   -CurrentOperation "Enumerating Public Folders, Please Wait..." `
                   -Status "This may take some time, especially in large organizations..."
    
    # Get all public folders and store in variable
    $PubFolders = Get-PublicFolder -Recurse -ResultSize Unlimited
    
    # Create hash table to hold results
    $SubFolderTable = @{}
    
    # Write-Host "Analyzing Public Folders and Building Results Table..."
    
    # Create a counter variable
    $Count = 0
    
    # Enumerate through public folders one at a time
    $PubFolders | ForEach-Object `
    {
    
        # Update the progress bar as each public folder is analyzed
        Write-Progress -Activity "Public Folder Subfolder Count Tool" `
                       -CurrentOperation "Analyzing Public Folders and Building Results Table..." `
                       -Status "Completion Status:" `
                       -PercentComplete ([int] (($Count / $PubFolders.Count) * 100))
    
        # If the parent path is null, this is the root folder, so skip it
        if ($_.ParentPath -ne $null) `
        {
    
        # If the parent path is not contained in the hash table, ...
        if ($SubFolderTable[$_.ParentPath] -eq $null) `
            {
    
                # ...  add it with a value of 1 (since this is the first subfolder of that parent)
                $SubFolderTable[$_.ParentPath] = 1
            }
    
            else `
            {
    
                # ...  otherwise, increment the count of the existing parent path.
                $SubFolderTable[$_.ParentPath]++
            }
        }
    
        # Increment the counter variable
        $Count++
    }
    
    Write-Progress -Activity "Public Folder Subfolder Count Tool" `
                   -CurrentOperation "Counting folders with more than $MaxSubFolders subfolders..." `
                   -Status "Please wait..."
    
    # Write-Host "Counting folders with more than $MaxSubFolders subfolders..."
    $LargeFoldersCount = ($SubFolderTable.GetEnumerator() | Where-Object { $_.Value -gt $MaxSubFolders } | Measure-Object).Count
    
    if ($LargeFoldersCount -gt 0)
    {
    
        # Display count of folders with too many subfolders
        Write-Host "Number of folders with more than $MaxSubFolders subfolders: $LargeFoldersCount"
    
        # Display help information for table
        Write-Host "`nList of folders with more than $MaxSubfolders subfolders and subfolder count of each:"
    
        # Display hash table containing results sorted by number of subfolders, from largest to smallest
        $SubFolderTable.GetEnumerator() | Where-Object { $_.Value -gt $MaxSubFolders } | Sort-Object -Property Value -Descending | Format-Table
    
        # Display count of folders with too many subfolders
        Write-Host "`nNumber of folders with more than $MaxSubFolders subfolders: $LargeFoldersCount"
        Write-Host "`nFull output is available in the object `$SubFolderTable.`n"
     
    }
    
    else
    {
    
        # Display a message indicating no folders with too many subfolders were found
        Write-Host "`nNo folders found containing more than $MaxSubFolders subfolders."
    }

    -Gary

    This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.

  • Gary's $this and that about PowerShell and Exchange

    List Mailbox Sizes for both Exchange 2003 and Exchange 2007

    • 3 Comments

    This is a script I put together because…well I don't remember what prompted me to write it.  Who cares :)

    This script uses the built-in cmdlets for Exchange 2007 find 2007 servers and legacy servers.  It then uses WMI to talk to the legacy servers to get the mailbox sizes.  Technically, this script requires Exchange 2007 Management tools to be installed to even get this kicked off.  It wouldn't be very difficult to have it check for the existence or registration of the Exchange snap-in and behave differently if it were run on an a non Exchange 2007 server (or tools machine).  It would require PowerShell to be installed.  Moral of the story, it should be able to made to work against just Exchange 2000 and 2003 without 2007.

    I am going to confess, I haven't been able to test this extensively.  If anyone out there runs into issues with it, let me know and I can get it fixed up.  I asked a few colleagues, but they all are bums and didn't test it for me. :)

    Let me know what you think either way.

    Here is the sample code:

    # Script to get mailbox sizes from both Exchange 2007+ servers and legacy Exchange             
    # 2000 and 2003 Servers             
    #             
    # This script is provided "AS IS" with no warranties, and confers no rights.             
    # Use of included script samples are subject to the terms specified at             
    # http://www.microsoft.com/info/cpyright.htm.             
    # Written by: Gary Siepser, Microsoft, Premier Field Engineer
        
                
    #Get list of only E2k7 or later mailboxes as we get their size through a cmdlet             
    #, but the mailboxes still on E2K3 have to come from WMI             
    $2007Users = Get-Mailbox -RecipientTypeDetails usermailbox   
              
                
    #Get a list of the legacy servers so we can connect to WMI on each one             
    $legacyservers = get-exchangeserver | Where-Object {-not $_.IsExchange2007OrLater}  
              
                
    #get the stats on all the E2K7 Mailboxes.  This may yield a number of red and yellow             
    #errors and warnings respectively.  These can be squashed through error and warning             
    #preference.             
    $2007Userstats = $2007Users | get-mailboxstatistics
        
                
    #loop through the legacy servers to get the userstats through WMI             
    foreach ($server in $legacyservers)             
    {             
        # Use the right WMI namespace and class to get to mailbox data and add it to a building             
        # array to hold it all so we can combine this with the non-legacy user data already             
        # collected             
        $legacyusers += Get-WmiObject -ComputerName $server -Namespace root\MicrosoftExchangeV2 -Class Exchange_mailbox             
    }    
             
                
    #Now we have all the data in two seperate variables.  Unfortunately the objects types             
    # in those variables is quite different. We are going to go ahead and create a simple             
    # new object type and populate it with the data from each list to make a new common             
    # list of mailbox names and sizes.             
    #Create the object and give it the properties we want to use             
    $templateobject = New-Object PSObject             
    $templateobject = $templateobject | Select-Object Name,Server,MailboxSizeinMB
      
                
    # Now we need to loop through the users lists and populate these custom objects             
    # into a new array
     
                
    #Create the empty array             
    $results = @()  
             
                
    # Loop through the 2007 user stats first             
    foreach ($user in $2007Userstats)             
    {             
        #copy the template object to this actual instance we want to add to the building list             
        $newobjectInstance = $templateobject |  Select-Object *      
           
                
        #Populate the properties with the data             
        $newobjectInstance.Name = $user.DisplayName             
        $newobjectInstance.Server = $user.ServerName   
              
                
        #We need to pull out the size in Bytes from the object that is in totalitemsize             
        # and then devide it by 1MB and round it off.  This gives us a nice number with             
        # two decimals places...nicer that what the .ToMB() method gives us)             
        $newobjectInstance.MailboxSizeinMB = [Math]::Round(($user.totalitemsize.value.ToBytes() / 1MB),2)      
           
                
        #add this object isntance to the result array             
        $results += $newobjectInstance             
    }        
         
                
    #now loop through the WMI data we got back for the legacy servers and users             
    foreach ($user in $legacyusers)             
    {             
        #copy the template object to this actual instance we want to add to the building list             
        $newobjectInstance = $templateobject |  Select-Object *       
          
                
        #Populate the properties with the data             
        $newobjectInstance.Name = $user.MailboxDisplayName             
        $newobjectInstance.Server = $user.ServerName       
          
                
        #We need to pull out the size in Bytes from the object that is in totalitemsize             
        # and then devide it by 1MB and round it off.  This gives us a nice number with             
        # two decimals places...nicer that what the .ToMB() method gives us)             
        $newobjectInstance.MailboxSizeinMB = [Math]::Round(($user.size / 1MB *1KB),2)            
     
                
        #add this object isntance to the result array             
        $results += $newobjectInstance             
    }            
     
                
    # Sort the results in a more meaningful way            
    $results = $results | Sort-Object mailboxsizeinMBDescending     
            
                
    #Do whatever you want now, in this case simply output the results.            
    $results             
                
                
            

    -Gary

    This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.

Page 1 of 1 (3 items)