When you encounter a situation where the transport queues are filling up or you just want to get messages out of a transport queue for further usage, you have to export the messages to the file system.
With Exchange 2007 you could easily utilize the Export-Message cmdlet to export suspended messages from a transport queue:
Get-Message -Queue MYSERVER\29489 | ?{$_.Status -eq "Suspended"} | Export-Message -Path D:\MessageExport
With Exchange Server 2010 or newer the Path attribute had been removed from the Export-Message cmdlet. The cmdlet now returns a binary object that needs to be assembled to a readable text message.
You can only export suspended messages, as the transport service might take precedence on non suspended messages. You can either suspend the queue or suspend single messages. The following example for Exchange Server 2010 or newer suspends the messages, but not the queue itself.
Identify the queue holding messages to be exported
Beispiel: Auflistung der Warteschlage auf Server MCMEP01
Get-Queue -Server MCMEP01 Identity DeliveryType Status MessageCount Velocity RiskLevel OutboundIPPool NextHopDomain -------- ------------ ------ ------------ -------- --------- -------------- ------------- MCMEP01\18 SmtpDeliv... Ready 0 0 Normal 0 MXEDB19 MCMEP01\23 SmtpDeliv... Ready 0 0 Normal 0 MXEDB08 MCMEP01\24 SmtpDeliv... Ready 0 0 Normal 0 MXEDB01 MCMEP01\25 SmartHost... Ready 3 0 Normal 0 [10.10.11.1] MCMEP01\53 SmtpDeliv... Ready 0 0 Normal 0 MXEDB03 MCMEP01\Submission Undefined Ready 512 0 Normal 0 Submission MCMEP01\Shadow\3 ShadowRed... Ready 2 0 Normal 0 MCMEP04.mcsmemail.de MCMEP01\Shadow\4 ShadowRed... Ready 2 0 Normal 0 MCMEP03.mcsmemail.de MCMEP01\Shadow\5 ShadowRed... Ready 3 0 Normal 0 MCMEP02.mcsmemail.de MCMEP01\Shadow\6 ShadowRed... Ready 2 0 Normal 0 MCMEP07.mcsmemail.de MCMEP01\Shadow\15 ShadowRed... Ready 1 0 Normal 0 MCMEP08.mcsmemail.de
Suspend all Messages in Queue MCMEP01\Submission
Get-Queue MCMEP01\Submission | Get-Message ResultSize Unlimited | Suspend-Message
Fetch all messages to an array and export all messages to the local file system. You can either export all messages by just enumerating the messages or by using the message subject as the file name. Using the message subject posed the risk that the subject might contain a character that is not allowed for file names.
$array = @(Get-Message -Queue MCMEP01\Submission -ResultSize Unlimited | ?{$_.Status -eq "Suspened"}) $array | ForEach-Object {$m++;Export-Message $_.Identity | AssembleMessage -Path ("E:\Export\"+$m+".eml")} $array | ForEach-Object {$m++;$filename="E:\Export\"+$m+"_"+$_.subject+".eml"; Export-Message $_.identity | AssembleMessage -path $filename}
The messages exported to the local file system can now be copied to the Exchange Transport Replay folder, which exists on each Exchange Server having an Exchange 2010 Hub Transport role or Exchange 2013/2016 Mailbox role.
After successful export of all suspended messages you want to delete the suspended messages from the queue. Ensure to use -WithNDR $false as otherwise all senders will receive a Non Delivery Report (NDR).
Get-Queue MCMEP01\Submission | Get-Message -ResultSize Unlimited | ?{$_.Status -eq "Suspened"} | Remove-Message -WithNDR $false
Messages saved to the Replay folder will be picked up the transport service. When a messages is picked up, the file extension changes to .TMP. You will not be able to delete a file at this point as the file is locked by the transport service. After a message file has been processed successfully, the file is deleted by the transport service. If there is any issue with the message file the file extension will change to .BAD.
Once upon a time at an Exchange Conference near you, a member of the Exchange Product Group (PG) announced that the very last Exchange Server will go away when having an active Exchange hybrid setup.
This was a hot topic for discussions at the Microsoft Exchange Conferences (MEC, @IamMEC) in 2012 and 2014, already. Since then the Exchange PG came up with a number of reasons why this is not possible. The question on when we will finally be able to remove the very last Exchange Server from the on-premises Exchange organization was asked every year at the Ignite Conference.
Currently, the supported scenario for hybrid configurations between your on-premises Exchange organization and Exchange Online requires that you keep the last Exchange Server for creating, and managing Exchange related objects, even if those objects are located in Exchange Online.
The following diagram illustrates the current requirements:
In the past, there was communication on certain interim solutions that were supposed to support you in removing the last Exchange Server from your Exchange organization. Such interim solutions were:
At Ignite those solutions even made it into the official session catalog:
All those interim solutions leave your on-premises Exchange organization and the Active Directory configuration in an uncomfortable twilight-zone. It was still something that worked somehow, but you knew it was officially not supported, and the secure and stable operation of the hybrid configuration was at risk.
But wait...
Removing the last Exchange Server is supported! (at least when all components are released)
The new approach for managing your Exchange Online tenancy after migrating your on-premises Exchange organization to Exchange Online does not require an on-premises Exchange Server.
The new mode of operation reduces your on-premises requirements to:
The following diagram illustrates the new modern Exchange Online Management experience:
Simply you remove the requirement to use on-premises Exchange Server to write to your on-premises Active Directory. Instead, Azure AD Connect uses a new synchronization capability to handle the new Exchange Management experience in the AAD Connect MetaVerse. The on-premises AD-connector writes the changes to Active Directory which keeps the Active Directory up-to-date for all other on-premises solutions that require identities to have a proper state.
You execute all Exchange-related actions using the new Exchange Online Management PowerShell module, or, if needed, the new Modern Exchange Admin Center (EAC, which was announced at Ignite 2019.
Before you uninstall the last Exchange Server from your on-premises Exchange organization, ensure that you
PS C:\> Get-WindowsFeature Display Name Name Install State ------------ ---- ------------- [ ] Active Directory Certificate Services AD-Certificate Available [ ] Certification Authority ADCS-Cert-Authority Available [ ] Certificate Enrollment Policy Web Service ADCS-Enroll-Web-Pol Available [ ] Certificate Enrollment Web Service ADCS-Enroll-Web-Svc Available [ ] Certification Authority Web Enrollment ADCS-Web-Enrollment Available [ ] Network Device Enrollment Service ADCS-Device-Enrollment Available [ ] Online Responder ADCS-Online-Cert Available [ ] Active Directory Domain Services AD-Domain-Services Available [ ] Active Directory Federation Services ADFS-Federation Available [ ] Active Directory Lightweight Directory Services ADLDS Available [ ] Active Directory Rights Management Services ADRMS Available [ ] Active Directory Rights Management Server ADRMS-Server Available [ ] Identity Federation Support ADRMS-Identity Available [ ] Device Health Attestation DeviceHealthAttestat... Available [ ] DHCP Server DHCP Available [ ] DNS Server DNS Available [ ] Exchange Online Remote Features EXORemote Available [ ] Fax Server Fax Available [X] File and Storage Services FileAndStorage-Services Installed [X] File and iSCSI Services File-Services Installed [X] File Server FS-FileServer Installed [ ] BranchCache for Network Files FS-BranchCache Available [...]
PS C:\> Install-WindowsFeature -Name EXORemote Display Name Name Install State ------------ ---- ------------- [ ] Active Directory Certificate Services AD-Certificate Available [ ] Certification Authority ADCS-Cert-Authority Available [ ] Certificate Enrollment Policy Web Service ADCS-Enroll-Web-Pol Available [ ] Certificate Enrollment Web Service ADCS-Enroll-Web-Svc Available [ ] Certification Authority Web Enrollment ADCS-Web-Enrollment Available [ ] Network Device Enrollment Service ADCS-Device-Enrollment Available [ ] Online Responder ADCS-Online-Cert Available [ ] Active Directory Domain Services AD-Domain-Services Available [ ] Active Directory Federation Services ADFS-Federation Available [ ] Active Directory Lightweight Directory Services ADLDS Available [ ] Active Directory Rights Management Services ADRMS Available [ ] Active Directory Rights Management Server ADRMS-Server Available [ ] Identity Federation Support ADRMS-Identity Available [ ] Device Health Attestation DeviceHealthAttestat... Available [ ] DHCP Server DHCP Available [ ] DNS Server DNS Available [X] Exchange Online Remote Features EXORemote Installed [ ] Fax Server Fax Available [X] File and Storage Services FileAndStorage-Services Installed [X] File and iSCSI Services File-Services Installed [X] File Server FS-FileServer Installed [ ] BranchCache for Network Files FS-BranchCache Available [...]
Even though not explicitly stated, you should restart the server after installing the Windows feature.
As part of the next AAD Connect synchronization cycle, the magic happens.
Verify that you can edit the Exchange related attributes of synchronized Active Directory objects in Exchange Online or Azure AD before you remove your last Exchange Server.
Whey ready to uninstall the last Exchange Server you must use the following command line parameters to remove the server as intended. Otherwise, you'll leave the Exchange organization in an inchoate state. Ensure that you use an administrative PowerShell session.
./Setup.exe /mode:uninstall /SwitchToMEMA /IAcceptExchangeOnlineLicenseTerms
Normally, you do not have to accept license terms when uninstalling Exchange Server, but in this case, you have to accept the Exchange Online license terms.
Enjoy the modern experience and management options of Exchange Online!
Exchange Conferences
This scripts helps to suspend all messages in an Exchange transport queue and to export all suspended messages to a given target folder.
The script uses the AssembleMessage cmdlet to properly export queued messages as .eml files.
Optionally, all exported messages can be removed from the transport queue.
This script requires the GlobalFunctions module for logging.
# EXAMPLE 1 # Export messages from queue MCMEP01\45534 to D:\ExportedMessages and do not delete messages after export .\Export-MessageQueue -Queue MCMEP01\45534 -Path D:\ExportedMessages # EXAMPLE 2 # Export messages from queue MCMEP01\45534 to D:\ExportedMessages and delete messages after export .\Export-MessageQueue -Queue MCMEP01\45534 -Path D:\ExportedMessages -DeleteAfterExport
As always: Test and familiarize yourself with the script in a test or development environment.
The use of Exchange Edge Transport Servers requires the synchronization of user and configuration data from internal Exchange Servers to the Edge Transport Servers. The synchronization utilizes secure LDAP (EdgeSync) to transmit the data securely and is based on an Edge Subscription.
When you create a new Edge Subscription on your internal Exchange Servers by importing the Edge Subscription XML-file, establishing the EdgeSync-connection might fail.
You will find the following error in the application event log of the internal Exchange Server:
Log Name: Application Source: MSExchange EdgeSync Event ID: 1035 Task Category: Synchronization Level: Error Keywords: Classic Description: EdgeSync failed to synchronize because it only supports Cryptographic API certificates. The local Hub Transport server's default certificate with thumbprint XYZ isn't a CAPI certificate. To set a CAPI certificate as the default certificate, use the Enable-ExchangeCertificate cmdlet with the Services parameter using the value of SMTP.
The private key of the current Exchange Transport default certificate of the internal Exchange servers uses a CNG private key. EdgeSync requires a CAPI1 based private key.
This problem occurs primarily when using an Enterprise Certificate Authority using certificate templates with individual template settings.
Get-TransportService | ft Name,InternalTransportCertificateThumbprint
certutil -v -store my > cert.txt
If both attribute are of the value 0, the certificate if a CNG certificate.
The section might look like this:
Unique container name: XYZ Provider = Microsoft Software Key Storage Provider ProviderType = 0 Flags = 20 (32) CRYPT_MACHINE_KEYSET -- 20 (32) KeySpec = 0 -- XCN_AT_NONE
Use OpenSSL to convert the CNG certificate to a CAPI1 certificate.
Using OpenSSL requires the download of the Windows release of OpenSSL. I recommend to not install the software on the Exchange Server but a separate Windows server or your administrative desktop system. Additionally, you need the certificate with its private key as a PFX-file.
Use the following steps to convert the CNG certificate to a CAPI1 certificate.
openssl pkcs12 -in CERT.pfx -out cert.pem -nodes
openssl pkcs12 -export -in cert.pem -out NEWCERT.pfx
The new PFX-file is now a CAPI1 certificate. The new certificate has the same thumbprint. Now you must replace the current certificate used by Exchange Server with the new certificate.
Replacing the certificate requires a downtime of each Exchange Server requiring the certificate replacement. This is due to the requirement to remove the CNG certificate first, following the import of the CAPI1 certificate. Afterward, you need to enable the required Exchange services.
Get-ExchangeCertificate -Server SERVERNAME
# It is mandatory to answer the query for replacing the default certificate with YES Enable-ExchangeCertificate -Thumbprint THUMBPRINT -Services SMTP # Restart the transport service Restart-Service MSExchangeTransport
# It is mandatory to answer the query for replacing the default certificate with YES Enable-ExchangeCertificate -Thumbprint NEWCERTTHUMBPRINT -Services SMTP # Restart the transport service Restart-Service MSExchangeTransport
Now, that you updated the local Exchange Servers there is one more step that needs to be checked on the Edge Transport Servers.
Edge Transport Servers are not domain-joined and therefore do not receive any GPO-based configuration. Each required configuration must be performed locally. To ensure that the default transport certificate of the internal Exchange servers can be used for cryptographic operations we must ensure that the certificate chain of that certificate is present in the certificate store of Edge Transport servers.
Take a look at the certificate chain of the converted CAPI1 certificate and import the Root-CA and Subordinate-CA certificates into the Edge Transport servers local certificate store. You must ensure that the certificates are placed into appropriate stores:
Next, you create a new Edge Subscription on your Edge Transport server and create a new subscription for the Active Directory site on the internal Exchange Server. The internal Exchange Servers are now able to establish an EdgeSync connection and encrypting the data transferred to the Edge Transport servers.
When you receive the TLS certificate as PFX/PKCS12 file, you import the certificate and the private key. The import process itself defines the priavte key Crypto Provider. Using the following command line you ensure that the import process suses the legacy crypto provider.
certutil -csp "Microsoft RSA SChannel Cryptographic Provider" -importpfx my MYCERTpfx
Enjoy Exchange Server and Edge Transport!
This is a wrap-up of an older post that had originally been published on my former website.
Even though that this post focusses on Exchange 2010 transport agents, you will get an understand on what is required to create an Exchange 2013/2016 aka Version 15 transport agent.
Writing your own transport agent for Exchange 2010 is not really complicated. With a Visual Studio C# Class project you are ready to go.
The follow picture shows the Visual Studio Solution as it has been used for the Message Modifier Solution.
Besides the C# class the solution contains the following Powershell script to simplify development and deployment:
The transport agent intercepts a message from a given sender address and performs the following actions:
// AttachmentModify // ---------------------------------------------------------- // Example for intercepting email messages in an Exchange 2010 transport queue // // The example intercepts messages sent from a configurable email address(es) // and checks the mail message for attachments have filename in to format // // WORKBOOK_{GUID} // // Changing the filename of the attachments makes it easier for the information worker // to identify the reports in the emails and in the file system as well. // Copyright (c) Thomas Stensitzki // ---------------------------------------------------------- using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Xml; // the lovely Exchange using Microsoft.Exchange.Data.Transport; using Microsoft.Exchange.Data.Transport.Smtp; using Microsoft.Exchange.Data.Transport.Email; using Microsoft.Exchange.Data.Transport.Routing; namespace SFTools.Messaging.AttachmentModify { #region Message Modifier Factory /// <summary> /// Message Modifier Factory /// </summary> public class MessageModifierFactory : RoutingAgentFactory { /// <summary> /// Instance of our transport agent configuration /// This is for a later implementation /// </summary> private MessageModifierConfig messageModifierConfig = new MessageModifierConfig(); /// <summary> /// Returns an instance of the agent /// </summary> /// <param name="server">The SMTP Server</param> /// <returns>The Transport Agent</returns> public override RoutingAgent CreateAgent(SmtpServer server) { return new MessageModifier(messageModifierConfig); } } #endregion #region Message Modifier Routing Agent /// <summary> /// The Message Modifier Routing Agent for modifying an email message /// </summary> public class MessageModifier : RoutingAgent { // The agent uses the fileLock object to synchronize access to the log file private object fileLock = new object(); /// <summary> /// The current MailItem the transport agent is handling /// </summary> private MailItem mailItem; /// <summary> /// This context to allow Exchange to continue processing a message /// </summary> private AgentAsyncContext agentAsyncContext; /// <summary> /// Transport agent configuration /// </summary> private MessageModifierConfig messageModifierConfig; /// <summary> /// Constructor for the MessageModifier class /// </summary> /// <param name="messageModifierConfig">Transport Agent configuration</param> public MessageModifier(MessageModifierConfig messageModifierConfig) { // Set configuration this.messageModifierConfig = messageModifierConfig; // Register an OnRoutedMessage event handler this.OnRoutedMessage += OnRoutedMessageHandler; } /// <summary> /// Event handler for OnRoutedMessage event /// </summary> /// <param name="source">Routed Message Event Source</param> /// <param name="args">Queued Message Event Arguments</param> void OnRoutedMessageHandler(RoutedMessageEventSource source, QueuedMessageEventArgs args) { lock (fileLock) { try { this.mailItem = args.MailItem; this.agentAsyncContext = this.GetAgentAsyncContext(); // Get the folder for accessing the config file string dllDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Fetch the from address from the current mail item RoutingAddress fromAddress = this.mailItem.FromAddress; Boolean boWorkbookFound = false; // We just want to modifiy subjects when we modified an attachement first #region External Receive Connector Example // CHeck first, if the mail item does have a ReceiveConnectorName property first to prevent ugly things to happen if (mailItem.Properties.ContainsKey("Microsoft.Exchange.Transport.ReceiveConnectorName")) { // This is just an example, if you want to do something with a mail item which has been received via a named external receive connector if (mailItem.Properties["Microsoft.Exchange.Transport.ReceiveConnectorName"].ToString().ToLower() == "externalreceiveconnectorname") { // do something fancy with the email } } #endregion RoutingAddress catchAddress; // Check, if we have any email addresses configured to look for if (this.messageModifierConfig.AddressMap.Count > 0) { // Now lets check, if the sender address can be found in the dictionary if (this.messageModifierConfig.AddressMap.TryGetValue(fromAddress.ToString().ToLower(), out catchAddress)) { // Sender address found, now check if we have attachments to handle if (this.mailItem.Message.Attachments.Count != 0) { // Get all attachments AttachmentCollection attachments = this.mailItem.Message.Attachments; // Modify each attachment for (int count = 0; count < this.mailItem.Message.Attachments.Count; count++) { // Get attachment Attachment attachment = this.mailItem.Message.Attachments[count]; // We will only transform attachments which start with "WORKBOOK_" if (attachment.FileName.StartsWith("WORKBOOK_")) { // Create a new filename for the attachment // [MODIFIED SUBJECT]-[NUMBER].[FILEEXTENSION] String newFileName = MakeValidFileName(string.Format("{0}-{1}{2}", ModifiySubject(this.mailItem.Message.Subject.Trim()), count + 1, Path.GetExtension(attachment.FileName))); // Change the filename of the attachment this.mailItem.Message.Attachments[count].FileName = newFileName; // Yes we have changed the attachment. Therefore we want to change the subject as well. boWorkbookFound = true; } } // Have changed any attachments? if (boWorkbookFound) { // Then let's change the subject as well this.mailItem.Message.Subject = ModifiySubject(this.mailItem.Message.Subject); } } } } } catch (System.IO.IOException ex) { // oops Debug.WriteLine(ex.ToString()); this.agentAsyncContext.Complete(); } finally { // We are done this.agentAsyncContext.Complete(); } } // Return to pipeline return; } /// <summary> /// Build a new subject, if the first 10 chars of the original subject are a valid date. /// We muste transform the de-DE format dd.MM.yyyy to yyyyMMdd for better sortability in the email client. /// </summary> /// <param name="MessageSubject">The original subject string</param> /// <returns>The modified subject string, if modification was possible</returns> private static string ModifiySubject(string MessageSubject) { string newSubject = String.Empty; if (MessageSubject.Length >= 10) { string dateCheck = MessageSubject.Substring(0, 10); DateTime dt = new DateTime(); try { // Check if we can parse the datetime if (DateTime.TryParse(dateCheck, out dt)) { // lets fetch the subject starting at the 10th character string subjectRight = MessageSubject.Substring(10).Trim(); // build a new subject newSubject = string.Format("{0:yyyyMMdd} {1}", dt, subjectRight); } } finally { // do nothing } } return newSubject; } /// <summary> /// Replace invalid filename chars with an underscore /// </summary> /// <param name="name">The filename to be checked</param> /// <returns>The sanitized filename</returns> private static string MakeValidFileName(string name) { string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars())); string invalidRegExStr = string.Format(@"[{0}]+", invalidChars); return Regex.Replace(name, invalidRegExStr, "_"); } } #endregion #region Message Modifier Configuration /// <summary> /// Message Modifier Configuration class /// </summary> public class MessageModifierConfig { /// <summary> /// The name of the configuration file. /// </summary> private static readonly string configFileName = "SFTools.MessageModify.Config.xml"; /// <summary> /// Point out the directory with the configuration file (= assembly location) /// </summary> private string configDirectory; /// <summary> /// The filesystem watcher to monitor configuration file updates. /// </summary> private FileSystemWatcher configFileWatcher; /// <summary> /// The from address /// </summary> private Dictionary<string, RoutingAddress> addressMap; /// <summary> /// Whether reloading is ongoing /// </summary> private int reLoading = 0; /// <summary> /// The mapping between domain to catchall address. /// </summary> public Dictionary<string, RoutingAddress> AddressMap { get { return this.addressMap; } } /// <summary> /// Constructor /// </summary> public MessageModifierConfig() { // Setup a file system watcher to monitor the configuration file this.configDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); this.configFileWatcher = new FileSystemWatcher(this.configDirectory); this.configFileWatcher.NotifyFilter = NotifyFilters.LastWrite; this.configFileWatcher.Filter = configFileName; this.configFileWatcher.Changed += new FileSystemEventHandler(this.OnChanged); // Create an initially empty map this.addressMap = new Dictionary<string, RoutingAddress>(); // Load the configuration this.Load(); // Now start monitoring this.configFileWatcher.EnableRaisingEvents = true; } /// <summary> /// Configuration changed handler. /// </summary> /// <param name="source">Event source.</param> /// <param name="e">Event arguments.</param> private void OnChanged(object source, FileSystemEventArgs e) { // Ignore if load ongoing if (Interlocked.CompareExchange(ref this.reLoading, 1, 0) != 0) { Trace.WriteLine("load ongoing: ignore"); return; } // (Re) Load the configuration this.Load(); // Reset the reload indicator this.reLoading = 0; } /// <summary> /// Load the configuration file. If any errors occur, does nothing. /// </summary> private void Load() { // Load the configuration XmlDocument doc = new XmlDocument(); bool docLoaded = false; string fileName = Path.Combine(this.configDirectory, MessageModifierConfig.configFileName); try { doc.Load(fileName); docLoaded = true; } catch (FileNotFoundException) { Trace.WriteLine("Configuration file not found: {0}", fileName); } catch (XmlException e) { Trace.WriteLine("XML error: {0}", e.Message); } catch (IOException e) { Trace.WriteLine("IO error: {0}", e.Message); } // If a failure occured, ignore and simply return if (!docLoaded || doc.FirstChild == null) { Trace.WriteLine("Configuration error: either no file or an XML error"); return; } // Create a dictionary to hold the mappings Dictionary<string, RoutingAddress> map = new Dictionary<string, RoutingAddress>(100); // Track whether there are invalid entries bool invalidEntries = false; // Validate all entries and load into a dictionary foreach (XmlNode node in doc.FirstChild.ChildNodes) { if (string.Compare(node.Name, "domain", true, CultureInfo.InvariantCulture) != 0) { continue; } XmlAttribute domain = node.Attributes["name"]; XmlAttribute address = node.Attributes["address"]; // Validate the data if (domain == null || address == null) { invalidEntries = true; Trace.WriteLine("Reject configuration due to an incomplete entry. (Either or both domain and address missing.)"); break; } if (!RoutingAddress.IsValidAddress(address.Value)) { invalidEntries = true; Trace.WriteLine(String.Format("Reject configuration due to an invalid address ({0}).", address)); break; } // Add the new entry string lowerDomain = domain.Value.ToLower(); map[lowerDomain] = new RoutingAddress(address.Value); Trace.WriteLine(String.Format("Added entry ({0} -> {1})", lowerDomain, address.Value)); } // If there are no invalid entries, swap in the map if (!invalidEntries) { Interlocked.Exchange<Dictionary<string, RoutingAddress>>(ref this.addressMap, map); Trace.WriteLine("Accepted configuration"); } } } #endregion }
You are not able to list public folders in a co-existence scenario with Exchange Server 2007 and Exchange Server 2010/2013 using the Exchange 2007 EMS or EMC.
When you try to execute Get-PublicFolder you receive the following error:
Get-PublicFolder " There is no existing PublicFolder that matches the following Identity: '\'. Please make sure that you specified the correct PublicFolder Identity and that you have the necessary permissions to view PublicFolder.
This might happen after you have removed the first Exchange 2007 mailbox server, but not the last Exchange 2007 mailbox server.
Exchange Server 2007 uses the Exchange System Attendant to access the public folder store and fails if the System Attendant discovery in Active Directory does not provide a proper configuration.
KB 2621350 describes the discovery process:
There two annoying things about these steps
The magic System Attendant mailbox has been removed from Exchange 2010. But the System Attendent configuration node does still exist in the Active Directory Configuration Partition for compatibility reasons. The configured attributes of the System Attendant entry vary depending on the version of the installed Exchange Server.
In regards to the public folder issue, we need to focus on the following:
To fix the public folder access issue for Exchange Server 2007, set the homeMDB and homeMTA attributes. Set the Exchange System Attendant attributes to appropriate values for your Exchange servers.
Repeat steps 4 to 8 for each Exchange 2013 server in your environment.
Repeat steps 4 to 13 for each Exchange 2010 server in your environment.
Wait for Active Directory replication and retry to access the public folders using Get-PublicFolder in an Exchange Server 2007 Management Shell.
It might be required to restart the Exchange 2007 Information Store and System Attendant service of the Exchange 2007 server in question
Use an administrative PowerShell
Restart-Service MSExchangeIS Restart-Service MSExchangeSA
I haven’t noticed any issues in production environments so far. If you encounter any issues in your environment, feel free to leave a comment.
Do you need assistance with your Exchange Server setup? You have questions about your Exchange Server infrastructure and going hybrid? You are interested in what Exchange Server 2016 has to offer for your environment?
Contact me at thomas@mcsmemail.de Follow at https://twitter.com/stensitzki
This script reads the Exchange schema version from the Active Directory schema partition.
The Exchange organization name is fetched from Active Directory automatically. The script fetches at forest level:
The script fetches at domain level:
Code Samples
# Fetch all version information in the Active Directory forest .\Get-ExchangeServerVersionInfo.ps1
Sample Output:
PS D:\Scripts> .\Get-ExchangeServerVersionInfo.ps1 Exchange Server Schema and Object Information for forest [VARUNA.ROOT] Exchange Organization Name : VARUNA-GROUP Active Directory Schema rangeUpper: 15332 Working on VARUNA.ROOT MESO Container objectVersion : 13236 Exchange Configuration msExchProductId : 15.01.1466.003 Exchange Configuration objectVersion : 16213 Working on VARUNAGROUP.DE MESO Container objectVersion : 13236
Enjoy Exchange Server!