Skip to main content
Back to Blog

PowerShell 101 for Exchange Administrators

November 23, 2020

PowerShell 101 for Exchange Administrators

PowerShell was publicly released to the public in 2006 and since then is experiencing a growing interest among the Microsoft World Community IT Pros. The main reason for this success is that it empowers administrators due to its ease of use and powerful scripting capabilities.

It is the successor of the command-line utility and tries to compete with all the shell scripting that the Unix guys benefit from. In this blog post, you will find tips related to all basics required to start learning PowerShell and ease your day-to-day life as an administrator.

Enable hybrid end-user experience monitoring to ensure the hybrid services are up and performing well. Get it a test run today!

Basics

The first thing you need to know is that PowerShell commands (called commandlets, and usually referred as “cmdlets”) are always named in the same way of a verb and a noun:

Verb-noun

Therefore it is usually quick and easy to remember commands, especially the ones you will be using on a regular basis. The absolutely first cmdlet you have to know is how to get help and you might have already guessed its name:

Get-Help

Thanks to aliases, Unix guys can keep their old habits and use “man” if they prefer?

It starts to get very easy finding the most basics cmdlets to get system info like processes and windows services:

Get-Service
Get-Process

And PowerShell is (almost) the same for every application that leverages it, therefore you can start the Exchange Management Shell and enjoy the same fun:

Get-Mailbox
Get-MailboxServer
Get-MailboxStatistics

In order to extend the scripting capabilities, cmdlets can be combined with one another using the magical pipe character “|”. When using pipelining in PowerShell every object that is returned from the first cmdlet is passed on to the right side of the pipe. It is important to notice that in PowerShell what is transferred through the pipe are objects and not characters:

Get-Service “Martello*” | Stop-Service -WhatIf

oliv1 resized 600Here what would happen is that every Windows Service that starts with “Martello” will be transferred to the “Stop-Service” cmdlet that will tell what it would do IF we were to run it, as the parameter “WhatIf” was used. Very useful parameter to check and anticipate what will happen before it does. Once you want to execute the cmdlets just remove that parameter.

Formatting and Exporting Results

In order to format the result you can use the well-named “Format-Table” or “Format-List” cmdlets, and choose which properties to return:

Get-Service | Format-Table Name, Status, CanStop -AutoSize

For exporting your results it might be more convenient to convert to a CSV format and write it into a file on disk:

Get-Service | ConvertTo-Csv | Out-File “C:\temp\Services.csv”

You can also refine results using the “Where-Object” cmdlets. In order to use that cmdlet you need to know how to point to the object passed through the pipe, and the object can be referred to as “$_” as in this example if we want only the “stopped” services:

Get-Service | Where {$_.Status -eq “Stopped”}

Exchange Administration

So now let’s take a closer look at what you can achieve with these simple cmdlets for your Exchange organization.

Top 10 Mailboxes with the most items:

Get-Mailbox -ResultSize unlimited| Get-MailboxStatistics | Sort-Object “ItemCount” -Descending | Select-Object -First 10 | Format-Table DisplayName, ItemCount, TotalItemSize

Last 20 Entries in the event log that were Errors related to Exchange:

Get-EventLog Application | Where { $_.Source -Ilike “*Exchange*” -and $_.EntryType -eq “Error”} | select -last 20

Total number of Mailboxes in my organization:

(Get-Mailbox -Resultsize unlimited).count

Number of Mailboxes grouped per Mailbox Database:

Get-Mailbox | Group-Object -Property:Database | Select-Object name, count

List of all emails received by a specific email address in the last hour:

Get-MessageTrackingLog -Start (Get-Date).AddHours(-1) -EventId DELIVER -Recipients username@domain.com

So that was not too complicated to get all this information for your Exchange organization. Therefore I would greatly encourage you to get more familiar with PowerShell cmdlets, leveraging scripts is quickly becoming the new standard for managing servers and collecting data.

For those of you eager to learn more about PowerShell for Exchange here are some great resources I would like to recommend:


How to Resolve a PowerShell Kerberos Issue

Today I was trying to connect to an Exchange 2013 server using remote PowerShell, and I had this strange error come up:

Connecting to remote server SERVERNAME failed with the following error message: WinRM cannot process the request. The following error with errorcode 0x80090311 occurred while using Kerberos authentication: There are currently no logon servers available to service the logon request.

Possible causes are:

  • The user name or password specified are invalid.
  • Kerberos is used when no authentication method and no user name are specified.
  • Kerberos accepts domain user names, but not local user names.
  • The Service Principal Name (SPN) for the remote computer name and port does not exist.
  • The client and remote computers are in different domains and there is no trust between the two domains.

After checking for the above issues, try the following:

  • Check the Event Viewer for events related to authentication.
  • Change the authentication method; add the destination computer to the WinRM TrustedHosts configuration setting or use HTTPS transport. Note that computers in the TrustedHosts list might not be authenticated.

For more information about WinRM configuration, run the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic.

 

When I researched this error on the Internet, every case mentioned that this can happen when there is no trust between the domain or when no trusted host is set. In my case, everything was set properly. I tried addressing the issue on other computers and it was working fine. My computer was the only one failing to connect remotely with PowerShell…

After looking over the Exchange server event log, I found out that I could not see the successful logon event. Then, I tried to connect using the Negotiate authentication and found that NTLM was used in the event log instead of Kerberos…That’s when I thought the issue had to be related to Kerberos.

Once I started to install Network monitors to look at the network frames on both ends, I found a TechNet article explaining the steps on how to troubleshoot Kerberos. The article instructed me to clear all the caches, including Kerberos tickets. I started immediately with the Kerberos tickets cache, and it solved the issue!

So, in case you encounter similar issues with Kerberos, this could be your solution (in cmd.exe):

KList purge

I hope this will save you time someday, as I spent several hours finding the issue myself. For any other PowerShell errors, this article will help you find even more solutions.


PowerShell Tip: Easily Manage all of Your “BYOD” Devices

The “Bring Your Own Device” Trend (BYOD) has been making significant contributions to the enterprise for quite a few years now. About 44% of developed markets have already adopted their own devices at work!

The biggest headache with BYOD is how to easily manage and organize all of those devices. Policies help, but the final goal is to collect information on these devices to better understand how everything is working. In this article, I will show you some useful PowerShell functions to monitor Devices, and some dedicated specifically to Apple devices!

Step 1: List all of the devices including owners and the OS

Get-ActiveSyncDevice | select-object DeviceModel,FriendlyName,DeviceOS,UserDisplayName | sort-object devicemodel | Ft -autosize –wrap

Step 2: Control which devices are used on Exchange

I will not go into too much detail here because there are some great articles already available online. But, here are some really important steps to follow:

  • Choose a restrictive organization policy (Are you not allowed? Get out dude!)
  • Create Device Rules Access if you already know some accepted models (Windows Phone or others)
  • Create a rule for unknown devices (To go in quarantine)
  • Setup a quarantine notification email
  • Check the quarantine devices to see all blocked devices (in the ECP)
  • Allow devices that you have accepted and create rules for similar devices to prevent repetitive operations

If you prefer PowerShell, you can set this up by using a command like this:

Set-ActiveSyncOrganizationSettings –DefaultAccessLevel Quarantine -AdminMailRecipients adminmotan@letsexchange.com, messaging.team@letsexchange.com –UserMailInsert “Your mobile device is temporarily blocked from synchronizing with the server while permissions are verified.”

In order to set rules, you will need to get model devices. The best way to do this is by looking in the IIS logs. As EAS uses HTTP, every request is recorded via the IIS weblogs.

By default, it’s saved in C:\inetpub\logs\logfiles\W3SVC1\. There, we can search for ActiveSync entries where we will have entries to see the username, DeviceID, and DeviceType among other types of information.

A good command to easily receive your logs is by using the PowerShell command: Export-ActiveSyncLog

Export-ActiveSyncLog –Filename c:\intepub\logs\logfiles\W3SVC\xxxxxxxx.log –OutputPath c:\Temp\Logs

This command will give you six logs in CSV format:

  • Users.csv
  • Servers.csv
  • Hourly.csv
  • StatusCode.csv
  • PolicyCompliance.csv
  • UserAgents.csv

Devices Models can be found in the UserAgents.csv file.

Step 3: Evaluate the population by device to get a good overview of the park.

The best option is to use PowerShell for a list of devices to get the amount and deploy it for each one. This command will help you:

(Get-CASMailbox –ResultSize Unlimited -Filter {HasActiveSyncDevicePartnership -eq $True} | Get-Mailbox) | ForEach {Get-ActiveSyncDeviceStatistics -Mailbox $_} | group  DeviceModel | sort count -descending  | Select count, name

Ok now you have a good overview of your devices currently connected to ActiveSync. We should check for some resources to monitor our Apple devices.

This script is very flexible and permits you to export results in the CSV format.

If you have old devices in your organization and need to clean them, use this script:

Get-ActiveSyncDevice -ResultSize unlimited | Get-ActiveSyncDeviceStatistics | where {$_.LastSyncAttemptTime -lt (get-date).adddays(-30)}

Then remove it. To do this you can pipe the previous command with this one:

foreach-object {Remove-ActiveSyncDevice ([string]$_.Guid) -confirm:$false}

That’s it! Now you have the tools you need to easily manage all your devices at once. Let me know if you followed my directions and how it worked out for you. Have any questions for me? Just leave your comment below!


Troubleshooting Unknown PowerShell Error Messages

Dealing with PowerShell remotely can cause many headaches when it comes to troubleshooting issues. Therefore, I have gathered the “best” PowerShell errors I have encountered so far in which the error messages returned are not always very meaningful…

1. Memory Issue

When initializing or executing cmdlets on a remote PowerShell session, you receive one of the following errors:

Exception of type ‘System.OutOfMemoryException’ was thrown.
+ CategoryInfo : OpenError: (SERVENAME:String) [], RemoteException
+ FullyQualifiedErrorId : PSSessionStateBroken

 

[SEVERNAME] Processing data from remote server SERVERNAME failed with the following error message: The WSMan provider host process did not return a proper response.  A provider in the host process may have behaved improperly. For more information, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo          : OpenError: (SERVERNAME:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : 1726,PSSessionStateBroken

 

New-PSSession : [SEVERNAME] Remoting data is missing TargetObject property.
+   $s = New-PSSession -computername $computername -Credential:$cred -Authenticati …+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : OpenError: (System.Manageme….RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException   + FullyQualifiedErrorId : PSSessionOpenFailed

As the first error message specifies, an overflow of memory in the remote session has occurred. Open a PowerShell prompt on the remote server and display the configuration of winrs using:

winrm get winrm/config/winrs

Check the “MaxMemoryPerShellMB” value. It is set by default to 150 MB on Windows Server 2008 R2 and Windows 7. This is something that Microsoft changed in Windows Server 2012 and Windows 8 to 1024 MB.

In order to resolve this issue, you need to increase the value to at least 512 MB with the following command:

winrm set winrm/config/winrs `@`{MaxMemoryPerShellMB=`”512`”`}

2. SPNs Issues

While enabling the remote PowerShell using “winrm quickconfig” or enabling CredSSP using “Enable-WSManCredSSP”, you might encounter the following error:

WSManFault Message = WinRM cannot process the request. The following error occurred while using Negotiate authentication: An unknown security error occurred. Possible causes are:

  • The user name or password specified are invalid.
  • Kerberos is used when no authentication method and no user name are specified.
  • Kerberos accepts domain user names, but not local user names.
  • The Service Principal Name (SPN) for the remote computer name and port does not exist.
  • The client and remote computers are in different domains and there is no trust between the two domains. After checking for the above issues, try the following:
  • Check the Event Viewer for events related to authentication.
  • Change the authentication method; add the destination computer to the WinRM TrustedHosts configuration setting or use HTTPS transport.
    Note that computers in the TrustedHosts list might not be authenticated.
    -For more information about WinRM configuration, run the following command: winrm help config.
    Error number: -2144108387 0x8033809D An unknown security error occurred.

This can come from an existing SPN for HTTP. So check the following in a command prompt:

setspn.exe -L COMPUTERNAME

Eventually, you might have to query it directly using:

setspn.exe -Q http/COMPUTERNAME

If it does exist, then you have to remove it:

setspn.exe -D http/COMPUTERNAME

Then, proceed to whatever you were configuring. And finally, re-enable your SPN:

setspn.exe -A http/COMPUTERNAME

3. Network Interface in Public domains

[SEVERNAME] Processing data from remote server SERVERNAME failed with the following error message: The WSMan provider host process did not return a proper response.  A provider in the host process may have behaved improperly. For more information, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo          : OpenError: (SERVERNAME:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : 1726,PSSessionStateBroken

This common PowerShell error can also occur when one of the network interfaces of the local computer is set to Public Domain, which is very frequent when configuring your local virtual machine (e.g. “VirtualBox Host-Only Network adapter”). In this case, you will have to disable the Network Adapter that is in the Public domain during the configuration.

I am sure that we will discover new, amazing messages to come from PowerShell, especially with remote use.

If you are interested in enhancing your PowerShell experience by automatically loading scripts, we think you will find this article very useful.

Did we not cover an issue you’re having? Check out how we can make your wish for a solution come true!

Do not hesitate to let us know your “favourite” error messages in the comments below and we will do our best to help you troubleshoot them.


Enhance your PowerShell Experience by Automatically Loading Scripts

Ever wanted to customize your PowerShell terminal so you can have all your custom functions or scripts loaded automatically? Although it is possible to create aliases, functions, or variables, everything is only kept for the current PowerShell session, meaning that those changes will be lost after you close the shell.

In order to store the information PowerShell, you can use a “Profile” that is loaded at every session. We’ll now explain how to enable your PowerShell profile and customize it.

The Different Profiles

You can have four different profiles in Windows PowerShell, listed below in load order, the most specific having precedence over less specific profiles where they apply:

  • %windir%\system32\WindowsPowerShell\v1.0\profile.ps1
    This profile applies to all users and all shells.
  • %windir%\system32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
    This profile applies to all users, but only to the Microsoft.PowerShell shell.
  • %UserProfile%\My Documents\WindowsPowerShell\profile.ps1
    This profile applies only to the current user, but affects all shells.
  • %UserProfile%\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
    This profile applies only to the current user and the Microsoft.PowerShell shell.

Enabling A Profile

Open a PowerShell terminal and type in

$profile

This should display the path that would be used to store your profile. However, it might not be created yet, so in order to find out, type the following:

test-path $profile

If the result is true, you’re all set, else type the following to create the profile:

new-item -path $profile -itemtype file -force

You’re now all set with your profile, and can customize it, by launching the PowerShell ISE:

powershell_ise $profile

Auto-Load Scripts on PowerShell Startup

In the ps1 file of your profile, usually named “Microsoft.PowerShell_profile.ps1”, you can enter the following code, so that the folder of your choice will contain scripts that will be executed on startup:

# directory where my scripts are stored
$psdir=“D:\Documents\Powershell\Scripts\autoload” 
# load all ‘autoload’ scripts
Get-ChildItem “${psdir}\*.ps1” | %{.$_}
Write-Host “Custom PowerShell Environment Loaded” 

This script will browse the folder given in input, and load all the files. The files I have in this folder are “.ps1” files that contain a PowerShell function.

In order for those scripts to be executed, the policy needs to be updated to something else than “restricted”, at least “remotesigned” by running the following:

Set-ExecutionPolicy remoteSigned

Here you go, you can now put as many “.ps1” files as you need in your “autoload” folder, and these will be executed everytime you launch your PowerShell session.

Sources

Learn more about Martello’s Microsoft Exchange Hybrid monitoring solution here.

Share

Recent Posts

Return to top