Powershell Script: Removing AutoMapping for Shared or Previously Delegated Office365/Exchange Mailbox

In an earlier post, I wrote about how to setup auto-forwarding for a user’s email if they are on an extended leave from the office. One of the interesting problems that has arisen once that employee returned was that their email folder was still showing up in my list of mailboxes in Outlook 2016, even after the forward was disabled and delegate access was confirmed as being removed/never setup in the first place.

Resolving the issue involves a little bit of Powershell, and while the script code isn’t terribly difficult, I did have to piece it together from a couple of different sources and deal with the fact that I had MFA (multi-factor authentication) enabled on my account.

In the end, I disabled MFA briefly on my account while I executed the script below. I was unsuccessful being able to connect to Exchange Online via Powershell with MFA and gave up because I didn’t think it was worth my time to troubleshoot for something that was fairly insignificant for my use case.

Below is the script I used:

#Must disable MFA in order for this to work. Otherwise, there are supposedly ways to get MFA to work with this script, but I wasn't successful with them
$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session
Remove-MailboxPermission -Identity <MailboxToRemove> -User <UserWhoWantsMailboxRemoved> -AccessRights FullAccess
Add-MailboxPermission -Identity <MailboxToRemove> -User <UserWhoWantsMailboxRemoved> -AccessRights FullAccess -AutoMapping:$false
Remove-PSSession $Session

Resources:

Useful Gmail and Outlook Filters

Every once in awhile I get the urge to clean up emails and get rid of a bunch of stuff I just don’t need. On one hand, it’s nice to be able to reference old emails, but on the other you risk exposing your personal information to potential evil-doers (hackers or even mining/advertiser information if you’re using a freemail account like Gmail). Do you really need those emails from 8 years ago? Probably not.

Anyway, regardless of the motivation, here are some useful filters that I use:

Gmail

Gmail filters listed below are all performed by typing the unbolded text from the bullets below into your search box. You can combine them in any way you like – just put a space between each filter

  • All unread email: label:unread
  • All emails sent or received prior to a specific date: before:2018/1/1
  • All emails without a label: -has:userlabels
  • All emails not in the inbox, sent, drafts, or chat folders: -in:inbox -in:drafts -in:sent -in:chat
  • All emails with attachments: has:attachment
  • All emails at least as large as a certain size (in bytes – example is about 5MB): size:5242880

Outlook

Outlook is a little bit different as it offers some pretty powerful features such as search folders, but filtering can still be performed by using the search box for most items

  • All unread email (multiple options):
    • Choose the folder you want
      • On the right side of the mail pane, there is a dropdown that defaults to “All.” You can select “Unread” from the dropdown to see only unread
    • Create a search folder
      • On the left pane (of the default view) that shows your email folders, scroll down to the bottom of the Data File/Account where you want to view unread messages
      • Look for a folder called “Search Folders”
        • Right click on “Search Folders” and choose “New Search Folder”
        • Select “Unread Mail” from the list in the popup window that opens and hit OK
    • Drag the new “Unread Mail” search folder into your favorites (Optional)
  • All emails received prior to a specific date: received:<2018/1/1
  • All emails sent prior to a specific date: sent:<2018/1/1
  • All emails with attachments: hasattachment:yes (or has:attachment)
  • All emails with an attachment matching a specific file extension: ext:jpg
  • All emails at least as large as a certain size (in bytes – example is about 5MB): messagesize:>=5 MB

Resources:

Office 365 – Set Out of Office Response and Forward all User Emails as Exchange Admin

I recently had a member of my organization go on maternity leave, and, because of the way that babies work, she wasn’t able to set an out of office response or a forward.

Thankfully, with Exchange Admin Center, it’s pretty easy to do both.

Setting a User’s Out of Office Response

The first thing is to essentially impersonate a user as the Office Admin:

  1. Navigate to Exchange Admin Center on office.com (you have to be an Administrator to do this, obviously)
  2. Click your Name/Icon in the very upper right-hand corner and choose “Another user…”
  3. Choose the user in your organization that you need to update from the popup window that follows
  4. On the right sidebar of the window that opens for that user, you should see “shortcuts to other things you can do”
    1. In that list is “Set up an automatic reply message”

That’s it. Once you’ve chosen to Set up an automatic reply message, you can format the message however you want (one for internal users and one for external users)

Forwarding a User’s emails

  1. Log into Office 365 Admin Center
  2. Choose Users -> Active Users
  3. Find the User whose email you want to forward and click their name
  4. In the sidebar that opens, expand “Mail Settings” and click “Edit” next to Email Forwarding
  5. Flip forwarding to On and enter the email address where the messages forward
    1. You can optionally choose to keep a copy of the email in the inbox for the original user

Pretty straightforward, but not something I do all that frequently, so I know I’ll forget by the next time I have to do it 🙂

Powershell Script: Rename Files with Characters that are Invalid for OneDrive for Business and Sharepoint Online

I’m a big fan of Microsoft’s Office 365 offerings, and one of my favorite components of that is the OneDrive storage that comes with each user. It’s a great way to make sure files are always backed up and available anywhere you have an internet connection.

One of the challenges you can encounter when using OneDrive for Business is that not all characters that are valid for filenames in a Windows environment are valid for use in OneDrive (and Sharepoint Online files for that matter). According to this article, the list of invalid characters are: \ / : * ? ” < > | # %.

There are several other restrictions (such as file size and name length), but the invalid characters issue is the one I encounter the most frequently, especially when I migrate a user who has a lot of files to OneDrive for Business. In one case, there were over 1,000 files with invalid characters, and I wasn’t about to rename those by hand.

Looking online, there are several great resources for Powershell scripts to solve this problem, although some were out of date and did not accurately reflect the current restrictions of the files files. In the end, there were two sources I liked a lot and I combined them into a single script and modified that. It’s not a perfect or complete script, and I don’t consider myself even proficient with Powershell. However, this script has worked well for me.

First, the sources:

Use PowerShell to check for illegal characters before uploading multiple files into SharePoint

Fix file names for Skydrive Pro syncing

I liked that the second one outputs to a TSV file that you can open with Excel and review. The first source just prints to the console, and when you have a ton of files, that isn’t very useful. Because of the output to the file, I was able to tweak things a little more, adding in a few additional things, such as searching for files with “%20” in the name (html encoded space).

function Check-IllegalCharacters ($Path, $OutputFile, [switch]$Fix, [switch]$Verbose)
{
    #The maximum allowed number of characters of a file's full path + name
    $maxCharacters = 400
    #The maximum file size
    $maxFileSize = 2147483648
    #A list of file types that can't be sync'd
    $invalidFileTypes = ".tmp", ".ds_store"
    #A list of file names that can't be sync'd
    $invalidFileNames = "desktop.ini", "thumbs.db", "ehthumbs.db"

    Write-Host Checking files in $Path, please wait...

    #Only run for a valid path
    if (!(test-path $path))
    {
        'Invalid path for file renames'
    }
    else
    {
        #if the output file exists empty it first
        if (test-path $outputFile)
        {
            clear-content $outputFile
        }
        #add headers to output file
        Add-Content $outputFile "File/Folder Name	New Name	Comments";

        #Get all files and folders under the path specified
        $items = Get-ChildItem -Path $Path -Recurse
        foreach ($item in $items)
        {
            #Keep a flag to indicate whether or not we can perform the updates (some problems are deal breakers)
            $valid = $true
            #Keep an array list for comments
            $comments = New-Object System.Collections.ArrayList
            
            if ($item.PSIsContainer) { $type = "Folder" }
            else { $type = "File" }
        
            #Check if item name is longer than the max characters in length
            if ($item.Name.Length -gt $maxCharacters)
            {
                [void]$comments.Add("$($type) $($item.Name) is $($item.Name.Length) characters (max is $($maxCharacters)) and will need to be truncated")
                $valid = $false
            }

            if($item.Length -gt $maxFileSize)
            {
                [void]$comments.Add("$($type) $($item.Name) is $($item.Length / 1MB) MB (max is $($maxFileSize / 1MB)) and cannot be synchronized.")   
                $valid = $false
            }

            if($invalidFileNames.Contains($item.Name))
            {
                [void]$comments.Add("$($type) $($item.Name) is not a valid filename for file sync.")
                $valid = $false
            }

            if($invalidFileTypes.Contains($item.Name.Substring($item.Name.Length-4)))
            {
                [void]$comments.Add("$($type) $($item.Name) type $($item.Name.Substring($item.Name.Length-4)) is not a valid file type for file sync.")
                $valid = $false
            }
           
            #Technically all of the following are illegal \ / : * ? " < > | # %
            #However, all but the last two are already invalid Windows Filename characters, so we don't have to worry about them
            $illegalChars = '[#%]'
            filter Matches($illegalChars)
            {
                $item.Name | Select-String -AllMatches $illegalChars |
                Select-Object -ExpandProperty Matches
                Select-Object -ExpandProperty Values
            }
            
            #Replace illegal characters with legal characters where found
            $newFileName = $item.Name
            Matches $illegalChars | ForEach-Object {
                if($Verbose){ [void]$comments.Add("Illegal string '$($_.Value)' found") }
                #These characters may be used on the file system but not SharePoint
                if ($_.Value -match "#") { $newFileName = ($newFileName -replace "#", "-") }
                if ($_.Value -match "%20") { $newFileName = ($newFileName -replace "%20", " ") }
                if ($_.Value -match "%") { $newFileName = ($newFileName -replace "%", "-") }
            }

            if($comments.Count -gt 0)
            {
                #output the details
                Add-Content $outputFile "$($item.FullName)	$($item.FullName -replace $([regex]::escape($item.Name)), $($newFileName))	$($comments -join ', ')"    
                if($Verbose)
                { 
                    Write-Host $($type) $($item.FullName): $($comments -join ', ') -ForegroundColor Red
                }
            }
                
            #Fix file and folder names if found and the Fix switch is specified
            if ($newFileName -ne $item.Name)
            {
                if($fix -and $valid)
                {
                    Rename-Item $item.FullName -NewName ($newFileName)
                    if($Verbose)
                    {
                        Write-Host $($type) $($item.Name) has been changed to $($newFileName) -ForegroundColor Yellow
                    }
                }
            }
        }
    }
    Write-Host "Done"
}

#Example: Check-IllegalCharacters -Path 'C:\Users\User\Downloads\Files With Errors' -OutputFile 'C:\Users\User\Desktop\RenamedFiles.tsv' -Verbose -Fix

I noticed that I ran into some errors when files were deeply nested – for example if you have to rename a file in a folder that also was renamed. Re-running the script a few times fixed that problem for me (each time it would fix one more layer of folders – so if you had a file inside 3 levels of folders that also needed renaming, it would take four passes in order to complete everything.

Configuring a Fraud Detection Whitelist on Office 365 / Exchange 365

I’ve been setup with Office 365 for around a year, and I’m still discovering little things to tweak and optimize. One such thing I ran across today was a little message in some emails that were generated by an on-premises web server:

This sender failed our fraud detection checks and may not be who they appear to be. Learn about spoofing

While the link provided by Microsoft about spoofing describes spoofing in detail, it doesn’t say anything about when you know something isn’t fraudulent and want to prevent it from flagging Exchange. After doing a little digging (I started by looking for some kind of a whitelist or whitelisting certain IP addresses on Exchange/Office 365), I came across a very helpful article: This Sender Failed Our Fraud Detection Checks and May Not Be Who They Appear to Be.

In a nutshell, the problem is that the email headers specify an origination IP address (our web server’s address) that isn’t allowed as part of Exchange’s SPF (Sender Policy Framework) configuration. The SPF configuration will examine an email’s domain and ensure that the domain matches an allowed list, to prevent fraudulent sending. After all, it’s pretty damn easy to spoof an email address using software.

SPF filters are added to your DNS records, and are pretty easy to update. To that end, I logged into my DNS provider and took a look at the records for my domain. There, I found a TXT record that was setup when I initially configured Office 365. This record had the following value:

v=spf1 include:spf.protection.outlook.com -all

In order to add my web server’s address to this record and thus resolve my issue, the line simply needed to be modified as such:

v=spf1 ip4:xxx.xxx.xxx.xxx include:spf.protection.outlook.com -all

xxx.xxx.xxx.xxx is, of course, the IP address you want to “whitelist.”

Once the old DNS record expires (I have a TTL of 1 hour on this record), the new configuration should take effect and your messages will no longer be destined for your Junk Email folder.

How to Add and Manage Outlook Rules/Filters for Office 365 Shared Mailboxes

It isn’t obvious, but you can setup and manage rules for shared mailboxes in Office 365 just as you do for users’ mailboxes. It isn’t obvious how to it is because you can’t administer these rules through the desktop client (or any other client) like you can with user mailboxes, and the settings for managing these rules doesn’t even appear to be available from the Office 365 or Exchange administration panel. Here are the steps to take:

  • Login to Office 365 with an account that has administrative access to the Shared Mailbox
  • Enter the following Url into your browser to get to the shared mailbox options page: https://outlook.office365.com/ecp/<email address>, where <email address> is the email address of the shared mailbox with the rules you want to manage.
    • For example, if your email address was info@contoso.com, you would enter the address https://outlook.office365.com/ecp/info@contoso.com
  • Click the “Organize email” section on the left menu

This method also works for editing user mailbox rules, provided you have access.

Note: As of this writing, Internet Explorer is the only browser I have tried that successfully adds a rule that involves a sent from or sent to criteria. Chrome and Firefox both give a CORS error because selecting people tries to open the contacts for the account, and that application is part of a different domain. The message I receive from Firefox is:

Load denied by 08:59:07.114 Load denied by X-Frame-Options: https://outlook.office.com/owa/#viewmodel=OwaOptionRichPeoplePickerViewModelFactory does not permit cross-origin framing.

Managing Global Rules

If you simply want to edit global rules that affects all mail flowing to/from your organization, you can follow the steps below:

Getting to the Exchange Admin Center

  • Login to Office 365 with an account that has administrative access to the Shared Mailbox
  • Open the Admin Center by clicking the “Admin” button
  • When the Admin Center opens, click the “Admin Centers” link on the left-side menu and choose “Exchange”

Managing Rules

  • From the Exchange Admin Center, there is a “Rules” link under the mail flow section. Remember, this area only allows you perform a limited set of actions, that does NOT include moving a message to a specific folder, as these rules are at a global level. The actions you can perform here are:
    • Forward the message for approval…
    • Redirect the message to…
    • Block the message…
    • Add recipients…
    • Apply a disclaimer to the message…
    • Modify the message properties…
    • Modify the message security…
    • Prepend the subject of the message with…
    • Generate incident report and send it to…
    • Notify the recipient with a message…