O365 / Exchange one-liners

This post is way overdue, but better late than never right?

I spent a few good months late last year working on a O365 migration project and I thought I would share some of the powershell commands that I used for various tasks throughout the project. I have gathered some of these from various blogs (many from exchangeserverpro.com) and the Microsoft KB articles. I thought it might be useful to have them in once place and could prove useful to you folk in the wild.

First thing’s first – Connecting. Here is a write up of the process and the per-requisite software you need, but below is what I’ve used to connect my ps session.

Set-ExecutionPolicy Unrestricted -Force   
$O365CREDS = Get-Credential
$ONPREMCREDS = Get-Credential
$SESSION = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $O365CREDS -Authentication Basic -AllowRedirection
Import-PSSession $SESSION
Connect-MsolService -Credential $O365CREDS

#Test connectivity
Get-Mailbox yourmailboxname
Disable Clutter

This was a real annoyance for our users

Get-Mailbox User@vkarps.com | Set-Clutter -Enable $False
Set maximum send / receive size limit for all users with @domainname address

Note: This command might need to change based on the size of your tenant (There are a few documented methods out there). I found that if you have a large number of users, you will constantly get prompted to authenticate or the command will fail once it reaches the threshold.

Get-Mailbox *@vkarps.com | Set-Mailbox -MaxReceiveSize 150MB -MaxSendSize 150MB

Another thing to keep in mind with this one is that you’re limited to your Send / Receive connectors, so keep an eye out on those limits.

Setup Out of Office
Set-MailboxAutoReplyConfiguration -Identity user@vkarps.com -AutoReplyStateEnabled -ExternalMessage "I'm on vacation" -InternalMessage "I'm on vacation"
Migration batch operations
Get current migraiton batches in your tenant
Get the migration progress of each mailbox in a migraiton batch
Get-MigrationUser -BatchId "vKARPS-Batch3-Finance" | Get-MigrationUserStatistics | FT -AutoSize
Create migration batch from csv, but dont start syncing.

Here I have allowed for a bad item limit of 100 and set allowincremental syncs to true. This is so that when the batch it started, it will not automatically cutover.

 New-MigrationBatch -Name "vKARPS-Batch4-Legal" -SourceEndpoint Australia -TargetDeliveryDomain vkarps.mail.onmicrosoft.com -CSVData ([System.IO.File]::ReadAllBytes("c:ScriptsLegalUsers.csv")) -AllowIncrementalSyncs $True -BadItemLimit 100 -NotificationEmails admin@vkarps.com
Start a migration batch
Start-MigrationBatch -Identity "vKARPS-Batch4-Legal"
Stop a migration batch
Stop-MigrationBatch -Identity "vKARPS-Batch4-Legal"
Remove a user from a migration batch, without deleting the batch.

Note that the batch must be in a stopped state.

Remove-MigrationUser -Identity noob.user@vkarps.com
Get details about a user that is been migrated
Get-MigraitonUser -identity financeteam@vkarps.com
Get detailed migraiton log for a user.

This is useful when troubleshooting migration issues

Get-MigrationUserStatistics noob@vkarps.com -IncludeReport -Diagnostics | Export-Clixml noob@vkarps.com.xml
Mailbox Assessment

These are some one-liners to extract information to help target mailboxes for assesment to see if theyre ready to migrate to O365. This was also helpful to find relationships between user mailboxes and shared mailboxes. Note that these are to be run on the on-premises exchange servers.

Get last logon time

This was useful to identify mailboxes which have not been used in a while or are too large to be migrated. In my case most these were candidates for deletion, saving us licenses in Exchange Online or having difficult conversations to reduce mailbox sizes. Placing in a delimiter allows you to easily separate the columns in excel (text to columns function).

Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName,LastLoggedOnUserAccount,LastLogonTime,LastLogoffTime,ItemCount,TotalItemSize | Export-Csv -Delimiter ~ -NoTypeInformation C:\temp\MailboxLastLogon.csv
Get Mailbox permissions

This command will list the permissions for each mailbox if one or more accounts have access to it, apart from itself. You may have to do some further filtering on accounts, depending on your environment

Get-Mailbox -ResultSize unlimited | Get-MailboxPermission | where {$_.user.tostring() -ne "NT AUTHORITY\SELF" -and $_.IsInherited -eq $false} | Select Identity,User,@{Name='Access Rights';Expression={[string]::join(', ', $_.AccessRights)}} | Export-Csv -NoTypeInformation C:\Temp\MailboxPermissions.csv
Get Mailbox Type

This is helpful to quickly filter on the types of mailboxes and identity any that are not setup correctly. For example if a shared mailbox is setup as a user mailbox.

 Get-Mailbox -ResultSize unlimited | Select-Object SamAccountName,DisplayName,RecipientTypeDetails,PrimarySMTPAddress | Export-CSV C:\temp\MailboxType.csv
MSOL user operations
Assign License

This will obviously vary depending on what license you wish to apply, but here is the syntax

Set-MSolUserLicense -UserPrincipalName user@vkarps.com -UsageLocation AU
Set-MSolUserLicense -UserPrincipalName user@vkarps.com -AddLicenses vkarps:EXCHANGESTANDARD

Remove License
Set-MSolUserLicense -UserPrincipalName user@vkarps.com -RemoveLicenses vkarps:EXCHANGESTANDARD

Check License Status for users with @DOMAINNAME address
Get-MsolUser -All | Where-Object {$_.UserPrincipalName -like "*@vkarps.com"}

Check license status for list of users in a .csv file

The csv file needs to name ‘name’ in the first line, each following line should contain a valid UPN.

Import-CSV C:\Scripts\NewUsers.csv | ForEach {Get-MsolUser -UserPrincipalName "$($_.Name)"}
Change UPN for MSOL user

I came across instances where user accounts were created with UPNs for other domains / namespaces which were replicated created in the cloud. It can also happen if the UPN is changed after the account has been provisioned in Exchange Online. This issue becomes apparent when you try to migrate the user to Exchange Online. Here is an article which explains the issue:

Set-MsolUserPrincipalName -UserPrincipalName "noob.user@lab.vkarps.com" -NewUserPrincipalName "noob.user@vkarps.onmicrosoft.com"

Set-MsolUserPrincipalName -UserPrincipalName "noob.user@vkarps.onmicrosoft.com" -NewUserPrincipalName "noob.user@vkarps.com"
Deleting mailbox and user from Exchange Online

This may be required for a number of reasons, in my case it was due to duplicate mailboxes (one on-premises and the other on O365). Note: The mailbox will be deleted and cannot be recovered.

Remove-msoluser –userprincipalname noob.user@vkarps.com
Get-msoluser –userprincipalname noob.user@vkarps.com -ReturnDeletedUsers  fl *objectID*
Remove-MsolUser -ObjectID -RemoveFromRecycleBin -Force

You may have to give it a couple of minutes before running the next command

Get-mailbox -SoftdeletedMailbox noob.user@vkarps.com | Remove-Mailbox -PermanentlyDelete -Force -Confirm:$fals



Disconnected from Host. Agent is out of date and needs a manual upgrade

I recently performed an upgrade of vCenter from 5.0 to 5.5 and had an issue with just one of my hosts connecting to vCenter.

Agent out of date
First I tried to re-connect it manually with no success. I restarted the management agents from the iLO (not sure why I didn’t try go via ssh first as I normally would) and tried to connect again as this worked for other issues I came across in the past, still nothing.

OK, let’s uninstall the FDM client manually and try re-connecting the host (KB1003714):

cp /opt/vmware/uninstallers/VMware-fdm-uninstall.sh /tmp
chmod +x /tmp/VMware-fdm-uninstall.sh

This time upon connecting the host I was prompted for the root credentials and proceeded through the wizard; enter next error:

Cannot contact the specified host (host1.lab.local) 
The host may not be available on the network, a network configuration 
problem may exist, or the management services on this host may not be

Here, I did a sanity check and confirmed that DNS resolution was working and that I could ping the host, all good. Hrmm…

Next I went over to take a look at the fdm.log and the vpxa.log which were not giving me much to go off. So I went over to the events tab in the c# client to have another look at the error and noticed I was also getting and incorrect username and password error, dafaq ?

So I decided to ssh to the host to confirm that I wasn’t mistyping the password and then realized that I couldn’t initiate a session:

"Network Error: Connection Refused"

I checked lockdown mode was disabled, SSH service was running and the host firewall rules were okay. Hrmm, why on earth is SSH refusing my connection?

Some quick google-fu came up with the following KB article (KB1039095).
Back to the iLO I went and sure enough, the inetd.conf file was blank
I copied the contents of the configuration file from another host in the cluster, restarted the ssh daemon along with the management agents.

Here is my inetd.conf incase you don’t have another host to copy from:

# Internet server configuration database

# Remote shell access

ssh      stream   tcp   nowait   root   /usr/lib/vmware/openssh/bin/sshd       sshd ++swap,group=host/vim/vimuser/terminal/ssh -i
ssh      stream   tcp6  nowait   root   /usr/lib/vmware/openssh/bin/sshd       sshd ++swap,group=host/vim/vimuser/terminal/ssh -i

# VMware authentication daemon
authd   stream    tcp   nowait   root   /sbin/authd           authd
authd   stream    tcp6  nowait   root   /sbin/authd           authd

I could now ssh to the host, great! I proceeded to try connect the host and voila the host was able to connect back into the cluster.

As vCenter needs to copy over the installation files to the host for the installation of the FDM agent it was unable to do so as SSH was busted.

Here is another useful article I used during my troubleshooting (KB2004429).