Yesterday I have had the pleasure of doing a TechNet LiveMeeting here @Microsoft Belgium, entitled “Using the Power of PowerShell to manage your Exchange Online and Exchange On Premises Environment”. The session was not a level 400 deep dive in PowerShell, but its aim was to give some tips on how one can use PowerShell to manage an Exchange Online tenant in Office365.
Connecting to Exchange Online is easy, and boils down to launching Windows PowerShell, creating a new persistent connection to the remote Exchange Client Access Server, and importing it, like can be seen in the picture below:
Two remarks here:
1. Before you are able to run the cmdlet Import-PSSession, you need to make sure you are allowed to run scripts... When you run Import-PSSession and you get the following error message:
Import-Module : There were errors in loading the format data file:Microsoft.PowerShell, , C:\Users\Seppe\AppData\Local\Temp\tmp_0740bdd5-5276-4377-a890-50bb10d3d32b_cuwqdhef.gqv\tmp_0740bdd5-5276-4377-a890-50bb10d3d32b_cuwqdhef.gqv.format.ps1xml : File skipped because of the following validation exception: File C:\Users\Seppe\AppData\Local\Temp\tmp_0740bdd5-5276-4377-a890-50bb10d3d32b_cuwqdhef.gqv\tmp_0740bdd5-5276-4377-a890-50bb10d3d32b_cuwqdhef.gqv.format.ps1xml cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details..At line:3 char:30+ Import-Module <<<< -Name $name -Alias * -Function * -Prefix$prefix -DisableNameChecking:$disableNameChecking -PassThru -ArgumentList @($session) + CategoryInfo : InvalidOperation: (:) [Import-Module], RuntimeEx ception + FullyQualifiedErrorId : FormatXmlUpateException,Microsoft.PowerShell.Com mands.ImportModuleCommand
To enable the execution of scripts you can run the cmdlet Set-ExecutionPolicy Unrestricted. This can be enforced btw by using Group Policies, you can download the ADM Group Policy Template for PowerShell here. For more information on Set-ExecutionPolicy, check this link here.
2. Do not forget the parameter AllowRedirection, which will enable redirection to the appropriate Exchange server using different URI.
When you launch the Exchange Management Shell, you are using Remote PowerShell to connect to a Client Access Server in your Exchange On Premises environment, as you can see by running Get-PSSession after launching Exchange Management Shell
If you would then create a new persistent connection to Exchange Online using the directions mentioned above, you would get the following warning when importing the PowerShell Session:
“WARNING: Proxy creation has been skipped for the following command: …., because it would shadow an existing local command. Use the AllowClobber parameter if you want to shadow existing local commands.”
If you would use the parameter AllowClobber, you would indeed shadow the existing commands, meaning, you would hide or replace the original commands: eg. running Get-Mailbox would retrieve the Exchange Online mailboxes, but you wouldn’t be able to retrieve the ones in your Exchange On Premises organization anymore in this EMS Session!
Solution: Use the Prefix parameter, which will add the given prefix to the nouns in the names of the imported commands.
Running the following Import-PSSession $Session –Prefix o365 will import all the commands, but will prefix all the nouns with o365, running Get-DistributionGroup will return a list of all Distribution Groups in my On Premises Exchange Organization, where-as running Get-o365DistributionGroup will return a list of Distribution Groups in my Exchange Online environment:
Looking at the definition of Remove-PSSession on TechNet
The Remove-PSSession cmdlet closes Windows PowerShell sessions (PSSessions) in the current session. It stops any commands that are running in the PSSessions, ends the PSSession, and releases the resources that the PSSession was using. If the PSSession is connected to a remote computer, Remove-PSSession also closes the connection between the local and remote computers.
Why would you do this for your Exchange Online? Because if you do not close the Windows PowerShell window without disconnecting from the server-side session, your connection will remain open for 15 minutes. And you have a limit of three connections to the server-side session at one time per account.
If you want to know more about Windows PowerShell Profiles, please head over here @MSDN, where you can dive into the wonders of profiles. The reason it might be useful to use profiles here is that you don’t need to type everything every single time you want to connect to your Exchange Online environment. By entering the New-PSSession and Import-PSSession lines in any of the 4 profiles, you can create functions that you can call upon when you want to connect to your Office365 Exchange Online tenant by simply entering Connect-ExchangeOnline.
In the TechNet LiveMeeting I created my Windows PowerShell Profile, the process on how to create this is clearly described here, and here’s what it looks like after creating it:
By using the so-called Windows PowerShell user profile, this will only work for the currently logged on user, and only for the Microsoft.PowerShell shell, if I launch ISE for example, you won’t have the functions Connect-ExchangeOnline and Disconnect-ExchangeOnline:
In the below example I will schedule a task to when run will create a excel file with an overview of the mailbox sizes, the tricky part is the passing of your credentials. I have chosen to first create a passwordfile, which will contain the password of the user that will be used to connect in the script to the Exchange Online tenant:
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C:\users\ilvancri\MyPassword.txt
Then I have created a script called “Mailboxsizes.ps1”, that will connect to Office365, create the csv file, and remove the PSSession in the end:
$password = type C:\users\ilvancri\MyPassword.txt | ConvertTo-SecureString $userid = "firstname.lastname@example.org" $cred = New-Object System.Management.Automation.PSCredential $userid,$password $global:session365 = New-PSSession -configurationname Microsoft.Exchange -connectionuri https://ps.outlook.com/powershell/ -credential $cred -authentication Basic -AllowRedirection Import-PSSession $global:session365
Get-Mailbox | Get-MailboxStatistics | Select Displayname,TotalItemSize, ItemCount | Export-csv .\mailboxsizes.csv Get-PSSession | Remove-PSSession
Now it’s time to create a batch file that when run, will launch and execute the script:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command ". 'C:\users\ilvancri\mailboxsize.ps1'
And now you can schedule to run the BAT file when needed.
Thanks Ilse for posting this. I just was able to get the last 20 minutes of your session so this is an nice one to know. Just one remark: we use the 'arguments' part of the task to add the parameters to powershell.exe and also add -executionpolicy unrestricted to it. Important practical remark: have this as the first parmater if you didn't altered your 'default execution policy' or it doesn't work!
Harold, you are right, I've added the set-executionpolicy in the beginning now to highlight it!
Hello Ilse, I know this is more than an year old post. But you certainly seem to know a great deal about remoting into exchange. I'm hoping maybe you'll reply something back. In Exchange 2007 SP3 environment, is it possible to remote in using powershell and manage using exchange shell? Been searching without a clear answer...
Unable to export Distribution Group Membership - For Large DG with more than 1000 Members.when using Get-DistributionGroupMember <GroupName> -Resultsize Unlimited
Getting error : Active Directory operation failed on BLUPR05A001DC01.NAMPR05A001.prod.outlook.com. Additional information: Active Directory rejected page
d search cookie because a cookie handle was discarded by a Domain Controller or a different LDAP connection was used on subsequent page r
etrieval. Paged search needs to be restarted and will succeed.
Additional information: The parameter is incorrect.
Just a note on storing credentials....In your example here, you've got your password stored in plain text. This is generally not a best practice as your account will be compromised if anyone gains read access to the directory in which the text file is stored. Furthermore, even if you encrypted the password to a secure string and stored the encrypted data, encryption reversal is trivial:$cred = Get-Credential write-host $cred.GetNetworkCredential().password.ToString()Stay secure! There is no appropriate way to store a credential such that a script can access it but a human with the right knowledge cannot.