Use PowerShell formatting features to your advantage!

While troubleshooting customers' cases my colleague Tudor Chirita and I have seen many situations where data collected was incomplete, unreadable/unusable or simple hard to follow.

We intend to present you some formatting options that can be used to have a more friendly experience.

The most used formatting options are: Format-Table and Format-List. Almost everybody is using them with their aliases: FT and FL

  • Format-Table

C:\> Get-Mailbox | Format-Table

Name Alias ServerName ProhibitSendQuota
---- ----- ---------- -----------------
user1 user1 db5pr0101mb2040 99 GB (106,300,440,576 bytes)
user2 user2 vi1pr0101mb2014 49.5 GB (53,150,220,288 bytes)
user3 user3 he1pr0102mb2748 99 GB (106,300,440,576 bytes)
user4 user4 db5pr01mb1560 99 GB (106,300,440,576 bytes)

If their properties have large text we can adapt format with FT -Wrap -AutoSize

  • Format-List

C:\> Get-Mailbox Admin | Format-List

AcceptMessagesOnlyFromSendersOrMembers : {}
AddressListMembership : {\Staff GAL, \Mailboxes(VLV), \All Mailboxes(VLV), \All Recipients(VLV)...}
AdministrativeUnits : {}
Alias : admin
ArbitrationMailbox :
BypassModerationFromSendersOrMembers : {}
OrganizationalUnit : Exchange Hosted Organizations/
CustomAttribute1 :
CustomAttribute10 :
CustomAttribute11 :
CustomAttribute12 :
CustomAttribute13 :
CustomAttribute14 :
CustomAttribute15 :
CustomAttribute2 :
CustomAttribute3 :
CustomAttribute4 :
CustomAttribute5 :
CustomAttribute6 :
CustomAttribute7 :
CustomAttribute8 :
CustomAttribute9 :
ExtensionCustomAttribute1 : {}
ExtensionCustomAttribute2 : {}
ExtensionCustomAttribute3 : {}
ExtensionCustomAttribute4 : {}
ExtensionCustomAttribute5 : {}
DisplayName : VictorL
EmailAddresses : {SPO:SPO_74020321-9dc9-4a7a-beba-33d0d7b74f03@SPO_e0ef2476-0aff-4b94-a7cb-68dc9a0555b8,,,}

As you can see some properties have truncated values. Example: AddressListMembership and EmailAddresses.

By default, PowerShell displays only the first 4 values of a property. This setting is stored in a variable called $FormatEnumerationLimit

C:\> $FormatEnumerationLimit 4

PowerShell can be configured not to truncate the output by setting this variable to -1.

$FormatEnumerationLimit = -1

Another way to see all values of EmailAddresses property is to use Select-Object command with ExpandProperty option:

C:\> Get-Mailbox Admin | Select-Object -ExpandProperty EmailAddresses SPO:SPO_74020321-9dc9-4a7a-beba-33d0d7b74f03@SPO_e0ef2476-0aff-4b94-a7cb-68dc9a0555b8

Select-Object can also to used with  parameter ExcludeProperty to exclude some properties that could clutter the output.
When analyzing Exchange Online migration reports you can get:

$stats = Get-MoveRequestStatistics -IncludeReport -Diagnostic -DiagnosticArgument Verbose $stats | fl

The output is large because the properties Report and DiagnosticInfo contain lots of data. If we want to have a fast overview of the output we can exclude those and analyze them later.

$stats | select -ExcludeProperty Report,DiagnosticInfo

Now we can see the status of the migration and other parameters and if needed we can investigate separately the too large properties:


When using Format-Table, Format-List or Select-Object we can choose which properties to see by specifying property name or part of the name and wildcard character " * "

C:\> Get-MailboxFolderStatistics admin | FT Name, items*

Name ItemsInFolder ItemsInFolderAndSubfolders
---- ------------- --------------------------
Top of Information Store 1 555
Archive 0 0
Calendar 20 31
Calendar1 0 0
Contacts 5 96
{A9E2BC46-B3A0-4243-B315-60D991004455} 3 3

Reading a large output of a  CmdLet can be gradually done in PowerShell console (not in ISE) by using "| more" or "Out-Host -Paging" like below:

Get-Command –Command | select name, module | more Get-Command –Command | select name, module | Out-Host -Paging get-help Get-MoveRequestStatistics -full |more get-help Get-MoveRequestStatistics -full |Out-Host -Paging

Navigate through result:  <Space> next page;  <Enter>next line; <Q> Quit

To get the result straight into the clipboard  you can use |clip

get-mailbox admin | select ExchangeGUID | clip

To sort the results  you can use "Sort" and the name property you want sort by. Default sorting is done Ascending; if you want to sort descending you have to use "-Descending" parameter.

Get-MailboxFolderStatistics admin |select name,folderpath,foldertype |sort folderpath

Name FolderPath FolderType
---- ---------- ----------
Archive /Archive Archive
Calendar /Calendar Calendar
Calendar Logging /Calendar Logging CalendarLogging
Calendar1 /Calendar/Calendar1 User Created


Grouping the result

The Group-Object cmdlet displays objects in groups based on the value of a specified property. Group-Object returns a table with one row for each property value and a column that displays the number of items with that value.

Get-Mailbox -ResultSize Unlimited |Group-Object RecipientTypeDetails

Count Name Group
----- ---- -----
23 UserMailbox {admin2, admin3, admin4, cloud1, cloud3, cloud4, cloud5, cloud6,...
6 SharedMailbox {cloud2, journal_mbx, shared1, shared2, shared3, shared4}
1 DiscoveryMailbox {Discovery Search Mailbox}
1 SchedulingMailbox {IT}
2 RoomMailbox {room1, room4}


Show Hidden Parameter (-force)

PowerShell cannot define private parameters. These can only be hidden. To see all parameters (including hidden ones) of an object you can use * -force
E.g.: Showing all parameters of the last error received in current session of PowerShell vs default view of the error:

$e= $error[0]

$e.Exception | fl
The operation couldn't be performed because object 'John Doe' couldn't be found on ''.

$e.Exception | fl * -f
SerializedRemoteException : Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException: The operation couldn't be performed because
object 'dfadfad' couldn't be found on ''.
at Microsoft.Exchange.Configuration.Tasks.Task.ThrowError(Exception exception, ErrorCategory errorCategory, Object
target, String helpUrl)
at Microsoft.Exchange.Configuration.Tasks.GetObjectWithIdentityTaskBase`2.InternalProcessRecord()
at Microsoft.Exchange.Configuration.Tasks.GetRecipientObjectTask`2.InternalProcessRecord()
at Microsoft.Exchange.Management.RecipientTasks.GetRecipientWithAddressListBase`2.InternalProcessRecord()
at Microsoft.Exchange.Configuration.Tasks.Task.b__92_1()
at Microsoft.Exchange.Configuration.Tasks.Task.InvokeRetryableFunc(String funcName, Action func, Boolean
SerializedRemoteInvocationInfo : System.Management.Automation.InvocationInfo
ErrorRecord : The operation couldn't be performed because object 'John Doe' couldn't be found on
WasThrownFromThrowStatement : False
Message : The operation couldn't be performed because object 'John Doe' couldn't be found on
Data : {}
InnerException :
TargetSite :
StackTrace :
HelpLink :
Source :
HResult : -2146233087



When it comes to exporting data in order to either analyze later or do the formatting in another program, there are a few options available:

  • Export-Clixml - this cmdlet allows a serialized object to be exported instead of just a text file. You can then use the Import-Clixml cmdlet to re-create the saved object based on the contents of that file.

Get-MailboxStatistics -Identity $mailbox -IncludeMoveReport | Export-Clixml C:\temp\Mailbox_statistics.xml $Stats = Import-Clixml C:\temp\Mailbox_statistics.xml


  • Export-Csv - this cmdlet cmdlet creates a CSV file of the objects that you submit. Each object is represented as a line or row of the CSV.

Note: Do not format objects before sending them to the Export-CSV cmdlet. If you do, the format properties are represented in the CSV file, instead of the properties of the original objects. To export only selected properties of an object, use the Select-Object cmdlet.

 Notable parameters:
-Path: Specifies the path to the CSV output file.
-Append: Adds the new script data to the end of the output file, instead of replacing the existing file contents.
-Delimiter: Specifies a delimiter to separate the property values. The default is a comma
-NoTypeInformation: omits the type information from the CSV file.

  • By default, PowerShell sends its command output to the PowerShell console. However, you can direct the output to a text file.

Use the Out-File cmdlet, which sends command output to a text file.

$obj | Out-File C:\temp\props.txt

" > " Sends output to the specified file.

Get-Process > Process.txt

  • The Out-GridView cmdlet sends the output from a command to a grid view window where the output is displayed in an interactive table.

Get-Mailbox | Out-GridView

The command uses the PassThru parameter of Out-GridView, which lets you send multiple items down the pipeline. The example below will show all soft deleted mailboxes and let the user select one or more mailbox(es) to recover and store it/them in $MailboxToRecover $MailboxToRecover = Get-Mailbox -SoftDeletedMailbox -ResultSize Unlimited | Out-GridView -PassThru

  • The Tee-Object cmdlet saves command output in a file or variable and also sends it down the pipeline.

get-mailbox admin | select * | Tee-Object C:\temp\mymbx.txt


Hide Output

Sometime could be useful to hide output from being displayed on the screen console. There are multiple ways to accomplish this:

  • redirect the output to $null

Add-MailboxPermission shared1 -User admin -AccessRights FullAccess > $null

  • explicit cast the result object to [void]

[void](Add-MailboxPermission shared1 -User admin -AccessRights FullAccess)

  • The Out-Null cmdlet sends its output to NULL, in effect, removing it from the pipeline and preventing the output to be displayed at the screen.

Add-MailboxPermission shared1 -User admin -AccessRights FullAccess | Out-Null


How to sort properties of a cmdlet

Cmdlet properties can also be sorted as desired. In this example, the properties of Get-Mailbox are sorted alphabetically for an easier view.

$obj = get-mailbox user1 $obj |select ($obj |gm |? MemberType -eq Property | sort name).name $obj |select ($obj |gm -MemberType Property| sort name).name $obj | fl -property ($obj| gm | sort name).name $obj | %{ $newobj = new-object psobject; $ | Sort Name | %{ Add-Member -Inp $newobj NoteProperty $_.Name $_.Value}; $newobj } | fl