I’ve just added two new scripts to my Office365 repository on GitHub. One connects PowerShell to SharePoint Online using PnP PowerShell. The other connects to Exchange Online. Both run on Linux, and both use certificate-based, app-only authentication. There’s no browser, no typed password and no MFA prompt at connection time.
Why bother building Linux versions? Mostly because of agents.
Built for machines that nobody is sitting at
More and more of my admin work now gets done by autonomous agents like Grok Bot, running on a Linux computer that no person is logged into. An agent like that can write and run PowerShell happily enough. Where it gets stuck is sign-in. A pop-up browser window, a device code that someone has to type in, or an MFA approval on a phone all stop it in its tracks. Automation that needs a human to tap “approve” isn’t really automation.
Both scripts work the same way and have two main modes:
-GenerateLocalCertificate creates an RSA-2048/SHA256 certificate, a public .cer file and an encrypted .pfx using .NET, so you don’t need OpenSSL.
-UseCertificateAuth connects with the encrypted PFX: PnP.PowerShell for SharePoint, ExchangeOnlineManagement for Exchange.
-ProvisionEntraApp (optional) creates or reuses an Entra app, uploads the public certificate, grants the required permissions and saves a connection profile. This setup step uses a one-off device-code sign-in by an administrator.
For true headless runs, use -noprompt (alias -Headless). Supply the PFX password through a SecureString parameter, or name an environment variable with -CertificatePasswordEnvironmentVariable. The scripts never print or save that password. Saved profiles mean that, once set up, an agent can connect with little more than -UseCertificateAuth -ProfileName Contoso -Headless.
You’ll need Linux and PowerShell 7.4 or later (7.4.6+ for the PnP script), plus PnP.PowerShell 3.0.0+ or ExchangeOnlineManagement 3.5.0+. Missing modules install for the current user, with no sudo needed. Use -noupdate to skip update checks.
A word on security
Unattended access is powerful, so treat it with respect. The SharePoint provisioning grants Sites.FullControl.All, which means full control of every site. The Exchange provisioning grants Exchange.ManageAsApp and adds the app to Organization Management, which means full Exchange administration. The scripts keep certificates and profiles in dedicated folders locked to mode 700, with files at mode 600, and they refuse symbolic links there. Even so, that .pfx is effectively an admin key. Guard it, keep its password in a vault or a protected environment variable, and only put it on machines you trust. If your scenario needs less, trim the app’s permissions.
To me, this is where Microsoft 365 administration is heading. More of the routine work will be done by agents on machines that nobody watches, and the bottleneck is getting them connected securely without a human in the loop. Take the scripts, try them in a test tenant, and let me know how you go.
Important: Mailbox Auditing is Already ON by Default
Good news! Since 2019, Microsoft automatically enables mailbox auditing for all Exchange Online organizations. This means logging is already active for your mailboxes without requiring any manual configuration.
Should You Enable All Available Logging?
No, you should NOT enable all available logging. Here’s why:
Microsoft’s Recommendation: Use the default audit configuration, which Microsoft automatically manages and updates
Storage Impact: Audit logs consume storage space in each mailbox’s Recoverable Items folder (counts against the 30GB default limit)
Performance Consideration: Excessive logging can impact mailbox performance
Automatic Updates: Microsoft automatically adds new important actions to the default audit configuration as they’re released
What’s Logged by Default
The default configuration logs these critical actions:
Result should show False (meaning auditing is enabled)
Step 3: Check Current Mailbox Audit Status
# For a specific mailbox
Get-Mailbox -Identity "user@domain.com" | Format-List Name,AuditEnabled,DefaultAuditSet
# For all mailboxes
Get-Mailbox -ResultSize Unlimited | Format-Table Name,AuditEnabled,DefaultAuditSet
Step 4: Use Default Settings (Recommended)
# Restore default auditing for a mailbox that was customized
Set-Mailbox -Identity "user@domain.com" -DefaultAuditSet Admin,Delegate,Owner
Step 5: Only If Necessary – Customize Specific Actions
# Example: Add MailboxLogin tracking for owner actions
Set-Mailbox -Identity "user@domain.com" -AuditOwner @{Add="MailboxLogin"}
# Example: Set specific admin actions (overwrites defaults - not recommended)
Set-Mailbox -Identity "user@domain.com" -AuditAdmin MessageBind,FolderBind,HardDelete
Step 6: Configure Retention Period
# Default is 90 days, can extend up to 365 days (E5 license required for >180 days)
Set-Mailbox -Identity "user@domain.com" -AuditLogAgeLimit 180
# Apply to all mailboxes
Get-Mailbox -ResultSize Unlimited | Set-Mailbox -AuditLogAgeLimit 180
Step 7: Verify Configuration
# Check what actions are being audited
Get-Mailbox -Identity "user@domain.com" | Select-Object -ExpandProperty AuditAdmin
Get-Mailbox -Identity "user@domain.com" | Select-Object -ExpandProperty AuditDelegate
Get-Mailbox -Identity "user@domain.com" | Select-Object -ExpandProperty AuditOwner
Method 2: Microsoft 365 Admin Center (Limited Options)
Note: The GUI provides limited mailbox audit configuration options. Most settings require PowerShell.
Government contractors with security clearance requirements
Legal hold scenarios requiring complete activity documentation
3. Forensic Investigations
Use Case: Post-incident analysis
Determining if a compromised account’s emails were actually read
Investigating data exfiltration attempts
Proving or disproving unauthorized access claims
Building timeline of activities during security incidents
4. Privileged Account Monitoring
Use Case: Enhanced monitoring for administrative accounts
Service accounts that shouldn’t have regular email activity
Shared mailboxes with sensitive information
Discovery mailboxes used for legal searches
Executive assistant mailboxes with delegated access
Best Practices If You Enable FolderBind/MessageBind for Owners
1. Selective Implementation
# Enable only for specific high-risk mailboxes
Set-Mailbox -Identity "CEO@company.com" -AuditOwner @{Add="FolderBind","MessageBind"}
# Create a list of VIP users
$VIPUsers = "CEO@company.com","CFO@company.com","Legal@company.com"
foreach ($user in $VIPUsers) {
Set-Mailbox -Identity $user -AuditOwner @{Add="FolderBind","MessageBind"}
}
DLP policies: Focus on preventing data loss rather than tracking all access
The MailItemsAccessed Alternative (E5 Licenses)
For organizations with E5 licenses, MailItemsAccessed is a superior alternative that:
Intelligently aggregates similar activities (reduces noise by 80-90%)
Provides both sync and bind operation tracking
Includes deduplication (removes duplicate entries within 1-hour windows)
Records InternetMessageId for precise message tracking
Better suited for forensic investigations
Automatically enabled for E5 users
Summary Recommendation
Enable FolderBind/MessageBind for owners ONLY when:
You have specific compliance or security requirements
Monitoring high-risk individuals or during investigations
You have the resources to analyze the massive data volume
Storage and performance impacts have been evaluated
You’ve implemented automated analysis tools
Otherwise: Stick with the default configuration and use alternative methods like DLP policies, insider risk management, and the MailItemsAccessed action (for E5 users) for more effective security monitoring.
This guide will provide a comprehensive, production-safe approach using both the Microsoft 365 Defender portal and Exchange Online PowerShell. We will start with a baseline of security and then layer on advanced protections. The core strategy involves keeping the default EOP anti-malware policy as a foundational safety net while creating a higher-priority, custom policy for sensitive users, such as executives and finance teams. This ensures critical assets have the most aggressive, up-to-date protection without disrupting the entire organisation. We’ll also cover essential features like the Common Attachment Filter, Safe Attachments, Safe Links, and the Zero-hour Auto Purge (ZAP) engine to defend against a wide array of evolving threats, from zero-day malware to sophisticated phishing attacks.
1. Prerequisites & Licensing Checks
Before you begin, it’s crucial to understand your licensing model.
Exchange Online Protection (EOP): This is the baseline email security included with all Microsoft 365 subscriptions (e.g., Business Basic, Standard, E3). It provides fundamental anti-malware and anti-spam protection.
Microsoft Defender for Office 365 (MDO): This is an add-on or an included feature in higher-tier plans (e.g., Microsoft 365 Business Premium, E5). MDO Plan 1 adds Safe Attachments and Safe Links, while MDO Plan 2 adds advanced hunting, investigation, and automation features (e.g., Threat Explorer, Automated Investigation and Response). This guide assumes you may have an MDO licence and will detail the optional add-ons.
2. Policy Inventory & Strategic Approach
Best Practice: Do not modify the Default anti-malware policy. This ensures a consistent baseline of protection across all users who aren’t covered by a custom policy. Instead, create new, more restrictive policies for targeted, high-risk groups. Policies are processed by priority (0 being the highest), so a new custom policy with priority 0 will apply to its users, and the default policy will catch everyone else.
GUI Method: Inventory Existing Policies
Navigate to the Microsoft Defender portal at https://security.microsoft.com.
Go to Email & collaboration → Policies & rules → Threat policies.
Under the Policies section, click on Anti-malware. You will see the default policy and any custom ones you have created.
PowerShell Method: Inventory Existing Policies
First, connect to Exchange Online.
PowerShell
# Connect to Exchange Online PowerShell
Connect-ExchangeOnline -UserPrincipalName <your-admin-email> -ShowProgress:$true
Then, view the current policies.
PowerShell
# Get all malware filter policies and their associated rules
Get-MalwareFilterPolicy
Get-MalwareFilterRule
3. Recommended Anti-malware Settings
This section details the recommended settings for your new custom anti-malware policy.
GUI Method: Creating a New Policy
In the Microsoft Defender portal, go to the Anti-malware page from the previous step.
Click Create a policy.
Give the policy a descriptive Name (e.g., High-Risk Users - Anti-malware Policy) and a Description. Click Next.
On the Users and domains page, choose the users, groups, or domains you want to protect. For our example, select Groups and search for ExecutiveTeam. Click Next.
On the Protection settings page, configure the following:
Protection settings
Enable zero-hour auto purge for malware: This is a service-side feature that, when enabled, automatically removes previously delivered malicious messages from user mailboxes. It’s a key part of EOP and is highly recommended.
Quarantine policy: Use the default AdminOnlyAccessPolicy. The rationale is simple: end-users should not be able to release malware. This prevents them from accidentally or maliciously releasing a dangerous file.
Common attachments filter
Check Enable common attachments filter. This is a powerful, extension-based block list that is a fantastic first line of defence. The list of file types has been expanded by Microsoft, but you should periodically review it.
Click Customize file types and ensure a robust list of high-risk file types is selected. The list should include: exe, dll, js, jse, vbs, vbe, ps1, com, cmd, bat, jar, scr, reg, lnk, msi, msix, iso, img, 7z, zipx. You can also add other file types that are not needed in your environment, such as wsf, wsh, url.
Notifications
Admin notifications: Check Notify an admin about undelivered messages from internal senders and Notify an admin about undelivered messages from external senders. Use your security mailbox for this (e.g., security@contoso.com).
Sender notifications:Do not enable Notify internal sender or Notify external sender. Notifying external senders can validate their address for future spam, and an internal sender’s mailbox might be compromised, which could alert the attacker.
PowerShell Method: Creating and Configuring the Policy
This script is idempotent (you can run it multiple times without errors) and will create or update the policies as needed.
PowerShell
# --- PowerShell Script to Configure Exchange Online Anti-malware Policies ---
# Define variables for your tenant
$tenantDomain = "contoso.com"
$highRiskGroupName = "ExecutiveTeam"
$adminNotificationMailbox = "security@contoso.com"
$policyName = "High-Risk Users - Anti-malware Policy"
$ruleName = "High-Risk Users - Anti-malware Rule"
# Define the common attachment filter file types
$fileTypes = @(
'ade','adp','ani','app','bas','bat','chm','cmd','com','cpl',
'crt','csh','dll','exe','fxp','hlp','hta','inf','ins','isp',
'jar','js','jse','ksh','lnk','mda','mdb','mde','mdt','mdw',
'mdz','msc','msi','msix','msp','mst','pcd','pif','prg','ps1',
'reg','scr','sct','shb','shs','url','vb','vbe','vbs','wsc',
'wsf','wsh','xnk','iso','img','7z','zipx','docm','xlsm'
)
# Connect to Exchange Online
Connect-ExchangeOnline -UserPrincipalName $adminNotificationMailbox -ShowProgress:$true
# Check if the policy exists
$policy = Get-MalwareFilterPolicy -Identity $policyName -ErrorAction SilentlyContinue
if ($null -ne $policy) {
Write-Host "Policy '$policyName' already exists. Updating settings..." -ForegroundColor Yellow
Set-MalwareFilterPolicy -Identity $policyName `
-Action DeleteMessage `
-EnableFileFilter:$true `
-FileTypes $fileTypes `
-EnableInternalSenderAdminNotifications:$true `
-EnableExternalSenderAdminNotifications:$true `
-AdminDisplayName "Custom policy for high-risk users."
} else {
Write-Host "Policy '$policyName' not found. Creating a new one..." -ForegroundColor Green
New-MalwareFilterPolicy -Name $policyName `
-Action DeleteMessage `
-EnableFileFilter:$true `
-FileTypes $fileTypes `
-EnableInternalSenderAdminNotifications:$true `
-EnableExternalSenderAdminNotifications:$true `
-AdminDisplayName "Custom policy for high-risk users."
}
# Check if the rule exists
$rule = Get-MalwareFilterRule -Identity $ruleName -ErrorAction SilentlyContinue
if ($null -ne $rule) {
Write-Host "Rule '$ruleName' already exists. Updating settings..." -ForegroundColor Yellow
Set-MalwareFilterRule -Identity $ruleName `
-MalwareFilterPolicy $policyName `
-Comments "Applies to high-risk group." `
-SentToMemberOf $highRiskGroupName `
-Priority 0
} else {
Write-Host "Rule '$ruleName' not found. Creating a new one..." -ForegroundColor Green
New-MalwareFilterRule -Name $ruleName `
-MalwareFilterPolicy $policyName `
-Comments "Applies to high-risk group." `
-SentToMemberOf $highRiskGroupName `
-Priority 0
}
Write-Host "Configuration complete. Run 'Get-MalwareFilterPolicy' and 'Get-MalwareFilterRule' to verify." -ForegroundColor Green
4. Defender for Office 365 Add-ons (If Licensed)
These advanced policies provide an additional layer of protection.
Safe Attachments: This sandboxing technology “detonates” email attachments in a virtual environment to detect zero-day malware.
Block: The most secure option. Messages with attachments are held while being scanned. If a threat is found, the message is blocked and quarantined. This can introduce a short delay (minutes) for emails with attachments.
Dynamic Delivery: A balance between security and user experience. The email body is delivered immediately with a placeholder for the attachment. The attachment is delivered once the scan is complete. Use this for users who can tolerate a minor delay on the attachment itself but need the email content right away. For a high-risk user group, Block is often the recommended setting.
Safe Links: This feature scans URLs at the time of the click, not just upon arrival. If a URL is later determined to be malicious, it will be blocked even if it was safe when the email was first received.
Zero-hour Auto Purge (ZAP): ZAP for malware is included in EOP and is enabled by default. MDO adds ZAP for high-confidence phishing and spam. This is a powerful, service-side feature that removes messages that have already been delivered to a user’s inbox if new threat intelligence indicates they are malicious. There is no per-policy PowerShell switch for this; its behaviour is managed by the service and the policy’s action on detection.
5. Quarantine Policies
Quarantine policies control what users can do with messages held in quarantine.
The default quarantine policy for malware (AdminOnlyAccessPolicy) prevents end-users from releasing messages. This is the recommended setting. You can create a new policy and enable notifications or release requests for other threat types (e.g., spam), but for malware, keep it locked down.
You can set up quarantine notifications (digests) for users, which provide a summary of messages in their quarantine.
6. Testing & Validation
Once your policies are configured, you must validate them.
The EICAR Test
Use a safe, legal test file to validate your policies. The EICAR (European Institute for Computer Antivirus Research) test file is a non-malicious file that all major anti-malware programs will detect.
To test the Common Attachment Filter, create a plain text file, rename it to eicar.zip, and place the EICAR string X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* inside it.
To test Safe Attachments, send a test email with the EICAR file attached (as a .zip or other container) to a user in your test group.
Verifying with Message Trace
In the Microsoft Defender portal, go to Email & collaboration → Exchange message trace.
Search for the test message.
Click on the message to view details. The Event field should show a Fail status with the reason Malware.
Header Analysis: You can also check the message headers. Look for the X-Forefront-Antispam-Report header and the SCL (Spam Confidence Level) and PCL (Phishing Confidence Level) values. A message blocked by an anti-malware policy will have a CAT (Category) entry indicating malware.
7. Ongoing Monitoring & Tuning
Threat Explorer (MDO P2) / Reports (EOP): Regularly review the Threat Explorer (or Reports for EOP) in the Microsoft Defender portal to see what threats are being blocked. This helps you identify trends, attack vectors, and potential false positives.
Configuration Analyzer: Located under Email & collaboration → Policies & rules → Threat policies → Configuration analyzer, this tool compares your custom policies to Microsoft’s recommended Standard and Strict preset security policies. Use it to find and fix settings that are less secure than the recommended baselines.
ORCA Module: The Office 365 Recommended Configuration Analyzer (ORCA) is a community-developed PowerShell module that provides a comprehensive report of your M365 security posture. While not an official Microsoft tool, it’s an excellent resource for a deeper dive.
False Positive/Negative Submissions: If a legitimate message is blocked (false positive) or a malicious message gets through (false negative), you must submit it to Microsoft for analysis to improve their detection engines. The submission workflow is found under Actions & submissions → Submissions in the Microsoft Defender portal.
8. Change Control & Rollback
Documentation: Always document any changes made to a policy, including the date, reason, and the specific settings changed.
Phased Rollout: When creating a new policy, first apply it to a small test group before rolling it out to production users.
Rollback: If you encounter issues, you can disable the custom policy in the GUI by toggling its status to Off or with PowerShell using Set-MalwareFilterRule -Identity "Rule Name" -State Disabled. You can also decrease its priority to ensure it no longer applies.
9. Final Checklist
Use this checklist to ensure all best practices have been implemented.
[ ] Prerequisites: Confirm M365 Business Premium or Defender for Office 365 licensing for advanced features.
[ ] Policy Strategy: Leave the default anti-malware policy untouched as a safety net.
[ ] New Policy: Create a new custom anti-malware policy for high-risk users/groups (e.g., ExecutiveTeam).
[ ] Action: Set the action for malware detection to Quarantine the message.
[ ] Common Attachment Filter: Enable and verify a comprehensive list of high-risk file extensions.
[ ] Admin Notifications: Configure admin notifications for malware detections.
[ ] Sender Notifications: Disable notifications for both internal and external senders.
[ ] Safe Attachments (if licensed): Configure a new policy and set the action to Block for high-risk users.
[ ] Safe Links (if licensed): Configure a new policy to scan URLs in emails at the time of click.
[ ] Quarantine Policies: Confirm the quarantine policy for malware is set to AdminOnlyAccessPolicy to prevent user releases.
[ ] Testing: Send a test email with a containerised EICAR file to a user in the new policy’s scope.
[ ] Validation: Use Message Trace to confirm the message was blocked, and review the headers for malware detection results.
[ ] Monitoring: Schedule a regular review of threat reports and submissions.
[ ] Tuning: Address false positives/negatives by submitting them to Microsoft.
[ ] Change Control: Document all changes and have a rollback plan in place.
[ ] Configuration Analyser: Run the Configuration Analyser and compare your policies to Microsoft’s recommended settings.
For more information, refer to these authoritative resources:
You expand the options under Email & collaboration as shown above and select Policies and rules. On the right you select Threat policies as shown above.
If you scroll down the page a bit you’ll find Quarantine policies as shown above, which you should select.
The notification period is controlled in the GUI via the Global settings menu option as shown above.
From the dialog that appears from the right, scroll down to the bottom of the page and you will find an option Send end-user spam notifications as shown above. Presently, the minimum you can configure this to is Within 4 hours.
After you make any changes here select the Save button at the very bottom to update these settings for all the quarantine policies you have.
An important part of good security in Microsoft 365 is to ensure you are capturing all the logs available. Exchange Online has a number of actions that can be audited and some may not be enabled in your environment. The list available and what is enabled by default can be found here:
This video is the technical session from my May 2023 Need to Know webinar that focuses on helping people understand Microsoft 365. The aim is to help viewers get an overview of how Exchange Online Protection secures their environment and where they can go to made additional adjustments if required.