Tag Archives: PowerShell

Retrieve mailbox statistics in PowerShell for a large number of users

I’m currently working on a project where we are going to move 24,000 mailboxes from Exchange 2016 to Exchange Online. For planning purposes we would like to know the basic statistics, like the LastLogonTime and the number of items (both regular and deleted).

To retrieve this for my mailbox, you can use something like this:

[PS] C:\ >Get-Mailbox -Identity wesseliusj | Get-MailboxStatistics | Select DisplayName,LastLogonTime,ItemCount,TotalItemSize,DeletedItemsCount,TotalDeletedItemSize

DisplayName          : Wesselius, J
LastLogonTime        : 28-4-2022 11:24:46
ItemCount            : 1065
TotalItemSize        : 63.95 MB (67,060,296 bytes)
DeletedItemsCount    :
TotalDeletedItemSize : 5.421 MB (5,684,477 bytes)

The TotalItemSize and TotalDeletedItemSize are returned as text and not numeric. Since I want to export everything into a CSV file and import into Excel for further processing, it must be converted to a numeric value. This is called a calculated property and this is possible using the following option:

@{Name="TotalItemSizeMB";Expression={$_.TotalItemSize.Value.ToBytes()}}

ToBytes() can also be ToKB(), ToMB() or ToGB() depending on your situation.

The previous command and output will now be something like this:

[PS] C:\>Get-Mailbox -Identity wesseliusj | Get-MailboxStatistics | Select DisplayName,LastLogonTime,ItemCount,@{Name="TotalItemSizeMB";Expression={$_.TotalItemSize.Value.ToBytes()}},DeletedItemsCount,@{Name="TotalDeletedItemSizeMB";Expression={$_.TotalDeletedItemSize.Value.ToBytes()}}


DisplayName            : Wesselius, J
LastLogonTime          : 28-4-2022 11:24:46
ItemCount              : 1065
TotalItemSizeMB        : 67060293
DeletedItemsCount      :
TotalDeletedItemSizeMB : 5684477

Now use the Export-Csv command and we are good to go (you would hope 😊):

[PS] C:\>get-mailbox -ResultSize unlimited | Get-MailboxStatistics | Select DisplayName,LastLogonTime,ItemCount,@{Name="TotalItemSizeMB";Expression={$_.TotalItemSize.Value.ToBytes()}},DeletedItemsCount,@{Name="TotalDeletedItemSizeMB";Expression={$_.TotalDeletedItemSize.Value.ToBytes()}} | export-csv -Path statistics.csv -nti

Sending data to a remote command failed with the following error message: [ClientAccessServer=EXCH2016,BackEndServer=exch2016.labs.local,RequestId=c0430cd6-6f4d-48a5-8434-d59ebcd91887,TimeStamp=28-4-2022 10:03:54] [FailureCategory=W
SMan-Others] The total data received from the remote client exceeded the allowed maximum. The allowed maximum is 524288000. For more information, see the about_Remote_Troubleshooting Help topic.
    + CategoryInfo          : OperationStopped: (exch2016.labs.local:String) [], PSRemotingTransportException
    + FullyQualifiedErrorId : JobFailure
    + PSComputerName        : exch2016.labs.local

[PS] C:\>

Unfortunately, the Get-Mailbox command retrieves all 24,000 mailboxes in one run and then tries to use this as input for the Get-MailboxStatistics command. While this works for just a few mailboxes, it runs out of memory for a large set of mailboxes.

The solutions here (or one of the solutions) is to import all mailboxes into a variable, and loop through all mailboxes in this variable combined with the Get-MailboxStatistics command:

[PS] C:\> $Mailboxes = Get-Mailbox -Resultsize Unlimited
[PS] C:\> ForEach ($Mailbox in $Mailboxes) {Get-MailboxStatistics $Mailbox | Select  DisplayName,LastLogonTime,ItemCount,@{Name="TotalItemSizeMB";Expression={$_.TotalItemSize.Value.ToBytes()}},DeletedItemsCount,@{Name="TotalDeletedItemSizeMB";Expression={$_.TotalDeletedItemSize.Value.ToBytes()}} | export-csv -Path statistics.csv -nti}

One last question I got is to add a UPN or alias of the user to the CSV file. UPN and alias are not in the object that’s returned by Get-MailboxStatistics, but are returned by the Get-Mailbox command. To get these properties in the output, again a calculated property must be used, similar to the size properties that were used earlier.

To retrieve the UPN, use something similar to the following:

@{Name = "UPN"; Expression={$Mailbox.UserPrincipalName}}

This will take the UserPrincipalName property of the first command (Get-Mailbox) and parse it into the output. The entire command will be:

[PS] C:\>ForEach ($Mailbox in $Mailboxes) {Get-MailboxStatistics $Mailbox | Select  DisplayName, @{Name = "UPN"; Expression={$Mailbox.UserPrincipalName}},LastLogonTime,ItemCount,@{Name="TotalItemSizeMB";Expression={$_.TotalItemSize.Value.To
Bytes()}},DeletedItemsCount,@{Name="TotalDeletedItemSizeMB";Expression={$_.TotalDeletedItemSize.Value.ToBytes()}}}

DisplayName            : Wesselius, J
UPN                    : j.wesselius@Exchangelabs.nl
LastLogonTime          : 29-4-2022 14:22:03
ItemCount              : 1098
TotalItemSizeMB        : 68714749
DeletedItemsCount      :
TotalDeletedItemSizeMB : 5822787

[PS] C:\>

This will create a proper CSV file with all the information I need, ready to import into Excel 😊

More information regarding working with large number of users (in Office 365) please check the following article: Running PowerShell cmdlets for large numbers of users in Office 365 – Microsoft Tech Community

Send from Alias in Exchange Online

A bit later than planned, but I was attending a training last week, but a long-awaited feature in Exchange is sending mail from another email address that is stamped on a user, a so called alias. In a typical environment, a mailbox has a primary SMTP address and this address is used to send an receive email. This can be something like j.wesselius@exchangelabs.nl. Besides this primary SMTP address there can be more SMTP addresses that can be used to receive mail, for example Mr.Exchange@exchangelabs.nl or MasterOfDisaster@exchangelabs.nl. In Exchange on-premises and Exchange Online, these Aliasses are only used to receive email, not to send email. Up until now that is (for Exchange Online, no idea if they want to enable this for Exchange on-premises).

Microsoft has started to roll out the Send From Alias in Exchange Online starting in January 2022 (it was already announced back in April 2021) and it is available in Outlook on the Web and Outlook for iOS and Outlook for Android. Outlook for the PC will follow, according to Microsoft in Q2, 2022.

To enable the Send from Alias in Exchange Online, execute the following command in Exchange Online PowerShell:

[PS] C:\> Set-OrganizationConfig -SendFromAliasEnabled $True

It takes some time before effective, in my case it worked the next day.

All SMTP proxy addresses on a mailbox are available for this. When you logon as a user and go to settings | Mail | Compose and Reply you can check which aliases you want to use. + Addresses are also shown and so are the mail.onmicrosoft.com addresses. Don’t know who thought this was useful, in my opinion you don’t want to use these (internal) addresses at all:

Now when you write a new email in Outlook on the Web and select the From option, you can select the email address that you checked in the previous step.

The proxy addresses that are selected in the first step (the OWA settings) will automatically available in Outlook for Android and Outlook for iOS.

When you send an email using one of these aliases as a from address, it will automatically be visible in the recipient mailbox, in this example in Gmail:

I don’t expect much use of this feature until Outlook for the desktop will offer it, but it’s a nice add-on (finally).

New Exchange Online PowerShell v2

When using PowerShell with Exchange Online you can use the ‘good old traditional’ way to connect to Exchange Online:

$ExCred = Get-Credential 
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $ExCred -Authentication Basic -AllowRedirection
Import-PSSession $Session

This is not a recommended way to connect to Exchange Online using your tenant admin account, it uses basic authentication (will be decommissioned in 2021) and MFA (number one prerequisite for tenant admin security!) is not possible.

The second option is the Exchange Online Remote PowerShell Module which you can download from the Exchange Online Admin Center (use Internet Explorer for this download!) as shown in the following screenshot:

Exchange Online PowerShell Module

This is a separate PowerShell module you can start and use the Connect-EXOPSSession command to connect to Exchange Online. This PowerShell module users Modern Authentication and supports Multi-Factor Authentication.

The latest (and newest) option is the Exchange Online PowerShell V2 module. This module works far more efficient with large datasets than the previous PowerShell modules for Exchange Online. It also supports Modern Authentication and Multi-Factor Authentication.

To install the Exchange Online PowerShell V2 module you first have to install the PowerShellGet module using the Install-Module PowershellGet command:

Install-Module PowershellGet

Followed by the Install-Module -Name ExchangeOnlineManagement command:

Install-Module ExchangeOnlineManagement

When installed you can use the Connect-ExchangeOnline command to connect to Exchange Online. When MFA for your admin account is configured it will automatically use it:

Connect-ExchangeOnline

The differences between V1 and V2 are clearly visible in the commands. All V2 commands contain EXO, like:

  • Get-Mailbox vs Get-EXOMailbox
  • Get-Recipient vs Get-EXORecipient
  • Get-MailboxStatistics vs Get-EXOMailboxStatistics
  • Get-CASMailbox vs Get-EXOCASMailbox

This means that all scripts you have written for use with Exchange Online need to be changed to reflect the V2 commands.

For a complete overview you can use the Get-Command *EXO* to retrieve all PowerShell commands that contain EXO (still very limited 🙂 ):

Get-Command EXO

The Exchange Online PowerShell V2 module is still in preview, the current version is 0.3582.0 which you can check using the Get-Module ExchangeOnlineManagement command:

Get-Module ExchangeOnlineManagement

The Exchange Online PowerShell v2 module is a work in progress, but it the future of PowerShell in Exchange Online, so you should keep an eye on this development.

More Information

Use the Exchange Online PowerShell V2 module – https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/exchange-online-powershell-v2/exchange-online-powershell-v2?view=exchange-ps

Install-Module MSOnline fails with unable to download from URI

When installing the MSOnline module using the Install-Module MSOnline command in PowerShell it fails with a cryptic error like:

WARNING: Unable to download from URI ‘https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409’ to ”.
WARNING: Unable to download the list of available providers. Check your internet connection.
PackageManagement\Install-PackageProvider : No match was found for the specified search criteria for the provider ‘NuGet’. The package provider requires ‘PackageManagement’ and ‘Provider’ tags. Please check if the specified package has the tags.

And

WARNING: Unable to download from URI ‘https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409’ to ”.
WARNING: Unable to download the list of available providers. Check your internet connection.
PackageManagement\Get-PackageProvider : Unable to find package provider ‘NuGet’. It may not be imported yet. Try ‘Get-PackageProvider -ListAvailable’.
Install-Module : NuGet provider is required to interact with NuGet-based repositories. Please ensure that ‘2.8.5.201’ or newer version of NuGet provider is installed.

As shown in the following screenshot:

Install-PackageProvider

It turns out that this is a TLS issue, PowerShell does not use TLS 1.2 by default, while Microsoft requires TLS 1.2 from clients. To set TLS 1.2 usage for PowerShell, you can use the following command:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Now if you try again, it will install the MSOnline module:

Install-Module MSOnline

This is a per session setting, if you want to enable it for all sessions, add the previous command to the Microsoft.PowerShell_profile.ps1 and Microsoft.PowerShellISE_profile.ps1 profiles (use Notepad $Profile for this.

More information

Azure ActiveDirectory (MSOnline) – https://docs.microsoft.com/en-us/powershell/azure/active-directory/install-msonlinev1?view=azureadps-1.0

Block creation of Office 365 Groups

I’m an old school IT guy, in my world provisioning is done via the IT department or via a provisioning tool. What I don’t want is that regular users create all kinds of objects in my environment, whether it be Active Directory, Azure Active Directory or Office 365.

In Office 365 everything is different, multiple services (Outlook, Teams, Planner, SharePoint, PowerBI and others) are using Office 365 Groups under the hood. So, when users create a new plan in Planner or a new team in Teams, they also create an Office 365 Group in Azure Active Directory.

I’m currently working in a 12,000-user environment, and the last thing I want to happen is 12,000 users randomly creating all kinds of groups, ending up in a total mess where nobody can find information and where it is impossible to delete anything without hurting other people.

The solution for this is to assign the creation of new Office 365 to a security group in Azure Active Directory (this can be a cloud object or a synchronized object). To create a new security group in Azure Active Directory you can use the following PowerShell command:

New-AzureADGroup -DisplayName "O365 Group Creators" -SecurityEnabled:$True -MailEnabled:$False -MailNickName "Nothing"

New-AzureADGroup

Note. It is also possible to create a security group in the Azure AD Portal.

The next step is to assign the permission to create Office 365 Groups to this new security group. This can only be achieved using PowerShell and the Azure AD Preview Module, using the following script:

$GroupName = "O365 Group Creators"
$AllowGroupCreation = "False"
Connect-AzureAD
$settingsObjectID = (Get-AzureADDirectorySetting | Where-object -Property Displayname -Value "Group.Unified" -EQ).id
if(!$settingsObjectID)
{
  $template = Get-AzureADDirectorySettingTemplate | Where-object {$_.displayname -eq "group.unified"}
  $settingsCopy = $template.CreateDirectorySetting()
  New-AzureADDirectorySetting -DirectorySetting $settingsCopy
  $settingsObjectID = (Get-AzureADDirectorySetting | Where-object -Property Displayname -Value "Group.Unified" -EQ).id
}
$settingsCopy = Get-AzureADDirectorySetting -Id $settingsObjectID
$settingsCopy["EnableGroupCreation"] = $AllowGroupCreation
if($GroupName)
{
  $settingsCopy["GroupCreationAllowedGroupId"] = (Get-AzureADGroup -SearchString $GroupName).objectid
}
Set-AzureADDirectorySetting -Id $settingsObjectID -DirectorySetting $settingsCopy
(Get-AzureADDirectorySetting -Id $settingsObjectID).Values

When you run this script, you will see a similar output:

GroupCreators

The first box corresponds to the objectID of the security group we’ve created in the first step, just compare with the ObjectID shown in the first screenshot.

The second box shows $false for the EnableGroupCreation property, indicating no other groups are allowed to create Office 365 Groups.

All members of the security group we just created are allowed to create Office 365 groups. There are some exceptions though, Exchange admins, SharePoint admins, Teams admins and User Management admins are by default allowed to create Office 365 groups as well, but typically these are not regular users.

This way you can control who is able to create Office 365 Groups in your environment, and make sure group creation doesn’t explode in your tenant.

More information