Added Files
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
#requires -Version 3.0
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Remove the access to Outlook for all Mailboxes in an Microsoft Office 365 Tenant
|
||||
|
||||
.DESCRIPTION
|
||||
Remove the access to Outlook for all Mailboxes in an Microsoft Office 365 Tenant
|
||||
It will remove access to OWA (Outlook Web Application), Exchange Active Sync (EAS), Outlook App and Outlook (part of the Office Suite).
|
||||
|
||||
.PARAMETER CredentialUser
|
||||
The UPN of the admin user
|
||||
|
||||
.PARAMETER CredentialFile
|
||||
File where the credential will be stored
|
||||
|
||||
Make sure that this is secured!
|
||||
|
||||
.PARAMETER ProxyAccessType
|
||||
Determines which mechanism is used to resolve the host name. The acceptable values for this parameter are:
|
||||
- IEConfig
|
||||
- WinHttpConfig
|
||||
- AutoDetect
|
||||
- NoProxyServer
|
||||
- None
|
||||
|
||||
The default value is None.
|
||||
|
||||
For information about the values of this parameter, see the description of the System.Management.Automation.Remoting.ProxyAccessTypehttp://go.microsoft.com/fwlink/?LinkId=144756 (http://go.microsoft.com/fwlink/?LinkId=144756) enumeration in the Microsoft Developer Network (MSDN) library.
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> .\Approve-CASMailboxSettings.ps1
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> .\Approve-CASMailboxSettings.ps1 -verbose
|
||||
|
||||
.NOTES
|
||||
I created the script to run automated (via Windows scheduler) and it will save the password in a plain text file.
|
||||
You might want to use another option to gain access to Exchange Online
|
||||
|
||||
Please check all values before using the script!
|
||||
|
||||
TODO: Run the script once before using it as scheduled task! This will create and save the credentials.
|
||||
#>
|
||||
[CmdletBinding(ConfirmImpact = 'None')]
|
||||
param
|
||||
(
|
||||
[Parameter(ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[Alias('Username', 'AdminUser')]
|
||||
[string]
|
||||
$CredentialUser = 'youradmin.user@contoso.com',
|
||||
[Parameter(ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[Alias('CredFile', 'SecretFile')]
|
||||
[string]
|
||||
$CredentialFile = ($env:LOCALAPPDATA + '\exocreds.txt'),
|
||||
[Parameter(ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName)]
|
||||
[ValidateSet('IEConfig', 'WinHttpConfig', 'AutoDetect', 'NoProxyServer', 'None', IgnoreCase = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[Alias('PSSessionOptionProxy')]
|
||||
[string]
|
||||
$ProxyAccessType = 'None'
|
||||
)
|
||||
|
||||
begin
|
||||
{
|
||||
# Admin User (Global Admin or min. Exchange Online Admin role)
|
||||
if (-not ($CredentialUser))
|
||||
{
|
||||
$CredentialUser = 'youradmin.user@contoso.com'
|
||||
}
|
||||
|
||||
# Where to store the password?
|
||||
if (-not ($CredentialFile))
|
||||
{
|
||||
$CredentialFile = ($env:LOCALAPPDATA + '\exocreds.txt')
|
||||
}
|
||||
}
|
||||
|
||||
process
|
||||
{
|
||||
#region CredentialHandler
|
||||
try
|
||||
{
|
||||
if (-not (Test-Path -Path $CredentialFile -ErrorAction SilentlyContinue))
|
||||
{
|
||||
# Do we have any credentials in memory (variable)
|
||||
if (-not ($ExoCreds))
|
||||
{
|
||||
#
|
||||
$paramGetCredential = @{
|
||||
Message = 'Bitte mit einem Exchange Online Admin Benutzer anmelden'
|
||||
UserName = $CredentialUser
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
$ExoCreds = (Get-Credential @paramGetCredential)
|
||||
}
|
||||
|
||||
# Splat the parameters
|
||||
$paramOutFile = @{
|
||||
FilePath = $CredentialFile
|
||||
Force = $true
|
||||
Encoding = 'utf8'
|
||||
ErrorAction = 'Stop'
|
||||
Confirm = $false
|
||||
}
|
||||
|
||||
# Save the file
|
||||
$null = ($ExoCreds.Password | ConvertFrom-SecureString | Out-File @paramOutFile)
|
||||
}
|
||||
else
|
||||
{
|
||||
# Splat the parameters
|
||||
$paramGetContent = @{
|
||||
Path = $CredentialFile
|
||||
Force = $true
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
$paramConvertToSecureString = @{
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
|
||||
# Read and convert the file wit the password
|
||||
$PwdSecureString = (Get-Content @paramGetContent | ConvertTo-SecureString @paramConvertToSecureString)
|
||||
|
||||
# Splat the parameters
|
||||
$paramNewObject = @{
|
||||
TypeName = 'System.Management.Automation.PSCredential'
|
||||
ArgumentList = $CredentialUser, $PwdSecureString
|
||||
}
|
||||
|
||||
# Create the credential object
|
||||
$ExoCreds = (New-Object @paramNewObject)
|
||||
|
||||
# Remove the password string from memory
|
||||
$PwdSecureString = $null
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||
|
||||
# Only here to catch a global ErrorAction overwrite
|
||||
break
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
#endregion CredentialHandler
|
||||
|
||||
#region ConnectExchangeOnline
|
||||
try
|
||||
{
|
||||
# Proxy Handling
|
||||
<#
|
||||
-ProxyAccessType <ProxyAccessType>
|
||||
Determines which mechanism is used to resolve the host name. The acceptable values for this parameter are:
|
||||
- IEConfig
|
||||
- WinHttpConfig
|
||||
- AutoDetect
|
||||
- NoProxyServer
|
||||
- None
|
||||
|
||||
The default value is None.
|
||||
For information about the values of this parameter, see the description of the System.Management.Automation.Remoting.ProxyAccessTypehttp://go.microsoft.com/fwlink/?LinkId=144756 (http://go.microsoft.com/fwlink/?LinkId=144756) enumeration in the Microsoft Developer Network (MSDN) library.
|
||||
|
||||
Source:
|
||||
Get-Help New-PSSessionOption -Detailed
|
||||
#>
|
||||
if ($ProxyAccessType)
|
||||
{
|
||||
# Splat the parameters
|
||||
$paramNewPSSessionOption = @{
|
||||
ProxyAccessType = $ProxyAccessType
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
|
||||
# Do we need a proxy to access Office 365?
|
||||
$ProxyOptions = (New-PSSessionOption @paramNewPSSessionOption)
|
||||
}
|
||||
|
||||
# Cleanup
|
||||
$ExoSession = $null
|
||||
|
||||
# Splat the parameters
|
||||
$paramGetPSSession = @{
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$paramRemovePSSession = @{
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
Confirm = $false
|
||||
}
|
||||
|
||||
# Remove all existing Exchange Online Sessions
|
||||
$null = (Get-PSSession @paramGetPSSession | Where-Object {
|
||||
$_.ComputerName -eq 'outlook.office365.com'
|
||||
} | Remove-PSSession @paramRemovePSSession)
|
||||
|
||||
# Splat the parameters
|
||||
$paramNewPSSession = @{
|
||||
ConfigurationName = 'Microsoft.Exchange'
|
||||
ConnectionUri = 'https://outlook.office365.com/powershell-liveid/'
|
||||
Credential = $ExoCreds
|
||||
Authentication = 'Basic'
|
||||
AllowRedirection = $true
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
|
||||
# Proxy settings needed?
|
||||
if ($ProxyOptions)
|
||||
{
|
||||
$paramNewPSSession.SessionOption = $ProxyOptions
|
||||
}
|
||||
|
||||
# Create the session
|
||||
$ExoSession = (New-PSSession @paramNewPSSession)
|
||||
|
||||
# Splat the parameters
|
||||
$paramImportPSSession = @{
|
||||
Session = $ExoSession
|
||||
DisableNameChecking = $true
|
||||
AllowClobber = $true
|
||||
ErrorAction = 'Stop'
|
||||
WarningAction = 'Continue'
|
||||
}
|
||||
|
||||
# Create the Session
|
||||
$null = (Import-PSSession @paramImportPSSession)
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||
|
||||
# Only here to catch a global ErrorAction overwrite
|
||||
break
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
#endregion ConnectExchangeOnline
|
||||
|
||||
#region SetCASMailbox
|
||||
try
|
||||
{
|
||||
# Check if the session is alive
|
||||
if (-not (Get-Command -Name Get-CASMailbox))
|
||||
{
|
||||
# Splat the parameters
|
||||
$paramWriteError = @{
|
||||
Exception = 'Es scheint ein Problem mit der Exchange Online Verbindung zu geben!'
|
||||
Message = 'Die erforderlichen Exchnage Online Befehle wurden nicht gefunden!'
|
||||
Category = 'ResourceUnavailable'
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
Write-Error @paramWriteError
|
||||
|
||||
# Make sure we are done!
|
||||
throw
|
||||
}
|
||||
|
||||
# Splat the parameters
|
||||
$paramGetCASMailbox = @{
|
||||
ResultSize = 'unlimited'
|
||||
Filter = {
|
||||
(name -notlike 'DiscoverysearchMailbox*')
|
||||
}
|
||||
ErrorAction = 'Stop'
|
||||
WarningAction = 'Continue'
|
||||
}
|
||||
$paramSetCASMailbox = @{
|
||||
ActiveSyncEnabled = $false
|
||||
ImapEnabled = $false
|
||||
MAPIEnabled = $false
|
||||
OutlookMobileEnabled = $false
|
||||
OWAEnabled = $false
|
||||
OWAforDevicesEnabled = $false
|
||||
PopEnabled = $false
|
||||
SmtpClientAuthenticationDisabled = $false
|
||||
UniversalOutlookEnabled = $false
|
||||
Confirm = $false
|
||||
ErrorAction = 'Continue'
|
||||
WarningAction = 'Continue'
|
||||
}
|
||||
|
||||
# Remove the outlook access from all mailboxes
|
||||
$null = (Get-CASMailbox @paramGetCASMailbox | Set-CASMailbox @paramSetCASMailbox)
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||
|
||||
# Only here to catch a global ErrorAction overwrite
|
||||
break
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
#endregion SetCASMailbox
|
||||
}
|
||||
|
||||
end
|
||||
{
|
||||
# Cleanup
|
||||
$ExoSession = $null
|
||||
|
||||
# Splat the parameters
|
||||
$paramGetPSSession = @{
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$paramRemovePSSession = @{
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
Confirm = $false
|
||||
}
|
||||
|
||||
# Remove all existing Exchange Online Sessions
|
||||
$null = (Get-PSSession @paramGetPSSession | Where-Object {
|
||||
$_.ComputerName -eq 'outlook.office365.com'
|
||||
} | Remove-PSSession @paramRemovePSSession)
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
@@ -0,0 +1,617 @@
|
||||
function Export-DistributionGroup2Cloud
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Function to convert/migrate on-premises Exchange distribution group to a Cloud (Exchange Online) distribution group
|
||||
|
||||
.DESCRIPTION
|
||||
Copies attributes of a synchronized group to a placeholder group and CSV file.
|
||||
After initial export of group attributes, the on-premises group can have the attribute "AdminDescription" set to "Group_NoSync" which will stop it from be synchronized.
|
||||
The "-Finalize" switch can then be used to write the addresses to the new group and convert the name. The final group will be a cloud group with the same attributes as the previous but with the additional ability of being able to be "self-managed".
|
||||
Once the contents of the new group are validated, the on-premises group can be deleted.
|
||||
|
||||
.PARAMETER Group
|
||||
Name of group to recreate.
|
||||
|
||||
.PARAMETER CreatePlaceHolder
|
||||
Create placeholder DistributionGroup wit ha given name.
|
||||
|
||||
.PARAMETER Finalize
|
||||
Convert a given placeholder group to final DistributionGroup.
|
||||
|
||||
.PARAMETER ExportDirectory
|
||||
Export Directory for internal CSV handling.
|
||||
|
||||
.EXAMPLE
|
||||
PS> Export-DistributionGroup2Cloud -Group "DL-Marketing" -CreatePlaceHolder
|
||||
|
||||
Create the Placeholder for the distribution group "DL-Marketing"
|
||||
|
||||
.EXAMPLE
|
||||
PS> Export-DistributionGroup2Cloud -Group "DL-Marketing" -Finalize
|
||||
|
||||
Transform the Placeholder for the distribution group "DL-Marketing" to the real distribution group in the cloud
|
||||
|
||||
.NOTES
|
||||
This function is based on the Recreate-DistributionGroup.ps1 script of Joe Palarchio
|
||||
|
||||
License: BSD 3-Clause
|
||||
|
||||
.LINK
|
||||
https://gallery.technet.microsoft.com/PowerShell-Script-to-Move-5c3cd668
|
||||
|
||||
.LINK
|
||||
http://blogs.perficient.com/microsoft/?p=32092
|
||||
#>
|
||||
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory,
|
||||
HelpMessage = 'Name of group to recreate.')]
|
||||
[string]
|
||||
$Group,
|
||||
[switch]
|
||||
$CreatePlaceHolder,
|
||||
[switch]
|
||||
$Finalize,
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$ExportDirectory = 'C:\scripts\PowerShell\exports\ExportedAddresses\'
|
||||
)
|
||||
|
||||
begin
|
||||
{
|
||||
# Defaults
|
||||
$SCN = 'SilentlyContinue'
|
||||
$CNT = 'Continue'
|
||||
$STP = 'Stop'
|
||||
}
|
||||
|
||||
process
|
||||
{
|
||||
If ($CreatePlaceHolder.IsPresent)
|
||||
{
|
||||
# Create the Placeholder
|
||||
If (((Get-DistributionGroup -Identity $Group -ErrorAction $SCN).IsValid) -eq $True)
|
||||
{
|
||||
# Splat to make it more human readable
|
||||
$paramGetDistributionGroup = @{
|
||||
Identity = $Group
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$OldDG = (Get-DistributionGroup @paramGetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object -Process {
|
||||
$Group = $Group.Replace($_, '_')
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
$OldName = [string]$OldDG.Name
|
||||
$OldDisplayName = [string]$OldDG.DisplayName
|
||||
$OldPrimarySmtpAddress = [string]$OldDG.PrimarySmtpAddress
|
||||
$OldAlias = [string]$OldDG.Alias
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramGetDistributionGroupMember = @{
|
||||
Identity = $OldDG.Name
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$OldMembers = ((Get-DistributionGroupMember @paramGetDistributionGroupMember).Name)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
If (!(Test-Path -Path $ExportDirectory -ErrorAction $SCN -WarningAction $CNT))
|
||||
{
|
||||
Write-Verbose -Message (' Creating Directory: {0}' -f $ExportDirectory)
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramNewItem = @{
|
||||
ItemType = 'directory'
|
||||
Path = $ExportDirectory
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (New-Item @paramNewItem)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# Define variables - mostly for future use
|
||||
$ExportDirectoryGroupCsv = $ExportDirectory + '\' + $Group + '.csv'
|
||||
|
||||
try
|
||||
{
|
||||
# TODO: Refactor in future version
|
||||
'EmailAddress' > $ExportDirectoryGroupCsv
|
||||
$OldDG.EmailAddresses >> $ExportDirectoryGroupCsv
|
||||
'x500:' + $OldDG.LegacyExchangeDN >> $ExportDirectoryGroupCsv
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
# Define variables - mostly for future use
|
||||
$NewDistributionGroupName = 'Cloud- ' + $OldName
|
||||
$NewDistributionGroupAlias = 'Cloud-' + $OldAlias
|
||||
$NewDistributionGroupDisplayName = 'Cloud-' + $OldDisplayName
|
||||
$NewDistributionGroupPrimarySmtpAddress = 'Cloud-' + $OldPrimarySmtpAddress
|
||||
|
||||
# TODO: Replace with Write-Verbose in future version of the function
|
||||
Write-Output -InputObject (' Creating Group: {0}' -f $NewDistributionGroupDisplayName)
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramNewDistributionGroup = @{
|
||||
Name = $NewDistributionGroupName
|
||||
Alias = $NewDistributionGroupAlias
|
||||
DisplayName = $NewDistributionGroupDisplayName
|
||||
ManagedBy = $OldDG.ManagedBy
|
||||
Members = $OldMembers
|
||||
PrimarySmtpAddress = $NewDistributionGroupPrimarySmtpAddress
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (New-DistributionGroup @paramNewDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
# Wait for 3 seconds
|
||||
$null = (Start-Sleep -Seconds 3)
|
||||
|
||||
# Define variables - mostly for future use
|
||||
$SetDistributionGroupIdentity = 'Cloud-' + $OldName
|
||||
$SetDistributionGroupDisplayName = 'Cloud-' + $OldDisplayName
|
||||
|
||||
# TODO: Replace with Write-Verbose in future version of the function
|
||||
Write-Output -InputObject (' Setting Values For: {0}' -f $SetDistributionGroupDisplayName)
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramSetDistributionGroup = @{
|
||||
Identity = $SetDistributionGroupIdentity
|
||||
AcceptMessagesOnlyFromSendersOrMembers = $OldDG.AcceptMessagesOnlyFromSendersOrMembers
|
||||
RejectMessagesFromSendersOrMembers = $OldDG.RejectMessagesFromSendersOrMembers
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (Set-DistributionGroup @paramSetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
# Define variables - mostly for future use
|
||||
$SetDistributionGroupIdentity = 'Cloud-' + $OldName
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramSetDistributionGroup = @{
|
||||
Identity = $SetDistributionGroupIdentity
|
||||
AcceptMessagesOnlyFrom = $OldDG.AcceptMessagesOnlyFrom
|
||||
AcceptMessagesOnlyFromDLMembers = $OldDG.AcceptMessagesOnlyFromDLMembers
|
||||
BypassModerationFromSendersOrMembers = $OldDG.BypassModerationFromSendersOrMembers
|
||||
BypassNestedModerationEnabled = $OldDG.BypassNestedModerationEnabled
|
||||
CustomAttribute1 = $OldDG.CustomAttribute1
|
||||
CustomAttribute2 = $OldDG.CustomAttribute2
|
||||
CustomAttribute3 = $OldDG.CustomAttribute3
|
||||
CustomAttribute4 = $OldDG.CustomAttribute4
|
||||
CustomAttribute5 = $OldDG.CustomAttribute5
|
||||
CustomAttribute6 = $OldDG.CustomAttribute6
|
||||
CustomAttribute7 = $OldDG.CustomAttribute7
|
||||
CustomAttribute8 = $OldDG.CustomAttribute8
|
||||
CustomAttribute9 = $OldDG.CustomAttribute9
|
||||
CustomAttribute10 = $OldDG.CustomAttribute10
|
||||
CustomAttribute11 = $OldDG.CustomAttribute11
|
||||
CustomAttribute12 = $OldDG.CustomAttribute12
|
||||
CustomAttribute13 = $OldDG.CustomAttribute13
|
||||
CustomAttribute14 = $OldDG.CustomAttribute14
|
||||
CustomAttribute15 = $OldDG.CustomAttribute15
|
||||
ExtensionCustomAttribute1 = $OldDG.ExtensionCustomAttribute1
|
||||
ExtensionCustomAttribute2 = $OldDG.ExtensionCustomAttribute2
|
||||
ExtensionCustomAttribute3 = $OldDG.ExtensionCustomAttribute3
|
||||
ExtensionCustomAttribute4 = $OldDG.ExtensionCustomAttribute4
|
||||
ExtensionCustomAttribute5 = $OldDG.ExtensionCustomAttribute5
|
||||
GrantSendOnBehalfTo = $OldDG.GrantSendOnBehalfTo
|
||||
HiddenFromAddressListsEnabled = $True
|
||||
MailTip = $OldDG.MailTip
|
||||
MailTipTranslations = $OldDG.MailTipTranslations
|
||||
MemberDepartRestriction = $OldDG.MemberDepartRestriction
|
||||
MemberJoinRestriction = $OldDG.MemberJoinRestriction
|
||||
ModeratedBy = $OldDG.ModeratedBy
|
||||
ModerationEnabled = $OldDG.ModerationEnabled
|
||||
RejectMessagesFrom = $OldDG.RejectMessagesFrom
|
||||
RejectMessagesFromDLMembers = $OldDG.RejectMessagesFromDLMembers
|
||||
ReportToManagerEnabled = $OldDG.ReportToManagerEnabled
|
||||
ReportToOriginatorEnabled = $OldDG.ReportToOriginatorEnabled
|
||||
RequireSenderAuthenticationEnabled = $OldDG.RequireSenderAuthenticationEnabled
|
||||
SendModerationNotifications = $OldDG.SendModerationNotifications
|
||||
SendOofMessageToOriginatorEnabled = $OldDG.SendOofMessageToOriginatorEnabled
|
||||
BypassSecurityGroupManagerCheck = $True
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (Set-DistributionGroup @paramSetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
}
|
||||
Else
|
||||
{
|
||||
Write-Error -Message ('The distribution group {0} was not found' -f $Group) -ErrorAction $CNT
|
||||
}
|
||||
}
|
||||
ElseIf ($Finalize.IsPresent)
|
||||
{
|
||||
# Do the final steps
|
||||
|
||||
# Define variables - mostly for future use
|
||||
$GetDistributionGroupIdentity = 'Cloud-' + $Group
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramGetDistributionGroup = @{
|
||||
Identity = $GetDistributionGroupIdentity
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$TempDG = (Get-DistributionGroup @paramGetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
$TempPrimarySmtpAddress = $TempDG.PrimarySmtpAddress
|
||||
|
||||
try
|
||||
{
|
||||
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object -Process {
|
||||
$Group = $Group.Replace($_, '_')
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
$OldAddressesPatch = $ExportDirectory + '\' + $Group + '.csv'
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramImportCsv = @{
|
||||
Path = $OldAddressesPatch
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$OldAddresses = @(Import-Csv @paramImportCsv)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$NewAddresses = $OldAddresses | ForEach-Object -Process {
|
||||
$_.EmailAddress.Replace('X500', 'x500')
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
$NewDGName = $TempDG.Name.Replace('Cloud-', '')
|
||||
$NewDGDisplayName = $TempDG.DisplayName.Replace('Cloud-', '')
|
||||
$NewDGAlias = $TempDG.Alias.Replace('Cloud-', '')
|
||||
|
||||
try
|
||||
{
|
||||
$NewPrimarySmtpAddress = ($NewAddresses | Where-Object -FilterScript {
|
||||
$_ -clike 'SMTP:*'
|
||||
}).Replace('SMTP:', '')
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramSetDistributionGroup = @{
|
||||
Identity = $TempDG.Name
|
||||
Name = $NewDGName
|
||||
Alias = $NewDGAlias
|
||||
DisplayName = $NewDGDisplayName
|
||||
PrimarySmtpAddress = $NewPrimarySmtpAddress
|
||||
HiddenFromAddressListsEnabled = $False
|
||||
BypassSecurityGroupManagerCheck = $True
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (Set-DistributionGroup @paramSetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
$paramSetDistributionGroup = @{
|
||||
Identity = $NewDGName
|
||||
EmailAddresses = @{
|
||||
Add = $NewAddresses
|
||||
}
|
||||
BypassSecurityGroupManagerCheck = $True
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (Set-DistributionGroup @paramSetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
|
||||
# Splat to make it more human readable
|
||||
$paramSetDistributionGroup = @{
|
||||
Identity = $NewDGName
|
||||
EmailAddresses = @{
|
||||
Remove = $TempPrimarySmtpAddress
|
||||
}
|
||||
BypassSecurityGroupManagerCheck = $True
|
||||
ErrorAction = $STP
|
||||
WarningAction = $CNT
|
||||
}
|
||||
try
|
||||
{
|
||||
$null = (Set-DistributionGroup @paramSetDistributionGroup)
|
||||
}
|
||||
catch
|
||||
{
|
||||
$line = ($_.InvocationInfo.ScriptLineNumber)
|
||||
|
||||
# Dump the Info
|
||||
Write-Warning -Message ('Error was in Line {0}' -f $line)
|
||||
|
||||
# Dump the Error catched
|
||||
Write-Error -Message $_ -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
}
|
||||
Else
|
||||
{
|
||||
Write-Error -Message " ERROR: No options selected, please use '-CreatePlaceHolder' or '-Finalize'" -ErrorAction $STP
|
||||
|
||||
# Something that should never be reached
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
{
|
||||
<#
|
||||
From the original Script Author
|
||||
|
||||
Name: Recreate-DistributionGroup.ps1
|
||||
|
||||
Version: 1.0
|
||||
|
||||
Description: Copies attributes of a synchronized group to a placeholder group and CSV file.
|
||||
After initial export of group attributes, the on-premises group can have the attribute "AdminDescription" set to "Group_NoSync" which will stop it from be synchronized.
|
||||
The "-Finalize" switch can then be used to write the addresses to the new group and convert the name. The final group will be a cloud group with the same attributes as the previous but with the additional ability of being able to be "self-managed".
|
||||
Once the contents of the new group are validated, the on-premises group can be deleted.
|
||||
|
||||
Requires: Remote PowerShell Connection to Exchange Online
|
||||
|
||||
Author: Joe Palarchio
|
||||
|
||||
Usage: Additional information on the usage of this script can found at the following blog post: http://blogs.perficient.com/microsoft/?p=32092
|
||||
|
||||
Disclaimer: This script is provided AS IS without any support. Please test in a lab environment prior to production use.
|
||||
#>
|
||||
}
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
@@ -0,0 +1,275 @@
|
||||
#requires -Version 3.0 -Modules ExchangeOnlineManagement
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a basic report of Mobile Devices
|
||||
|
||||
.DESCRIPTION
|
||||
Get a basic report of Mobile Devices connected to the Microsoft 365 Tenant
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> .\Get-MobileDeviceReporting.ps1
|
||||
|
||||
.LINK
|
||||
Connect-ExchangeOnline
|
||||
|
||||
.LINK
|
||||
Get-MobileDevice
|
||||
|
||||
.LINK
|
||||
Get-MobileDeviceStatistics
|
||||
|
||||
.NOTES
|
||||
Nothing fancy! Only a basic report as CSV
|
||||
#>
|
||||
[CmdletBinding(ConfirmImpact = 'None')]
|
||||
param ()
|
||||
|
||||
begin
|
||||
{
|
||||
# Cleanup
|
||||
$Stats = $null
|
||||
$DeviceStats = $null
|
||||
$Report = $null
|
||||
$MobileDeviceList = $null
|
||||
|
||||
# Garbage Collection
|
||||
[GC]::Collect()
|
||||
|
||||
try
|
||||
{
|
||||
$paramConnectExchangeOnline = @{
|
||||
ShowBanner = $true
|
||||
BypassMailboxAnchoring = $true
|
||||
ExchangeEnvironmentName = 'O365Default'
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$null = (Connect-ExchangeOnline @paramConnectExchangeOnline)
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
# output information. Post-process collected info, and log info (optional)
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
$paramWriteError = @{
|
||||
Message = $e.Exception.Message
|
||||
ErrorAction = 'Stop'
|
||||
Exception = $e.Exception
|
||||
TargetObject = $e.CategoryInfo.TargetName
|
||||
}
|
||||
Write-Error @paramWriteError
|
||||
|
||||
# Only here to catch a global ErrorAction overwrite
|
||||
exit 1
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
|
||||
# Create new object
|
||||
$Report = @()
|
||||
}
|
||||
|
||||
|
||||
process
|
||||
{
|
||||
# Get all mobile devices in the Microsoft 365 tenant
|
||||
<#
|
||||
Option: -ActiveSync
|
||||
Description: The ActiveSync switch filters the results by Exchange ActiveSync devices.
|
||||
Source: https://docs.microsoft.com/en-us/powershell/module/exchange/get-mobiledevice?view=exchange-ps
|
||||
#>
|
||||
try
|
||||
{
|
||||
$paramGetMobileDevice = @{
|
||||
ResultSize = 'unlimited'
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
$MobileDeviceList = (Get-MobileDevice @paramGetMobileDevice)
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
# output information. Post-process collected info, and log info (optional)
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
$paramWriteError = @{
|
||||
Message = $e.Exception.Message
|
||||
ErrorAction = 'Stop'
|
||||
Exception = $e.Exception
|
||||
TargetObject = $e.CategoryInfo.TargetName
|
||||
}
|
||||
Write-Error @paramWriteError
|
||||
|
||||
# Only here to catch a global ErrorAction overwrite
|
||||
exit 1
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
|
||||
# Loop over the List
|
||||
foreach ($Device in $MobileDeviceList)
|
||||
{
|
||||
$Stats = $null
|
||||
$DeviceStats = $null
|
||||
|
||||
try
|
||||
{
|
||||
$paramGetMobileDeviceStatistics = @{
|
||||
Identity = $Device.Guid.toString()
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
$Stats = (Get-MobileDeviceStatistics @paramGetMobileDeviceStatistics)
|
||||
|
||||
$DeviceStats = [PSCustomObject]@{
|
||||
Identity = $Device.Identity -replace '\\.+'
|
||||
DeviceType = $Device.DeviceType
|
||||
DeviceOS = $Device.DeviceOS
|
||||
DeviceUserAgent = $Stats.DeviceUserAgent
|
||||
DeviceModel = $Stats.DeviceModel
|
||||
ClientType = $Stats.ClientType
|
||||
FirstSyncTime = $Stats.FirstSyncTime
|
||||
LastSuccessSync = $Stats.LastSuccessSync
|
||||
LastSyncAttemptTime = $Stats.LastSyncAttemptTime
|
||||
LastPolicyUpdateTime = $Stats.LastPolicyUpdateTime
|
||||
LastPingHeartbeat = $Stats.LastPingHeartbeat
|
||||
}
|
||||
|
||||
$Report += $DeviceStats
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
# output information. Post-process collected info, and log info (optional)
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
Write-Warning -Message $info.Exception
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
}
|
||||
|
||||
# Create a Timestamp (check if this is OK for you)
|
||||
$TimeStamp = (Get-Date -Format yyyyMMdd_HHmmss)
|
||||
|
||||
# Export the CSV Report
|
||||
try
|
||||
{
|
||||
$paramExportCsv = @{
|
||||
Path = ('.\MobileDeviceReport' + $TimeStamp + '.csv')
|
||||
Force = $true
|
||||
Encoding = 'UTF8'
|
||||
Delimiter = ';'
|
||||
NoTypeInformation = $true
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
($Report | Export-Csv @paramExportCsv)
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
# output information. Post-process collected info, and log info (optional)
|
||||
$info | Out-String | Write-Verbose
|
||||
|
||||
$paramWriteError = @{
|
||||
Message = $e.Exception.Message
|
||||
ErrorAction = 'Stop'
|
||||
Exception = $e.Exception
|
||||
TargetObject = $e.CategoryInfo.TargetName
|
||||
}
|
||||
Write-Error @paramWriteError
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
finally
|
||||
{
|
||||
# Disconnect from Exchange Online
|
||||
$paramDisconnectExchangeOnline = @{
|
||||
Confirm = $false
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$null = (Disconnect-ExchangeOnline @paramDisconnectExchangeOnline)
|
||||
|
||||
# Cleanup
|
||||
$Stats = $null
|
||||
$DeviceStats = $null
|
||||
$Report = $null
|
||||
$MobileDeviceList = $null
|
||||
|
||||
# Garbage Collection
|
||||
[GC]::Collect()
|
||||
}
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
@@ -0,0 +1,393 @@
|
||||
function Get-enMailboxFolderPermissionReport
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a detailed mailbox folder permission report
|
||||
|
||||
.DESCRIPTION
|
||||
Get a detailed mailbox folder permission report and exports this report to a given CSV file.
|
||||
You can select only user-mailboxes, only shared-mailboxes or both for the reporting.
|
||||
|
||||
.PARAMETER Identity
|
||||
The Identity parameter specifies the mailbox that you want to view.
|
||||
You can use any value that uniquely identifies the mailbox.
|
||||
|
||||
Default is * (all)
|
||||
|
||||
.PARAMETER MailboxType
|
||||
The type is the value for the regular RecipientTypeDetails.
|
||||
|
||||
The acceptable values for this parameter are:
|
||||
- UserMailbox
|
||||
- User
|
||||
- SharedMailbox
|
||||
- Shared
|
||||
- All
|
||||
|
||||
The Default is ALL
|
||||
|
||||
.PARAMETER ResultSize
|
||||
The ResultSize parameter specifies the maximum number of results to return.
|
||||
If you want to return all requests that match the query, use unlimited for the value of this parameter.
|
||||
|
||||
The default value is unlimited.
|
||||
|
||||
.PARAMETER Path
|
||||
Specifies the path to the CSV output file.
|
||||
|
||||
The default is 'C:\scripts\PowerShell\Reports\MailboxFolderPermissionReport.csv'
|
||||
|
||||
.PARAMETER Encoding
|
||||
Specifies the encoding for the exported CSV file.
|
||||
The acceptable values for this parameter are:
|
||||
- Unicode
|
||||
- UTF7
|
||||
- UTF8
|
||||
- ASCII
|
||||
- UTF32
|
||||
- BigEndianUnicode
|
||||
- Default
|
||||
- OEM
|
||||
|
||||
Default is UTF8
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Get-enMailboxFolderPermissionReport
|
||||
|
||||
Get a detailed mailbox folder permission report
|
||||
|
||||
.NOTES
|
||||
Developed and tested with Exchange Online, it should work with on Premises Exchange 2010/2010/2016/2019
|
||||
|
||||
This is open-source software, if you find an issue try to fix it yourself.
|
||||
There is no support and/or warranty in any kind
|
||||
|
||||
.LINK
|
||||
http://www.enatec.io
|
||||
|
||||
.LINK
|
||||
Get-Mailbox
|
||||
|
||||
.LINK
|
||||
Get-MailboxFolderStatistics
|
||||
|
||||
.LINK
|
||||
Get-MailboxFolderPermission
|
||||
|
||||
.LINK
|
||||
Export-Csv
|
||||
#>
|
||||
|
||||
[CmdletBinding(ConfirmImpact = 'None')]
|
||||
param
|
||||
(
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyString()]
|
||||
[AllowEmptyCollection()]
|
||||
[Alias('Mailbox', 'MailboxID', 'MailboxIdentity')]
|
||||
[string]
|
||||
$Identity = '*',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateSet('UserMailbox', 'User', 'SharedMailbox', 'Shared', 'All', IgnoreCase = $true)]
|
||||
[string]
|
||||
$MailboxType = 'All',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('MailboxResultSize')]
|
||||
[string]
|
||||
$ResultSize = 'Unlimited',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('CsvReport', 'CsvFile')]
|
||||
[string]
|
||||
$Path = 'C:\scripts\PowerShell\Reports\MailboxFolderPermissionReport.csv',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM', IgnoreCase = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('CsvEncoding')]
|
||||
[string]
|
||||
$Encoding = 'UTF8'
|
||||
)
|
||||
|
||||
begin
|
||||
{
|
||||
#region Cleanup
|
||||
$MailboxPermissionReport = $null
|
||||
$AllMailboxes = $null
|
||||
$MailboxCount = $null
|
||||
$MailboxFolderPermission = $null
|
||||
$ProgressStatus = $null
|
||||
#endregion Cleanup
|
||||
|
||||
#region Defaults
|
||||
$SCT = 'SilentlyContinue'
|
||||
$CNT = 'Continue'
|
||||
|
||||
if (-not ($Identity))
|
||||
{
|
||||
$Identity = '*'
|
||||
}
|
||||
|
||||
if (-not ($MailboxType))
|
||||
{
|
||||
$MailboxType = 'All'
|
||||
}
|
||||
|
||||
if (-not ($ResultSize))
|
||||
{
|
||||
$ResultSize = 'Unlimited'
|
||||
}
|
||||
|
||||
if (-not ($Path))
|
||||
{
|
||||
$Path = 'C:\scripts\PowerShell\Reports\MailboxFolderPermissionReport.csv'
|
||||
}
|
||||
|
||||
if (-not ($Encoding))
|
||||
{
|
||||
$Encoding = 'UTF8'
|
||||
}
|
||||
#endregion Defaults
|
||||
|
||||
#region MailboxType
|
||||
Write-Verbose -Message 'Get the mailboxes'
|
||||
|
||||
#region paramGetMailbox
|
||||
$paramGetMailbox = @{
|
||||
Identity = $Identity
|
||||
ResultSize = $ResultSize
|
||||
ErrorAction = $SCT
|
||||
WarningAction = $CNT
|
||||
}
|
||||
#endregion paramGetMailbox
|
||||
|
||||
#region MailboxTypeSwitch
|
||||
switch ($MailboxType)
|
||||
{
|
||||
UserMailbox
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
User
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
SharedMailbox
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
Shared
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
All
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
default
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion MailboxTypeSwitch
|
||||
|
||||
#region GetAllMailboxes
|
||||
$AllMailboxes = (Get-Mailbox @paramGetMailbox | Where-Object @paramWhereObject | Sort-Object)
|
||||
#endregion GetAllMailboxes
|
||||
#endregion MailboxType
|
||||
}
|
||||
|
||||
process
|
||||
{
|
||||
if ($AllMailboxes)
|
||||
{
|
||||
# Create a new object for the report
|
||||
$MailboxPermissionReport = @()
|
||||
|
||||
# Create a counter for Write-Progress
|
||||
$MailboxCounter = ($AllMailboxes | Measure-Object).Count
|
||||
|
||||
# Set the start counter for Write-Progress to 1
|
||||
$MailboxCount = 1
|
||||
|
||||
#region MailboxLoop
|
||||
Write-Verbose -Message 'Process all mailboxes'
|
||||
|
||||
ForEach ($SingleMailbox in $AllMailboxes)
|
||||
{
|
||||
# Update Write-Progress
|
||||
$ProgressActivity = ('Working on Mailbox {0} of {1} ({2})' -f $MailboxCount, $MailboxCounter, $SingleMailbox.UserPrincipalName)
|
||||
$ProgressStatus = ('Getting folders for mailbox: {0} ({1})' -f $SingleMailbox.DisplayName, $SingleMailbox.UserPrincipalName)
|
||||
|
||||
Write-Verbose -Message $ProgressStatus
|
||||
|
||||
$paramWriteProgress = @{
|
||||
Status = $ProgressStatus
|
||||
Activity = $ProgressActivity
|
||||
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
|
||||
}
|
||||
Write-Progress @paramWriteProgress
|
||||
|
||||
# Get all folder for the mailbox
|
||||
$AllFolders = ($SingleMailbox | Get-MailboxFolderStatistics -FolderScope All | ForEach-Object -Process {
|
||||
$_.folderpath
|
||||
} | ForEach-Object -Process {
|
||||
$_.replace('/', '\')
|
||||
})
|
||||
|
||||
ForEach ($SingleFolder in $AllFolders)
|
||||
{
|
||||
# Update Write-Progress
|
||||
$ProgressStatus = ('Get permissions for {0}' -f ($SingleMailbox.UserPrincipalName + ':' + $SingleFolder))
|
||||
|
||||
Write-Verbose -Message $ProgressStatus
|
||||
|
||||
$paramWriteProgress = @{
|
||||
Status = $ProgressStatus
|
||||
Activity = $ProgressActivity
|
||||
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
|
||||
}
|
||||
Write-Progress @paramWriteProgress
|
||||
|
||||
# Get mailbox folder permissions with Get-MailboxFolderPermission
|
||||
$MailboxFolderPermission = $null
|
||||
$paramGetMailboxFolderPermission = @{
|
||||
Identity = ($SingleMailbox.Alias + ':' + $SingleFolder)
|
||||
ErrorAction = $SCT
|
||||
}
|
||||
$MailboxFolderPermission = (Get-MailboxFolderPermission @paramGetMailboxFolderPermission)
|
||||
|
||||
# store results in variable
|
||||
$MailboxPermissionReport += $MailboxFolderPermission | Where-Object -FilterScript {
|
||||
$_.User -notlike 'Default' -and $_.User -notlike 'Anonymous' -and $_.AccessRights -notlike 'None' -and $_.AccessRights -notlike 'Owner'
|
||||
} | Select-Object -Property @{
|
||||
name = 'Name'
|
||||
expression = {
|
||||
$SingleMailbox.Name
|
||||
}
|
||||
}, @{
|
||||
name = 'UserPrincipalName'
|
||||
expression = {
|
||||
$SingleMailbox.UserPrincipalName
|
||||
}
|
||||
}, FolderName, @{
|
||||
name = 'User'
|
||||
expression = {
|
||||
$_.User -join ','
|
||||
}
|
||||
}, @{
|
||||
name = 'AccessRights'
|
||||
expression = {
|
||||
$_.AccessRights -join ','
|
||||
}
|
||||
}
|
||||
|
||||
# Cleanup
|
||||
$MailboxFolderPermission = $null
|
||||
}
|
||||
|
||||
# Update the counter
|
||||
$MailboxCount++
|
||||
|
||||
Write-Verbose -Message ('Done with processing {0}' -f $SingleMailbox.UserPrincipalName)
|
||||
}
|
||||
#endregion MailboxLoop
|
||||
|
||||
#region Reporter
|
||||
if ($MailboxPermissionReport)
|
||||
{
|
||||
$paramExportCsv = @{
|
||||
Path = $Path
|
||||
Force = $true
|
||||
NoTypeInformation = $true
|
||||
Confirm = $false
|
||||
ErrorAction = 'Stop'
|
||||
WarningAction = $CNT
|
||||
}
|
||||
$null = ($MailboxPermissionReport | Export-Csv @paramExportCsv)
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning -Message 'None of the Mailboxes has special permissions set'
|
||||
}
|
||||
#endregion Reporter
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning -Message 'No Mailboxes found that matches your search criteria'
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
{
|
||||
#region Cleanup
|
||||
$MailboxPermissionReport = $null
|
||||
$AllMailboxes = $null
|
||||
$MailboxCount = $null
|
||||
$MailboxFolderPermission = $null
|
||||
$ProgressStatus = $null
|
||||
#endregion Cleanup
|
||||
}
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
@@ -0,0 +1,335 @@
|
||||
function Get-enMailboxPermissionReport
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a detailed mailbox permission report
|
||||
|
||||
.DESCRIPTION
|
||||
Get a detailed mailbox permission report and exports this report to a given CSV file.
|
||||
You can select only user-mailboxes, only shared-mailboxes or both for the reporting.
|
||||
|
||||
.PARAMETER Identity
|
||||
The Identity parameter specifies the mailbox that you want to view.
|
||||
You can use any value that uniquely identifies the mailbox.
|
||||
|
||||
Default is * (all)
|
||||
|
||||
.PARAMETER MailboxType
|
||||
The type is the value for the regular RecipientTypeDetails.
|
||||
|
||||
The acceptable values for this parameter are:
|
||||
- UserMailbox
|
||||
- User
|
||||
- SharedMailbox
|
||||
- Shared
|
||||
- All
|
||||
|
||||
The Default is ALL
|
||||
|
||||
.PARAMETER ResultSize
|
||||
The ResultSize parameter specifies the maximum number of results to return.
|
||||
If you want to return all requests that match the query, use unlimited for the value of this parameter.
|
||||
|
||||
The default value is unlimited.
|
||||
|
||||
.PARAMETER Path
|
||||
Specifies the path to the CSV output file.
|
||||
|
||||
The default is 'C:\scripts\PowerShell\Reports\MailboxPermissionReport.csv'
|
||||
|
||||
.PARAMETER Encoding
|
||||
Specifies the encoding for the exported CSV file.
|
||||
The acceptable values for this parameter are:
|
||||
- Unicode
|
||||
- UTF7
|
||||
- UTF8
|
||||
- ASCII
|
||||
- UTF32
|
||||
- BigEndianUnicode
|
||||
- Default
|
||||
- OEM
|
||||
|
||||
Default is UTF8
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Get-enMailboxPermissionReport
|
||||
|
||||
Get a detailed mailbox permission report
|
||||
|
||||
.NOTES
|
||||
Developed and tested with Exchange Online, it should work with on Premises Exchange 2010/2010/2016/2019
|
||||
|
||||
This is open-source software, if you find an issue try to fix it yourself.
|
||||
There is no support and/or warranty in any kind
|
||||
|
||||
.LINK
|
||||
http://www.enatec.io
|
||||
|
||||
.LINK
|
||||
Get-Mailbox
|
||||
|
||||
.LINK
|
||||
Get-RecipientPermission
|
||||
|
||||
.LINK
|
||||
Export-Csv
|
||||
#>
|
||||
|
||||
[CmdletBinding(ConfirmImpact = 'None')]
|
||||
param
|
||||
(
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyString()]
|
||||
[AllowEmptyCollection()]
|
||||
[Alias('Mailbox', 'MailboxID', 'MailboxIdentity')]
|
||||
[string]
|
||||
$Identity = '*',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateSet('UserMailbox', 'User', 'SharedMailbox', 'Shared', 'All', IgnoreCase = $true)]
|
||||
[string]
|
||||
$MailboxType = 'All',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('MailboxResultSize')]
|
||||
[string]
|
||||
$ResultSize = 'Unlimited',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('CsvReport', 'CsvFile')]
|
||||
[string]
|
||||
$Path = 'C:\scripts\PowerShell\Reports\MailboxPermissionReport.csv',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM', IgnoreCase = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('CsvEncoding')]
|
||||
[string]
|
||||
$Encoding = 'UTF8'
|
||||
)
|
||||
|
||||
begin
|
||||
{
|
||||
#region Cleanup
|
||||
$MailboxPermissionReport = $null
|
||||
$AllMailboxes = $null
|
||||
#endregion Cleanup
|
||||
|
||||
#region Defaults
|
||||
$SCT = 'SilentlyContinue'
|
||||
$CNT = 'Continue'
|
||||
|
||||
if (-not ($Identity))
|
||||
{
|
||||
$Identity = '*'
|
||||
}
|
||||
|
||||
if (-not ($MailboxType))
|
||||
{
|
||||
$MailboxType = 'All'
|
||||
}
|
||||
|
||||
if (-not ($ResultSize))
|
||||
{
|
||||
$ResultSize = 'Unlimited'
|
||||
}
|
||||
|
||||
if (-not ($Path))
|
||||
{
|
||||
$Path = 'C:\scripts\PowerShell\Reports\MailboxPermissionReport.csv'
|
||||
}
|
||||
|
||||
if (-not ($Encoding))
|
||||
{
|
||||
$Encoding = 'UTF8'
|
||||
}
|
||||
#endregion Defaults
|
||||
|
||||
#region MailboxType
|
||||
Write-Verbose -Message 'Get the mailboxes'
|
||||
|
||||
#region paramGetMailbox
|
||||
$paramGetMailbox = @{
|
||||
Identity = $Identity
|
||||
ResultSize = $ResultSize
|
||||
ErrorAction = $SCT
|
||||
WarningAction = $CNT
|
||||
}
|
||||
#endregion paramGetMailbox
|
||||
|
||||
#region MailboxTypeSwitch
|
||||
switch ($MailboxType)
|
||||
{
|
||||
UserMailbox
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
User
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
SharedMailbox
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
Shared
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
All
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
default
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion MailboxTypeSwitch
|
||||
|
||||
#region GetAllMailboxes
|
||||
$AllMailboxes = (Get-Mailbox @paramGetMailbox | Where-Object @paramWhereObject | Sort-Object)
|
||||
#endregion GetAllMailboxes
|
||||
#endregion MailboxType
|
||||
}
|
||||
|
||||
process
|
||||
{
|
||||
if ($AllMailboxes)
|
||||
{
|
||||
# Create a new object for the report
|
||||
$MailboxPermissionReport = @()
|
||||
|
||||
# Create a counter for Write-Progress
|
||||
$MailboxCounter = ($AllMailboxes | Measure-Object).Count
|
||||
|
||||
# Set the start counter for Write-Progress to 1
|
||||
$MailboxCount = 1
|
||||
|
||||
#region MailboxLoop
|
||||
Write-Verbose -Message 'Process all mailboxes'
|
||||
|
||||
ForEach ($SingleMailbox in $AllMailboxes)
|
||||
{
|
||||
# Update Write-Progress
|
||||
$ProgressActivity = ('Working on Mailbox {0} of {1} ({2})' -f $MailboxCount, $MailboxCounter, $SingleMailbox.UserPrincipalName)
|
||||
$ProgressStatus = ('Getting folders for mailbox: {0} ({1})' -f $SingleMailbox.DisplayName, $SingleMailbox.UserPrincipalName)
|
||||
|
||||
Write-Verbose -Message $ProgressStatus
|
||||
|
||||
$paramWriteProgress = @{
|
||||
Status = $ProgressStatus
|
||||
Activity = $ProgressActivity
|
||||
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
|
||||
}
|
||||
Write-Progress @paramWriteProgress
|
||||
|
||||
$MailboxPermissionReport += $SingleMailbox | Get-MailboxPermission | Where-Object -FilterScript {
|
||||
($_.IsInherited -eq $false) -and -not ($_.User -match 'NT AUTHORITY')
|
||||
} | Select-Object -Property 'Identity', @{
|
||||
Name = 'UserPrincipalName'
|
||||
Expression = {
|
||||
$SingleMailbox.UserPrincipalName
|
||||
}
|
||||
}, 'User', @{
|
||||
Name = 'Access Rights'
|
||||
Expression = {
|
||||
$_.AccessRights -join ','
|
||||
}
|
||||
} -ErrorAction $CNT -WarningAction $CNT
|
||||
}
|
||||
#endregion MailboxLoop
|
||||
|
||||
#region Reporter
|
||||
if ($MailboxPermissionReport)
|
||||
{
|
||||
$paramExportCsv = @{
|
||||
Path = $Path
|
||||
Force = $true
|
||||
NoTypeInformation = $true
|
||||
Confirm = $false
|
||||
ErrorAction = 'Stop'
|
||||
WarningAction = $CNT
|
||||
}
|
||||
$null = ($MailboxPermissionReport | Export-Csv @paramExportCsv)
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning -Message 'None of the Mailboxes has special permissions set'
|
||||
}
|
||||
#endregion Reporter
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning -Message 'No Mailboxes found that matches your search criteria'
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
{
|
||||
#region Cleanup
|
||||
$MailboxPermissionReport = $null
|
||||
$AllMailboxes = $null
|
||||
#endregion Cleanup
|
||||
}
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
@@ -0,0 +1,335 @@
|
||||
function Get-enMailboxSendAsReport
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a detailed mailbox Send permission report
|
||||
|
||||
.DESCRIPTION
|
||||
Get a detailed mailbox Send permission report and exports this report to a given CSV file.
|
||||
You can select only user-mailboxes, only shared-mailboxes or both for the reporting.
|
||||
|
||||
.PARAMETER Identity
|
||||
The Identity parameter specifies the mailbox that you want to view.
|
||||
You can use any value that uniquely identifies the mailbox.
|
||||
|
||||
Default is * (all)
|
||||
|
||||
.PARAMETER MailboxType
|
||||
The type is the value for the regular RecipientTypeDetails.
|
||||
|
||||
The acceptable values for this parameter are:
|
||||
- UserMailbox
|
||||
- User
|
||||
- SharedMailbox
|
||||
- Shared
|
||||
- All
|
||||
|
||||
The Default is ALL
|
||||
|
||||
.PARAMETER ResultSize
|
||||
The ResultSize parameter specifies the maximum number of results to return.
|
||||
If you want to return all requests that match the query, use unlimited for the value of this parameter.
|
||||
|
||||
The default value is unlimited.
|
||||
|
||||
.PARAMETER Path
|
||||
Specifies the path to the CSV output file.
|
||||
|
||||
The default is 'C:\scripts\PowerShell\Reports\MailboxSendAsReport.csv'
|
||||
|
||||
.PARAMETER Encoding
|
||||
Specifies the encoding for the exported CSV file.
|
||||
The acceptable values for this parameter are:
|
||||
- Unicode
|
||||
- UTF7
|
||||
- UTF8
|
||||
- ASCII
|
||||
- UTF32
|
||||
- BigEndianUnicode
|
||||
- Default
|
||||
- OEM
|
||||
|
||||
Default is UTF8
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Get-enMailboxSendAsReport
|
||||
|
||||
Get a detailed mailbox permission report
|
||||
|
||||
.NOTES
|
||||
Developed and tested with Exchange Online, it should work with on Premises Exchange 2010/2010/2016/2019
|
||||
|
||||
This is open-source software, if you find an issue try to fix it yourself.
|
||||
There is no support and/or warranty in any kind
|
||||
|
||||
.LINK
|
||||
http://www.enatec.io
|
||||
|
||||
.LINK
|
||||
Get-Mailbox
|
||||
|
||||
.LINK
|
||||
Get-RecipientPermission
|
||||
|
||||
.LINK
|
||||
Export-Csv
|
||||
#>
|
||||
|
||||
[CmdletBinding(ConfirmImpact = 'None')]
|
||||
param
|
||||
(
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyString()]
|
||||
[AllowEmptyCollection()]
|
||||
[Alias('Mailbox', 'MailboxID', 'MailboxIdentity')]
|
||||
[string]
|
||||
$Identity = '*',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateSet('UserMailbox', 'User', 'SharedMailbox', 'Shared', 'All', IgnoreCase = $true)]
|
||||
[string]
|
||||
$MailboxType = 'All',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('MailboxResultSize')]
|
||||
[string]
|
||||
$ResultSize = 'Unlimited',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('CsvReport', 'CsvFile')]
|
||||
[string]
|
||||
$Path = 'C:\scripts\PowerShell\Reports\MailboxSendAsReport.csv',
|
||||
[Parameter(ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM', IgnoreCase = $true)]
|
||||
[AllowEmptyCollection()]
|
||||
[AllowEmptyString()]
|
||||
[Alias('CsvEncoding')]
|
||||
[string]
|
||||
$Encoding = 'UTF8'
|
||||
)
|
||||
|
||||
begin
|
||||
{
|
||||
#region Cleanup
|
||||
$MailboxPermissionReport = $null
|
||||
$AllMailboxes = $null
|
||||
#endregion Cleanup
|
||||
|
||||
#region Defaults
|
||||
$SCT = 'SilentlyContinue'
|
||||
$CNT = 'Continue'
|
||||
|
||||
if (-not ($Identity))
|
||||
{
|
||||
$Identity = '*'
|
||||
}
|
||||
|
||||
if (-not ($MailboxType))
|
||||
{
|
||||
$MailboxType = 'All'
|
||||
}
|
||||
|
||||
if (-not ($ResultSize))
|
||||
{
|
||||
$ResultSize = 'Unlimited'
|
||||
}
|
||||
|
||||
if (-not ($Path))
|
||||
{
|
||||
$Path = 'C:\scripts\PowerShell\Reports\MailboxSendAsReport.csv'
|
||||
}
|
||||
|
||||
if (-not ($Encoding))
|
||||
{
|
||||
$Encoding = 'UTF8'
|
||||
}
|
||||
#endregion Defaults
|
||||
|
||||
#region MailboxType
|
||||
Write-Verbose -Message 'Get the mailboxes'
|
||||
|
||||
#region paramGetMailbox
|
||||
$paramGetMailbox = @{
|
||||
Identity = $Identity
|
||||
ResultSize = $ResultSize
|
||||
ErrorAction = $SCT
|
||||
WarningAction = $CNT
|
||||
}
|
||||
#endregion paramGetMailbox
|
||||
|
||||
#region MailboxTypeSwitch
|
||||
switch ($MailboxType)
|
||||
{
|
||||
UserMailbox
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
User
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
SharedMailbox
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
Shared
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
All
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
default
|
||||
{
|
||||
$paramWhereObject = @{
|
||||
FilterScript = {
|
||||
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion MailboxTypeSwitch
|
||||
|
||||
#region GetAllMailboxes
|
||||
$AllMailboxes = (Get-Mailbox @paramGetMailbox | Where-Object @paramWhereObject | Sort-Object)
|
||||
#endregion GetAllMailboxes
|
||||
#endregion MailboxType
|
||||
}
|
||||
|
||||
process
|
||||
{
|
||||
if ($AllMailboxes)
|
||||
{
|
||||
# Create a new object for the report
|
||||
$MailboxPermissionReport = @()
|
||||
|
||||
# Create a counter for Write-Progress
|
||||
$MailboxCounter = ($AllMailboxes | Measure-Object).Count
|
||||
|
||||
# Set the start counter for Write-Progress to 1
|
||||
$MailboxCount = 1
|
||||
|
||||
#region MailboxLoop
|
||||
Write-Verbose -Message 'Process all mailboxes'
|
||||
|
||||
ForEach ($SingleMailbox in $AllMailboxes)
|
||||
{
|
||||
# Update Write-Progress
|
||||
$ProgressActivity = ('Working on Mailbox {0} of {1} ({2})' -f $MailboxCount, $MailboxCounter, $SingleMailbox.UserPrincipalName)
|
||||
$ProgressStatus = ('Getting folders for mailbox: {0} ({1})' -f $SingleMailbox.DisplayName, $SingleMailbox.UserPrincipalName)
|
||||
|
||||
Write-Verbose -Message $ProgressStatus
|
||||
|
||||
$paramWriteProgress = @{
|
||||
Status = $ProgressStatus
|
||||
Activity = $ProgressActivity
|
||||
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
|
||||
}
|
||||
Write-Progress @paramWriteProgress
|
||||
|
||||
$MailboxPermissionReport += $SingleMailbox | Get-RecipientPermission | Where-Object -FilterScript {
|
||||
($_.IsInherited -eq $false) -and -not ($_.Trustee -match 'NT AUTHORITY')
|
||||
} | Select-Object -Property 'Identity', @{
|
||||
Name = 'UserPrincipalName'
|
||||
Expression = {
|
||||
$SingleMailbox.UserPrincipalName
|
||||
}
|
||||
}, 'Trustee', @{
|
||||
Name = 'Access Rights'
|
||||
Expression = {
|
||||
$_.AccessRights -join ','
|
||||
}
|
||||
} -ErrorAction $CNT -WarningAction $CNT
|
||||
}
|
||||
#endregion MailboxLoop
|
||||
|
||||
#region Reporter
|
||||
if ($MailboxPermissionReport)
|
||||
{
|
||||
$paramExportCsv = @{
|
||||
Path = $Path
|
||||
Force = $true
|
||||
NoTypeInformation = $true
|
||||
Confirm = $false
|
||||
ErrorAction = 'Stop'
|
||||
WarningAction = $CNT
|
||||
}
|
||||
$null = ($MailboxPermissionReport | Export-Csv @paramExportCsv)
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning -Message 'None of the Mailboxes has special permissions set'
|
||||
}
|
||||
#endregion Reporter
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning -Message 'No Mailboxes found that matches your search criteria'
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
{
|
||||
#region Cleanup
|
||||
$MailboxPermissionReport = $null
|
||||
$AllMailboxes = $null
|
||||
#endregion Cleanup
|
||||
}
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
29
Powershell/PowerShell-collection/ExchangeOnline/LICENSE
Normal file
29
Powershell/PowerShell-collection/ExchangeOnline/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology <http://enatec.io>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,370 @@
|
||||
function Search-MailboxItemDeletion
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Search for deletions in mailboxes
|
||||
|
||||
.DESCRIPTION
|
||||
Search for deletions in mailboxes, single or all
|
||||
|
||||
.PARAMETER Days
|
||||
Day (period) to search, max. 90 (or 30, based on your O365/M365 license).
|
||||
The default is 7 (for the last 7 days)
|
||||
Minimum is 1, maximum is 90. This will be checked
|
||||
|
||||
.PARAMETER Mailbox
|
||||
Mailbox Address
|
||||
e.g. info@contoso.com
|
||||
|
||||
.PARAMETER All
|
||||
Get all deletes, for all mailboxes
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -All
|
||||
|
||||
Get all deletes, for all mailboxes
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Days 2 | Where-Object -FilterScript { $_.Folder -ne 'Deleted Items' }
|
||||
|
||||
Get all deletes of the last 2 days, for all mailboxes, but we exclude one Folder.
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Days 7 | Where-Object -FilterScript { $_.Folder -ne 'Deleted Items' }
|
||||
|
||||
Get all deletes of the last 7 days, for all mailboxes, but we exclude one Folder.
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Days 30 | Where-Object -FilterScript { ($_.Folder -ne 'Drafts') -and ($_.Action -ne 'SoftDelete') }
|
||||
|
||||
Get all deletes of the last 30 days, for all mailboxes, but we exclude one Folder and the 'SoftDelete' action
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Days 21 -All
|
||||
|
||||
Get all deletes for the last 21 days, for all mailboxes
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -All | Out-GridView
|
||||
|
||||
Search for Deletions in all mailboxes and open the result in the GridView (e.g. for filtering)
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Mailbox 'info@contoso.com'
|
||||
|
||||
Search for Deletions in the mailbox 'info@contoso.com'
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Mailbox 'info@contoso.com' | Select-Object -Property 'Timestamp', 'Action', 'Status' , 'User', 'Mailbox', 'Subject', 'Folder', 'Client', 'ClientIP'
|
||||
|
||||
Search for Deletions in the mailbox 'info@contoso.com', and get a few more properties (e.g. Status, Client, and ClientIP).
|
||||
Might be handy to see from where it was triggered and what client was used.
|
||||
|
||||
.EXAMPLE
|
||||
PS C:\> Search-MailboxItemDeletion -Mailbox 'info@contoso.com' | Export-CSV -NoTypeInformation -Path c:\scripts\PowerShell\exports\ExchangeOnlineMailboxDeletes.csv
|
||||
|
||||
Search for Deletions in the mailbox 'info@contoso.com' and export the result into a CSV File (e.g. for a basic reporting or further investigation in Excel)
|
||||
|
||||
.OUTPUTS
|
||||
array
|
||||
|
||||
.LINK
|
||||
Search-UnifiedAuditLog
|
||||
|
||||
.NOTES
|
||||
For now, the following properties are supported:
|
||||
Action string
|
||||
AppId string
|
||||
Client string
|
||||
ClientIP string
|
||||
External bool
|
||||
ExternalAccess bool
|
||||
Folder string
|
||||
InternalLogonType int
|
||||
InternetMessageId string
|
||||
LogonType int
|
||||
Mailbox string
|
||||
MailboxGuid string
|
||||
MessageId string
|
||||
OrganizationId string
|
||||
OrganizationName string
|
||||
OriginatingServer string
|
||||
SessionId string
|
||||
Status string
|
||||
Subject string
|
||||
TimeStamp string
|
||||
User string
|
||||
|
||||
By default, the following properties are returned (all others can be selected):
|
||||
TimeStamp string
|
||||
Action string
|
||||
User string
|
||||
Mailbox string
|
||||
Subject string
|
||||
Folder string
|
||||
|
||||
Requirements:
|
||||
PowerShell or Windows PowerShell
|
||||
Exchange Online connection (e.g. the installed Module and you need to be connected with a user that has rights to use Search-UnifiedAuditLog)
|
||||
|
||||
A future version might support Wildcards in the Mailbox parameter and/or multi Mailbox searches.
|
||||
Workaround: use Where-Object with a powerful FilterScript!
|
||||
#>
|
||||
[CmdletBinding(DefaultParameterSetName = 'All',
|
||||
ConfirmImpact = 'None')]
|
||||
[OutputType([array])]
|
||||
param
|
||||
(
|
||||
[Parameter(ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateNotNull()]
|
||||
[int]
|
||||
$Days = 7,
|
||||
[Parameter(ParameterSetName = 'Single', HelpMessage = 'Mailbox Address e.g. info@contoso.com',
|
||||
Mandatory,
|
||||
ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateNotNull()]
|
||||
[Alias('MailboxName', 'MailboxAddress')]
|
||||
[string]
|
||||
$Mailbox,
|
||||
[Parameter(ParameterSetName = 'All')]
|
||||
[switch]
|
||||
$All
|
||||
)
|
||||
|
||||
begin
|
||||
{
|
||||
# Garbage Collection
|
||||
[GC]::Collect()
|
||||
|
||||
# Cleanup
|
||||
$Records = $null
|
||||
|
||||
# TimeSpan
|
||||
$StartDate = (Get-Date).AddDays(-$Days)
|
||||
|
||||
# Now
|
||||
$EndDate = (Get-Date)
|
||||
|
||||
#region HelperFunctions
|
||||
function Get-StandardMembersFromPSObject
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Filter the given properties from a given Object
|
||||
|
||||
.DESCRIPTION
|
||||
Filter the given properties from a given Object
|
||||
|
||||
.PARAMETER InputObject
|
||||
The input object, must be a psobject.
|
||||
|
||||
.PARAMETER Properties
|
||||
The properties to select from the given input object.
|
||||
Multiple values needs to separated by a comma.
|
||||
|
||||
.EXAMPLE
|
||||
Get-StandardMembersFromPSObject -InputObject Value -Properties Value
|
||||
Describe what this call does
|
||||
|
||||
.OUTPUTS
|
||||
psobject
|
||||
|
||||
.NOTES
|
||||
Just an internal Helper function
|
||||
|
||||
.LINK
|
||||
https://learn-powershell.net/2013/08/03/quick-hits-set-the-default-property-display-in-powershell-on-custom-objects/
|
||||
.LINK
|
||||
http://stackoverflow.com/questions/1369542/can-you-set-an-objects-defaultdisplaypropertyset-in-a-powershell-v2-script/1891215#1891215
|
||||
|
||||
.INPUTS
|
||||
psobject, string
|
||||
#>
|
||||
[CmdletBinding(ConfirmImpact = 'None')]
|
||||
[OutputType([psobject])]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory,
|
||||
ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName,
|
||||
HelpMessage = 'The input object, must be a psobject.')]
|
||||
[ValidateNotNull()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[psobject]
|
||||
$InputObject,
|
||||
[Parameter(ValueFromPipeline,
|
||||
ValueFromPipelineByPropertyName)]
|
||||
[ValidateNotNull()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[Alias('DefaultProperties')]
|
||||
[string[]]
|
||||
$Properties = $null
|
||||
)
|
||||
|
||||
process
|
||||
{
|
||||
try
|
||||
{
|
||||
$defaultDisplayPropertySet = (New-Object -TypeName System.Management.Automation.PSPropertySet -ArgumentList ('DefaultDisplayPropertySet', [string[]]$Properties))
|
||||
$PSStandardMembers = ([Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet))
|
||||
$InputObject | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers -Force
|
||||
}
|
||||
catch
|
||||
{
|
||||
#region ErrorHandler
|
||||
# get error record
|
||||
[Management.Automation.ErrorRecord]$e = $_
|
||||
|
||||
# retrieve information about runtime error
|
||||
$info = [PSCustomObject]@{
|
||||
Exception = $e.Exception.Message
|
||||
Reason = $e.CategoryInfo.Reason
|
||||
Target = $e.CategoryInfo.TargetName
|
||||
Script = $e.InvocationInfo.ScriptName
|
||||
Line = $e.InvocationInfo.ScriptLineNumber
|
||||
Column = $e.InvocationInfo.OffsetInLine
|
||||
}
|
||||
|
||||
# output information. Post-process collected info, and log info (optional)
|
||||
$info | Out-String | Write-Verbose
|
||||
#endregion ErrorHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion HelperFunctions
|
||||
}
|
||||
|
||||
process
|
||||
{
|
||||
# Get the UnifiedAuditLog Data, with the delete operations
|
||||
$Records = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations 'HardDelete', 'SoftDelete')
|
||||
|
||||
# Do we have a result
|
||||
if ($Records)
|
||||
{
|
||||
Write-Verbose -Message ('Processing ' + $Records.Count + ' audit records...')
|
||||
|
||||
# Create a new Object
|
||||
$Report = [Collections.Generic.List[Object]]::new()
|
||||
|
||||
foreach ($Rec in $Records)
|
||||
{
|
||||
$AuditData = (ConvertFrom-Json -InputObject $Rec.Auditdata)
|
||||
|
||||
if ($AuditData.ResultStatus -eq 'PartiallySucceeded')
|
||||
{
|
||||
$MessageSubject = '# Not fully deleted by' + $AuditData.ClientInfoString + ' #'
|
||||
}
|
||||
else
|
||||
{
|
||||
$MessageSubject = ($AuditData.AffectedItems.Subject -split '\n')[0]
|
||||
}
|
||||
|
||||
$ReportLine = [PSCustomObject] @{
|
||||
TimeStamp = (Get-Date -Date ($AuditData.CreationTime) -Format g)
|
||||
User = $AuditData.UserId
|
||||
Action = $AuditData.Operation
|
||||
Status = $AuditData.ResultStatus
|
||||
Mailbox = $AuditData.MailboxOwnerUPN
|
||||
MailboxGuid = $AuditData.MailboxGuid
|
||||
Subject = $MessageSubject
|
||||
MessageId = ($AuditData.AffectedItems.Id -split '\n')[0]
|
||||
InternetMessageId = ($AuditData.AffectedItems.InternetMessageId -split '\n')[0]
|
||||
Folder = $AuditData.Folder.Path.Split('\')[1]
|
||||
Client = $AuditData.ClientInfoString
|
||||
AppId = $AuditData.AppId
|
||||
ClientIP = $AuditData.ClientIP
|
||||
External = $AuditData.ExternalAccess
|
||||
SessionId = $AuditData.SessionId
|
||||
ExternalAccess = $AuditData.ExternalAccess
|
||||
InternalLogonType = $AuditData.InternalLogonType
|
||||
LogonType = $AuditData.LogonType
|
||||
OrganizationName = $AuditData.OrganizationName
|
||||
OrganizationId = $AuditData.OrganizationId
|
||||
OriginatingServer = $AuditData.OriginatingServer
|
||||
}
|
||||
|
||||
# Define the default properties and support Select-Object
|
||||
Get-StandardMembersFromPSObject -InputObject $ReportLine -Properties 'Timestamp', 'Action', 'User', 'Mailbox', 'Subject', 'Folder'
|
||||
|
||||
# Add to the reporting
|
||||
$Report.Add($ReportLine)
|
||||
}
|
||||
|
||||
$Records = $null
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Output -InputObject 'No deletion records found.'
|
||||
break
|
||||
}
|
||||
|
||||
# Create a new array object
|
||||
$Output = @()
|
||||
|
||||
# Single or all ?
|
||||
switch ($PsCmdlet.ParameterSetName)
|
||||
{
|
||||
'Single'
|
||||
{
|
||||
$Output = ($Report | Where-Object -FilterScript {
|
||||
# You might want to tweak the filter to support Wildcards or more the one mailbox
|
||||
$_.Mailbox -eq $Mailbox
|
||||
})
|
||||
}
|
||||
'All'
|
||||
{
|
||||
$Output = ($Report | Sort-Object -Property Mailbox)
|
||||
}
|
||||
}
|
||||
|
||||
# Cleanup
|
||||
$Report = $null
|
||||
}
|
||||
|
||||
end
|
||||
{
|
||||
# Just dump the result to the terminal
|
||||
$Output
|
||||
|
||||
# Cleanup
|
||||
$Output = $null
|
||||
|
||||
# Garbage Collection
|
||||
[GC]::Collect()
|
||||
}
|
||||
}
|
||||
|
||||
#region LICENSE
|
||||
<#
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, enabling Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#>
|
||||
#endregion LICENSE
|
||||
|
||||
#region DISCLAIMER
|
||||
<#
|
||||
DISCLAIMER:
|
||||
- Use at your own risk, etc.
|
||||
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||
- This is a third-party Software
|
||||
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||
- The Software is not supported by Microsoft Corp (MSFT)
|
||||
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||
#>
|
||||
#endregion DISCLAIMER
|
||||
Reference in New Issue
Block a user