After experiencing a lot of down time, We decided to move this site to CrystalTech.com. CrystalTech.com is powered by only the finest Windows servers providing the best performance, reliability, and value anywhere.

 Verify Public Folder Migration between Exchange 2000 and Exchange 2007

Author Message
turranx
  • Total Posts : 59
  • Scores: 0
  • Reward points : 0
  • Joined: 2/7/2006
  • Location: Cincinnati, OH
Verify Public Folder Migration between Exchange 2000 and Exchange 2007 - Monday, June 30, 2008 5:24 PM
0
NOTE: Please do not use the code from the first two posts in this thread.  They are BETA quality code.  The third post holds the final (and much better behaved) revision.

Have you ever wondered if your Exchange 2000 Public Folders have successfully replicated to your new Exchange 2007 server?  Consider using this script.  It compares the item count and byte count of each Public Folder (recursive) that exist on both the old and new Exchange servers.  The results are printed to the screen for you to look over.  There are a number of changes I still have to make (logic flow, error handling, etc.), site my sources, figure out why many folders have identical item counts but slightly different byte counts, and figure out why some of the folders are coming back without results at all.  This code is entierly READ ONLY, so feel free to run it against your production servers.

The reason why I'm posting this beta code now is because I'm looking for feedback to help me polish up a final version.  I've seen some of the code you guys churn out on this forum; you're good.

This code was run successfully from my Exchange 2007 SP1 server. Please populate the variables in the top three lines of the script with your own values.

 <-- Beta code removed.  Look at third post for latest version -->
 


The output looks like this (below). 
The item and byte count for the old server are on the left. 
The item and byte count for the new server are on the right.
 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
 Inspecting: HTTP ://<Exchange 2000 Server Name>/public/<Folder A>/<Folder 1>
       Items: 2 vs 2
       Bytes: 18686 vs 18977
 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
 Inspecting: HTTP ://<Exchange 2000 Server Name>/public/<Folder A>/<Folder 2>
       Items: 38 vs 38
       Bytes: 1485075 vs 1497899
 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
 


Background:
My company is migrating from Exchange 2000 to Exchange 2007.  We have a couple hundred Public Folders that need to be moved over.  Much of the documentation available online (http://msexchangeteam.com/archive/2007/06/25/445429.aspx) deals with Exchange 2003 to 2007 migration but shys away from Exchange 2000 to 2007.  No one would / could tell me how to truly verify, in an automated fashion, if the Public Folder data was completely replicated to the new Exchange Server.  Take a look at Question #3 in the afore mentioned link. 

I decided to write this script after spending several hours scouring the Internet for something to do this for me.  But like the drones (especially the ones not employed by Microsoft) that churn out Microsoft product related documentation, everybody skips by the hard questions.  Well not me.  I should really consider changing my user name to "The Angry Programmer".   
<message edited by turranx on Wednesday, July 23, 2008 12:26 AM>
Microsoft Windows 2000 Scripting Guide - The best book for newbie scripters
http://www.myspace.com/Evil__Overlord

turranx
  • Total Posts : 59
  • Scores: 0
  • Reward points : 0
  • Joined: 2/7/2006
  • Location: Cincinnati, OH
Almost out of Beta - Tuesday, July 15, 2008 12:14 AM
0
I've pounded on the code and shaped it into something much nicer; although I can't help but think it isn't bulletproof yet.  So for your enjoyment, I present revision two.

Enhancements:
  • Comments!  (What scripter doesn't love comments?)
  • XML file logging
  • More control over the mode of operation (Search for Security Permission Mode)


<-- Beta code removed.  Look at third post for latest version -->


An example of what the XML log looks like:

<?xml version="1.0" encoding="ISO-8859-1"?>
 <Public_Folders>
  <x />
  <Run_Details>
  <Start>
    <TimeStamp>
      <Year>2008</Year>
      <Month>7</Month>
      <Day>14</Day>
      <Time>15:03:08</Time>
    </TimeStamp>
  </Start>
  <Finish>
    <TimeStamp>
      <Year>2008</Year>
      <Month>7</Month>
      <Day>14</Day>
      <Time>20:35:07</Time>
    </TimeStamp>
  </Finish>
  <Parameters>
    <Connect_As_Identity>UserID</Connect_As_Identity>
    <New_Server>Exchange2007</New_Server>
    <Old_Server>Exchange2000</Old_Server>
    <Security_Permission_Mode>No Changes</Security_Permission_Mode>
  </Parameters>
  </Run_Details>
  <Public_Folder ID="2">
  <Name>Access</Name>
  <Path>\Access</Path>
  <Friendly_Web_Address>HTTP://OldServer/public/Access</Friendly_Web_Address>
  <Proper_Web_Address>HTTP://OldServer/public/Access</Proper_Web_Address>
  <Replica>
    <Server_Name>NewServer</Server_Name>
    <Web_Address_Header>https://NewServer</Web_Address_Header>
    <Item_Count>0</Item_Count>
    <Byte_Count>0</Byte_Count>
  </Replica>
  </Public_Folder>
  <Public_Folder ID="3">
  <Name>Access Issues</Name>
  <Path>\Access\Access Issues</Path>
  <Friendly_Web_Address>HTTP://OldServer/public/Access/Access Issues</Friendly_Web_Address>
  <Proper_Web_Address>HTTP://OldServer/public/Access/Access%20Issues</Proper_Web_Address>
  <Replica>
    <Server_Name>OldServer</Server_Name>
    <Web_Address_Header>http://OldServer</Web_Address_Header>
    <Item_Count>26</Item_Count>
    <Byte_Count>529033</Byte_Count>
  </Replica>
  <Replica>
    <Server_Name>NewServer</Server_Name>
    <Web_Address_Header>https://NewServer</Web_Address_Header>
    <Item_Count>26</Item_Count>
    <Byte_Count>540999</Byte_Count>
  </Replica>
  </Public_Folder>
 ...
<message edited by turranx on Wednesday, July 23, 2008 12:26 AM>
Microsoft Windows 2000 Scripting Guide - The best book for newbie scripters
http://www.myspace.com/Evil__Overlord

turranx
  • Total Posts : 59
  • Scores: 0
  • Reward points : 0
  • Joined: 2/7/2006
  • Location: Cincinnati, OH
Finished! - Wednesday, July 23, 2008 12:24 AM
0
This is the third and final version of my script to compare Item and Byte counts for Public Folders that are replicated between Exchange 2000 and Exchange 2007 servers.  Since we will be taking our Exchange 2000 server offline soon, my ability to develop / test / support this script has ended. 

Enhancements:
  • Require declaration of Powershell variables (kind of like Option Explicit for VBS)
  • Fixed some inconsistencies in logging
  • Updated output XML file name to include a timestamp
  • Added additional documentation within the code
If you've used this code in your environment to monitor Public Folder replication or even dissected the code to learn how to do something new, please post a comment below.  I feel really good when I know that someone has benefitted from my work.

 #--------------------==--------------------#
 set-psdebug –strict        # Require declaration of powershell variables
 # Set-PSDebug -Off        # Do not require declaration of variables
 
 #------------ Exchange Servers ------------#
 #
 # Populate $NewServer and $OldServer with your
 # Exchange 2007 and Exchange 2000 server names.  
 # Don't use <server name>.<domain>.com notation.
 $NewServer = "<Exchange 2007 server name>";
 $OldServer = "<Exchange 2000 server name>";
 
 
 #---------- Public Folder Access ----------#
 #
 # Please use a fresh account for this script.  It does not need
 # any administrative rights but I did make it a member of the new
 # Exchange Public Folder Administrators security group.  I 
 # suggest a fresh account because access permissions to Public 
 # Folders will be added AND removed for this account.  You don't 
 # want to screw up a legitimate production account, do you?  This
 # account will also need a mailbox.
 $ConnectAs = "<domain>\<userID>";    
 $Password  = "<password>";
 
 
 #-------- Security Permission Mode --------#
 #
 # This section needs a little explanation before you're ready to use it.  It it very likely that $ConnectAs will be unable
 # to view every Public Folder hosted on $OldServer.  At first this script would check permissions, add 'FolderVisible' if
 # necessary, wait for the permissions to replicate to $OldServer, then compare Public Folder size and item count between
 # $OldServer and $NewServer.  This was it was taking upwards of 20 minutes for the security permission update to replicate
 # to $OldServer each and every time an Access Denied error was encountered.  This is why I broke out the functionality into
 # four modes.  Each mode has a description after it.  I recommend that you run this script under $Security_Permission_Mode_Add
 # by setting $Current_SPM = $Security_Permission_Mode_Add; wait a couple hours or a day for Public Folder Replication to 
 # work its magic; re-run this script using $Security_Permission_Mode_Add_Only_As_Necessary; then when you have all your results, 
 # run it once more using $Security_Permission_Mode_Remove to clean up the security changes.
 $Security_Permission_Mode_Add = 1;    # Don't perform any mailbox compares.  Just add 'FolderVisible' permissions for $ConnectAs where necessary.
 $Security_Permission_Mode_Remove = 2;  # Don't perform any mailbox compares.  Just remove any 'FolderVisible' permissions for $ConnectAs.
 $Security_Permission_Mode_No_Changes = 3;  # Compare Public Folders using the security permissions available at the time of the run.
 $Security_Permission_Mode_Add_Only_As_Necessary = 4;  # Add 'FolderVisible' permissions for $ConnectAs where necessary, 
                                                       # wait for Public Folder Replication, then compare folders.
 $Security_Permission_Mode_Add_Remove_As_Necessary = 5;  # Add 'FolderVisible' permissions for $ConnectAs where necessary, 
                                                         # wait for Public Folder Replication, compare folders, then remove added permissions.
 $Current_SPM = $Security_Permission_Mode_Add_Only_As_Necessary;
 $TimeConstant = [int32]1800; # How many seconds to wait for security permissions to replicate from $OldServer to $NewServer per folder.
                             # This value is neede only during $Security_Permission_Mode_Add_Only_As_Necessary
 
 #-------------- Quit On Error -------------#
 #
 # If an error is encountered while attempting to 
 # access the Public Folders via the web site hosted by 
 # $OldServer, the script will halt or not based on the config below.
 # This flag is active during modes $Security_Permission_Mode_No_Changes,
 # $Security_Permission_Mode_Add_Only_As_Necessary, and
 # $Security_Permission_Mode_Add_Remove_As_Necessary.
 $Quit_On_Error = $True;
 
 
 function Check_Security([string]$PublicFolderHTTPFormat, [string]$UserID, [string]$Pass)
 {
    $strRequest = "<?xml version=""1.0"" ?><D:propfind xmlns:D=""DAV:"" xmlns:E=""http://schemas.microsoft.com/mapi/proptag/""><D:prop><E:x0e080014 /><E:x36020003 /></D:prop></D:propfind>";
    $xmlHTTP = new-object -com msxml2.xmlhttp;
    #Write-Host `t Accessing: $PublicFolderHTTPFormat;
    #Write-Host `t UserID:    $UserID;
    #Write-Host `t Password:  $Pass;
    if ($PublicFolderHTTPFormat.EndsWith(".")) {
        $tempString = $PublicFolderHTTPFormat + "%20";
    } 
    else {
        $tempString = $PublicFolderHTTPFormat
    }
    $xmlHTTP.Open("PROPFIND", $tempString, $False, $UserID, $Pass);
    $xmlHTTP.setRequestHeader("Content-type:", "text/xml");
    $xmlHTTP.setRequestHeader("Depth","0");
    $xmlHTTP.send($strRequest);         
    #Write-Host `t xmlHTTP.Status: $xmlHTTP.Status;
    return $xmlHTTP.Status;
 }
 
 [xml] $xmlLog = '<?xml version="1.0" encoding="ISO-8859-1"?> <Public_Folders><x /></Public_Folders>';
 
 # Generate Run Details
 $xmlRunDetails = $xmlLog.CreateElement("Run_Details");
 $xmlStart =  $xmlLog.createelement("Start");
 $xmlFinish = $xmlLog.createelement("Finish");
 
 $xmlTimeStamp = $xmlLog.CreateElement("TimeStamp");
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Year"))))).psBase.InnerText = Get-Date -UFormat %Y;
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Month"))))).psBase.InnerText = Get-Date -Format %M;
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Day"))))).psBase.InnerText = Get-Date -Format %d;
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Time"))))).psBase.InnerText = Get-Date -UFormat %T;
 
 $xmlParameters = $xmlLog.CreateElement("Parameters");
 ($xmlParameters.AppendChild(($($xmlLog.CreateElement("Connect_As_Identiy"))))).psBase.InnerText = $ConnectAs;
 ($xmlParameters.AppendChild(($($xmlLog.CreateElement("New_Server"))))).psBase.InnerText = $NewServer;
 ($xmlParameters.AppendChild(($($xmlLog.CreateElement("Old_Server"))))).psBase.InnerText = $OldServer;
 
 switch ($Current_SPM) 
 { 
    $Security_Permission_Mode_Add {($xmlParameters.AppendChild(($($xmlLog.CreateElement("Security_Permission_Mode"))))).psBase.InnerText = "Add";}
    $Security_Permission_Mode_Remove {($xmlParameters.AppendChild(($($xmlLog.CreateElement("Security_Permission_Mode"))))).psBase.InnerText = "Remove";}
    $Security_Permission_Mode_No_Changes {($xmlParameters.AppendChild(($($xmlLog.CreateElement("Security_Permission_Mode"))))).psBase.InnerText = "No Changes";}
    $Security_Permission_Mode_Add_Only_As_Necessary {($xmlParameters.AppendChild(($($xmlLog.CreateElement("Security_Permission_Mode"))))).psBase.InnerText = "Add Only As Necessary";}
    $Security_Permission_Mode_Add_Remove_As_Necessary {($xmlParameters.AppendChild(($($xmlLog.CreateElement("Security_Permission_Mode"))))).psBase.InnerText = "Add Remove As Necessary";}
 }
 
 $Catch_Output = $xmlStart.AppendChild($xmlTimeStamp);
 $Catch_Output = $xmlRunDetails.AppendChild($xmlStart);
 $Catch_Output = $xmlRunDetails.AppendChild($xmlFinish);
 $Catch_Output = $xmlRunDetails.AppendChild($xmlParameters);
 $Catch_Output = $xmlLog.Public_Folders.AppendChild($xmlRunDetails);
 
 $intCounter = 0;
 Get-PublicFolder -recurse | 
 foreach {
    if ($_.ParentPath -ne $null) 
    { 
       if ([int]$($_.ParentPath.get_length()) -gt 1)
       {
           $PublicFolderPath = $_.ParentPath + "\" + $_.name
       }
       else 
       {
           $PublicFolderPath = "\" + $_.name
       }
    }
    else
    {
        $PublicFolderPath = "\" + $_.name
    }
    # NOTE: Microsoft allows the character "/" to be used in Public Folder Names.
    # Try to reference a Public Folder named "Pizza/Toppings" via HTTP.  You can't.  The web server 
    # will come back with a HTTP 409 error.  So we have to do a replace with "_xF8FF_".
    $PublicFolderWebPath = "HTTP://" + $OldServer + "/public" + $PublicFolderPath.replace("/","_xF8FF_").replace("\","/");
    $PublicFolderWebPathProper = $PublicFolderWebPath.replace(" ","%20");
    
    $ReplicaSet = $_.Replicas;
    
    $intCounter += 1; 
    $xmlFolder = $xmlLog.CreateElement("Public_Folder");
    $xmlFolder.SetAttribute("ID",[string]$intCounter);
    ($xmlFolder.AppendChild(($($xmlLog.CreateElement("Name"))))).psBase.InnerText = $_.name;
    ($xmlFolder.AppendChild(($($xmlLog.CreateElement("Path"))))).psBase.InnerText = $PublicFolderPath;
    ($xmlFolder.AppendChild(($($xmlLog.CreateElement("Friendly_Web_Address"))))).psBase.InnerText = $PublicFolderWebPath;
    ($xmlFolder.AppendChild(($($xmlLog.CreateElement("Proper_Web_Address"))))).psBase.InnerText = $PublicFolderWebPathProper;    
    Write-Host $_.name;
    $Error_Flag = $False;
    forEach ( $Replica in $ReplicaSet ) {
        if ( ($([string]$Replica).ToLower().Contains($OldServer.ToLower()) -eq $true))
        {
            # The code that deals with XML came from this location:
            # [link=http://www.infinitec.de/post/2004/12/Retrieve-the-size-of-a-Microsoft-Exchange-20002003-public-folder.aspx]http://www.infinitec.de/post/2004/12/Retrieve-the-size-of-a-Microsoft-Exchange-20002003-public-folder.aspx[/link]
            # It was written by Henning Krause.  Many thanks for the excelent code.
            # Visit Henning's Blog at [link=http://www.infinitec.de/author/hkrause.aspx]http://www.infinitec.de/author/hkrause.aspx[/link]
            # I translated it into PowerShell 
            # A list of additional (and sometimes available) MAPI properties are available at:
            # [link=http://svn.opengroupware.org/OGoProjects/evolution-groupdav/trunk/utils/mapi-properties]http://svn.opengroupware.org/OGoProjects/evolution-groupdav/trunk/utils/mapi-properties[/link]
    
            $strRequest = "<?xml version=""1.0"" ?><D:propfind xmlns:D=""DAV:"" xmlns:E=""http://schemas.microsoft.com/mapi/proptag/""><D:prop><E:x0e080014 /><E:x36020003 /></D:prop></D:propfind>";
            $xmlHTTP = new-object -com msxml2.xmlhttp;
            $xmlDOC = new-object -com msxml2.DOMDocument;
            $return = $xmlDOC.loadXML($strRequest);
            Write-Host Inspecting: $PublicFolderWebPath;
            
            $xmlReplica = $xmlLog.CreateElement("Replica");
            ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Server_Name"))))).psBase.InnerText = $OldServer;
            ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Web_Address_Header"))))).psBase.InnerText = 'http://' + $OldServer;
            
            # I was getting a lot of HTTP 401 Access Denied errors while attempting to access
            # some public folders.  In the end, I discovered that this is because the account
            # I'm using to troll through the Public Folders does not have 'FolderVisible'
            # rights for that particular Public Folder after ALL the folder permissions have
            # been compiled and compared against the user $ConnectAs.
            # So I got the bright idea to check for a denied condition ("401" error) and
            # grant 'FolderVisible' access rights using the "Add-PublicFolderClientPermission"
            # cmdlet that you'll see a couple lines below.  Initially I didn't use the "-Server"
            # parameter.  This caused some heartache.  The security changes were being made on 
            # $NewServer and would eventually take effect on $OldServer through the Public
            # Folder replication.  This was not fast enough.  I tried to grant rights on 
            # $OldServer manually using the Exchange System Manager (remember: $OldServer is
            # running Exchange 2000) and after a few seconds, I got this email in my inbox:
            #   'A folder design conflict has occurred in "Public Folder Store (<$OldServer>)". 
            #    The design of this folder has been simultaneously modified on two or more 
            #    folder replicas. Only the set of changes made last have been saved.'
            # Having read this, I decided to attempt to use the "-Server $OldServer" parameter
            # of the cmdlet "Add-PublicFolderClientPermission".
            # This failed miserably since $OldServer is running Exchange 2000.  My only option
            # left was to grant permissions on $NewServer then wait for the predefined replication
            # period before continuing. Through repeated testing, I found that I had to keep
            # keep extending the wait time stored in $Time for the security replication to take place.
            
            # Test to see if $ConnectAs can access $PublicFolderPath.
            # If not, add an Access Control Entry (ACE) into the Access Control Llist (ACL) 
            # for $PublicFolderPath.
            $Time = $TimeConstant;
            $TimeCounter = $Time;
            If (( $(Check_Security $PublicFolderWebPathProper $ConnectAs $Password) -eq 401 ) -and `
                   (($Current_SPM -eq $Security_Permission_Mode_Add) -or `
                    ($Current_SPM -eq $Security_Permission_Mode_Add_Only_As_Necessary) -or `
                    ($Current_SPM -eq $Security_Permission_Mode_Add_Remove_As_Necessary)) ) {
                Write-Host `t Adding FolderVisible Access Permissions to $NewServer;
                ($xmlFolder.AppendChild(($($xmlLog.CreateElement("Security_Action"))))).psBase.InnerText = `
                    "Adding FolderVisible Access Permissions to " + $NewServer + " for " + $ConnectAs;
                $return = Add-PublicFolderClientPermission -Identity $PublicFolderPath -User $ConnectAs -AccessRights "FolderVisible" -Confirm:$False;
                If (($Current_SPM -eq $Security_Permission_Mode_Add) -or `
                    ($Current_SPM -eq $Security_Permission_Mode_Add_Only_As_Necessary)) {
                    $strActivity = 'Waiting up to ' + $Time.tostring() + ' Seconds for Public Folder replication to push permissions to ' + $OldServer;
                    $Show = $True;
                    While (($Time -gt 0) -and ($(Check_Security $PublicFolderWebPathProper $ConnectAs $Password) -eq 401)){
                        Write-Progress -Activity $strActivity -Status "Polling in 10 Second Intervals" -SecondsRemaining $TimeCounter -Completed $Show;
                        Start-Sleep -Seconds 10;
                        $TimeCounter = $TimeCounter - 10;
                        if ($Show -eq $True) {$Show = $False} else {$Show = $True}
                    }
                    Write-Progress -Activity $strActivity -Status "Polling in 10 Second Intervals" -SecondsRemaining $Time; #-PercentComplete (0) -Completed:$True;
                    Write-Progress -Activity $strActivity -Status "Polling in 10 Second Intervals" -Completed:$True;
                }
            }
            # We've passed through the section of code that makes security changes on the fly (if necessary).
            # Hopefully by this point everything is in order to allow a successful read attempt when we access
            # the HTTP directory that represents the Public Folder located on $OldServer.
            if ( ($Current_SPM -eq $Security_Permission_Mode_Add_Remove_As_Necessary) -or `
                 ($Current_SPM -eq $Security_Permission_Mode_Add_Only_As_Necessary) -or `
                 ($Current_SPM -eq $Security_Permission_Mode_No_Changes) ) {
                # Some of the folders in my organization ended with a period (".") and a space.
                # I tried removing just the space but that frequently didn't work; the space would come back.
                # If I removed both the space and the period, the change stuck.  So when I encounter
                # a public folder that ends with a period, I append a space to the end of the URL.
                # Then IIS can access the URL.  This is explained again in greater detail several
                # lines below.  Search for "404_Error" and look for the big blob of text above it.
                if ($PublicFolderWebPathProper.EndsWith(".")) {
                    $tempString = $PublicFolderWebPathProper + "%20";
                } 
                else {
                    $tempString = $PublicFolderWebPathProper
                }
                $xmlHTTP.Open("PROPFIND", $tempString, $False, $ConnectAs, $Password);
                $xmlHTTP.setRequestHeader("Content-type:", "text/xml");
                $xmlHTTP.setRequestHeader("Depth","0");
                $xmlHTTP.send($strRequest);
                $xmlDOC = $xmlHTTP.ResponseXML;
                if ( $xmlHTTP.Status -eq 207 ) {
                    $xmlDOC.SetProperty("SelectionNamespaces", "xmlns:ex='http://schemas.microsoft.com/mapi/proptag/'");
                    # To see the properties & methods for a single XML Node (Msxml2.DOMDocument), uncomment this next line
                    # forEach ( $property in $($xmlDOC.selectSingleNode("//ex:x36020003") | get-member)) { Write-Host $property }
                    
                    Write-Host `t Items: $xmlDOC.selectSingleNode("//ex:x36020003").text vs `
                        $(Get-PublicFolderStatistics -identity $PublicFolderPath).ItemCount.ToString();
                    Write-Host `t Bytes: $xmlDOC.selectSingleNode("//ex:x0e080014").text vs `
                        $(Get-PublicFolderStatistics -identity $PublicFolderPath).TotalItemSize.ToString().Replace("B","");
                    ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Item_Count"))))).psBase.InnerText = $xmlDOC.selectSingleNode("//ex:x36020003").text;
                    ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Byte_Count"))))).psBase.InnerText = $xmlDOC.selectSingleNode("//ex:x0e080014").text;
                }
                else {
                    Write-Host `t Failed: $xmlHTTP.Status `n`t Actual HTTP path attempted: $PublicFolderWebPathProper
                    if ($TimeCounter -eq 0) { 
                        Write-Host '`tTime expired while waiting for security permissions to replicate to $oldServer';
                        ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Time_Error"))))).psBase.InnerText = `
                            'Time expired while waiting for security permissions to replicate to $oldServer';
                    }
                    if ($xmlHTTP.Status.ToString() -eq "404") { 
                        $string =   "`t A 'Not Found' error was encountered.  Please check the public folder `
                                    `n`t " + $PublicFolderPath + "`n and make sure there are no trailing spaces. `
                                    `n`t The easiest way to do this is to open Exchange System Manager `
                                    `n`t on the Exchange 2000 server an navigate to 'Administrative `
                                    `n`t Groups' -> 'First Administrative Group' -> 'Folders' -> `
                                    `n`t 'Public Folders' -> " + $PublicFolderPath + `
                                    "`n`n`t Before you continue running this script, I recommend that `
                                    `n`t you visit [link=http://visualbasicscript.com/m_61934/tm.htm]http://visualbasicscript.com/m_61934/tm.htm[/link] and run `
                                    `n`t this script to discover which Public Folders have trailing spaces. `
                                    `n`n`t If the above does not work, try this method:`
                                    `n`t Using PFDavAdmin.exe, grant yourself 'Owner' Rights to the Public Folder `
                                    `n`t then go to that folder within Outlook.  Change the Name of the troublesome `
                                    `n`t folder.  Verify the name change in both Exchange 2000's Exchange System  `
                                    `n`t Manager and Exchange 2007 SP1's Public Folder Management Console.  When `
                                    `n`t both GUI's show the new name, then rerun this script. `
                                    `n`t NOTE: I encountered one particular folder that had a period and a space `
                                    `n`t at the end of its name ('. ').  Outlook displayed the period but not the `
                                    `n`t trailing space.  Removing the period also removed the space in Exchange `
                                    `n`t 2000's Exchange System Manager.  Using Outlook to add the period back `
                                    `n`t to the end of the Public Folder name also added the trailing space `
                                    `n`t automatically.  In the end, I decided to remove the trailing space `
                                    `n`t and period to make sure it worked.";
                        Write-Host $string;
                        ($xmlReplica.AppendChild(($($xmlLog.CreateElement("404_Error"))))).psBase.InnerText = $string;
                    }
                    $Error_Flag = $True;
                }
                $Catch_Output = $xmlFolder.AppendChild($xmlReplica);
            }
            # Look for the Access Control Entry for the user $ConnectAs and remove it.
            if ( ($Current_SPM -eq $Security_Permission_Mode_Add_Remove_As_Necessary) -or `
                 ($Current_SPM -eq $Security_Permission_Mode_Remove) ) {
                foreach ($ACE in $(get-publicfolderclientpermission -Identity $PublicFolderPath)) {
                    #Write-Host `t ACE:. $([string]$ACE.User) 
                    #Write-Host `t User: $([string]$(get-user $ConnectAs).Identity)
                    #Write-Host `t $([string]$ACE.User).CompareTo( $([string]$(get-user $ConnectAs).Identity) )
                    if ($([string]$ACE.User).CompareTo( $([string]$(get-user $ConnectAs).Identity) ) -eq 0) {
                        ($xmlFolder.AppendChild(($($xmlLog.CreateElement("Security_Action"))))).psBase.InnerText = `
                            "Removing all Access Permissions to " + $NewServer + " for " + $ConnectAs;
                        Remove-PublicFolderClientPermission -Identity $PublicFolderPath -User $ACE.User -AccessRights $ACE.AccessRights -Confirm:$False;
                        Write-Host `t Removing ACE for $ConnectAs;
                    }
                }
            }
            Write-host -------------------------------
            Write-host -------------------------------
            $Catch_Output = $xmlFolder.AppendChild($xmlReplica);
        }
        # This code only centers upon $NewServer and $OldServer; which must be explicitly
        # defined at the beginning of the script.  A good project for someone else would
        # be to modify this script to handle 3+ Exchange servers and NOT require $OldServer
        # be explicitly defined.
        if ( ($([string]$Replica).ToLower().Contains($NewServer.ToLower()) -eq $true))
        {
            $xmlReplica = $xmlLog.CreateElement("Replica");
            ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Server_Name"))))).psBase.InnerText = $NewServer;
            ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Web_Address_Header"))))).psBase.InnerText = 'https://' + $NewServer;
            ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Item_Count"))))).psBase.InnerText = `
                $(Get-PublicFolderStatistics -identity $PublicFolderPath).ItemCount.ToString();
            ($xmlReplica.AppendChild(($($xmlLog.CreateElement("Byte_Count"))))).psBase.InnerText = `
                $(Get-PublicFolderStatistics -identity $PublicFolderPath).TotalItemSize.ToString().Replace("B","");
            $Catch_Output = $xmlFolder.AppendChild($xmlReplica);
        }
        $Catch_Output = $xmlLog.Public_Folders.AppendChild($xmlFolder);
        if (($Quit_On_Error -eq $True) -and ($Error_Flag -eq $True)) {exit;}
    }
 }
 Write-Host "Done Processing Folders.`nWriting Log to file.";
 $xmlTimeStamp = $xmlLog.CreateElement("TimeStamp");
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Year"))))).psBase.InnerText = Get-Date -UFormat %Y;
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Month"))))).psBase.InnerText = Get-Date -Format %M;
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Day"))))).psBase.InnerText = Get-Date -Format %d;
 ($xmlTimeStamp.AppendChild(($($xmlLog.CreateElement("Time"))))).psBase.InnerText = Get-Date -UFormat %T;
 $Catch_Output = $xmlFinish.AppendChild($xmlTimeStamp);
 $xmlLog.Save($(join-path -path $(get-location).tostring() ((get-date -UFormat `
    "%Y %m %d %A %r Ex2000 vs Ex2007 Public Folders.xml").replace(" ","_").replace(",","").replace(":","_"))));
 


This code generates an XML log file as its output.  Take a look here: http://www.visualbasicscript.com/m_62487/tm.htm for the Powershell script that will parse the log file and tell you which folders are out of sync.
An example of what the XML log looks like:

 <?xml version="1.0" encoding="ISO-8859-1"?>
 <Public_Folders>
 <x />
 <Run_Details>
 <Start>
   <TimeStamp>
     <Year>2008</Year>
     <Month>7</Month>
     <Day>14</Day>
     <Time>15:03:08</Time>
   </TimeStamp>
 </Start>
 <Finish>
   <TimeStamp>
     <Year>2008</Year>
     <Month>7</Month>
     <Day>14</Day>
     <Time>20:35:07</Time>
   </TimeStamp>
 </Finish>
 <Parameters>
   <Connect_As_Identity>UserID</Connect_As_Identity>
   <New_Server>Exchange2007</New_Server>
   <Old_Server>Exchange2000</Old_Server>
   <Security_Permission_Mode>No Changes</Security_Permission_Mode>
 </Parameters>
 </Run_Details>
 <Public_Folder ID="2">
 <Name>Access</Name>
 <Path>\Access</Path>
 <Friendly_Web_Address>HTTP://OldServer/public/Access</Friendly_Web_Address>
 <Proper_Web_Address>HTTP://OldServer/public/Access</Proper_Web_Address>
 <Replica>
   <Server_Name>NewServer</Server_Name>
   <Web_Address_Header>https://NewServer</Web_Address_Header>
   <Item_Count>0</Item_Count>
   <Byte_Count>0</Byte_Count>
 </Replica>
 </Public_Folder>
 <Public_Folder ID="3">
 <Name>Access Issues</Name>
 <Path>\Access\Access Issues</Path>
 <Friendly_Web_Address>HTTP://OldServer/public/Access/Access Issues</Friendly_Web_Address>
 <Proper_Web_Address>HTTP://OldServer/public/Access/Access%20Issues</Proper_Web_Address>
 <Replica>
   <Server_Name>OldServer</Server_Name>
   <Web_Address_Header>http://OldServer</Web_Address_Header>
   <Item_Count>26</Item_Count>
   <Byte_Count>529033</Byte_Count>
 </Replica>
 <Replica>
   <Server_Name>NewServer</Server_Name>
   <Web_Address_Header>https://NewServer</Web_Address_Header>
   <Item_Count>26</Item_Count>
   <Byte_Count>540999</Byte_Count>
 </Replica>
 </Public_Folder>
 ...
<message edited by turranx on Wednesday, July 23, 2008 12:27 AM>
Microsoft Windows 2000 Scripting Guide - The best book for newbie scripters
http://www.myspace.com/Evil__Overlord

dm_4ever
  • Total Posts : 3673
  • Scores: 82
  • Reward points : 0
  • Joined: 6/29/2006
  • Location: Orange County, California
RE: Finished! - Thursday, July 24, 2008 12:59 PM
0
I just wanted to say thanks for your many posts....I'm still not using Powershell all that much, but I am sure your various posts will come in very handy when the time comes.
dm_4ever

My philosophy: K.I.S.S - Keep It Simple Stupid
Read Me: http://www.visualbasicscript.com/m_24727/tm.htm
Frequently Asked Stuff: http://www.visualbasicscript.com/m_47117/tm.htm