Here is a consolidation of few features and tips from my scripting practice for Exchange Server.
First thing any scripter need is a script editor, and when you talk about script editors for Powershell, there is a minimum feature requirement. Of them are Tab completion, Testing and debugging the script from the editor itself.
Getting the Exchange cmdlets inside the editor, for this first thing required is to have Exchange Management Tools Installed. After that we open Powershell ISE and check the loaded PS Snapins
PS C:\> Get-PSSnapin | Format-Table Name
Name                                                                                                                                 Â
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Microsoft.PowerShell.Diagnostics                                                                                                     Â
Microsoft.WSMan.Management                                                                                                           Â
Microsoft.PowerShell.Core                                                                                                            Â
Microsoft.PowerShell.Utility                                                                                                         Â
Microsoft.PowerShell.Host                                                                                                            Â
Microsoft.PowerShell.Management                                                                                                      Â
Microsoft.PowerShell.Security                                                                                                        Â
You can see from above all the loaded snapins, and you wont see Exchange Snapins.
Now, lets check for the Registered but not loaded Snapins
PS C:\> Get-PSSnapin -Registered | Format-Table Name
Name                                                                                                                                 Â
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Microsoft.Exchange.Management.PowerShell.E2010Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Microsoft.Exchange.Management.PowerShell.Setup                                                                                       Â
Microsoft.Exchange.Management.Powershell.SupportÂ
You can see above there are three snapins registered by Exchange Server 2010
Now load the Registered Snapins to the current session
PS C:\> Get-PSSnapin -Registered | Add-PSSnapin
PS C:\> Get-PSSnapin | Format-Table Name
Name                                                                                                                                 Â
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Microsoft.PowerShell.Diagnostics                                                                                                     Â
Microsoft.WSMan.Management                                                                                                           Â
Microsoft.PowerShell.Core                                                                                                            Â
Microsoft.PowerShell.Utility                                                                                                         Â
Microsoft.PowerShell.Host                                                                                                            Â
Microsoft.PowerShell.Management                                                                                                      Â
Microsoft.PowerShell.Security                                                                                                        Â
Microsoft.Exchange.Management.PowerShell.E2010Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Microsoft.Exchange.Management.PowerShell.Setup                                                                                       Â
Microsoft.Exchange.Management.Powershell.Support                                               Â
                                     Â
Now we have all the exchange cmdlets loaded in our session, good to go.
Lets try a simple Exchange 2010 Cmdlet.
PS C:\> Get-Mailbox
Get-Mailbox : Value cannot be null.
Parameter name: serverSettings
At line:1 char:12
+ Get-Mailbox <<<<Â
   + CategoryInfo         : NotSpecified: (:) [Get-Mailbox], ArgumentNullException
   + FullyQualifiedErrorId : System.ArgumentNullException,Microsoft.Exchange.Management.RecipientTasks.GetMailbox
Why is this error, whenever you start Exchange Management Console or Exchange Management Shell, it initiates a Powershell Remoting Session, and sets some default settings such as the Domain Controller, the Exchange Server to connect etc. In above case these things didnt happen as we have just loaded the raw cmdlets using PSSnapin.
Another important point to note is RBAC, when you load just the snapins, you are loading the raw cmdlets to the session. Exchange is not intended to be managed like that. There is Robust Access Control layer built into the management tools, the Role Based Access Control (RBAC). When we do execute cmdlets by just loading PSSnapin it DOESNT RESPECT RBAC, and when RBAC is not respected the cmdlets may give unexpected results.
PS: But this doenst block us from using the snapins to easily write or edit scripts, only thing is that you cannot run the scripts here.
The solution is to use Implicit Remoting:
In this method, we are not loading any Snapins, we just create an implicit remoting session using Import-PSSession and that session loads all the cmdlets we need, it also respects the RBAC (So you only get access to the Cmdlets you have been assigned inside the RBAC Roles).
Creating an Implicit Remoting session to Exchange Server 2010
#If you want to pass an alternate credential to connect to exchange you can use -Credential parameter
#To interactively pass the credentials use -Credential (Get-Credential) to the New-PSSession Cmdlet
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri <a href="http://EXSRV1/Powershell</a>Import-PSSession">http://EXSRV1/Powershell</a> Import-PSSession</a> -Session $session
WARNING: Some imported command names include unapproved verbs which might make them less discoverable. Use the Verbose parameter for mo
re detail or type Get-Verb to see the list of approved verbs.
ModuleType Name                     ExportedCommands                                                         Â
---------- ----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ----------------Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Script    tmp_819a88a7-c59d-464e... {Get-IRMConfiguration, New-MailUser, Enable-CmdletExtensionAgent, New-Ac...
There is one warning always comes as powershell checks for the cmdlet verbs used, its due to the cmdlets Clean-MailboxDatabase, and Retry-Queue, both Clean and Retry are not in the Approved verbs you get when executing the cmdlet Get-Verb
Now lets try Get-Mailbox
PS C:\> Get-Mailbox
Name                     Alias               ServerName      ProhibitSendQuota                                                    Â
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â -----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ----------Â Â Â Â Â Â -----------------Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Administrator            Administrator       ex2010mbx11     unlimited                                                            Â
DiscoverySearchMailbox... DiscoverySearchMa... ex2010mbx11Â Â Â Â Â 50 GB (53,687,091,200 bytes)Â Â
All is well!
Now lets see another difference to keep in mind while scripting for Exchange
Lets see some examples from Exchange Management Shell
[PS] C:\>$mbx = Get-Mailbox -Identity Administrator
[PS] C:\>$mbx.EmailAddresses
SmtpAddress       : [email protected]
AddressString     : [email protected]
ProxyAddressString : smtp:[email protected]
Prefix            : SMTP
IsPrimaryAddress  : False
PrefixString      : smtp
SmtpAddress       : [email protected]
AddressString     : [email protected]
ProxyAddressString : SMTP:[email protected]
Prefix            : SMTP
IsPrimaryAddress  : True
PrefixString      : SMTP
you can see from above that you are getting a complete object of email address which has properties like prefixstring, IsPrimaryAddress etc. see the members of email address object
[PS] C:\>$mbx.emailaddresses | Get-Member
  TypeName: Microsoft.Exchange.Data.SmtpProxyAddress
Name              MemberType Definition
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â ---------- ----------
CompareTo         Method    int CompareTo(Microsoft.Exchange.Data.ProxyAddress othe
Equals            Method    bool Equals(System.Object obj), bool Equals(Microsoft.E
GetHashCode       Method    int GetHashCode()
GetSimilarProxy   Method    Microsoft.Exchange.Data.ProxyAddressBase GetSimilarProx
GetType           Method    type GetType()
ToPrimary         Method    Microsoft.Exchange.Data.ProxyAddressBase ToPrimary()
ToSecondary       Method    Microsoft.Exchange.Data.ProxyAddressBase ToSecondary()
ToString          Method    string ToString()
TryDeencapsulate  Method    bool TryDeencapsulate(Microsoft.Exchange.Data.ProxyAddr
AddressString     Property  System.String AddressString {get;}
IsPrimaryAddress  Property  System.Boolean IsPrimaryAddress {get;}
Prefix            Property  Microsoft.Exchange.Data.ProxyAddressPrefix Prefix {get;
PrefixString      Property  System.String PrefixString {get;}
ProxyAddressString Property  System.String ProxyAddressString {get;}
SmtpAddress       Property  System.String SmtpAddress {get;}
Similarly you can see the members of the Mailbox itself, for readability i have remove most of them. The relevant member to our discussion is EmailAddresses properyt, you can see here that its a of ProxyAddressCollection
[PS] C:\>$mbx | Get-Member
  TypeName: Microsoft.Exchange.Data.Directory.Management.Mailbox
Name                                  MemberType  Definition
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ----------Â Â ----------
Clone                                 Method      System.Object Clone()
Equals                                Method      bool Equals(System.Object obj)
GetHashCode                           Method      int GetHashCode()
GetProperties                         Method      System.Object[] GetProperties(Syst
GetType                               Method      type GetType()
ToString                              Method      string ToString()
...
EmailAddresses                        Property    Microsoft.Exchange.Data.ProxyAddressCollection EmailAddresses {g...
Now lets observe the same from Powershell Console or the ISE
PS C:\> $mbx = Get-Mailbox -Identity Administrator
PS C:\> $mbx.EmailAddresses
smtp:[email protected]
SMTP:[email protected]
you can see here, that you are not getting anything like prefix or IsPrimary etc. you are just getting the data as string.
To prove that lets use Get-Member on the EmailAddresses property or you can also use
# $mbx.EmailAddresses.GetType()
PS C:\> $mbx.EmailAddresses | Get-Member
  TypeName: System.String
Name            MemberType           Definition                                                                                    Â
----Â Â Â Â Â Â Â Â Â Â Â Â ----------Â Â Â Â Â Â Â Â Â Â Â ----------Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
Clone           Method               System.Object Clone()                                                                         Â
CompareTo       Method               int CompareTo(System.Object value), int CompareTo(string strB)                                Â
Contains        Method               bool Contains(string value)                                                                   Â
CopyTo          Method               System.Void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)      Â
EndsWith        Method               bool EndsWith(string value), bool         Â
So, having looked at all those details, just see if we see any difference in the Retried Object itself in the $mbx variable
PS C:\> $mbx | Get-Member
  TypeName: Deserialized.Microsoft.Exchange.Data.Directory.Management.Mailbox
Name                                  MemberType  Definition                                                                       Â
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ----------Â Â ----------Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
ToString                              Method      string ToString(), string ToString(string format, System.IFormatProvider formatP...
PSComputerName                        NoteProperty System.String PSComputerName=ex2010mbx11                                         Â
RunspaceId                            NoteProperty System.Guid RunspaceId=1c8913bb-b202-4b52-8a13-17da41b77056                      Â
AcceptMessagesOnlyFrom                Property    Deserialized.Microsoft.Exchange.Data.Directory.ADMultiValuedProperty`1[[Microsof...
AcceptMessagesOnlyFromDLMembers       Property    Deserialized.Microsoft.Exchange.Data.Directory.ADMultiValuedProperty`1[[Microsof...
AcceptMessagesOnlyFromSendersOrMembers Property    Deserialized.Microsoft.Exchange.Data.Directory.ADMultiValuedProperty`1[[Microsof...
AddressBookPolicy                     Property     {get;set;}                                                                      Â
AddressListMembership                 Property    Deserialized.Microsoft.Exchange.Data.Directory.ADMultiValuedProperty`1[[Microsof...
Alias                                 Property    System.String {get;set;}                                                         Â
AntispamBypassEnabled                 Property    System.Boolean {get;set;}                                                        Â
ArbitrationMailbox                    Property     {get;set;}                                                                      Â
ArchiveDatabase                       Property     {get;set;}                                                                      Â
ArchiveDomain                         Property     {get;set;}            Â
....
EmailAddresses                        Property    Deserialized.Microsoft.Exchange.Data.ProxyAddressCollection {get;set;}
Important information to look here is the word "Deserialized" in the beginning of Type Name and also in the definition of EmailAddresses property. This brings us another concept to understand, "How Powershell Remoting perform Data Transfer". This discussion is to not dig into that area, i will provide some reference links to read abou it at the end of this post. Its already explained by Powershell Experts out there.
Deserialized Objects
In short, it does somthing called serialization and deserializaion - a form of transporting data or live objects using XML. When objects get deserialized they loose their dynamic nature, so you cannot perform most of the method calls or explore dynamic nature of the Object.
Lets understand this by looking at another example:
Exploring maibox database object inside the script, lets first look at from Powershell Console or ISe
$Db = Get-MailboxDatabase MBX11-DB01
PS C:\> $Db.EdbFilePath
C:\MBX11-DB01\mbx11-db01.edb
the property EdbFilePath gives you the complete path to the Database File.
you can see the type of this property is string
PS C:\> $Db.EdbFilePath.GetType()
IsPublic IsSerial Name                                    BaseType                                    Â
-------- -------- ----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â --------Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
True    True    String                                  System.Object
Also if you execute below you get no output:
PS C:\> $Db.EdbFilePath.PathName
PS C:\>
Now, perform the same steps from Exchange Management Shell
[PS] C:\>$Db = Get-MailboxDatabase MBX11-DB01
[PS] C:\>$Db.EdbFilePath
IsPathInRootDirectory : False
PathName             : C:\MBX11-DB01\mbx11-db01.edb
IsLocalFull          : True
IsUnc                : False
DriveName            : C:
ServerName           :
You can see here the property EdbFilePath is not just a string here its a complete object with more classified information, such as the DriveName property, PathName property etc.
[PS] C:\>$Db.EdbFilePath.PathName
C:\MBX11-DB01\mbx11-db01.edb
here you get an output from the EdbFilePath.PathName, notice this failed in the pervious example in ISE.
This is because EdbFilePath is not a string object here.
[PS] C:\>$Db.EdbFilePath | gm
  TypeName: Microsoft.Exchange.Data.EdbFilePath
Name                       MemberType Definition
----Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ---------- ----------
CompareTo                  Method    int CompareTo(Microsoft.Exchange.Data.Lo
Equals                     Method    bool Equals(System.Object value), bool E
GetHashCode                Method    int GetHashCode()
GetType                    Method    type GetType()
ToString                   Method    string ToString()
ValidateDirectoryPathLength Method    System.Void ValidateDirectoryPathLength(
ValidateEdbFileExtension   Method    System.Void ValidateEdbFileExtension()
ValidateFilePathLength     Method    System.Void ValidateFilePathLength()
DriveName                  Property  System.String DriveName {get;}
IsLocalFull                Property  System.Boolean IsLocalFull {get;}
IsPathInRootDirectory      Property  System.Boolean IsPathInRootDirectory {ge
IsUnc                      Property  System.Boolean IsUnc {get;}
PathName                   Property  System.String PathName {get;}
ServerName                 Property  System.String ServerName {get;}
[PS] C:\>
What is the take way from this is while writing the script keep in mind the differences, such that in EMS $Db.EdbFilePath is an object by itself and if you requirement is to just get the path you can use $Db.EdbFilePath.PathName But this will not work when you are executing from PowerShell ISE.
Next thing to consider is testing and executing your Scripts for Exchange Environment, you can use PowerShell ISE to write scripts, but for testing run them from Exchange Management Shell, and keep in mind the difference like above and due to that the intellisence or tab completion will not show the nested properties.
Scheduling Exchange Server Scripts
Now a simple tip on how to schedule Exchange Server Scripts using Task Scheduler. Most of the times, you need to run scripts on a repeated intervel without user intervention. you can schedule your Exchange scripts and still use the EMS as the execution platform. i will just show the Action to trigger while creating a Scheuled Task for Exchange Server 2010 Script.
The Program to Run will be Powershell.exe
You should pass optional arguments to Powershell.exe to do things like running it in non-interactive Mode and/or hide the window etc. you can use -Command to say which command to run once powershell.exe is loaded
Field – Program to Run: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Field – Optional Arguments:
-NonInteractive -WindowStyle Hidden -command “. ‘C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1′; Connect-ExchangeServer -auto; “& ‘C:\Scripts\Report-ServerHealth.ps1′”
There different aspects in the optional arguments field:
1. -NonInteractive – Non Interactive Window
2. -WindowStyle Hidden – The Powershell windows is hidden (you can still the process in task manager)
3. -command – the command to execute after loading powershell.exe, this can be one or multiple cmdlets(seperated by “;”)
4. The first cmdlet “. ‘C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1′; -> the is the default script loaded when you open EMS
5. Connect-ExchangeServer -auto; -> this is the function which was loaded by the previous script called RemoteExchange.ps1
6. “& ‘C:\Scripts\Report-ServerHealth.ps1′” -> now the real script we have created (finally!!)
Its the era of Powershell v3 now, which bring more interesting features for writing scripts, such as Intellisense, auto module loading, show command feature etc.
PS v3 is not yet supported by Exchange server 2010 (that is at version SP2 RU5) so DO NOT go and install it on your Exchange Server 2010 Server, you will breat it.
but you can use it for script editing, on your desktop or scripting machine. below are few useful features:
The Improved Intellisense feature, this will help to discover cmdlets and parameter and even parameter values

Intellisense Examples:


Exploring Objects made easy (remember “Deserialization” EdbFilePath is a String here as you can see):

The Show-Command cmdlet to find out the syntax to use before you start scripting, you can fill out the GUI Form and then click on Copy to copy the syntax to clipboard.

Paste it back in the editor and you just got a complete syntax to use.

Some PSRemoting References:
 About_Remot_Output
 How objects are send to and from remote sessions
What else to look for while Writing or Editing Scripts for Exchange Server, do give your comments!