When you migrate messages from an alternative email solution (e.g. Lotus Notes) you might migrate sensitive content that must stay private in the new Exchange Server target location.
So how can you mark such messages as private?
The privacy level (Sensitivity) of a mailbox item is controlled by MAPI extended property 0x36.
The command line tool searches for messages containing a given text as a subject substring.
The c# code sets the extended property 0x36 to 2.
A mailbox is accessed using Exchange Web Services. The EWS endpoint is discovered using AutoDiscover for the selected mailbox.
The item modification is handled by the following code segment:
foreach (var extendedProperty in Message.ExtendedProperties) { if (extendedProperty.PropertyDefinition == extendedPropertyDefinition) { if (log.IsInfoEnabled) { log.Info(string.Format("Try to alter the message: {0}", Message.Subject)); } else { Console.WriteLine("Try to alter the message: {0}", Message.Subject); } // Set the value of the extended property (0 is Sensitivity normal, 2 would be private) Message.ExtendedProperties[extendedPropertyindex].Value = 2; // Update the item on the server with the new client-side value of the target extended property Message.Update(ConflictResolutionMode.AlwaysOverwrite); } extendedPropertyindex++; }
SetPrivateFlags.exe -mailbox user@domain.com -subject "[private]"
Search the mailbox for all messages having a subject string containing [private] and ask for changing each item if -logonly is not set to true. If -logonly is set to true only a log will be created.
SetPrivateFlags.exe -mailbox user@domain.com -subject "[private]" -noconfirmation
Search the mailbox for all messages having a subject string containing [private] and change all found messages without confirmation.
It should be noted that this solution is intended for use in migration scenarios.
When providing access to mailbox delegates you can enable access to your private elements as well. But access to shared mailboxes is not configured using the delegation workflow.
The code has been tested using Exchange Server 2013 CU15.
The program utilizes log4net to log detailed information to the file system. The configuration is controlled by the application's config file.
Any issues or feature requests? Use Github.
Like the code? Leave a note.
An update to the PowerShell script (Set-ReceiveConnectorIpAddress) to add or remove remote IP address ranges to/from Exchange Server receive connectors is available.
A new parameter to provide a comment on why an IP address is added or removed has been added to the script.
# EXAMPLE .\Set-ReceiveConnectorIpAddress.ps1 -ConnectorName MyConnector -IpAddress 10.10.10.1 -Action Remove -ViewEntireForest $true -Comment 'Personal request of upper management'
Get the most recent version at Github
This script adds or removes IP addresses or IP address ranges to/from existing Receive Connectors.
The input file can contain more than one IP address (range), one entry per line. The IP address parameter can be used to add a single IP address.
The script creates a new sub directory beneath the current location of the script. The script utilizes the directory as a log directory to store the current remote IP address ranges prior modification.
A log is written to the \log subfolder utilitzing the GlobalFunctions Logger object.
# Example 1 # Add all IP addresses stored in D:\Scripts\ip.txt to a receive connector named RelayConnector .\Set-ReceiveConnectorIpAddress.ps1 -ConnectorName RelayConnector -FileName D:\Scripts\ip.txt -Action Add
# Example 2 # Remove IP address 10.10.10.1 from a receive connector nameds MyConnector from all Exchange Servers in the forest .\Set-ReceiveConnectorIpAddress.ps1 -ConnectorName MyConnector -IpAddress 10.10.10.1 -Action Remove -ViewEntireForest $true
You want to know about the right on-premises Exchange Server architecture? A blog post about this topic has been published on the ESE blog yesterday.
Read the full blog post at ENow's ESE blog.
Enjoy reading.
The PowerShell script to purge Exchange Server and IIS log files has been updated to version 2.1.
The function Copy-LogFiles has been slightly rewritten and there has been a change in the cmdlet parameters.
When using ArchiveMode CopyAndZip or CopyZipAndDelete all copied log files in the EXCHANGESERVER\LOGS folder are added to a compressed archive. The script creates a separate archive for IIS and Exchange logs.
Code updated
function Copy-LogFiles { [CmdletBinding()] param( [string]$SourceServer, [string]$SourcePath, $FilesToMove, [string]$ArchivePrefix = '' ) if($SourceServer -ne '') { # path per SERVER for zipped archives $ServerRepositoryPath = Join-Path -Path $RepositoryRootPath -ChildPath $SourceServer # subfolder used as target for copying source folders and files $ServerRepositoryLogsPath = Join-Path -Path $ServerRepositoryPath -ChildPath $LogSubfolderName $ServerRepositoryPath = Join-Path -Path $RepositoryRootPath -ChildPath $SourceServer if(!(Test-Path -Path $ServerRepositoryPath)) { # Create new target directory for server, if does not exist $null = New-Item -Path $ServerRepositoryPath -ItemType Directory -Force -Confirm:$false } foreach ($File in $FilesToMove) { # target directory $targetDir = $File.DirectoryName.Replace($TargetServerFolder, $ServerRepositoryLogsPath) # target file path $targetFile = $File.FullName.Replace($TargetServerFolder, $ServerRepositoryLogsPath) # create target directory, if not exists if(!(Test-Path -Path $targetDir)) {$null = mkdir -Path $targetDir} # copy file to target $null = Copy-Item -Path $File.FullName -Destination $targetFile -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue } if($ZipArchive) { # zip copied log files $Archive = Join-Path -Path $ServerRepositoryPath -ChildPath ('{0}-{1}' -f $ArchivePrefix, $ArchiveFileName) $logger.Write(('Zip copied files to {0}' -f $ArchiveFileName)) # delete archive file, if already exists if(Test-Path -Path $Archive) {Remove-Item -Path $Archive -Force -Confirm:$false} try { # create zipped asrchive Add-Type -AssemblyName 'System.IO.Compression.FileSystem' [IO.Compression.ZipFile]::CreateFromDirectory($ServerRepositoryLogsPath,$Archive) } catch { $logger.Write(('Error compressing files from {0} to {1}' -f $ServerRepositoryLogsPath, $Archive),3) } finally { # cleanup, if compression was successful if($DeleteZippedFiles) { $logger.Write(('Deleting folder {0}' -f $ServerRepositoryLogsPath)) $null = Remove-Item -Path $ServerRepositoryLogsPath -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue } } } } }