Perform a search mailbox to specific mailbox items between a date range.

Scenario: You want to non-invasively search a mailbox in order to find content between a date range.

Solution:  Run the following command(s):

search-mailbox userid -searchquery “kind:Email AND Received:1/1/1900..08/01/2014” -EstimateResultOnly

Note: You can remove estimateresultonly and replace with TargetMailbox and TargetFolder to export the queried contents of the mailbox to view the messages.  You can also use the following KIND variables to target certain message types in your search:

Email
Meetings
Tasks
Notes
Docs
Journals
Contacts
IM

Script to determine Exchange ActiveSync devices and email the report

Scenario:  You want a script that will perform the following:

  1. Provide all device statistics in csv format attached to the email report.
  2. Provide a total of Devices in the email report
  3. Provide a total of mailboxes that have a device in the email report
  4. The average number of devices per user in the email report
  5. Provide a list of devices that have not connected in over 6 months in the email report
  6. Provide a count for each ActiveSync policy applied on all mailboxes in the email report
  7. Provide a list of the top 10 device types in the email report
  8. Provide a list of the top 10 users based on device count in the email report

Script: 

#Define Variables
    $age = 180
    $final_DeviceCount = @()
    $report = @()
    $file = $(((get-date).ToUniversalTime()).ToString("yyyyMMddThhmmssZ"))
    $filename = "C:tempeas_stat_"+$file+".csv"
    $today = get-date
    $final = @()
    $BreakLine = "
    "
 $stats = @("FirstSyncTime",
        "LastPolicyUpdateTime",
        "LastSyncAttemptTime",
        "LastSuccessSync",
        "DeviceType",
        "DeviceID",
        "DeviceUserAgent",
        "DeviceWipeSentTime",
        "DeviceWipeRequestTime",
        "DeviceWipeAckTime",
        "LastPingHeartbeat",
        "RecoveryPassword",
        "DeviceModel",
        "DeviceImei",
        "DeviceFriendlyName",
        "DeviceOS",
        "DeviceOSLanguage",
        "DevicePhoneNumber",
        "MailboxLogReport",
        "DeviceEnableOutboundSMS",
        "DeviceMobileOperator",
        "Identity",
        "Guid",
        "IsRemoteWipeSupported",
        "Status",
        "StatusNote",
        "DeviceAccessState",
        "DeviceAccessStateReason",
        "DeviceAccessControlRule",
        "DevicePolicyApplied",
        "DevicePolicyApplicationStatus",
        "LastDeviceWipeRequestor",
        "ClientVersion",
        "NumberOfFoldersSynced",
        "SyncStateUpgradeTime",
        "ClientType",
        "IsValid",
        "ObjectState"
          )

#Define Email Variables:
    $smtp = "smtp.domain.com"
    [string[]]$to = "steve@domain.com"
    $from = "MobileDeviceStats@domain.com
    $subject = "Mobile Device Stats using ActiveSync"

#Define HTTP Style
$b = "<style>
        BODY{
        font-family: Arial; font-size: 10pt;
        }
        TABLE{
        border: 1px solid black; border-collapse: collapse;
        }
        TH{
        border: 1px solid black; background: #dddddd; padding: 5px;
        }
        TD{
        border: 1px solid black; padding: 5px;
        }
    </style>"
#=========Begin Script===========
#Calculate Mailboxes with EAS Device Partnership
$MailboxesWithEASDevices = @(Get-CASMailbox -Resultsize Unlimited -erroraction SilentlyContinue | Where {$_.HasActiveSyncDevicePartnership} | Select Identity,SAMAccountName)
$MailboxesWithEASDevices = $MailboxeswithEasDevices | Sort SAMAccountName
#Loop Through Each Mailbox
Foreach ($Mailbox in $MailboxesWithEASDevices)
{
$EASDeviceStats = @(Get-MobileDeviceStatistics -Mailbox $Mailbox.Identity)
Write-Host "$($Mailbox.SAMAccountName) has $($EASDeviceStats.Count) device(s)"
#Build the Array
   $ServerObj = New-Object PSObject
   $ServerObj | Add-Member NoteProperty -Name "Alias" -Value $Mailbox.SAMAccountName
   $ServerObj | Add-Member NoteProperty -Name "DeviceCount" -Value $EASDeviceStats.Count
   $Final_DeviceCount += $ServerObj   
$MailboxInfo = Get-Mailbox $Mailbox.Identity | Select DisplayName,PrimarySMTPAddress,Alias
Foreach ($EASDevice in $EASDeviceStats)
    {$LastSuccessSync = ($EASDevice.LastSuccessSync)
 if ($LastSuccessSync -eq $null)
        {
            $syncAge = "Never"
        }
        else
        {
            $syncAge = ($today - $LastSuccessSync).Days
        }
       $reportObj = New-Object PSObject
       $reportObj | Add-Member NoteProperty -Name "DisplayName" -Value $MailboxInfo.DisplayName
       $reportObj | Add-Member NoteProperty -Name "EmailAddress" -Value $MailboxInfo.PrimarySMTPAddress
       $reportObj | Add-Member NoteProperty -Name "Alias" -Value $MailboxInfo.Alias
       $reportObj | Add-Member NoteProperty -Name "SyncAgeInDays" -Value $syncAge
            Foreach ($stat in $stats)
            {
                $reportObj | Add-Member NoteProperty -Name $stat -Value $EASDevice.$stat
            }
            $report += $reportObj
        }    
}
$report | Export-csv $filename
$TotalDevices = $report.count
$TotalMailboxes = $MailboxesWithEASDevices.Count
$AvgPerMailbox = $TotalDevices / $TotalMailboxes
$LastSync180  = ($Report | Where SyncAgeInDays -ge $age).count
$Top10DeviceTypes = $Report | group DeviceType | Sort Count -Descending | Select Count,Name -first 10 | ConvertTo-Html -head $b
$Top10Users = $final_DeviceCount | Sort DeviceCount -Descending | Select Alias,DeviceCount -first 10 | ConvertTo-Html -head $b
$PolicyApplied = $report | Group DevicePolicyApplied | Sort Count -Descending | Select Count,Name | ConvertTo-Html -head $b
$body =""
$attachment = $filename
$body += "<Font color=black>Attached are the Mobile Device Statistics within the entire OnPremise Exchange Organization using ActiveSync.</font><br><br><br>"
$body += "<b><Font color=#00008B>Total devices:</b><Font color=black> $TotalDevices</font><br><br>"
$body += "<b><Font color=#00008B>Total mailboxes that have a device:</b><Font color=black> $TotalMailboxes</font><br><br>"
$body += "<b><Font color=#00008B>Average Devices Per User:</b><Font color=black> $AvgPerMailbox</font><br><br>"
$body += "<b><Font color=#00008B>Devices with last sync time older than $age days:</b><Font color=black> $LastSync180</font><br><br>"
$body += "<b><Font color=#00008B>ActiveSync policy applied on all mailboxes:</b> $PolicyApplied</font><br><br>"
$body += "<b><Font color=#00008B>Top 10 device types:</b></font><br><br>"
$body += "<Font color=black>$Top10DeviceTypes</font><br><br><br>"
$body += "<b><Font color=#00008B>Top 10 users by device count:</b></font><br><br>"
$body += "<Font color=#00008B>$Top10Users</font><br><br><br>"
send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $body -BodyAsHtml -Priority high -Attachments $attachment

#=========End Script===========

Removing Exchange Databases Manually from AD

Scenario:  When you attempt to run Get-MailboxDatabase -includePreExchange2013, you get the following error:

The Exchange server for the database object ‘DB11’ wasn’t found in Active directory Domain Services. The object may be corrupted.

You then discovered the mailbox server that used to host the database no longer exists in your organization and was forcibly removed via ADSIEdit.

Resolution:  Remove the orphaned databases via ADSIEdit:

  1. Open ADSIEdit.
  2. Navigate to Configuration –> Configuration –>Services –> Microsoft Exchange –> Enterprise Exchange –> Administrative Groups –> Exchange Administrative Groups –> Databases.
  3. Locate the database and delete it.

Reconnect an Exchange 2013 Mailbox

Scenario:  A mailbox is disabled from an AD user account but it’s still present in Exchange as a disconnected Mailbox.  To view the disconnected mailbox:   Get-MailboxStatistics | Where DisconnectDate -ne $null

Resolution:  Run the following commands:

Connect-Mailbox “Display Name” -Database “DB Name” -user “AD User Object Name”

Example:

Connect-Mailbox “John Jacob” -Database DB03 -user “jjacob”

Exchange Script to Export Hardware Inventory and Exchange Settings

Scenario:    You want to export hardware inventory and Exchange settings from your Exchange Organization.

Script:  You can copy all the script parts and run at once OR run each script part separately depending on what you want to achieve.  You could also build on to this script using the same format with different get commands.

 

#Specify Variables
$today = (get-date -f yyyy-MM-dd)
$Folder = "C:TempCollection"
#Pull Exchange Server Hardware Inventory
$Servers = Get-ExchangeServer
$Servers | Export-csv $folder$today-Get-ExchangeServer.csv
($servers).name | Out-File $folder$today-Servers.txt
$serverList = "$folder$today-Servers.txt"
$outputCSV = "$folder$today-ServerInventory.csv"
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
pushd $dir
[System.Collections.ArrayList]$sysCollection = New-Object System.Collections.ArrayList($null)
foreach ($server in (Get-Content $serverList))
{
    "Collecting information from $server"
    $totCores=0
  
    try
    {
        [wmi]$sysInfo = get-wmiobject Win32_ComputerSystem -Namespace "rootCIMV2" -ComputerName $server -ErrorAction Stop
        [wmi]$bios = Get-WmiObject Win32_BIOS -Namespace "rootCIMV2" -computername $server
        [wmi]$os = Get-WmiObject Win32_OperatingSystem -Namespace "rootCIMV2" -Computername $server
        #[array]$disks = Get-WmiObject Win32_LogicalDisk -Namespace "rootCIMV2" -Filter DriveType=3 -Computername $server
        [array]$disks = Get-WmiObject Win32_LogicalDisk -Namespace "rootCIMV2" -Computername $server
        [array]$procs = Get-WmiObject Win32_Processor -Namespace "rootCIMV2" -Computername $server
        [array]$mem = Get-WmiObject Win32_PhysicalMemory -Namespace "rootCIMV2" -ComputerName $server
        [array]$nic = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "rootCIMV2" -ComputerName $server | where{$_.IPEnabled -eq "True"}
  
        $si = @{
            Server          = [string]$server
            Manufacturer    = [string]$sysInfo.Manufacturer
            Model           = [string]$sysInfo.Model
            TotMem          = "$([string]([System.Math]::Round($sysInfo.TotalPhysicalMemory/1gb,2))) GB"
            BiosDesc        = [string]$bios.Description
            BiosVer         = [string]$bios.SMBIOSBIOSVersion+"."+$bios.SMBIOSMajorVersion+"."+$bios.SMBIOSMinorVersion
            BiosSerial      = [string]$bios.SerialNumber
            OSName          = [string]$os.Name.Substring(0,$os.Name.IndexOf("|") -1)
            Arch            = [string]$os.OSArchitecture
            Processors      = [string]@($procs).count
            Cores           = [string]$procs[0].NumberOfCores
            NIC      = [string]$nic[0].IPAddress
        }
         
        $disks | foreach-object {$si."Drive$($_.Name -replace ':', '')"="$([string]([System.Math]::Round($_.Size/1gb,2))) GB"}
    }
    catch [Exception]
    {
        "Error communicating with $server, skipping to next"
        $si = @{
            Server          = [string]$server
            ErrorMessage    = [string]$_.Exception.Message
            ErrorItem       = [string]$_.Exception.ItemName
        }
        Continue
    }
    finally
    {
       [void]$sysCollection.Add((New-Object PSObject -Property $si))   
    }
}
 
$sysCollection `
    | select-object Server,NIC,TotMem,OSName,Arch,Processors,Cores,Manufacturer,Model,BiosDesc,BiosVer,BiosSerial,DriveA,DriveB,DriveC,DriveD,DriveE,DriveF,DriveG,DriveH,DriveI,DriveJ,DriveK,DriveL,DriveM,DriveN,DriveO,DriveP,DriveQ,DriveR,DriveS,DriveT,DriveU,DriveV,DriveW,DriveX,DriveY,DriveZ,ErrorMessage,ErrorItem `
    | sort -Property Server `
    | Export-CSV -path $outputCSV -NoTypeInformation  
#Export ClientAccess Settings
$CASServers = Get-ClientAccessServer 
$CASServers | %{Get-OWAVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-OWAVirtualDirectory.csv -append}
$CASServers | %{Get-ECPVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-ECPVirtualDirectory.csv -append}
$CASServers = Get-ClientAccessServer 
$CASServers | %{Get-OWAVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-OWAVirtualDirectory.csv -append}
$CASServers | %{Get-ECPVirtualDirectory -server $_.name  | Export-csv $folder$today-Ge$CASServers | %{Get-OABVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-OABVirtualDirectory.csv -append}
$CASServers | %{Get-MAPIVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-MAPIVirtualDirectory.csv -append}
$CASServers | %{Get-WebServicesVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-ServicesVirtualDirectory.csv -append}
$CASServers | %{Get-ActivesyncVirtualDirectory -server $_.name  | Export-csv $folder$today-Get-EASVirtualDirectory.csv -append}
$CASServers | %{Get-OutlookAnywhere -server $_.name | Export-csv $folder$today-Get-OutlookAnywhere.csv -append}
#Export Database Settings
$DB = Get-mailboxdatabase 
$DB | Export-csv $folder$today-Get-MailboxDatabase.csv -append
#Export DAG Settings
$DAG  = Get-DatabaseAvailabilityGroup 
$DAG | Export-csv $folder$today-Get-DatabaseAvailabilityGroup.csv

Determine what roles are needed to run specific commands when determining Role Groups

Scenario:  You want to provide only the roles necessary to a new role group based on the Exchange Shell commands that need to be ran.

Solution:  Run the following to determine which roles will need to be added to the role group:

Get-ManagementRoleEntry *Set-ActiveSyncMailboxPolicy  | Select Name,Role

Name                           Role
Set-ActiveSyncMailboxPolicy    Recipient Policies

Configure Exchange Certificate Based Authentication for Exchange ActiveSync

Scenario:  You want to enable Certificate Based Authentication for Exchange ActiveSync. TMG is already setup and configured to accept/process he CBA ActiveSync request. Now we need to configure Exchange so it can successfully accept the trusted authenticated hand off from TMG.
Our Environment:
 Exchange: Exchange 2013 SP1
Server:  Windows Server 2012
MDM:  Airwatch (Needed to push out the Certificates)
 We currently have and will keep servers that will process activesync via Basic Authentication with the server name: mail.domain.com. We have separate servers will process activesync via CBA with the servername: mailcba.domain.com.
Configuration Steps:
Exchange Certificate:
1. Add the new external and internal server names that will be used to point activesync devices to EAS CBA as an subject alternate name to the existing server. Currently you may use mail.domain.com as the server name for Exchange ActiveSync that processes Basic Authentication.  You will need a separate server name, such as mailcba.domain.com, that will be used to allow the TMG accept and pass this traffic to the new servers.  Also you need to include the internal server name as an subject alternative name.
 2. Import the Exchange certificate in Exchange and apply the IIS service to it.
Add Roles to Server:
3. Install the Client Certificate Mapping Authentication Role. In PowerShell run:
Import-Module ServerManager
Add-WindowsFeature Web-Client-Auth
 
ActiveSync Virtual Directory:
4. Remove Basic Authentication and Select Accept Client Certificate.
     a. Open Exchange EAC.
b. Navigate to Servers–>Virtual Directories
c. Open the ActiveSync Virtual Directory on the server you wish to enable Certificate Based Authentication.
d. Uncheck Basic Authentication and mark Accept Client Certificates.
 5. Add the Internal and External URLs accordingly, example: internalurl/externalurl: mailcba.domain.com  (Note: Becareful of AutoDiscover. If you have the option of not including this server for Autodiscover lookups for ActiveSync, mobile devices should not receive these server settings when they want to authenticate via Basic.  Else, you could set the internalurl/externalurl on the CBA virtual directories to the same urls as the ones accepting basic auth.
IIS Manager:
5. Enable Active Directory Client Certificate Authentication on the Server.
a. Open IIS Manager.
b. Click on the Server Name.
c. Click on Authentication
d. Enable Active Directory Client Certificate Authentication
e. Restart IIS Admin Service in Services console.
 6. Enable Client Certificate Mapping Authentication on the ActiveSync Virtual Directory.
a. From an elevated command prompt, navigate to C:windowssystem32inetsrv.
b. Enter in the following:
APPCMD.EXE set config “Default Web Site/Microsoft-Se
rver-ActiveSync” -section:system.webServer/security/authentication/clientCertificateMappingAuthentication /enabled:”True” /commit:apphost
 7. Change the UploadReadAheadSize from its default value (0) to the max size you wish to send from a activesync device. For example, if your sending limits is 35MB, the value will be 36700160.
a. Open IIS Manager.
b. Navigate to the Microsoft-Server-ActiveSync virtual directory.
c. Click on Configuration Editor.
d. Navigate to system.webserver/serverRuntime.
e. Edit the Value of uploadReadAheadSize to 36700160 and apply it.
 8.  Enable Windows Authentication on the Microsoft-Server-ActiveSync virtual Directory. (this is for TMG)
a. Open IIS Manager.
b. Navigate to the Microsoft-Server-ActiveSync virtual directory.
c. Click on Authentication.
d. Enable Windows Authentication.
 9.  Restart World Wide Web services from the services console.

Can not connect to a mailbox after reconnecting from disconnected state.

Scenario: After you reconnect a disconnected mailbox, you may receive the following error in Outlook Web App, ​ ‘Your mailbox has been disabled.’ You may also have trouble connecting to the mailbox in powershell or EMC saying the mailbox is not available.

Solution: Run the following command in Exchange Powershell: Update-StoreMailboxState -Database “db_name” -Identity “mailbox_guid”

Note: If you want to update the mailbox state for all mailboxes on a particular database

Get-MailboxStatistics -Database “db_name” | ForEach {Update-StoreMailboxState -Database $_.Database -Identity $_.MailboxGuid -Confirm:$False}

 

A mailbox sends out to more recipients than allowed by the recipient rate limit in their throttling policy

Scenario:  A mailbox has sent to more recipients than allowed by the recipient rate limit that is set in their throttling policy.  For example, a mailbox can only send out to  100 recipients within a 24 hour period, BUT that mailbox has managed to send out to 500 recipients. How can this be?

Whats happening:  A mailbox can send to more than 100 recipients if:

  1. The 24 hours have been completed for that mailbox. The will be able to send for up to another 100 recipients.
  2. The throttling service  has been restarted on the mailbox server where the mailbox is located.
  3. If a user sends to a distribution list, as the distribution list will only be counted as one recipient EVEN though there are 400 recipients in that distribution list.   

Here is the WorkFlow of the RecipientRateLimit in the throttling Policy.

Terminology:

Token: The token is the number of recipients which has already been processed

Token Bucket Map:  This is the table where the guids of the mailboxes are kept with the current RecipientRateLimit.

The token bucket map is held in memory when the Throttling service is started on the Mailbox Server. As new mailboxes send mail, they are added to the bucket map.  When the mailbox submits mail, the throttling service checks the bucket map to obtain the token for the sender.  In order for messages to be sent by the user, this must be true:

Obtain Token + Token Bucket Map <= RecipientRateLImit

Example: If the mailbox is trying to send 10 recipients ( Obtain Token) but the user has previously sent to 96 recipients (Token Bucket Map),  sending would fail:  96 + 10 <= 100

But if the mailbox is sending to 2 recipients (and not 10), sending would succeed:   96 + 2 <= 100

Distribution groups are counted as one sender  and further into the transport process of the sent message the  distribution groups is expanded into numerous recipients.  Sending a message from a mailbox always originates from the mailbox server where the mailbox is located on.  This is the mailbox server holds the Token Bucket Map.  The expansion of the distribution group can happen on any server during the transport process, thus its not logically possible to store the true number of recipients for that sender when a distribution group is used. This is why the Distribution Group is a loophole to the Recipient Rate Limit.

Script to Recreate Exchange Databases

Scenario:  You want to recreate an Exchange Database in order to reclaim the Windows space that the database was utilizing.  You have already moved all mailboxes off of the database.

 Script: This script performs the following:

  • Checks to make sure no mailboxes reside on the database
  • Removes All Database Copies
  • Removes the Databases from the folder directories ( You may have to edit this)
  • Creates the Database and sets the quotas to unlimited
  • Creates the Database Copies
  • Resolves any errors if the database copies are not healthy.
#Set DB Variable
$DB = "DB01"

#Sets ADServerSettings to see all Arbiratrion mailboxes
Set-ADServerSettings -ViewEntireForest $true

#Check for any mailboxes on the database
$1 = (get-mailbox -database $DB).count
$1 += (get-mailbox -database $DB -arbitration).count
$1 += (Get-mailbox -database $DB -archive).count
$1 += (Get-mailbox -database $DB -publicfolder).count
If ($1 -gt 0){
"Mailboxes still exist on this database. Please remove all mailboxes and run this script again."
}
Else
{
"Starting the Database Recreation Process"
"Determining Activation Preference"

#Gathers the Copies
$AP = Get-MailboxDatabaseCopyStatus $DB  | Sort ActivationPreference | Select -expandproperty Mailboxserver
$firstServer = $AP | Select -first 1
$OthServers = Get-MailboxDatabaseCopyStatus $DB | Where ActivationPreference -ne 1 | Sort ActivationPreference | Select -ExpandProperty MailboxServer
Write-Host "The Database is hosted on these 4 copies:  $AP"

#Remove all Mailbox Database copies from each server
Write-Host "Removing Database Copies"
get-mailboxdatabasecopystatus $DB | Where status -notlike *Mount* | Remove-mailboxdatabasecopy -confirm:$false 
remove-mailboxdatabase $DB -confirm:$false 

Write-Host "Sleeping 10 minutes for all processes to become unlocked"
Sleep 600
Write-Host "Deleting Folder structure on all copies"

#Delete Folder Structure
$AP | %{Remove-Item \$_c$$DBDB -force -recurse }
$AP | %{Remove-Item \$_c$$DBLogs -force -recurse }

#Create the DB
Write-Host "Creating $db"
New-MailboxDatabase $db -EDBFilePath C:$dbDB$db.edb -LogFolderPath C:$dbLogs -Server $firstserver 

#Wait for replication
Write-Host "Sleeping 3 minutes for replication"
Sleep 180

#Mount the Database
Write-Host "Mounting $db"
Mount-Database $db

#Wait for additional replication
Write-Host "Sleeping 3 minutes for replication"
Sleep 180

#Setting the DB Quotas
Write-Host "Setting DB Quotas to unlimited"
Set-MailboxDatabase $db -ProhibitSendReceiveQuota Unlimited -ProhibitSendQuota Unlimited -IssueWarningQuota Unlimited

#Create the additional Database Copies
$OthServers | %{
Write-Host "Creating DB copy for $DB on $_"
Add-MailboxDatabaseCopy $db -MailboxServer $_ 
}
#Resumes the DB Copies if they are not currently healthy
get-mailboxdatabasecopystatus $DB | Where status -notlike "Mounted" | Resume-mailboxdatabasecopy
}