Tuesday, October 20, 2020

Office 365 Spam Remover - Now supports MFA (Updated to support Content Searches)

Problem: A spam campaign has hit your tenant and affected mailboxes and the Campaign options in O365 are either unavailable or don't satisfy the executives yelling at you while you read this.

Resolution: Adjust this script to replace CONTOSO with your domain (if not it will prompt you). This will prompt you for your Exchange Admin credentials, offer you the chance to add more exchange admin accounts to run this under, prompt for the evil sender(s), date and time the spam campaign hit, and optionally the subject line(s) of the evil email messages so you don't accidentally remove too many messages. The script uses a message trace of all email sent to your tenant by the evil senders during the time frame specified and then searches those mailboxes to find the message(s) and NOW CREATES CONTENT SEARCHE(S)! From there, it will create the Purge action necessary to delete the message in question.

Last Updated May 21, 2019 to improve several sections based on feedback and optimizing. Another version of this script has been posted that has a GUI for all of the initial input using the Windows Presentation Framework built into Windows (so no special installs needed) at https://www.hornerit.com/2019/05/o365-spam-remover-script-now-with-gui.html. As with any script you get from the internet, no warranty is expressed or implied for this script so test it and tweak to your environment. I have tried to make it use UTC and avoid hard-coding any regional settings but your mileage may vary.


Update 2019-06-17 - I have moved my scripts to a github repository so that updates are easier to make. DO NOT WORRY - I do not make my github look freaking weird with folders and cryptic things that non-developers don't understand...my scripts are right there in the main folder and you can click them to view/copy/download: https://github.com/hornerit/powershell/blob/master/O365-SPAM-REMOVER-NoGUI-Public.ps1

O365 Spam Remover Script - now with a GUI and supports MFA (updated to use Content Search)

Problem: A spam campaign has hit your company and you want to remove the email from all inboxes in the tenant to help prevent people clicking bad links, freaking out, etc.

Solution: I've created a script as an update to the original script for this post. The newer ExchangeOnlineManagement powershell options appeared and the Search-Mailbox cmdlet has been deprecated...so, the new version creates a Content Search and also creates the appropriate purge actions to delete all email. This script will try to load a GUI for you with several options to control the senders, subject lines, and time frames the spam campaign was sent to make it much simpler on you to remove that phishing or spam campaign that made it through. If the GUI fails, it will fail back to an interactive cmd line script requesting the same info. As with any script you get from the internet, no warranty is expressed or implied for this script so test it and tweak to your environment. I have tried to make it use UTC and avoid hard-coding any regional settings but your mileage may vary.


https://github.com/hornerit/powershell/blob/master/O365-SPAM-REMOVER-GUI-Public.ps1

Update 10/20/2020 - The code has been overhauled and updated for content searches! Update 6/17/2019 - Moved the code to GitHub for easier updating. DO NOT WORRY - my github does not look like some giant mess of folders with cryptic things...the powershell files are right there on the screen and you can click any of them to view them in their entirety.

Update on May 22, 2019 - I have added some support to attempt to auto-load the Exchange Online for Powershell module and use it as priority over basic authentication.

Thursday, February 6, 2020

PowerShell Scripts - Get all Mailbox and Mailbox Folder permissions in O365 (New Exchange PowerShell)

Script updated several times between 2020-02-10 and 2020-03-5 to tweak different aspects when using the new Exchange Online powershell cmdlets that is currently in preview but is generally much more efficient for this task along with code readability and documentation updates. I received confirmation that getting mailbox folder permissions will return something more than a displayname and that we will eventually be able to use the FolderId instead of folder path since folder paths have problems with backslashes and other foreign characters.

Problem:
You need to document, monitor, and manage mailbox and mailbox folder permissions across an entire O365 tenant. When trying with PowerShell, we can't even pipe Get-Mailbox to Get-MailboxFolderStatistics to Get-MailboxFolderPermissions. When attempting these things in PowerShell, it has traditionally been extremely slow.

Source:
This gets weird when it comes to mailbox permissions (FullAccess, SendAs, SendOnBehalf), Calendar Permissions, and Mailbox Folder Permissions (tons of options) and HR wants you to verify that an employee does not have access to that mailbox even if they are rehired later. The old Exchange PowerShell commands transmitted too much data so the results of pulling large numbers of mailboxes and folders were too slow to be feasible. One could search the Unified Audit Log for folder permissions changes but that doesn't give the baseline.

Resolution:
You can download my interactive script that does the retrieval, partial retrieval, and resuming here: https://github.com/hornerit/powershell/blob/master/Get-O365MailboxPermissionsAcrossTenant.ps1. I have updated several times and maintain updates on it for tweaking special scenarios and will update once the new cmdlets offer different data.

No matter how you try, you will need to make a local copy of the mailbox/folder permissions somewhere because attempting to query this information is too time-intensive to be useful. With the advent of the new Exchange Online V2 powershell cmdlets, the performance of getting the mailbox, mailbox permissions, and folder permissions is not nearly as horrible as it was before.
Make sure you have an Exchange Admin account and you have installed the new Exchange module (Install-Module ExchangeOnlineManagement) - you can connect and even support MFA using Connect-ExchangeOnline and all the old ExchangeOnline commands still work. From there, the more straightforward version for mailbox permissions for 5 mailboxes is something like this:

Get-EXOMailbox -ResultSize 5 | 
     Get-EXOMailboxPermissions | 
     Where-Object { 
          $_.IsInherited -eq $false -and
          $_.Deny -eq $false 
     } | 
     Select-Object Identity,User,@{Label="AccessRights";Expression={$_.AccessRights -join ","}} |
     Export-CSV -Path "C:\someFolder\MailboxPermissions.csv" -Append

Or for the Mailbox Folder permissions (for 5 mailboxes):

Get-EXOMailbox -ResultSize 5 | 
     Get-EXOMailboxFolderStatistics | 
     Where-Object { 
          $_.SearchFolder -eq $false -and
          @("Root","Calendar","Inbox","User Created") -contains $_.FolderType -and 
          (@("IPF.Note","IPF.Appointment",$null) -contains $_.ContainerClass -or $_.Name -eq "Top of Information Store")
     } | 
     Select-Object @{Label="Identity";Expression={ 
          if($_.Name -eq "Top of Information Store"){
               $_.Identity.Substring(0,$_.Identity.IndexOf("\"))
          } else { 
               $_.Identity.Substring(0,$_.Identity.IndexOf("\"))+':'+$_.Identity.Substring($_.Identity.IndexOf("\")).Replace([char]63743,"/")
          }
     }} | 
     Get-EXOMailboxFolderPermissions | 
     Where-Object { $_.AccessRights -ne "None" } | 
     Select-Object Identity,FolderPath,User,@{Label="AccessRights";Expression={$_.AccessRights -join ","}} | 
     Export-CSV -Path "C:\someFolder\MailboxFolderPermissions.csv" -Append

Or combine them effectively (for 5 mailboxes):

Get-EXOMailbox -ResultSize 5 | 
     Tee-Object -Variable "myMailboxes" | 
     Get-EXOMailboxPermissions | 
     Where-Object { 
          $_.IsInherited -eq $false -and 
          $_.Deny -eq $false 
     } | 
     Select-Object Identity,User,@{Label="AccessRights";Expression={$_.AccessRights -join ","}} | 
     Export-CSV -Path "C:\someFolder\MailboxPermissions.csv" -Append
$myMailboxes | 
     Get-EXOMailboxFolderStatistics | 
     Where-Object { 
          $_.SearchFolder -eq $false -and 
          @("Root","Calendar","Inbox","User Created") -contains $_.FolderType -and 
          (@("IPF.Note","IPF.Appointment",$null) -contains $_.ContainerClass -or $_.Name -eq "Top of Information Store")
     } | 
     Select-Object @{Label="Identity";Expression={ 
          if($_.Name -eq "Top of Information Store"){ 
               $_.Identity.Substring(0,$_.Identity.IndexOf("\")) 
          } else { 
               $_.Identity.Substring(0,$_.Identity.IndexOf("\"))+':'+$_.Identity.Substring($_.Identity.IndexOf("\")).Replace([char]63743,"/")
          }
     }} | 
     Get-EXOMailboxFolderPermissions | 
     Where-Object { $_.AccessRights -ne "None" } | 
     Select-Object Identity,FolderPath,User,@{Label="AccessRights";Expression={$_.AccessRights -join ","}} | 
     Export-CSV -Path "C:\someFolder\MailboxFolderPermissions.csv" -Append

If you are a PowerShell person, you may notice 3 oddities with my combining:
  1. I did not use a foreach-object
  2. I used a Tee-Object
  3. There are a ton of filters and some weird Select-Object stuff going on in the middle with MailboxFolderStatistics

Foreach-Object breaks the new Exchange Online cmdlets' multithreading (speed) so I can't say Get-Mailboxes | foreach-object { Get-Mailboxpermissions; Get-MailboxFolderPermissions} ... It was actually faster to run each command separately. To keep from having to retrieve the mailboxes twice, Tee-Object allows you to take the output from the Get-EXOMailboxes, store it in a variable, and use it a second time once your first full command is over - and it does NOT break the PowerShell pipeline so speed stays happy!

The reason for all the weirdness with Get-EXOMailboxFolderStatistics is that when someone tries Get-MailboxFolderPermission but only supplies the email address of the mailbox, it only retrieves the folder permissions for the folder "Top of Information Store". So one might think - I will just use the Get-EXOMailboxFolderStatistics to get the list of all the folders within the mailbox and send THAT over to the Get-EXOMailboxFolderPermission...well, it fails miserably because the Identity of the folder from Statistics looks like mailbox@domain.com\NameOfFolder but the MailboxFolderPermissions cmdlet is expecting mailbox@domain.com:\NameOfFolder\NameofSubfolder. Right now, the FolderID is not supported in the new cmdlets, only the path. Either way, the lack of nice pairing is stupid, but fixable by Selecting the identity of folders and then fixing the name and then passing that along the pipeline. The other filters I throw in there are so that I don't ask about permissions for every dumb folder in a mailbox that might be some Teams, Yammer, etc. system folder and subfolder but I do want Calendars.