├── Export-MailboxFASAPermissions.ps1 ├── Export-MailboxFASAPermissions.ps1-01-December-2021-18-43-17-PM.txt ├── Export-MailboxFASAPermissions.ps1_2021-12-01-18-43-17.csv ├── Import-MailboxFASAPermissions.ps1 ├── README.md ├── Validate-CSVHeaders.ps1 └── sample.csv /Export-MailboxFASAPermissions.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 1.9.1 4 | 5 | .GUID 1391bc32-575f-4ec5-8e73-a3ba548203b6 6 | 7 | .AUTHOR SammyKrosoft 8 | 9 | .COMPANYNAME SammykroSoft 10 | 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Export Exchange Mailbox Send As, Full Access, and Send On Behalf permissions 16 | in a CSV file in order to later import them in another environment using the 17 | output CSV file. 18 | 19 | .DESCRIPTION 20 | This script requires the Exchange tools to run. 21 | 22 | It exports the following Exchange Mailbox permissions in a CSV file 23 | - Send As 24 | - Full Access 25 | - Send On Behalf To 26 | in order to be able to import them later in another environment using 27 | the output CSV file. 28 | 29 | The Output CSV file will contain the following information for each mailbox permissions 30 | information exported: 31 | 32 | Display Name, Primary SMTP Address, Full Access permissions, Send As permissions, Send On Behalf permissions 33 | 34 | The permissions can have one or more entries, which will be separated by semicolons (";") 35 | 36 | To import back the permissions if needed , you can use the associated Import-MailboxFASAPermissions.ps1 37 | script. 38 | 39 | Since the Send As and Full Access permissions can be granted to non-mailbox or 40 | non-mail enabled users, these are stored in the CSV in the form of DOMAIN\Alias. 41 | 42 | On the other hand, the Send On Behalf permission can be granted only to mailbox-enabled users, 43 | mail-enabled users and/or mail-enabled security groups only. For some reason, it is stored in 44 | the form of DOMAIN\OU1\Sub-OU1\...\Name - then, the script is designed to convert these - actually 45 | the script resolve these using Get-Mailbox -Identity DOMAIN\OU\...\Name to get and store the 46 | PrimarySMTPAddress of these users so that we have two advantages: 47 | > Not only we are sure that each SMTP address represents a unique user 48 | > Also it will be way easier for the IMPORT script to import these permissions back, wherever OU the 49 | target user will be located ! 50 | 51 | This is because the IMPORT script uses Set-Mailbox with the -SendOnBehalfTo, where we can 52 | specify an SMTP address, which will be converted to the corresponding DOMAIN\OU\Name of the 53 | corresponding user in the target environment. 54 | 55 | In other words, the SMTP address will be the KEY to match the SendOnBehalfTo permission to the 56 | right users and mailboxes on the target environments. 57 | 58 | .PARAMETER OutputFile 59 | Sets the file to which we want to store the results. 60 | By default, the script will generate a CSV report with the name of the script, 61 | with the date and time appended to it. 62 | 63 | .PARAMETER SharedMailboxes 64 | This indicates the script to export the SharedMailboxes only 65 | 66 | When combined with the -ResourceMailboxes, the script will export 67 | the Shared Mailboxes, and the Room and Equipment Mailboxes as well ! 68 | 69 | To export ALL mailboxes, just don't specify neither the SharedMailboxes 70 | nor the ResourceMailboxes parameter. 71 | 72 | .PARAMETER ResourceMailboxes 73 | This indicates the script to export the ResourceMailboxes only which 74 | consist of the Room and the Equipment Mailboxes. 75 | 76 | When combined with the -SharedMailboxes, the script will export the 77 | Shared Mailboxes, the Room and the Equipment mailboxes as well ! 78 | 79 | To export ALL mailboxes, just don't specify neither the SharedMailboxes 80 | nor the ResourceMailboxes parameter. 81 | 82 | .PARAMETER DistrutionGroupsOnly 83 | This will make the script export Distribution Groups and Dynamic Distribution Groups 84 | Send On Behalf Rights exports - this will not export Mailbox rights. 85 | 86 | NOTE: Distribution Groups have only SendOnBehalf and Send As permissions 87 | -> these have NOT "Full Access" rights : because these are not mailboxes 88 | 89 | .PARAMETER CheckVersion 90 | This parameter just dumps the script version. 91 | 92 | .INPUTS 93 | The script will scan all the mailboxes, but database by database to avoid to use 94 | all the RAM of the machine from which it's executed. 95 | 96 | .OUTPUTS 97 | A CSV file with either a name that you specify with the OutputFile parameter, or if not, 98 | the name of the script, containing the users Display Names, primary SMTP addresses, 99 | and the list of Send-As, Full Access and SendOnBehalfTo for each of these mailboxes. 100 | 101 | If the Send-As, Full Access and SendOnBehalfTo are multi-values, they are stored in the columns 102 | as semi-colon separated values, like Value1;value2;value3;... 103 | 104 | => when processing each permissions set, just use something like $ImportedCSV.SendAsPermissions -split ";" 105 | or $ImportedCSV.SendAsPermissions.Split(";") ... 106 | 107 | .EXAMPLE 108 | .\Export-MailboxFASAPermissions.ps1 109 | Will run the script and export the mailbox Display Names, primary SMTP Addresses, and all the 110 | Send As, Full Access and Send On Behalf To permissions on a CSV file. 111 | 112 | .EXAMPLE 113 | .\Export-MailboxFASAPermissions.ps1 -OutputFile C:\temp\EnvironmentPermissions.csv 114 | Will run the script and export permissions for all mailboxes, in the file specified on the 115 | OutputFile parameter : C:\temp\EnvironmentPermissions.csv 116 | 117 | .EXAMPLE 118 | .\Export-MailboxFASA.ps1 -SharedMailboxes 119 | Will run the script and export the Shared Mailboxes permissions as well as the Room and 120 | Equipment Mailboxes permissions, and store the result on the default CSV file named after 121 | the script, appended with the date and time of the execution, on the script directory 122 | 123 | .EXAMPLE 124 | .\Export-MailboxFASA.ps1 -ResourceMailboxes c:\temp\ResourceMailboxPermissions.csv 125 | Will run the script and export only the Room and Equipment Mailboxes permissions, and store 126 | the results in a CSV file c:\temp\ResourceMailboxPermissions.csv 127 | 128 | .EXAMPLE 129 | .\Export-MailboxFASA.ps1 -DistributionGroupsOnly 130 | Will run the script and export only the Distribugion Group permissions (Send As, GrantSendOnBehalfTo) in 131 | the default Output file format (Script_Name_Date_time.csv). This includes the Dynamic Distribution 132 | Groups. 133 | 134 | .EXAMPLE 135 | .\Export-MailboxFASA.ps1 -DistributionGroupsOnly -IncludeDynamic $false 136 | Will run the script to export permissions of Distribution Groups, excluding the Dynamic Distribugion 137 | Groups. 138 | 139 | 140 | .NOTES 141 | This script can be use alone to export a permissions map, but the output is designed so that it 142 | can be used with the Import-MailboxFASAPermissions.ps1 script to migrate permissions to another 143 | environment such as a LAB or a brand new one with the same users (Inter-Forest migration for example 144 | or move from an On-Prem to an outsourced environment such as Office 365) 145 | 146 | Some simple facts about the permissions exported on this script: 147 | 148 | "Sens As" permissions 149 | . Stored in the form of "DOMAIN\Alias" 150 | . Is set with Add-ADPermission 151 | . https://docs.microsoft.com/en-us/powershell/module/exchange/active-directory/Add-ADPermission?view=exchange-ps 152 | 153 | "Full Access" Permissions 154 | . Stored in the form of "DOMAIN\Alias" as well 155 | . Is set with Add-MailboxPermission 156 | . https://docs.microsoft.com/en-us/powershell/module/exchange/mailboxes/Add-MailboxPermission?view=exchange-ps 157 | 158 | "Send On Behalf Of" permissions 159 | . Stored in the form of "Domain.com/OU_Name/Sub_OU/Name" 160 | . Is set with Set-Mailbox 161 | . https://docs.microsoft.com/en-us/powershell/module/exchange/mailboxes/Set-Mailbox?view=exchange-ps 162 | . -GrantSendOnBehalfTo parameter accepts one or more values from the below : 163 | Display name 164 | Alias 165 | Distinguished name (DN) 166 | Canonical DN 167 | \ 168 | Email address 169 | GUID 170 | LegacyExchangeDN 171 | SamAccountName 172 | User ID or user principal name (UPN) 173 | 174 | .LINK 175 | https://technet.microsoft.com/en-ca/library/jj919240(v=exchg.150).aspx 176 | 177 | .LINK 178 | https://docs.microsoft.com/en-us/powershell/module/exchange/active-directory/add-adpermission?view=exchange-ps 179 | 180 | .LINK 181 | https://technet.microsoft.com/en-us/library/jj919240(v=exchg.150).aspx 182 | 183 | .LINK 184 | https://github.com/SammyKrosoft 185 | #> 186 | [CmdLetBinding(DefaultParameterSetName = "NormalRun")] 187 | Param( 188 | [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "NormalRun")][switch]$SharedMailboxes, 189 | [Parameter(Mandatory = $false, Position = 1, ParameterSetName = "NormalRun")][switch]$ResourceMailboxes, 190 | [Parameter(Mandatory = $false, Position = 2, ParameterSetName = "DLOnly")][Switch]$DistributionGroupsOnly, 191 | [Parameter(Mandatory = $false, Position = 3, ParameterSetName = "DLOnly")][boolean]$IncludeDynamic=$true, 192 | [Parameter(Mandatory = $false, Position = 4, ParameterSetName = "NormalRun")][Parameter(ParameterSetName = "DLOnly")][string]$OutputFile, 193 | [Parameter(Mandatory = $false, ParameterSetName = "MailboxList")][string[]]$MailboxList, 194 | [Parameter(Mandatory = $false, Position = 6, ParameterSetName = "CheckOnly")][switch]$CheckVersion 195 | ) 196 | 197 | <# ------- SCRIPT_HEADER (Only Get-Help comments and Param() above this point) ------- #> 198 | #Initializing a $Stopwatch variable to use to measure script execution 199 | $stopwatch = [system.diagnostics.stopwatch]::StartNew() 200 | #Using Write-Debug and playing with $DebugPreference -> "Continue" will output whatever you put on Write-Debug "Your text/values" 201 | # and "SilentlyContinue" will output nothing on Write-Debug "Your text/values" 202 | $DebugPreference = "Continue" 203 | # Set Error Action to your needs 204 | $ErrorActionPreference = "SilentlyContinue" 205 | #Script Version 206 | $ScriptVersion = "1.9.1" 207 | <# Version changes 208 | v1.9.1 - just forgot 209 | v1.9 - added ability to search one or more mailboxes based on a list -MailboxList parameter 210 | v1.8.2 - fixed Get-Mailbox -Database $Database to include double quotes for database names with spaces in it 211 | v1.8.1 - fixed OutputFile parameter to be included in both NormalRun and DLOnly parameters set 212 | v1.8 - fixed issue exporting Send As for Distribugion Groups (was trying to reference $DL.Identity but selected 213 | ALIAS, Send AS and Send On Behalf permissions only, forgot to add Identity in the Select) 214 | V1.7 - replaced Get-Mailbox with Get-Recipient to get primarySMTP Addresses of Grant 215 | Send On Behalf To entries 216 | Also added the ability to export GrantSendOnBehalfTo from Distribution Groups, 217 | Including by default the Dynamic distribution groups - specify $false to the 218 | -IncludeDynamic parameter to exclude Dynamic DLs 219 | v1 - Completed the script. 220 | v0.1 - first script version 221 | #> 222 | $ScriptName = $MyInvocation.MyCommand.Name 223 | If ($CheckVersion) {Write-Host "SCRIPT NAME :$ScriptName `nSCRIPT VERSION :$ScriptVersion";exit} 224 | # Log or report file definition 225 | # NOTE: use #PSScriptRoot in Powershell 3.0 and later or use $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition in Powershell 2.0 226 | $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition #<-- that's for Powershell 2.0 227 | $OutputReport = "$($ScriptPath)\$($ScriptName)_$(get-date -f yyyy-MM-dd-HH-mm-ss).csv" 228 | # Other Option for Log or report file definition (use one of these) 229 | $ScriptLog = "$($ScriptPath)\$($ScriptName)-$(Get-Date -Format 'dd-MMMM-yyyy-HH-mm-ss-tt').txt" 230 | <# ---------------------------- /SCRIPT_HEADER ---------------------------- #> 231 | <# -------------------------- DECLARATIONS -------------------------- #> 232 | [array]$report = @() 233 | $Databases = $null 234 | $DBProgressCount = $null 235 | $Mailboxes = @() 236 | <# /DECLARATIONS #> 237 | <# -------------------------- FUNCTIONS -------------------------- #> 238 | #region Functions 239 | function Log { 240 | <# 241 | .SYNOPSIS 242 | Function to log input string to file and display it to screen 243 | 244 | .DESCRIPTION 245 | Function to log input string to file and display it to screen. Log entries in the log file are time stamped. Function allows for displaying text to screen in different colors. 246 | 247 | .PARAMETER String 248 | The string to be displayed to the screen and saved to the log file 249 | 250 | .PARAMETER Color 251 | The color in which to display the input string on the screen 252 | Default is White 253 | Valid options are 254 | Black 255 | Blue 256 | Cyan 257 | DarkBlue 258 | DarkCyan 259 | DarkGray 260 | DarkGreen 261 | DarkMagenta 262 | DarkRed 263 | DarkYellow 264 | Gray 265 | Green 266 | Magenta 267 | Red 268 | White 269 | Yellow 270 | 271 | .PARAMETER LogFile 272 | Path to the file where the input string should be saved. 273 | Example: c:\log.txt 274 | If absent, the input string will be displayed to the screen only and not saved to log file 275 | 276 | .EXAMPLE 277 | Write-Log -String "Hello World" -Color Yellow -LogFile c:\log.txt 278 | This example displays the "Hello World" string to the console in yellow, and adds it as a new line to the file c:\log.txt 279 | If c:\log.txt does not exist it will be created. 280 | Log entries in the log file are time stamped. Sample output: 281 | 2014.08.06 06:52:17 AM: Hello World 282 | 283 | .EXAMPLE 284 | Write-Log "$((Get-Location).Path)" Cyan 285 | This example displays current path in Cyan, and does not log the displayed text to log file. 286 | 287 | .EXAMPLE 288 | "$((Get-Process | select -First 1).name) process ID is $((Get-Process | select -First 1).id)" | Write-Log -color DarkYellow 289 | Sample output of this example: 290 | "MDM process ID is 4492" in dark yellow 291 | 292 | .EXAMPLE 293 | Write-Log 'Found',(Get-ChildItem -Path .\ -File).Count,'files in folder',(Get-Item .\).FullName Green,Yellow,Green,Cyan .\mylog.txt 294 | Sample output will look like: 295 | Found 520 files in folder D:\Sandbox - and will have the listed foreground colors 296 | 297 | .LINK 298 | https://superwidgets.wordpress.com/2014/12/01/powershell-script-function-to-display-text-to-the-console-in-several-colors-and-save-it-to-log-with-timedate-stamp/ 299 | 300 | .NOTES 301 | Function by Sam Boutros 302 | v1.0 - 08/06/2014 303 | v1.1 - 12/01/2014 - added multi-color display in the same line 304 | v1.2 - 8 August 2016 - updated date time stamp format, protect against bad LogFile name 305 | 306 | #> 307 | 308 | [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] 309 | Param( 310 | [Parameter(Mandatory=$true, 311 | ValueFromPipeLine=$true, 312 | ValueFromPipeLineByPropertyName=$true, 313 | Position=0)] 314 | [String[]]$String, 315 | [Parameter(Mandatory=$false, 316 | Position=1)] 317 | [ValidateSet("Black","Blue","Cyan","DarkBlue","DarkCyan","DarkGray","DarkGreen","DarkMagenta","DarkRed","DarkYellow","Gray","Green","Magenta","Red","White","Yellow")] 318 | [String[]]$Color = "Green", 319 | [Parameter(Mandatory=$false, 320 | Position=2)] 321 | [String]$LogFile = $ScriptLog, 322 | [Parameter(Mandatory=$false, 323 | Position=3)] 324 | [Switch]$NoNewLine 325 | ) 326 | 327 | 328 | $LegalFileNameCharSet = "^[" + [Regex]::Escape("A-Za-z0-9^&'@{}[],$=!-#()%.+~_") + "]+$" 329 | if ($String.Count -gt 1) { 330 | $i=0 331 | foreach ($item in $String) { 332 | if ($Color[$i]) { $col = $Color[$i] } else { $col = "White" } 333 | Write-Host "$item " -ForegroundColor $col -NoNewline 334 | $i++ 335 | } 336 | if (-not ($NoNewLine)) { Write-Host " " } 337 | } else { 338 | if ($NoNewLine) { Write-Host $String -ForegroundColor $Color[0] -NoNewline } 339 | else { Write-Host $String -ForegroundColor $Color[0] } 340 | } 341 | 342 | if ($LogFile.Length -gt 2 -and !($LogFile -match $LegalFileNameCharSet)) { 343 | "$(Get-Date -format 'dd MMMM yyyy hh:mm:ss tt'): $($String -join " ")" | Out-File -Filepath $Logfile -Append 344 | } else { 345 | Write-debug "Log: Missing -LogFile parameter or bad LogFile name. Will not save input string to log file.." 346 | } 347 | } 348 | 349 | function _Progress { 350 | param([parameter(Mandatory = $false, Position = 1)] $PercentComplete = 100) 351 | Write-Progress -id 1 -activity "Working..." -status "In progress..." -PercentComplete ($PercentComplete) 352 | } 353 | 354 | Function Test-ExchTools(){ 355 | Try 356 | { 357 | Get-command Get-mailbox -ErrorAction Stop 358 | $ExchInstalledStatus = $true 359 | $Message = "Exchange tools are present !" 360 | Write-Host $Message -ForegroundColor Blue -BackgroundColor Red 361 | } 362 | Catch [System.SystemException] 363 | { 364 | $ExchInstalledStatus = $false 365 | $Message = "Exchange Tools are not present !" 366 | Write-Host $Message -ForegroundColor red -BackgroundColor Blue 367 | Exit 368 | } 369 | Return $ExchInstalledStatus 370 | } 371 | 372 | function IsEmpty($Param){ 373 | If ($Param -eq "All" -or $Param -eq "" -or $Param -eq $Null -or $Param -eq 0) { 374 | Return $True 375 | } Else { 376 | Return $False 377 | } 378 | } 379 | <# /FUNCTIONS #> 380 | #endregion Functions 381 | <# -------------------------- EXECUTIONS -------------------------- #> 382 | Log "********************** Beginning execution ***********************" Blue 383 | Log "Testing if Exchange tools are present" Blue 384 | Test-ExchTools 385 | Log "Exchange Tools present ! continuing to test if user specified Output file" Red 386 | If (IsEmpty $OutputFile) { 387 | Log "Not Output file specified, using the script standard name $OutputReport" Yellow 388 | $OutputFile = $OutputReport} 389 | 390 | Log "Checking if user specified -DistributionGroupsOnly switch..." Blue 391 | If ($DistributionGroupsOnly){ 392 | Log "User specified the -DistribugionGroupsOnly switch. Beginning Distribution Groups SendAs / GrantSendOnBehalfTo permissions dump..." Red 393 | #Process same as Mailboxes but replacing the mailbox objects with Get-DistributionList | Select Name,PrimarySMTPAddress, GrantSendOnBehalfTo 394 | #We have 2 sorts of Distribution Groups : regular Distribution Groups (can be based on Distribution or Security Groups) 395 | #And Dynamic Distribution Groups 396 | Log "Getting all distribution Groups" Blue 397 | $DLs = Get-DistributionGroup | Select Identity,Alias, DisplayName, primarySMTPAddress, @{Name='GrantSendOnBehalfTo';Expression={[string]::join(";", ($_.GrantSendOnBehalfTo))}} 398 | Log "Testing whether the user set the -IncludeDynamic boolean parameter to `$false (`$true by default)" Green 399 | If($IncludeDynamic){ 400 | Log "User didn't specify the -IncludeDynamic or set -IncludeDynamic to `$false - including Dynamic DLs" White 401 | $DLs += Get-DynamicDistributionGroup | Select Alias, DisplayName, primarySMTPAddress, @{Name='GrantSendOnBehalfTo';Expression={[string]::join(";", ($_.GrantSendOnBehalfTo))}} 402 | } 403 | 404 | If (IsEmpty $DLs){ 405 | $msgNoDLsFound = "No Distribution Lists found" 406 | Log $msgNoDLsFound Red 407 | Exit 408 | } 409 | 410 | Foreach ($DL in $DLs){ 411 | $msgWorkingOnDistributionGroup = "Working on Distribution Group $($DL.DisplayName) which Primary SMTP is $($DL.primarySMTPAddress.ToString())" 412 | Log $msgWorkingOnDistributionGroup Blue 413 | $SendAs = Get-ADPermission $($DL.identity) | ?{($_.extendedrights -like "*send-as*") -and ($_.isinherited -like "false") -and ($_.User -notlike "NT Authority\self")} 414 | #Initializing a new Powershell object to store our discovered properties 415 | $Obj = New-Object PSObject 416 | #Populating basic DL info to bind with SendAs / SendOnBehalf permissions 417 | $Obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $DL.DisplayName 418 | $obj | Add-Member -MemberType NoteProperty -Name "PrimarySMTPAddress" -Value $DL.PrimarySMTPAddress.ToString() 419 | 420 | If (IsEmpty $SendAs){ 421 | Log "No custom Send As permissions detected" 422 | $Obj | Add-Member -MemberType NoteProperty -Name "SendAsPermissions" -Value "" 423 | } Else { 424 | Log "Found one or more SendAs Permission ! Dumping ..." Red 425 | [array]$UsersWithSendAs = @() 426 | ForEach($SAright in $SendAs){$UsersWithSendAs += ($SARight.User.ToString())} 427 | $strUsersWithSendAs = $UsersWithSendAs -join ";" 428 | $Obj | Add-Member -MemberType NoteProperty -Name "SendAsPermissions" -Value $strUsersWithSendAs 429 | } 430 | 431 | If (IsEmpty ($DL.GrantSendOnBehalfTo)){ 432 | Log "No custom SendOnBehalf permissions detected" 433 | $Obj | Add-Member -MemberType NoteProperty -Name "SendOnBehalfPermissions" -Value "" 434 | } else { 435 | Log "Found one or more SendOnBehalf Permission ! Dumping ..." Blue 436 | $TableOfSendOnBehalfToConvert = $($DL.GrantSendOnBehalfTo) -Split (";") 437 | $SMTPAddressesOfSendOnBehalf = @() 438 | Foreach ($entry in $TableOfSendOnBehalfToConvert) { 439 | #Since the GrantSendOnBehalfTo entries HAVE to be mailbox-enabled users or mail enabled user or groups, 440 | #Getting primary SMTP address for each object, and storing these as a string separated by semicolon 441 | #to replace the string of DOMAIN/OU1/OU2/Name separated by semicolon 442 | $SMTPAddressesOfSendOnBehalf += (Get-Recipient $Entry).primarySMTPAddress 443 | } 444 | $SendOnBehalfConverted = $SMTPAddressesOfSendOnBehalf -join ";" 445 | $Obj | Add-Member -MemberType NoteProperty -Name "SendOnBehalfPermissions" -Value $SendOnBehalfConverted 446 | } 447 | 448 | $report += $Obj 449 | } 450 | } ElseIf ($MailboxList){ 451 | # ADDING THE -MAILBOXLIST PARAMETER TREATMENT 452 | # NOTE: The below code for this ElseIf {} block is copied from the last Else {} block below, 453 | #instead of taking all mailboxes from all databases, taking the list of mailboxes specified by the -MailboxList parameter aka $MailboxList variable 454 | $MailboxListCount = $MailboxList.Count 455 | Log "Using -MailboxList parameter, checking mailboxes from that list ($MailboxListCount mailboxes in the list)" 456 | Foreach ($CurMailbox in $MailboxList){ 457 | Log "Processing mailbox $CurMailbox" 458 | Try { 459 | $Mailbox = Get-mailbox $CurMailbox -ErrorAction STOP 460 | Log "SUCCESS - Successfully located mailbox $CurMailbox : its primary SMTP address is : $($Mailbox.PrimarySMTPAddress)" 461 | } Catch { 462 | Log "ERROR - Something wrong happened trying to locate mailbox $CurMailbox . Wrong mailbox ID ... tried another mailbox ID (SMTP address, display name, DN,...)" 463 | Log "ERROR - Skipping mailbox $CurMailbox" 464 | Continue 465 | } 466 | Log "Working on mailbox $($Mailbox.DisplayName) which Primary SMTP is $($Mailbox.primarySMTPAddress.ToString())" Blue 467 | $SendAs=Get-ADPermission $mailbox.identity | ?{($_.extendedrights -like "*send-as*") -and ($_.isinherited -like "false") -and ($_.User -notlike "NT Authority\self")} 468 | $FullAccess=Get-MailboxPermission $Mailbox | ?{($_.AccessRights -like "*fullaccess*") -and ($_.User -notlike "*nt authority\self*") -and ($_.User -notlike "*nt authority\system*") -and ($_.User -notlike "*Exchange Trusted Subsystem*") -and ($_.User -notlike "*Exchange Servers*") -and ($_.IsInherited -like "false")} 469 | $SendOnBehalf = $mailbox | Select Alias, @{Name='GrantSendOnBehalfTo';Expression={[string]::join(";", ($_.GrantSendOnBehalfTo))}} 470 | #Initializing a new Powershell object to store our discovered properties 471 | $Obj = New-Object PSObject 472 | #Populating basic mailbox info to bind with SendAs / FullMailbox / SendOnBehalf permissions 473 | $Obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $Mailbox.DisplayName 474 | $obj | Add-Member -MemberType NoteProperty -Name "PrimarySMTPAddress" -Value $Mailbox.PrimarySMTPAddress.ToString() 475 | 476 | If (IsEmpty $SendAs){ 477 | Log "No custom Send As permissions detected" 478 | $Obj | Add-Member -MemberType NoteProperty -Name "SendAsPermissions" -Value "" 479 | } Else { 480 | Log "Found one or more SendAs Permission ! Dumping ..." Red 481 | [array]$UsersWithSendAs = @() 482 | ForEach($SAright in $SendAs){$UsersWithSendAs += ($SARight.User.ToString())} 483 | $strUsersWithSendAs = $UsersWithSendAs -join ";" 484 | $Obj | Add-Member -MemberType NoteProperty -Name "SendAsPermissions" -Value $strUsersWithSendAs 485 | } 486 | 487 | If (IsEmpty $FullAccess){ 488 | Log "No custom Full Access permissions detected" 489 | $Obj | Add-Member -MemberType NoteProperty -Name "FullAccessPermissions" -Value "" 490 | } else { 491 | Log "Found one or more Full Access Permission ! Dumping ..." Blue 492 | [array]$UsersWithFullAccess = @() 493 | ForEach ($FARight in $FullAccess) {$UsersWithFullAccess += ($FARight.User.ToString())} 494 | $strUsersWithFullAccess = $UsersWithFullAccess -join ";" 495 | $Obj | Add-Member -MemberType NoteProperty -Name "FullAccessPermissions" -Value $strUsersWithFullAccess 496 | } 497 | 498 | If (IsEmpty ($SendOnBehalf.GrantSendOnBehalfTo)){ 499 | Log "No custom SendOnBehalf permissions detected" 500 | $Obj | Add-Member -MemberType NoteProperty -Name "SendOnBehalfPermissions" -Value "" 501 | } else { 502 | Log "Found one or more SendOnBehalf Permission ! Dumping ..." Blue 503 | $TableOfSendOnBehalfToConvert = $($SendOnBehalf.GrantSendOnBehalfTo) -Split (";") 504 | $SMTPAddressesOfSendOnBehalf = @() 505 | Foreach ($entry in $TableOfSendOnBehalfToConvert) { 506 | #Since the GrantSendOnBehalfTo entries HAVE to be mailbox-enabled users or mail enabled user or groups, 507 | #Getting primary SMTP address for each object, and storing these as a string separated by semicolon 508 | #to replace the string of DOMAIN/OU1/OU2/Name separated by semicolon 509 | $SMTPAddressesOfSendOnBehalf += (Get-Recipient $Entry).primarySMTPAddress 510 | } 511 | $SendOnBehalfConverted = $SMTPAddressesOfSendOnBehalf -join ";" 512 | $Obj | Add-Member -MemberType NoteProperty -Name "SendOnBehalfPermissions" -Value $SendOnBehalfConverted 513 | } 514 | #Appending the current object into the $report variable (it's an array, remember) 515 | $report += $Obj 516 | 517 | #Cleaning the variables now before the next loop... 518 | $SendOnBehalfConverted = $null 519 | $obj = $Null 520 | $SMTPAddressesOfSendOnBehalf = $null 521 | $TableOfSendOnBehalfToConvert = $null 522 | $SendOnBehalfConverted = $null 523 | $SendAs = $null 524 | $FullAccess = $null 525 | $SendOnBehalf = $null 526 | #... add more later 527 | } 528 | 529 | } Else { 530 | Log "Beginning routing to dump mailbox Send As, Full Access, and Send On Behalf permissions" 531 | Log "Getting all databases" 532 | $Databases = Get-MailboxDatabase 533 | $DBProgressCount = 0 534 | 535 | Foreach ($Database in $Databases){ 536 | $DBProgressCount++ 537 | Log "Processing Database $($Database.Name)" 538 | _Progress ($DBProgressCount/$($Databases.count)*100) 539 | 540 | $Mailboxescommand = "Get-Mailbox -resultsize unlimited -database `"$Database`"" 541 | If ($ResourceMailboxes -or $SharedMailboxes) { 542 | Log "Specified Resource Mailboxes parameter ? $ResourceMailboxes" 543 | Log "Specified SharedMailboxes parameter ? $SharedMailboxes" 544 | $MailboxesCommand += " -RecipientTypeDetails " 545 | $combo = @() 546 | If ($ResourceMailboxes){$Combo += @("RoomMailbox", "EquipmentMailbox") } 547 | If ($SharedMailboxes){$Combo += "SharedMailbox"} 548 | $combo = $Combo -join "," 549 | $MailboxesCommand += $combo 550 | } 551 | Log "The full mailbox command launched is :" 552 | Log $Mailboxescommand 553 | #Launch the command built with the above routine, based on the switches the admin chooses 554 | $Mailboxes = Invoke-expression $Mailboxescommand 555 | #If we don't "break" the current loop occurence with a "Continue" instruction, there will be an empty line in the CSV when there are no mailboxes in a given database 556 | If (IsEmpty $Mailboxes){Continue} 557 | 558 | #We cycle through each mailbox to get the permissions 559 | #It's time consuming because of the AD queries... 560 | Log "Parsing $($Mailboxes.count) mailboxes..." 561 | Foreach ($Mailbox in $Mailboxes) { 562 | Log "Working on mailbox $($Mailbox.DisplayName) which Primary SMTP is $($Mailbox.primarySMTPAddress.ToString())" Blue 563 | $SendAs=Get-ADPermission $mailbox.identity | ?{($_.extendedrights -like "*send-as*") -and ($_.isinherited -like "false") -and ($_.User -notlike "NT Authority\self")} 564 | $FullAccess=Get-MailboxPermission $Mailbox | ?{($_.AccessRights -like "*fullaccess*") -and ($_.User -notlike "*nt authority\self*") -and ($_.User -notlike "*nt authority\system*") -and ($_.User -notlike "*Exchange Trusted Subsystem*") -and ($_.User -notlike "*Exchange Servers*") -and ($_.IsInherited -like "false")} 565 | $SendOnBehalf = $mailbox | Select Alias, @{Name='GrantSendOnBehalfTo';Expression={[string]::join(";", ($_.GrantSendOnBehalfTo))}} 566 | #Initializing a new Powershell object to store our discovered properties 567 | $Obj = New-Object PSObject 568 | #Populating basic mailbox info to bind with SendAs / FullMailbox / SendOnBehalf permissions 569 | $Obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $Mailbox.DisplayName 570 | $obj | Add-Member -MemberType NoteProperty -Name "PrimarySMTPAddress" -Value $Mailbox.PrimarySMTPAddress.ToString() 571 | 572 | If (IsEmpty $SendAs){ 573 | Log "No custom Send As permissions detected" 574 | $Obj | Add-Member -MemberType NoteProperty -Name "SendAsPermissions" -Value "" 575 | } Else { 576 | Log "Found one or more SendAs Permission ! Dumping ..." Red 577 | [array]$UsersWithSendAs = @() 578 | ForEach($SAright in $SendAs){$UsersWithSendAs += ($SARight.User.ToString())} 579 | $strUsersWithSendAs = $UsersWithSendAs -join ";" 580 | $Obj | Add-Member -MemberType NoteProperty -Name "SendAsPermissions" -Value $strUsersWithSendAs 581 | } 582 | 583 | If (IsEmpty $FullAccess){ 584 | Log "No custom Full Access permissions detected" 585 | $Obj | Add-Member -MemberType NoteProperty -Name "FullAccessPermissions" -Value "" 586 | } else { 587 | Log "Found one or more Full Access Permission ! Dumping ..." Blue 588 | [array]$UsersWithFullAccess = @() 589 | ForEach ($FARight in $FullAccess) {$UsersWithFullAccess += ($FARight.User.ToString())} 590 | $strUsersWithFullAccess = $UsersWithFullAccess -join ";" 591 | $Obj | Add-Member -MemberType NoteProperty -Name "FullAccessPermissions" -Value $strUsersWithFullAccess 592 | } 593 | 594 | If (IsEmpty ($SendOnBehalf.GrantSendOnBehalfTo)){ 595 | Log "No custom SendOnBehalf permissions detected" 596 | $Obj | Add-Member -MemberType NoteProperty -Name "SendOnBehalfPermissions" -Value "" 597 | } else { 598 | Log "Found one or more SendOnBehalf Permission ! Dumping ..." Blue 599 | $TableOfSendOnBehalfToConvert = $($SendOnBehalf.GrantSendOnBehalfTo) -Split (";") 600 | $SMTPAddressesOfSendOnBehalf = @() 601 | Foreach ($entry in $TableOfSendOnBehalfToConvert) { 602 | #Since the GrantSendOnBehalfTo entries HAVE to be mailbox-enabled users or mail enabled user or groups, 603 | #Getting primary SMTP address for each object, and storing these as a string separated by semicolon 604 | #to replace the string of DOMAIN/OU1/OU2/Name separated by semicolon 605 | $SMTPAddressesOfSendOnBehalf += (Get-Recipient $Entry).primarySMTPAddress 606 | } 607 | $SendOnBehalfConverted = $SMTPAddressesOfSendOnBehalf -join ";" 608 | $Obj | Add-Member -MemberType NoteProperty -Name "SendOnBehalfPermissions" -Value $SendOnBehalfConverted 609 | } 610 | #Appending the current object into the $report variable (it's an array, remember) 611 | $report += $Obj 612 | 613 | #Cleaning the variables now before the next loop... 614 | $SendOnBehalfConverted = $null 615 | $obj = $Null 616 | $SMTPAddressesOfSendOnBehalf = $null 617 | $TableOfSendOnBehalfToConvert = $null 618 | $SendOnBehalfConverted = $null 619 | $SendAs = $null 620 | $FullAccess = $null 621 | $SendOnBehalf = $null 622 | #... add more later 623 | } 624 | } 625 | } 626 | 627 | 628 | # Get mailbox forward to from mailboxes:Change the items below that are in bold to fit your needs. 629 | # Get-Mailbox -Filter {ForwardingAddress -ne $Null} |Select Alias, ForwardingAddress | Export-Csv -NoType -encoding "unicode" C:\*location*\MailboxesForwardTo.csv 630 | # Get mailbox grant send on behalf to:Change the items below that are in bold to fit your needs. 631 | #Get-Mailbox -Filter {GrantSendOnBehalfTo -ne $Null} |Select Alias, @{Name='GrantSendOnBehalfTo';Expression={[string]::join(";", ($_.GrantSendOnBehalfTo))}} | Export-Csv -NoType -encoding "unicode" C:\*location*\MailboxesSendOnBehalf.csv 632 | Log "saving file in $OutputFile" 633 | $Report | export-csv -NoTypeInformation $OutputFile 634 | Notepad $OutputFile 635 | 636 | <# /EXECUTIONS #> 637 | <# -------------------------- CLEANUP VARIABLES -------------------------- #> 638 | $Report = $null 639 | $OutputReport = $null 640 | $obj = $null 641 | $strUsersWithSendAs = $null 642 | $strUsersWithFullAccess = $null 643 | $UsersWithSendAs = $null 644 | $UsersWithFullAccess = $null 645 | $SendOnBehalf = $null 646 | $FullAccess = $null 647 | $SendAs = $null 648 | $Mailboxes = $null 649 | $Databases = $Null 650 | <# /CLEANUP VARIABLES#> 651 | <# ---------------------------- SCRIPT_FOOTER ---------------------------- #> 652 | #Stopping StopWatch and report total elapsed time (TotalSeconds, TotalMilliseconds, TotalMinutes, etc... 653 | $stopwatch.Stop() 654 | Log "`n`nThe script took $($StopWatch.Elapsed.TotalSeconds) seconds to execute..." 655 | <# ---------------- /SCRIPT_FOOTER (NOTHING BEYOND THIS POINT) ----------- #> 656 | -------------------------------------------------------------------------------- /Export-MailboxFASAPermissions.ps1-01-December-2021-18-43-17-PM.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SammyKrosoft/Export-Import-Full-Access-SendAs-SendOnBehalfTo/1ba21f980ebb21ca67a9a20c270f8c1edbc595d4/Export-MailboxFASAPermissions.ps1-01-December-2021-18-43-17-PM.txt -------------------------------------------------------------------------------- /Export-MailboxFASAPermissions.ps1_2021-12-01-18-43-17.csv: -------------------------------------------------------------------------------- 1 | "DisplayName","PrimarySMTPAddress","SendAsPermissions","FullAccessPermissions","SendOnBehalfPermissions" 2 | "samdrey","samdrey@CanadaSam.ca","","","" 3 | "Discovery Search Mailbox","DiscoverySearchMailbox{D919BA05-46A6-415f-80AD-7E09334BB852}@CanadaSam.ca","","CANADADREY\Discovery Management","" 4 | "Tom Eg?rie","Tom.Egerie@CanadaSam.ca","","","" 5 | "Alain Posteur","Alain.Posteur@CanadaSam.ca","","","" 6 | "Aude Vaisselle","Aude.Vaisselle@CanadaSam.ca","","","" 7 | "Nordine Ateur","Nordine.Ateur@CanadaSam.ca","","","" 8 | "Harry Stocrate","Harry.Stocrate@CanadaSam.ca","","","" 9 | "Delphine Talaron","Delphine.Talaron@CanadaSam.ca","","","" 10 | "Paul Ution","Paul.Ution@CanadaSam.ca","","","" 11 | "Jean Aimarre","Jean.Aimarre@CanadaSam.ca","","","" 12 | "Phil Danstachambre","Phil.Danstachambre@CanadaSam.ca","","","" 13 | "Pat Icier","Pat.Icier@CanadaSam.ca","","","" 14 | "Kelly Diossi","Kelly.Diossi@CanadaSam.ca","","","" 15 | "Harry Cobeure","Harry.Cobeure@CanadaSam.ca","","","" 16 | "Sarah Croche","Sarah.Croche@CanadaSam.ca","","","" 17 | "Anne Orak","Anne.Orak@CanadaSam.ca","","","" 18 | "Otto Matik","Otto.Matik@CanadaSam.ca","","","" 19 | "Olga Minpleure","Olga.Minpleure@CanadaSam.ca","","","" 20 | "Abel Auboisdormant","Abel.Auboisdormant@CanadaSam.ca","","","" 21 | "Thomas Toketchup","Thomas.Toketchup@CanadaSam.ca","","","" 22 | "Alain Tuission","Alain.Tuission@CanadaSam.ca","","","" 23 | "Alex Pyration","Alex.Pyration@CanadaSam.ca","","","" 24 | -------------------------------------------------------------------------------- /Import-MailboxFASAPermissions.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | Imports mailbox Full Access, Send As and Send On Behalf of permissions 5 | from a CSV file. 6 | 7 | .DESCRIPTION 8 | This script Imports mailbox Full Access, Send As and Send On Behalf of 9 | permissions from a CSV file. The input CSV file must have the following headers : 10 | 11 | "DisplayName","PrimarySMTPAddress","SendAsPermissions","FullAccessPermissions","SendOnBehalfPermissions" 12 | 13 | The "DisplayName" header is optional, we just need to be able to idendify the 14 | mailbox into which we need to assign Full Access and/or Send-As and/or Send On Behalf 15 | permissions. Usually the SMTP address uniquely identify a mailbox in an organization, but 16 | since the script uses standard Exchange Management Shell cmdlets, we can also use any 17 | other value that Get-Mailbox understands and that uniquely identifies a mailbox. 18 | 19 | As per Get-Mailbox help, you can use any value that uniquely identifies the mailbox. 20 | For example: 21 | Name 22 | Display name 23 | Alias 24 | Distinguished name (DN) 25 | Canonical DN 26 | \ 27 | Email address 28 | GUID 29 | LegacyExchangeDN 30 | SamAccountName 31 | User ID or user principal name (UPN) 32 | 33 | 34 | .PARAMETER InputFile 35 | Choose the CSV file form you want to import the permissions map from. 36 | For smooth use, use the CSV file that was generated with the Export-MailboxFASAPermissions.ps1. 37 | If for some reason you want to manually create this CSV file must have the following headers: 38 | 39 | "DisplayName","PrimarySMTPAddress","SendAsPermissions","FullAccessPermissions","SendOnBehalfPermissions" 40 | 41 | The mailboxes identified by the PrimarySMTPADdress values must exist in the environment where you 42 | wish to import the permissions. 43 | The *SendAsPermissions* column must store one or more valid USERS in a Domain\alias format, and if we 44 | have more than one value, these must be separated by semicolons ";" 45 | The *FullAccessPermissions* column must use the same USERS format as the "SendAsPermissions" column. 46 | The *SendOnBehalfPermissions* column is the trickiest as it must contain the Domain/OU path where the user 47 | is located. It HAS to be mailbox-enabled or a mail-enabled user/security group ! 48 | From the docs.microsoft link: 49 | "The sender you specify for this parameter must a mailbox, mail user or mail-enabled security group" 50 | 51 | .PARAMETER Execute 52 | Without this parameter, the script just prints the command that the script 53 | would execute to restore the permissions. This is also a good way to troubleshoot 54 | if anything go wrong when we try to apply the changes. 55 | 56 | .PARAMETER CheckVersion 57 | This is just to check the script version. 58 | 59 | .INPUTS 60 | You must specify an input CSV file (see InputFile parameter) 61 | 62 | .OUTPUTS 63 | Script Log ... 64 | 65 | .EXAMPLE 66 | .\Import-MailboxFASAPermissions.ps1 -InputFile C:\temp\ContosoPermissionsMAP.csv 67 | Will parse the ContosoPermissionsMAP.csv file and apply the permissions defined 68 | in the file to the mailboxes defined in this input CSV file. 69 | 70 | .EXAMPLE 71 | .\Launch-YourScript.ps1 -CheckVersion 72 | Like in all my other scripts, this only dumps the script's version 73 | 74 | .NOTES 75 | "Sens As" permissions 76 | . Stored in the form of "DOMAIN\Alias" 77 | . Is set with Add-ADPermission 78 | . https://docs.microsoft.com/en-us/powershell/module/exchange/active-directory/Add-ADPermission?view=exchange-ps 79 | 80 | "Full Access" Permissions 81 | . Stored in the form of "DOMAIN\Alias" as well 82 | . Is set with Add-MailboxPermission 83 | . https://docs.microsoft.com/en-us/powershell/module/exchange/mailboxes/Add-MailboxPermission?view=exchange-ps 84 | 85 | "Send On Behalf Of" permissions 86 | . Stored in the form of "Domain.com/OU_Name/Sub_OU/Name" 87 | . Is set with Set-Mailbox 88 | . https://docs.microsoft.com/en-us/powershell/module/exchange/mailboxes/Set-Mailbox?view=exchange-ps 89 | . -GrantSendOnBehalfTo parameter accepts one or more values from the below : 90 | Display name 91 | Alias 92 | Distinguished name (DN) 93 | Canonical DN 94 | \ 95 | Email address 96 | GUID 97 | LegacyExchangeDN 98 | SamAccountName 99 | User ID or user principal name (UPN) 100 | 101 | .LINK 102 | https://github.com/SammyKrosoft 103 | #> 104 | [CmdLetBinding(DefaultParameterSetName = "NormalRun")] 105 | Param( 106 | [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "NormalRun")][string]$InputFile=".\sample.csv", 107 | [Parameter(Mandatory = $false, Position = 2, ParameterSetName = "NormalRun")][switch]$Execute, 108 | [Parameter(Mandatory = $false, Position = 3, ParameterSetName = "NormalRun")][string]$TargetDomain, 109 | [Parameter(Mandatory = $false, Position = 4, ParameterSetName = "CheckOnly")][switch]$CheckVersion 110 | ) 111 | 112 | <# ------- SCRIPT_HEADER (Only Get-Help comments and Param() above this point) ------- #> 113 | #Initializing a $Stopwatch variable to use to measure script execution 114 | $stopwatch = [system.diagnostics.stopwatch]::StartNew() 115 | #Using Write-Debug and playing with $DebugPreference -> "Continue" will output whatever you put on Write-Debug "Your text/values" 116 | # and "SilentlyContinue" will output nothing on Write-Debug "Your text/values" 117 | $DebugPreference = "Continue" 118 | # Set Error Action to your needs 119 | $ErrorActionPreference = "SilentlyContinue" 120 | #Script Version 121 | $ScriptVersion = "0.1" 122 | <# Version changes 123 | v0.1 : first script version 124 | v0.1 -> v0.5 : 125 | #> 126 | $ScriptName = $MyInvocation.MyCommand.Name 127 | If ($CheckVersion) {Write-Host "SCRIPT NAME : $ScriptName `nSCRIPT VERSION : $ScriptVersion";exit} 128 | # Log or report file definition 129 | # NOTE: use $PSScriptRoot in Powershell 3.0 and later or use $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition in Powershell 2.0 130 | $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition 131 | $OutputReport = "$($ScriptPath)\$($ScriptName)_$(get-date -f yyyy-MM-dd-hh-mm-ss).csv" 132 | # Other Option for Log or report file definition (use one of these) 133 | $ScriptLog = "$($ScriptPath)\$($ScriptName)-$(Get-Date -Format 'dd-MMMM-yyyy-hh-mm-ss-tt').txt" 134 | <# ---------------------------- /SCRIPT_HEADER ---------------------------- #> 135 | <# -------------------------- DECLARATIONS -------------------------- #> 136 | [array]$CSVFileRequiredHeaders = "PrimarySMTPAddress", "SendAsPErmissions", "FullAccessPermissions", "SendOnBehalfPermissions" 137 | 138 | <# /DECLARATIONS #> 139 | <# -------------------------- FUNCTIONS -------------------------- #> 140 | #region Functions 141 | function Write-Log { 142 | <# 143 | .SYNOPSIS 144 | Function to log input string to file and display it to screen 145 | 146 | .DESCRIPTION 147 | Function to log input string to file and display it to screen. Log entries in the log file are time stamped. Function allows for displaying text to screen in different colors. 148 | 149 | .PARAMETER String 150 | The string to be displayed to the screen and saved to the log file 151 | 152 | .PARAMETER Color 153 | The color in which to display the input string on the screen 154 | Default is White 155 | Valid options are 156 | Black 157 | Blue 158 | Cyan 159 | DarkBlue 160 | DarkCyan 161 | DarkGray 162 | DarkGreen 163 | DarkMagenta 164 | DarkRed 165 | DarkYellow 166 | Gray 167 | Green 168 | Magenta 169 | Red 170 | White 171 | Yellow 172 | 173 | .PARAMETER LogFile 174 | Path to the file where the input string should be saved. 175 | Example: c:\log.txt 176 | If absent, the input string will be displayed to the screen only and not saved to log file 177 | 178 | .EXAMPLE 179 | Write-Log -String "Hello World" -Color Yellow -LogFile c:\log.txt 180 | This example displays the "Hello World" string to the console in yellow, and adds it as a new line to the file c:\log.txt 181 | If c:\log.txt does not exist it will be created. 182 | Log entries in the log file are time stamped. Sample output: 183 | 2014.08.06 06:52:17 AM: Hello World 184 | 185 | .EXAMPLE 186 | Write-Log "$((Get-Location).Path)" Cyan 187 | This example displays current path in Cyan, and does not log the displayed text to log file. 188 | 189 | .EXAMPLE 190 | "$((Get-Process | select -First 1).name) process ID is $((Get-Process | select -First 1).id)" | Write-Log -color DarkYellow 191 | Sample output of this example: 192 | "MDM process ID is 4492" in dark yellow 193 | 194 | .EXAMPLE 195 | Write-Log 'Found',(Get-ChildItem -Path .\ -File).Count,'files in folder',(Get-Item .\).FullName Green,Yellow,Green,Cyan .\mylog.txt 196 | Sample output will look like: 197 | Found 520 files in folder D:\Sandbox - and will have the listed foreground colors 198 | 199 | .LINK 200 | https://superwidgets.wordpress.com/2014/12/01/powershell-script-function-to-display-text-to-the-console-in-several-colors-and-save-it-to-log-with-timedate-stamp/ 201 | 202 | .NOTES 203 | Function by Sam Boutros 204 | v1.0 - 08/06/2014 205 | v1.1 - 12/01/2014 - added multi-color display in the same line 206 | v1.2 - 8 August 2016 - updated date time stamp format, protect against bad LogFile name 207 | 208 | #> 209 | 210 | [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] 211 | Param( 212 | [Parameter(Mandatory=$true, 213 | ValueFromPipeLine=$true, 214 | ValueFromPipeLineByPropertyName=$true, 215 | Position=0)] 216 | [String[]]$String, 217 | [Parameter(Mandatory=$false, 218 | Position=1)] 219 | [ValidateSet("Black","Blue","Cyan","DarkBlue","DarkCyan","DarkGray","DarkGreen","DarkMagenta","DarkRed","DarkYellow","Gray","Green","Magenta","Red","White","Yellow")] 220 | [String[]]$Color = "Green", 221 | [Parameter(Mandatory=$false, 222 | Position=2)] 223 | [String]$LogFile = $ScriptLog, 224 | [Parameter(Mandatory=$false, 225 | Position=3)] 226 | [Switch]$NoNewLine 227 | ) 228 | 229 | 230 | $LegalFileNameCharSet = "^[" + [Regex]::Escape("A-Za-z0-9^&'@{}[],$=!-#()%.+~_") + "]+$" 231 | if ($String.Count -gt 1) { 232 | $i=0 233 | foreach ($item in $String) { 234 | if ($Color[$i]) { $col = $Color[$i] } else { $col = "White" } 235 | Write-Host "$item " -ForegroundColor $col -NoNewline 236 | $i++ 237 | } 238 | if (-not ($NoNewLine)) { Write-Host " " } 239 | } else { 240 | if ($NoNewLine) { Write-Host $String -ForegroundColor $Color[0] -NoNewline } 241 | else { Write-Host $String -ForegroundColor $Color[0] } 242 | } 243 | 244 | if ($LogFile.Length -gt 2 -and !($LogFile -match $LegalFileNameCharSet)) { 245 | "$(Get-Date -format 'dd MMMM yyyy hh:mm:ss tt'): $($String -join " ")" | Out-File -Filepath $Logfile -Append 246 | } else { 247 | Write-debug "Log: Missing -LogFile parameter or bad LogFile name. Will not save input string to log file.." 248 | } 249 | } 250 | 251 | function _Progress { 252 | param( 253 | [parameter(position = 0)] $Id = 1, 254 | [parameter(position = 1)] $PercentComplete=100, 255 | [parameter(position = 2)] $Activity = "Working...", 256 | [parameter(position = 3)] $Status="In Progress..." 257 | ) 258 | 259 | Write-Progress -id $Id -activity $Activity -status $Status -PercentComplete ($PercentComplete) 260 | } 261 | 262 | Function Test-ExchTools(){ 263 | Try 264 | { 265 | Get-command Get-mailbox -ErrorAction Stop 266 | $ExchInstalledStatus = $true 267 | $Message = "Exchange tools are present !" 268 | Write-Host $Message -ForegroundColor Blue -BackgroundColor Red 269 | } 270 | Catch [System.SystemException] 271 | { 272 | $ExchInstalledStatus = $false 273 | $Message = "Exchange Tools are not present !" 274 | Write-Host $Message -ForegroundColor red -BackgroundColor Blue 275 | Exit 276 | } 277 | Return $ExchInstalledStatus 278 | } 279 | 280 | function IsEmpty($Param){ 281 | If ($Param -eq "All" -or $Param -eq "" -or $Param -eq $Null -or $Param -eq 0) { 282 | Return $True 283 | } Else { 284 | Return $False 285 | } 286 | } 287 | 288 | 289 | <# 290 | .EXAMPLE 291 | ValidateHeadersFromCSV -FilePathAndName ".\sample.csv" -CSVFilerequiredHeaders "PrimarySMTPAddress", "SendAsPErmissions", "FullAccessPermissions", "SendOnBehalfPermissions" 292 | #> 293 | 294 | Function ValidateHeadersFromCSV { 295 | Param( 296 | [Parameter(Mandatory=$true, Position = 0, ParameterSetName = "NormalRun")][string]$FilePathAndName, 297 | [Parameter(Mandatory =$true, Position = 1, ParameterSetName = "NormalRun")][string[]]$CSVFilerequiredHeaders 298 | ) 299 | $DuplicateHeader = $false 300 | $MissingHeader = $false 301 | If (!(Test-Path $FilePathAndName)){ 302 | $MsgErrFileNotFound = "The file $InputFile is incorrect or doesn't exist ... please try again with another file or the correct path." 303 | Write-Host $MsgErrFileNotFound -BackgroundColor Yellow -ForegroundColor Red 304 | Return $false 305 | } Else { 306 | #Get the first line of the CSV file => THIS is what we will validate 307 | [string[]]$HeadersFromFile = (Get-content -Path $FilePathAndName | Select -first 1).Split(",") 308 | $HeadersFromFile = $HeadersFromFile.TrimStart() 309 | # $CSVFilerequiredHeaders 310 | # $CSVFilerequiredHeaders.count 311 | # $HeadersFromFile; 312 | # $HeadersFromFile.count 313 | 314 | # exit 315 | #Putting message in a variable to do localization in the future (French + English) 316 | $msgValidatingCSVHeader = "Validating the CSV headers..." 317 | Write-host $msgValidatingCSVHeader -BackgroundColor yellow -ForegroundColor blue 318 | #Parsing CSV required file headers coming from parameter 319 | #3 cases : 1 matching header in the file for each required header, 1 of the headers is missing, or we have duplicate headers 320 | Foreach ($RequiredHeader in $CSVFilerequiredHeaders) { 321 | Write-Host "Checking $RequiredHeader" -BackgroundColor Green 322 | #Header counter to identify whether we have no matches, one match, or more than one 323 | $HeaderMatch = 0 324 | #We compare each CSV required header with each header of the file -> 3 cases : 1 match (wanted), 0 matches (CSV file not valid) or more than 1 matches (duplicates in CSV headers, CSV File not valid) 325 | Foreach ($FileHeader in $HeadersFromFile){ 326 | if($($FileHeader) -eq $RequiredHeader -or $($FileHeader) -eq """$RequiredHeader"""){$HeaderMatch++} 327 | } 328 | If ($HeaderMatch -eq 1){ 329 | $msgFound1Match = "Ok" 330 | Write-Host $msgFound1Match -BackgroundColor green -ForegroundColor black 331 | } ElseIf($headerMatch -eq 0) { 332 | $msgErrMissingRequiredHeader = "$RequiredHeader not found in file or there are trailing space characters after $RequiredHeader! Please correct your CSV or select another CSV file." 333 | Write-Host $msgErrMissingRequiredHeader -ForegroundColor Red 334 | $MissingHeader = $true 335 | [array]$MissingHeaderDetails += $RequiredHeader 336 | } Else { 337 | $msgErrDuplicateRequiredHeader = "Cannot have more than 1 header named $RequiredHeader - please correct your CSV or select another CSV." 338 | Write-Host $msgErrDuplicateRequiredHeader -ForegroundColor Red 339 | $DuplicateHeader = $true 340 | [array]$DuplicateHeaderDetails += $RequiredHeader 341 | } 342 | } 343 | } 344 | If ($Missingheader -or $DuplicateHeader){ 345 | If ($MissingHeader){ 346 | $msgMissingHeader = "Missing Headers in file or space characters after Headers in the file: $($MissingHeaderDetails -join ", "), please use a CSV file with proper headers" 347 | Write-Host $msgMissingHeader -BackgroundColor yellow -ForegroundColor red 348 | } 349 | If ($DuplicateHeader){ 350 | $msgDuplicateHeader = "Duplicate Headers in file : $($DuplicateHeaderDetails -join ", "), please use a CSV file with proper headers" 351 | Write-Host $msgDuplicateHeader -BackgroundColor yellow -ForegroundColor red 352 | } 353 | return $False 354 | }Else{ 355 | Return $True 356 | } 357 | } 358 | 359 | <# /FUNCTIONS #> 360 | #endregion Functions 361 | <# /FUNCTIONS #> 362 | <# -------------------------- EXECUTIONS -------------------------- #> 363 | 364 | If (!(Test-Path $InputFile)){ 365 | $MsgErrFileNotFound = "The file $InputFile is incorrect or doesn't exist ... please try again with another file or the correct path." 366 | Write-Host $MsgErrFileNotFound -BackgroundColor Yellow -ForegroundColor Red 367 | Exit 368 | } Else { 369 | If(!(ValidateHeadersFromCSV -FilePathAndName $InputFile -CSVFilerequiredHeaders $CSVFileRequiredHeaders)){exit} 370 | $PermissionsMAP = Import-CSV $InputFile 371 | } 372 | 373 | ############################## Processing the MAP to re-add permissions the the target objects ########################## 374 | Foreach ($Item in $PermissionsMAP) { 375 | #Retrieve each mailbox object and storing it in a temporary variable 376 | If(!(Isempty $Item.PrimarySMTPAddress)){ 377 | $strCmd1 = "$CurrentMailbox = Get-Mailbox $($item.PrimarySMTPAddress)" 378 | If(!$Execute){ 379 | Write-Host $strCmd1 -Green 380 | } else { 381 | Invoke-Command $strCmd1 382 | } 383 | } 384 | #Processing the list of Send As Permissions ... 385 | # . Stored in the form of "DOMAIN\Alias" 386 | # . Is set with Add-ADPermission 387 | If(!(Isempty $Item.SendAsPermissions)){ 388 | $ListOfSendAsTemp = $Item.SendAsPermissions -split ";" 389 | $ListOfSendAsTemp 390 | Foreach ($User in $ListOfSendAsTemp){ 391 | $NewOrOldDomainUser = $User 392 | If (!(IsBlank $TargetDomain)){ 393 | $temp = $NewOrOldDomainUser -split "\" 394 | $NewOrOldDomainUser = $TargetDomain + "\" + $Temp[1] 395 | } Else {<#Else ... $TargetDomain is blank and then we import the permissions in the same domain defined in the permission entries as DOMAIN\Alias#>} 396 | $strCmd2 = "Get-Mailbox $($Item.PrimarySMTPAddress) | Add-ADPermission -User $NewOrOldDomainUser -ExtendedRights ""Send As""" 397 | If(!$Execute){ 398 | Write-Host $strCmd2 -ForegroundColor Green 399 | } Else { 400 | Invoke-Expression $strCmd2 401 | } 402 | } 403 | } 404 | #Processing the list of Full Access permissions ... 405 | # . Stored in the form of "DOMAIN\Alias" as well 406 | # . Is set with Add-MailboxPermission 407 | If(!(Isempty $Item.FullAccessPermissions)){ 408 | $ListOfFullAccessTemp = $Item.FullAccessPermissions -split ";" 409 | $ListOfFullAccessTemp 410 | Foreach ($User in $ListOfFullAccessTemp){ 411 | $NewOrOldDomainUser = $User 412 | If (!(IsBlank $TargetDomain)){ 413 | $temp = $NewOrOldDomainUser -split "\" 414 | $NewOrOldDomainUser = $TargetDomain + "\" + $Temp[1] 415 | } Else {<#Else ... $TargetDomain is blank and then we import the permissions in the same domain defined in the permission entries as DOMAIN\Alias#>} 416 | $strCmd3 = "Add-MailboxPermission -Identity $($Item.PrimarySMTPAddress) -User $NewOrOldDomainUser -AccessRights FullAccess" 417 | #Add -AutoMapping $false if you want to disable automapping ... 418 | If(!$Execute){ 419 | Write-Host $strCmd3 -ForegroundColor Green 420 | } Else { 421 | Invoke-Expression $strCmd3 422 | } 423 | } 424 | } 425 | #Processing the list of Send On Behalf permissions... 426 | # . Stored in the form of "Domain.com/OU_Name/Sub_OU/Name" 427 | # . Is set with Set-Mailbox 428 | If(!(Isempty $Item.SendOnBehalfPermissions)){ 429 | $ListOfSendOnBehalfTemp = $Item.SendOnBehalfPermissions -split ";" 430 | $ListOfSendOnBehalfTemp 431 | } 432 | } 433 | 434 | 435 | <# /EXECUTIONS #> 436 | <# -------------------------- CLEANUP VARIABLES -------------------------- #> 437 | $PermissionsMAP = $null 438 | <# /CLEANUP VARIABLES#> 439 | <# ---------------------------- SCRIPT_FOOTER ---------------------------- #> 440 | #Stopping StopWatch and report total elapsed time (TotalSeconds, TotalMilliseconds, TotalMinutes, etc... 441 | $stopwatch.Stop() 442 | Write-Host "`n`nThe script took $($StopWatch.Elapsed.TotalSeconds) seconds to execute..." 443 | $StopWatch = $null 444 | <# ---------------- /SCRIPT_FOOTER (NOTHING BEYOND THIS POINT) ----------- #> 445 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Export-Import-Full-Access-SendAs-SendOnBehalfTo 2 | Partner scripts to export, and/or import Full Access, Send As, Send On Behalf of permissions on an Exchange environment (tested on Exchange 2010, 2013, 2016, 2019) 3 | 4 | # Download latest version 5 | 6 | [EXPORT SCRIPT - Right-Click this link and chose "Save link as" to save the PowerShell script](https://raw.githubusercontent.com/SammyKrosoft/Export-Import-Full-Access-SendAs-SendOnBehalfTo/master/Export-MailboxFASAPermissions.ps1) 7 | 8 | [IMPORT SCRIPT (Alpha version, needs additional testing) - Right-Click this link and chose "Save link as" to save the PowerShell script](https://raw.githubusercontent.com/SammyKrosoft/Export-Import-Full-Access-SendAs-SendOnBehalfTo/master/Import-MailboxFASAPermissions.ps1) 9 | -------------------------------------------------------------------------------- /Validate-CSVHeaders.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | .EXAMPLE 4 | ValidateHeadersFromCSV -FilePathAndName ".\sample.csv" -CSVFilerequiredHeaders "PrimarySMTPAddress", "SendAsPErmissions", "FullAccessPermissions", "SendOnBehalfPermissions" 5 | #> 6 | 7 | Function ValidateHeadersFromCSV { 8 | Param( 9 | [Parameter(Mandatory=$true, Position = 0, ParameterSetName = "NormalRun")][string]$FilePathAndName, 10 | [Parameter(Mandatory =$true, Position = 1, ParameterSetName = "NormalRun")][string[]]$CSVFilerequiredHeaders 11 | ) 12 | $DuplicateHeader = $false 13 | $MissingHeader = $false 14 | If (!(Test-Path $FilePathAndName)){ 15 | $MsgErrFileNotFound = "The file $InputFile is incorrect or doesn't exist ... please try again with another file or the correct path." 16 | Write-Host $MsgErrFileNotFound -BackgroundColor Yellow -ForegroundColor Red 17 | Return $false 18 | } Else { 19 | #Get the first line of the CSV file => THIS is what we will validate 20 | [string[]]$HeadersFromFile = (Get-content -Path $FilePathAndName | Select -first 1).Split(",") 21 | $HeadersFromFile = $HeadersFromFile.TrimStart() 22 | # $CSVFilerequiredHeaders 23 | # $CSVFilerequiredHeaders.count 24 | # $HeadersFromFile; 25 | # $HeadersFromFile.count 26 | 27 | # exit 28 | #Putting message in a variable to do localization in the future (French + English) 29 | $msgValidatingCSVHeader = "Validating the CSV headers..." 30 | Write-host $msgValidatingCSVHeader -BackgroundColor yellow -ForegroundColor blue 31 | #Parsing CSV required file headers coming from parameter 32 | #3 cases : 1 matching header in the file for each required header, 1 of the headers is missing, or we have duplicate headers 33 | Foreach ($RequiredHeader in $CSVFilerequiredHeaders) { 34 | Write-Host "Checking $RequiredHeader" -BackgroundColor Green 35 | #Header counter to identify whether we have no matches, one match, or more than one 36 | $HeaderMatch = 0 37 | #We compare each CSV required header with each header of the file -> 3 cases : 1 match (wanted), 0 matches (CSV file not valid) or more than 1 matches (duplicates in CSV headers, CSV File not valid) 38 | Foreach ($FileHeader in $HeadersFromFile){ 39 | if($($FileHeader) -eq $RequiredHeader -or $($FileHeader) -eq """$RequiredHeader"""){$HeaderMatch++} 40 | } 41 | If ($HeaderMatch -eq 1){ 42 | $msgFound1Match = "Ok" 43 | Write-Host $msgFound1Match -BackgroundColor green -ForegroundColor black 44 | } ElseIf($headerMatch -eq 0) { 45 | $msgErrMissingRequiredHeader = "$RequiredHeader not found in file or there are trailing space characters after $RequiredHeader! Please correct your CSV or select another CSV file." 46 | Write-Host $msgErrMissingRequiredHeader -ForegroundColor Red 47 | $MissingHeader = $true 48 | [array]$MissingHeaderDetails += $RequiredHeader 49 | } Else { 50 | $msgErrDuplicateRequiredHeader = "Cannot have more than 1 header named $RequiredHeader - please correct your CSV or select another CSV." 51 | Write-Host $msgErrDuplicateRequiredHeader -ForegroundColor Red 52 | $DuplicateHeader = $true 53 | [array]$DuplicateHeaderDetails += $RequiredHeader 54 | } 55 | } 56 | } 57 | If ($Missingheader -or $DuplicateHeader){ 58 | If ($MissingHeader){ 59 | $msgMissingHeader = "Missing Headers in file or space characters after Headers in the file: $($MissingHeaderDetails -join ", "), please use a CSV file with proper headers" 60 | Write-Host $msgMissingHeader -BackgroundColor yellow -ForegroundColor red 61 | } 62 | If ($DuplicateHeader){ 63 | $msgDuplicateHeader = "Duplicate Headers in file : $($DuplicateHeaderDetails -join ", "), please use a CSV file with proper headers" 64 | Write-Host $msgDuplicateHeader -BackgroundColor yellow -ForegroundColor red 65 | } 66 | return $False 67 | }Else{ 68 | Return $True 69 | } 70 | } 71 | 72 | 73 | ValidateHeadersFromCSV -FilePathAndName ".\sample.csv" -CSVFilerequiredHeaders "PrimarySMTPAddress", "SendAsPErmissions", "FullAccessPermissions", "SendOnBehalfPermissions" -------------------------------------------------------------------------------- /sample.csv: -------------------------------------------------------------------------------- 1 | DisplayName, PrimarySMTPAddress, SendAsPermissions, FullAccessPermissions, SendOnBehalfPermissions 2 | Discovery Search Mailbox,MsExchDiscoveryMailboxD919BA05-46A6-415f-80AD-7E09334BB852@E2010Domain.com,,E2010DOMAIN\Discovery Management, 3 | RoomTest1 Ottawa,RoomTest1.Ottawa@E2010Domain.com,E2010DOMAIN\Administrator;E2010DOMAIN\Test;E2010DOMAIN\User14,E2010DOMAIN\Test,E2010Domain.com/UserIds/OIC/l_beaupin;E2010Domain.com/Users/Test Canada 4 | --------------------------------------------------------------------------------