Recipient Preview Filter for particular recipient container

Just quick post. You have dynamic distribution group, you need to scope it for special container and you need to preview recipients for that DDL just inside the container. Solution is easy. Use Where condition and with definition of recipient container in for of distinguished name:

 


$x = Get-DynamicDistributionGroup mailNY
GetRrecipient -RecipientPreviewFilter $x.recipientfilter |Where {$_.Distinguishedname -like "*OU=subou,OU=ou,DC=domain,DC=suffix"} |select *city*,distinguishedname

Easy? Yes, but I always forgot how I did it, so that is why I posted it.

SPF validation Powershell script

I have created a script to validate SPF records of your domains against kitterman´s WEB site.

  • Import functions Get-DNS and Validate-SPF into your Powershell profile or Powershell session.
  • Use is very easy. run Validate-SPF <domainname>, it returns if SPF is valid (PASS), invalid (FAIL) or not present (OK) and kittermann´s message as an object.

Update:Requires Windows 8 and newer (Thanks Wojciech)


function Validate-SPF ($domain){
$y = $null
$y = get-dns $domain | where {$_.strings -like "*spf1*"} | select name,strings
$res = $null
$res = "" | select domain,result,message,txt
$res.domain = $domain
if ($y -ne $null){
Write-Host "SPF present: $($y.strings). Checking validity ..." -ForegroundColor Green
$web = Invoke-WebRequest -Uri http://www.kitterman.com/spf/validate.html
$web.forms[0].fields.domain = "$($y.name)"
$result = Invoke-RestMethod http://www.kitterman.com/getspf2.py -Body $web.forms[0].fields
$message = $result.replace("`r`n","--")
$res.message = $result
$res.txt = $message
if ($message -like "*passed*"){
$res.result = "Passed"
} else {
$res.result = "FAIL"}
} else {
$res.message = "N/A"
$res.result = "OK"
$res.txt = "N/A"
}
return $res
}


function Get-DNS ([String]$domain){
resolve-dnsname $domain -type MX
resolve-dnsname $domain -type TXT

}spfvalidator

 

Exchange 2013,2010 – Event logs gathering (Health Check part 2.)

I have mede a script, which connects to all Exchange servers in organization using remote powershell and gathers all event logs for you to central place. Then it analyses event logs based on previous article (https://wordpress.com/post/40179192/2178/) (database of event IDs must be stored as CSV file delimited by semicolon). The result is again XLSX file with two worksheets. One is event ID raw data and second is analyzed event IDs. If there is event ID not present in the database, script marks it with “NEW IN DB, must be found first” in the Action row. If you run the script with empy CSV for database, it will generate XLSX as well, but you have to find solution for every event.

The script utilizes Export-XLSX.ps1. Thanks guys for the great job! https://gallery.technet.microsoft.com/office/Export-XLSX-PowerShell-f2f0c035

Example of script:

# Event logs gathering
Write-Host "Event logs gathering ... " -ForegroundColor White
#Event log variables
$exservers = get-exchangeserver
$evtlogdaysback = 1
$experfwizserver = hostname
$experfwizfilepath = "\\$($experfwizserver)\c$\ExchangeHealthCheck"   #  zmeneno z c:...
$outpath = "\\$($experfwizserver)\c$\ExchangeHealthCheck"
$WellKnownEventLogDB = import-csv .\wellknownevents.csv -Delimiter ";"

############## evt log gathering
$evtlogout = @()
  foreach ($exsvr in $exservers){
                Write-Host "Processing Exchange server $($exsvr.fqdn) ...."
                
                $evtlogout +=Invoke-Command -computername $exsvr.fqdn -ScriptBlock {
                $dat = ((get-date).adddays(-$args[0]))
                Get-eventlog -LogName * | select log | foreach {get-eventlog -LogName $_.log -EntryType Error,warning | where {$_.TimeGenerated -gt $dat} | select eventID,MachineName,Category,CategoryNumber,EntryType,Message,Source,TimeGenerated,PSComputerName}
                } -ArgumentList $evtlogdaysback

       }
       $bck = $evtlogout
$evtlogout = $evtlogout
#EVTlog cleaning
$i = 0
$out = @()
foreach ($line in $evtlogout){
$melio = ""
$line.message = $line.message.replace("`r`n","--")
foreach ($meli in $line.message){$melio = "$($melio) " + $meli}
$line.message = $melio
$out +=$line
}
# Event logs grouping, counting and comparing with WellKnownEventLogs Flat File DB
$res = @()
$WellKnownEventLogDB = import-csv .\wellknownevents.csv -Delimiter ";"
$analysedevents = "" | select count,eventid,entrytype,source,Message,action,affectedservers
$groupedlogs = $out | group eventID,source | sort name
foreach ($evtgroup in $groupedlogs){
        $match = 0
        foreach ($dbline in $WellKnownEventLogDB){

            if($dbline.Eventid -match $evtgroup.name.split(",")[0]){
              if($dbline.Source -match $evtgroup.name.split(",")[1].trim()){
                            $analysedevents = "" | select count,eventid,entrytype,source,Message,action,affectedservers
                            $analysedevents.eventid = $dbline.Eventid                        
                            $analysedevents.entrytype = $dbline.EntryType
                            $analysedevents.source = $dbline.Source
                            $analysedevents.Message  = $dbline.Message
                            $analysedevents.action =  $dbline.action
                            $analysedevents.affectedservers = "$($evtgroup.group | select machinename | group machinename | select name)"
                            $analysedevents.count = $evtgroup.count
                            $res +=$analysedevents
                            $match = 1
                            }
                }
        }
 $match
       if ($match -eq 1){}else{
            $analysedevents = "" | select count,eventid,entrytype,source,message,action,affectedservers
            $analysedevents.eventid = $evtgroup.name.split(",")[0]                        
            $analysedevents.EntryType = $evtgroup.group[0].EntryType
            $analysedevents.source = $evtgroup.name.split(",")[1].trim()
            $analysedevents.message  = $evtgroup.group[0].message
            $analysedevents.action =  "NEW IN DB, must be found first"
            $analysedevents.affectedservers = "$($evtgroup.group | select machinename | group machinename | select name)"
            $analysedevents.count = $evtgroup.count
                $res +=$analysedevents
               }

}
$res | .\Export-xlsx -path "$($xlsout)\EventLogs.xlsx" -WorKsheetname "Analyzed EVENT logs" -Append
$EvtNotExchrelLOGS  = $out
$EvtNotExchrelLOGS | .\Export-xlsx -path "$($xlsout)\EventLogs.xlsx" -WorKsheetname "Event logs raw data" -Append






################################################################################################################################################################################################################################


################################
#   Ends HERE                  #
################################


 Download:

https://onedrive.live.com/redir?resid=3941F86AC9A4F457!9241&authkey=!AG73DJErvetxKsM&ithint=file%2czip

 

Script to run and collect all Exchange performance counters from your environment to server running the tool

Script runs the remote PowerShell session against all Exchange servers, copies the Experfwiz utility to each one and runs performance counters for the time defined by administrator and exports results to the folder, from which it was run.

1. Copy ExPerfWiz.ps1 from: https://experfwiz.codeplex.com/ to some server running Exchange Management Shell to c:\ExchangeHealthCheck
2. Run the script
3. Enter Organization Admin Credentials
4. will be asked to confirm to run perfmon on each server

perfwiz

#Author: Zbynek Salon

#Description: This is small script to collect performace counters from All servrers in your environment to single folder on the server running perfwiz.

#Variables experfwiz

$date = get-date

$UserCredential = Get-Credential

$experfwizstart = $date.AddHours(1)

$experfwizduration = "04:00:00"

$experfwizinterval = 5

$experfwizserver = hostname

$experfwizfilepath = "\\$($experfwizserver)\c$\ExchangeHealthCheck" # zmeneno z c:...

$experfwizmaxsize = 512

###################################################################################################################################################################################################

#Performance counters

$localhost = hostname

$script = get-content "\\$($localhost)\c$\ExchangeHealthCheck\experfwiz.ps1"

$exservers = get-exchangeserver

 foreach ($exsvr in $exservers){

 Write-Host "Processing Exchange server $($exsvr.fqdn) ...."

 Invoke-Command -computername $exsvr.fqdn -ScriptBlock {

 #copying script to EXBIN

 Write-Host "Copying PerfWiz to $($args[1])."

 Test-Path $args[1]

 $exinstall = $args[1]

 $x = $args[0] 

 $x | out-file "$($exinstall)Scripts\experfwiz.ps1"

 Start-Sleep 1

 Write-Host "Importing Exchange PS Session." 

 $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$($args[8])/PowerShell/" -Authentication Kerberos -Credential $args[9] -AllowRedirection

 Import-PSSession $Session

 cd "$($exinstall)Scripts"

 Set-ExecutionPolicy Unrestricted -Confirm:$false

 .\experfwiz.ps1 -delete #-FilePath $args[5] -Server $args[6] -MaxSize $args[7]

 .\experfwiz.ps1 -Duration "$($args[3])" -interval "$($args[4])" -FilePath "$($args[5])" #-Server "$($args[6])" #-MaxSize $args[7]

 #running perfmon

 } -ArgumentList $script, $exinstall, $experfwizstart, $experfwizduration, $experfwizinterval, $experfwizfilepath, $experfwizserver, $experfwizmaxsize, $exsvr.fqdn, $usercredential

}

##################################################################################################################################################################################################

The script can be downloaded from my OneDrive:
OneDrive

Log Search script

This is just a small easy script to search log content in defined location and time range for string value. If you know what to search in many log files, it can help you to narrow search.

#logsearch
#Author: Zbynek Salon
#Path to search
$path = "D:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive"
#What to search (the best would be for example e-mail address for some SMTP log)
$str = "PRX5"
#Range (Start must be lower then end)
$start = "5/25/2014 8:00AM"
$end = "5/28/2014 9:00AM"
Get-ChildItem $path | where {($_.LastWriteTime -gt $Start) -and ($_.LastWriteTime -lt $End)} | foreach {$_
 $x = Get-Content -Path "$($path)\$_"
 if ($x -like "*$($str)*"){Write-Host "$_ contains $($str)" -ForegroundColor Green}
}

Script to gather FullAccess and SendAs permissions

I wrote this script to gather FullAccess and SendAs permissions to divide mailboxes into logical batches. May be somebody find it helpful.

#Purpose of this script is to gather full access and Send-As permissions of all mailboxes in organization
#Author: Zbynek Salon
#importing needed module
Import-Module Servermanager
$out = @()
$path = "c:\temp\FASA.txt"
$out +="Identity-email;Full Access;Send AS"
set-adserversettings -ViewEntireForest $true
#gathering info
$list = Get-Mailbox -resultsize unlimited | select alias,displayname,primarysmtpaddress,userprincipalname,distinguishedname
$i = 0
Foreach ($line in $list){
$sa = $null
$fa = $null
$fa = get-mailbox "$($line.distinguishedname)" | get-mailboxpermission | where {($_.IsInherited -like $false) -and ($_.accessrights -like "*Full*") -and ($_.user -notlike "*SELF*")}
$sa = get-mailbox "$($line.distinguishedname)" | get-adpermission | where {($_.extendedrights -like "*Send*") -and ($_.IsInherited -like $false) -and ($_.Deny -like $false)  -and ($_.user -notlike "*SELF*")}
Write-host "°°°°°°°°°°°°°°$($line.alias)"
#Full Access section
write-host "Full Access $($line.displayname)"
$fapo=$null
$sapo=$null
if ($fa -ne $null){
$fapo = "FA:"
            foreach ($fap in $fa){
                $o2=$null
                $o=$null
                #query object from AD using LDAP (translate SID to DN)
                $o = [adsi]"LDAP://<SID=$($fap.user.securityidentifier)>"
                #query needed properties of AD object (AD object is used to query for all object types
                $o2 = get-adobject "$($o.distinguishedname)" -properties * | select displayname,userprincipalname
                $o2
                if($o2 -ne $null){
                    $fapo = $fapo + "|$($o2.displayname)*$($o2.userprincipalname)"
                }
                else{$fapo = $fapo + "|NoExist*$($fap.user.securityidentifier)" }
            }
}else{}
#Send As section
write-host "Send - AS $($line.displayname)"
if ($sa -ne $null){
$sapo = "SA:"
            foreach ($sap in $sa){
                $u2=$null
                $u=$null
                $u = [adsi]"LDAP://<SID=$($sap.user.securityidentifier)>"
                $u2 = get-adobject "$($u.distinguishedname)" -properties * | select displayname,userprincipalname
                $u2
                if($u2 -ne $null){
                    $sapo = $sapo + "|$($u2.displayname)*$($u2.userprincipalname)"
                }
                else{$sapo = $sapo + "|NoExist*$($fap.user.securityidentifier)" }
            }
 }else{}
$out += "$($line.displayname)*$($line.userprincipalname);$($fapo);$($sapo)"
}

$out | out-file "$($path)"

Office 365 – Adding SMTP addresses while DirSync without ADFS/Hybrid

Background

There are limitations, when you deploy Office 365 without ADFS/Hybrid. In this article I would like to write about SMTP addresses.

  • This attribute is synchronized to Office 365
  • You cannot add SMTP addresses on the cloud side, so you have to use attribute editor or Powershell On-Premise instead
  • To use Powershell you need to import module for Server manager and one of the methods to add / remove or replace SMTP addresses is to use Set-ADUser cmdlet, where you add string values to multivalue property “ProxyAddresses”
  • More proxy addresses can be added at the time
get-aduser -identity "stokurev" | set-aduser -add @{'ProxyAddresses'=@("SMTP:anatolij.stokurev@domain.com","smtp:stokurev@domain.com")}

Example

As an example here is the script to double existing aliases with another domain suffix

#Purpose of this script is to double aliases of domain.suffix to domain.suffix2 as secondary SMTP addresses
#Author: Zbynek Salon
#importing needed module
Import-Module Servermanager
#gathering and adding aliases
$x = get-aduser -SearchBase "OU=SUFFIX2,OU=Office365,OU=People,DC=DOMAIN,DC=SUFFIX2" -filter * -pr * | select SAMAccountname,UserPrincipalName,proxyaddresses
foreach ($line in $x){
    foreach ($addr in $line.proxyaddresses){
                if ($addr -like "smtp:*"){
                $addr = $addr.replace("DOMAIN.SUFFIX","DOMAIN.SUFFIX2")
                $addr = $addr.replace("SMTP:","")
                $addr = $addr.replace("smtp:","")
                $addr
                get-aduser -identity "$($line.samaccountname)" | set-aduser -add @{'ProxyAddresses'=@("smtp:$($addr)")}

                }
                }
               
}
#check results
$y = get-aduser -SearchBase "OU=SUFFIX2,OU=Office365,OU=People,DC=DOMAIN,DC=SUFFIX2" -filter * -pr * | select SAMAccountname,UserPrincipalName,proxyaddresses
foreach ($line in $y){
$line.samaccountname
    foreach ($addr in $line.proxyaddresses){
                if ($addr -like "smtp:*"){
                $addr
                }
                }
               
}

Migration of receive connectors from Exchange 2007/2010 to Exchange 2013

I have recently came to a need to migrate many receive connectors from Exchange 2010 to Exchange 2013. I helped myself by creating a function, which migrates all connectors from one server to another. Connectors with same settings (bindings, Remote IP ranges and port) are skipped. Function doesn’t automatically grant anonymous relay permission, because I think it is worth setting manually after check of migrated connectors, since almost all environments are messy :).

Don’t forget, that there is major architecture change in Exchange 2013! All external e-mails should flow via FrontEndTransport service! The detailed description with consequences is mentioned here:

http://blogs.technet.com/b/rischwen/archive/2013/03/13/exchange-2013-mail-flow-demystified-hopefully.aspx

Description:

  • Connectors are listed from selected server
  • Default connectors are skipped
  • If you use server name in FQDN for some reason, please check FQDN manually after transfer
  • Connectors are being created with WhatIf parameter to keep admin informed of the changes
  • Admin can confirm, that connectors should be created if output is OK, if not, admin can skip
  • All tasks are recorded in Transcript (default location)
function Transfer-ReceiveConnectors ($source, $destination, $bindings, $transportrole){
$OldServer = $source
$NewServer = $destination
Write-Host "Transrtipt will be started."  -foregroundcolor "yellow"
start-transcript

Write-Host "Getting receive connectors..."  -foregroundcolor "yellow"
[array]$ReceiveConnectors = Get-ReceiveConnector -Server $OldServer | Where {$_.Name -notlike "Default $($OldServer)" -and $_.Name -notlike "Client $($OldServer)"} 

Write-Host "Testing creation of receive connectors..."  -foregroundcolor "yellow"
$ReceiveConnectors | foreach {
Write-Host "Testing: $($_.name)"  -ForegroundColor "blue" -BackgroundColor "White"
                New-ReceiveConnector -MaxAcknowledgementDelay $_.MaxAcknowledgementDelay -FQDN $_.fqdn -TransportRole $transportrole -Bindings $bindings -Name $_.Name -AuthMechanism $_.AuthMechanism -RemoteIPRanges $_.RemoteIPRanges -Banner $_.Banner -ChunkingEnabled $_.ChunkingEnabled -DefaultDomain $_.DefaultDomain -DeliveryStatusNotificationEnabled $_.DeliveryStatusNotificationEnabled -EightBitMimeEnabled $_.EightBitMimeEnabled -DomainSecureEnabled $_.DomainSecureEnabled -LongAddressesEnabled $_.LongAddressesEnabled -OrarEnabled $_.OrarEnabled -Comment $_.Comment -Enabled $_.Enabled -ConnectionTimeout $_.ConnectionTimeout -ConnectionInactivityTimeout $_.ConnectionInactivityTimeout -MessageRateLimit $_.MessageRateLimit -MaxInboundConnection $_.MaxInboundConnection -MaxInboundConnectionPerSource $_.MaxInboundConnectionPerSource -MaxInboundConnectionPercentagePerSource $_.MaxInboundConnectionPercentagePerSource -MaxHeaderSize $_.MaxHeaderSize -MaxHopCount $_.MaxHopCount -MaxLocalHopCount $_.MaxLocalHopCount -MaxLogonFailures $_.MaxLogonFailures -MaxMessageSize $_.MaxMessageSize -MaxProtocolErrors $_.MaxProtocolErrors -MaxRecipientsPerMessage $_.MaxRecipientsPerMessage -PermissionGroups $_.PermissionGroups -PipeliningEnabled $_.PipeLiningEnabled -ProtocolLoggingLevel $_.ProtocolLoggingLevel -RequireEHLODomain $_.RequireEHLODomain -RequireTLS $_.RequireTLS -EnableAuthGSSAPI $_.EnableAuthGSSAPI -ExtendedProtectionPolicy $_.ExtendedProtectionPolicy -SizeEnabled $_.SizeEnabled -TarpitInterval $_.TarpitInterval -Server $NewServer -WhatIf
        } 
Write-Host "If output is expected and you want to create connectors, please type Y."  -foregroundcolor "yellow"
$Continue = read-host "Please confirm: "
if ($continue -eq "Y"){
        $ReceiveConnectors | foreach {
                New-ReceiveConnector -MaxAcknowledgementDelay $_.MaxAcknowledgementDelay -FQDN $_.fqdn -TransportRole $transportrole -Bindings $bindings -Name $_.Name -AuthMechanism $_.AuthMechanism  -RemoteIPRanges $_.RemoteIPRanges -Banner $_.Banner -ChunkingEnabled $_.ChunkingEnabled -DefaultDomain $_.DefaultDomain -DeliveryStatusNotificationEnabled $_.DeliveryStatusNotificationEnabled -EightBitMimeEnabled $_.EightBitMimeEnabled -DomainSecureEnabled $_.DomainSecureEnabled -LongAddressesEnabled $_.LongAddressesEnabled -OrarEnabled $_.OrarEnabled -Comment $_.Comment -Enabled $_.Enabled -ConnectionTimeout $_.ConnectionTimeout -ConnectionInactivityTimeout $_.ConnectionInactivityTimeout -MessageRateLimit $_.MessageRateLimit -MaxInboundConnection $_.MaxInboundConnection -MaxInboundConnectionPerSource $_.MaxInboundConnectionPerSource -MaxInboundConnectionPercentagePerSource $_.MaxInboundConnectionPercentagePerSource -MaxHeaderSize $_.MaxHeaderSize -MaxHopCount $_.MaxHopCount -MaxLocalHopCount $_.MaxLocalHopCount -MaxLogonFailures $_.MaxLogonFailures -MaxMessageSize $_.MaxMessageSize -MaxProtocolErrors $_.MaxProtocolErrors -MaxRecipientsPerMessage $_.MaxRecipientsPerMessage -PermissionGroups $_.PermissionGroups -PipeliningEnabled $_.PipeLiningEnabled -ProtocolLoggingLevel $_.ProtocolLoggingLevel -RequireEHLODomain $_.RequireEHLODomain -RequireTLS $_.RequireTLS -EnableAuthGSSAPI $_.EnableAuthGSSAPI -ExtendedProtectionPolicy $_.ExtendedProtectionPolicy -SizeEnabled $_.SizeEnabled -TarpitInterval $_.TarpitInterval -Server $NewServer -verbose
                }
       Write-Host "Completed!"  -foregroundcolor "green"
}else {Write-Host "Aborted!"  -foregroundcolor "red"}
Stop-TranScript
}

Function can be called by the following cmdlet:

Transfer-ReceiveConnectors <SOURCE_SERVER> <TARGET_SERVER> <BINDINGS> <ExchangeTransportRole>

Examples:

Transfer-ReceiveConnectors EX2010 EX2013 0.0.0.0:25,[::]:25 FrontEndTransport
Transfer-ReceiveConnectors EX2010 EX2013 192.168.1.25:25 HubTransport

How to backup Exchange 2013 database on Windows Server 2012

Exchange backup limitations

  • Only Full backup is possible with log truncation (incremental backups do not flush logs)
  • Normal backup performance option does always full backup, but only differences are stored in the disk, while using file block mode.

After initial full backup every other backup of the same volume to the same target is incremental unless the following two conditions are fulfilled:

Configuring Windows backup

Installation

Windows Server Backup feature is not installed by default on Windows 2012 server. To gather status use the following Powershell command:

Get-windowsfeature | where {$_.name -like "*backup*"}

Backup_feature_not_installed

To install Windows Server Backup features run the following commands from Powershell. These commands will install needed features to run Exchange backup via command line tool Wbadmin.

Add-WindowsFeature "Windows-Server-Backup"

after-installation

In backup/restore of Exchange servers we will use Powershell commands. In comparison with previous version of Windows server there is no need to add pssnapin for backup to Exchange Powershell session.

Add new disk for Exchange backups. In script there used “Backup Disk” as volume name. (Depends on the HW you use so no further notice here)

Configuration

To successfully configure Windows Server Backup we must first define backup policy:

$WBPolicyFull = New-WBPolicy									# Create new WB policy object
$WhatToBackup = New-WBFileSpec –FileSpec E:\				# Define what to backup
Add-WBFileSpec –Policy $WBPolicyFull –FileSpec $WhatToBackup 	# Adding what to backup to backup policy
$WBTarget = New-WBbackupTarget –NetworkPath "\\Servername\sharename\path" 	# Selected backup disk will be set as target
Add-WBBackupTarget –Policy $WBPolicyFull -Target $WBTarget 		# Adding target to WB policy
Set-WBVssBackupOptions -Policy $WBPolicyFull -VssFullBackup			# Set method to use WSS Full Backup

Check

To check policy:

$WBPolicyFull

Policy configured in previous step might be started directly

Start-WBBackup -Policy $WBPolicyFull

Or scheduled

Set-WBSchedule –Policy $WBPolicyFull –Schedule 21:00

List backup sets

To list backup sets:

Get-WBBackupSet

Example 1

$WBPolicyFull = New-WBPolicy
$WhatToBackup = New-WBFileSpec –FileSpec E:\
Add-WBFileSpec –Policy $WBPolicyFull –FileSpec $WhatToBackup 
$WBTarget = New-WBbackupTarget –Networkpath "\\Ex13node2\g$\Full"
Add-WBBackupTarget –Policy $WBPolicyFull -Target $WBTarget
Set-WBVssBackupOptions -Policy $WBPolicyFull -VssFullBackup
Start-WBBackup -Policy $WBPolicyFull
Set-WBSchedule –Policy $WBPolicyFull –Schedule 21:00

or create a task

Example 2

Example can be downloaded on My Skydrive (E2013BackupW12.zip)

ZIP file contains:

  • *.PS1 file – script to perform backup
  • *.XML file – windows task file example

This Example creates backup of single mailbox database to local drive and then copies it to network drive.

$dat = get-date
start-transcript "C:\ExchangeScripts\Backups\DB_Backup_$($dat.day)_$($dat.month)_$($dat.year)_report.txt"
write-host "started"
new-item -ItemType Directory -path "G:\Full\$($dat.dayofweek)" -erroraction SilentlyContinue
New-PSDrive –Name “G” –PSProvider FileSystem –Root “\\fileserver\data\ExchangeBackup” –Persist
$target = "\\Ex13node2\d$\ExchangeBackup"
#$target = "\\fileserver\data\Full\$($dat.dayofweek)\ExchangeBackup"
$drive = "G:\Full\$($dat.dayofweek)"
$cleanup = $drive + "\WindowsImageBackup"
Remove-Item -Recurse -Force $cleanup
write-host $target
$policy = New-WBPolicy
$co_zalohovat= New-WBFileSpec -filespec F:\
Add-WBFileSpec -Policy $policy -FileSpec $co_zalohovat
$backuplocation = New-WBBackupTarget -networkpath $target
Add-WBBackupTarget -Policy $policy -Target $backuplocation
Set-WBVssBackupOptions -Policy $policy -VssFullBackup
Start-WBBackup -Policy $policy -force
get-wbjob -previous 1
Get-ChildItem –path $($target) -Recurse | Foreach-Object  { copy-item -Path $_.fullname -Destination $($drive) }
stop-transcript

Result of example

Example_run

This solution works only, when running backup from active database copy on member of DAG, which hosts only active DB copies.

Exchange 2010 – Get-MailboxDatabaseStatistics (your customization)

The function Get-MailboxDatabaseStatistics has been created due to a reporting purpose. I was focused mainly on script output and customization. It means that the most part of script code is related to the output and its attributes. Well it would be quite easy to modify the output attributes based on your needs.

Script prerequisites

  • Exchange Server 2010
  • Exchange Management Shell run-space
  • Organization Management (RBAC)

Script inputs

The function requires one of these inputs: DatabaseName, ServerName, AllDatabases. It means that we can call the function for example like:

Get-MailboxDatabaseStatistics -DatabaseName mbx11

Get-MailboxDatabaseStatistics -DatabaseName mbx11,mbx12

Get-MailboxDatabaseStatistics -DatabaseName (Get-MailboxDatabase|?{$_.Server -like "*exch10*"})

Get-MailboxDatabaseStatistics -ServerName exch10ser01

Get-MailboxDatabaseStatistics -AllDatabases

Database queries (code)

The function expects database name/names declared via input parameters. After input validation process, the function can process particular database name in a loop. The loop is uses for the following queries:

  • $query1 = Get-MailboxDatabase -Identity $db -Status
  • $query2 = Get-Mailbox -Database $db
  • $query3 = $query2 |%{if($_.Identity -ne $null){Get-MailboxStatistics -identity $_.identity}}

As can be seen above, here is the first thing for consideration/customization. Because the script processing time depends primarily on amount and severity of particular query. So the processing time can be decreased by query optimization (e.g. omit  Get-Mailbox etc.).

Output attributes (code)

The loop fills query variables ($query1, $query2, $query3) and those variables are used for declaration of attributes into output brick.  Due to this reason the brick can contain original values as same as a variation of values from all queries at once. Attributes are added into the brick through simple function addResultAttribute:

function addResultAttribute ($propertyName,$propertyValue){
$result | add-member -Type NoteProperty -name $propertyname -value $propertyValue
}

Just for example we can create attribute DatabaseName by using query1 (Get-MailboxDatabase -Status) and its parameter Name:

  • DatabaseName : DB01
addResultAttribute DatabaseName $query1.Name

Another example is attribute  RecipientTypeStatus which keeps amount of mailboxes based on  RecipientTypeDetails:

  • RecipientTypeStatus: {158-UserMailbox, 36-SharedMailbox, 1-DiscoveryMailbox, 3-ArbitrationMailbox}
addResultAttribute RecipientTypeStatus (($query2|Group-Object -Property RecipientTypeDetails|%{$_.Count,$_.Name -join ("-")}) -join (",")).ToString()

Feel free to modify brick attributes. The function contains only attributes regarding my scenario.

Scrip output

The output is brick wall as collection. It contains the brick with declared attributes for each database which was processed by the function. So it can looks like:

DatabaseName                     : DB01
DatabaseSize                     : 171 GB (183,619,878,912 bytes)
AvailableNewMailboxSpace         : 12.3 GB (13,204,750,336 bytes)
CircularLoggingEnabled           : False
LogFolderPath                    : E:\DB01
EdbFilePath                      : E:\DB01\DB01.edb
IssueWarningQuota                : 2.876 GB (3,088,056,320 bytes)
ProhibitSendQuota                : 3 GB (3,221,225,472 bytes)
ProhibitSendReceiveQuota         : 3.276 GB (3,517,972,480 bytes)
LastFullBackup                   : 5.4.2013 15:01:15
LastIncrementalBackup            : 7.4.2013 15:01:13
DatabaseCopiesCount              : 2
DatabaseCopiesServer             : {exch10Ser01, exch10Ser01}
TotalMailboxCount                : 198
RecipientTypeStatus              : {158-UserMailbox, 36-SharedMailbox, 1-DiscoveryMailbox, 3-ArbitrationMailbox}
MailboxWithDatabaseQuotaCount    : 189
ArchiveMailboxCount              : 130
SoftDeletedMailboxCount          : 
DisconectedMailboxCount          : 4
MailboxTotalItemSizeSumMB        : 138996
MailboxTotalDeletedItemSizeSumMB : 4931
MailboxTotalSizeSumMB            : 143927
MailboxTotalItemCount            : 1426074
Top3LargestMailboxSize           : {2.935 GB (3,151,719,330 bytes), 2.904 GB (3,118,361,061 bytes), 2.897 GB (3,110,582,421 bytes)}
Top3LargestMailboxName           : {Kasaj Filip, Salon Zbynek, Namjman Lukas}
MailboxLimitStatus               : {187-BelowLimit, 6-NoChecking, 4-IssueWarning}

Of course that we can use the function also:

[PS] C:\> $rep = Get-MailboxDatabaseStatistics -DatabaseName db01
[PS] C:\> $rep.MailboxTotalDeletedItemSizeSumMB
4916

[PS] C:\> $rep2 = Get-MailboxDatabaseStatistics -DatabaseName db01,db02
[PS] C:\> $rep2 | fl RecipientTypeStatus
RecipientTypeStatus : {158-UserMailbox, 36-SharedMailbox, 1-DiscoveryMailbox, 3-ArbitrationMailbox}
RecipientTypeStatus : {151-UserMailbox, 52-SharedMailbox}

[PS] C:\> Get-MailboxDatabaseStatistics -AllDatabases | Export-Csv -Delimiter "," -Encoding unicode -Path "C:rep.txt"

How to load the function

  • Save function as ps1 file.
  • Import the file into Exchange Management Shell.
  • That’s it.

07-04-2013 19-45-27Download: 

Get-MailboxDatabaseStatistics.ps1