Secure Application Model
For this post you'll want a secure application model Azure AD application configured. Use these instructions from Gavin Stone's Gavsto.com blog.
CIPP Inspired
The code in this script was inspired by functionality in the CyberDrain Improved Partner Portal (CIPP). Need to manage M365 tenants - CIPP is a fantastic open-source solution for M365 management for single tenants and Microsoft CSP Partners alike.
Background information
In my quest to stop having to use Windows Sandbox or VMs to access Exchange Online via PowerShell (we have WinRM basic auth disabled) I've been looking at solutions using the Exchange Online REST API to run commands against our customer tenants. The result of my efforts is a pretty complex script that introduces a method to run any PowerShell script in the context of a set of customer tenants.
You need to have configured the Secure Application Model according to the instructions linked above, you will need your SAM tokens etc accessible for this guide.
The script
loading...
Building Parameters
Personally I find passing parameters as a HashTable (splatting) easier to read and cleaner see what's happening so that's what I'm doing to explain the parameters for the script:
$EORESTParameters = @{
PartnerTenantId = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTTenantId' -AsPlainText) # Your partner tenant id.
ApplicationId = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTClientId' -AsPlainText) # Your SAM application / client id.
ApplicationSecret = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTClientSecret' -AsPlainText) # Your SAM application / client secret.
GraphRefreshToken = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTRefreshToken' -AsPlainText) # The Graph refresh t oken provided by the SAM app creation script.
ExchangeRefreshToken = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTExchangeRefreshToken' -AsPlainText) # The Exchange refresh token provided by the SAM app creation script.
UPN = '[email protected]' # The UPN of the user used to authorise your SAM tokens.
IncludeTenants = @('test1.onmicrosoft.com') # Run only on the tenants listed - using the tenant's `DefaultDomainName`. This input is NOT validated.
ExcludeTenants = @('test1.onmicrosoft.com') # Run on the all tenant except those listed - using the tenant's `DefaultDomainName`.
}
ScriptBlock
The -ScriptBlock
parameter passed to the script runs the commands given against each selected tenant's Exchange Online. You should use the Invoke-EORequest
cmdlet in the script block to send commands to ExchangeOnline. For example to get mailboxes:
-ScriptBlock { Invoke-EORequest -Commandlet 'Get-Mailbox' }
More examples are given below.
Result structure
Before returning values we add a property to object containing the customer's tenant id. This property is called EORCustomerId
and is the tenant GUID. You can use this to group results by the tenant they relate to.
| Group-Object -Property 'EORCustomerId'
Example uses
These examples assume use of the parameter splat above
Get Mailbox Plans
.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock { Invoke-EORequest -Commandlet 'Get-MailboxPlan' }
Get Shared, Room, Equipment, Group and Team mailboxes
.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock { Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ Filter = "((RecipientTypeDetails -eq 'SharedMailbox') -or (RecipientTypeDetails -eq 'RoomMailbox') -or (RecipientTypeDetails -eq 'EquipmentMailbox') -or (RecipientTypeDetails -eq 'GroupMailbox') -or (RecipientTypeDetails -eq 'TeamMailbox'))" } }
Get last login time.
.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
$Mailboxes = Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ ResultSize = 'Unlimited' }
ForEach ($Mailbox in $Mailboxes) { Invoke-EORequest -Commandlet 'Get-MailboxStatistics' -Parameters @{ Identity = $Mailbox.UserPrincipalName } | Select-Object -Property DisplayName, LastLoggedOnUserAccount, LastLogonTime }
}
Get mailboxes not logged into in the last 90 days.
.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
$Mailboxes = Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ ResultSize = 'Unlimited' }
ForEach ($Mailbox in $Mailboxes) { Invoke-EORequest -Commandlet 'Get-MailboxStatistics' -Parameters @{ Identity = $Mailbox.UserPrincipalName } | Where-Object { $_.LastLogonTime -lt (Get-Date).AddDays(-90) } | Select-Object -Property DisplayName, LastLoggedOnUserAccount, LastLogonTime }
}
Get inactive mailboxes.
.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ InactiveMailboxOnly = $True }
}
Set hidden from address list for all mailboxes.
.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
$Mailboxes = Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ ResultSize = 'Unlimited' }
ForEach ($Mailbox in $Mailboxes) { Invoke-EORequest -Commandlet 'Set-Mailbox' -Parameters @{ Identity = $Mailbox.UserPrincipalName; HiddenFromAddressListEnabled = $True } }
}