• Akash Blogging......

    Generating a report - Which folders have a Personal Tag applied to it (using EWS Managed API from PowerShell - Exchange 2010)

    • 0 Comments

    It’s time to do a bit of reporting!

    In my previous posts I have talked about Stamping Archive Policy Tag, Retention Policy Tag on Items, Retention Policy Tag on Folders but this time the requirement was different! In this case we wanted to scan the entire mailbox and find out the folders that have a Personal Tag applied to it.

    To summarize what the script below does:

    • We search the entire mailbox and look for folders that have a Folder class of IPF.Note
    • Loop through the folders and write out the Folder Path, Retention Policy Tag GUID and the Retention Period
    • The log can then be opened in Excel and manipulated in any way you like!

    Straight to code now. Create a .PS1 file with the code below:

    # The script requires the EWS managed API, which can be downloaded here:
    # http://www.microsoft.com/en-us/download/details.aspx?id=35371
    # This also requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$error = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.csv" # Path of the Log File

    function SearchFolders($MailboxName)
    {
    Write-host "Searching folders in Mailbox Name:" $MailboxName -foregroundcolor $info

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Number of Items to Get
    $FpageSize =50
    $FOffset = 0
    $MoreFolders =$true

    while ($MoreFolders)
    {

    #Setup the View to get a limited number of Items at one time
    $folderView = new-object Microsoft.Exchange.WebServices.Data.FolderView($FpageSize,$FOffset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $folderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep

    # Folder Path
    $PR_FOLDER_PATH = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);

    #PR_POLICY_TAG 0x3019
    $PolicyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);

    #PR_RETENTION_FLAGS 0x301D
    $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    #PR_RETENTION_PERIOD 0x301A
    $RetentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    $FPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet(
    [Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly,
    [Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,
    [Microsoft.Exchange.WebServices.Data.FolderSchema]::FolderClass);

    $FPropertySet.Add($PR_FOLDER_PATH);
    $FPropertySet.Add($PolicyTag);
    $FPropertySet.Add($RetentionPeriod);

    $folderView.PropertySet = $FPropertySet;

    #Create the Search Filter.
    $FoSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::FolderClass, "IPF.Note")

    $oFindFolders = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$FoSearchFilter,$folderView)

    $PolicyTagString = $null;

    foreach ($folder in $oFindFolders.Folders)
    {
    Write-host "Looking at Folder:" $folder.ExtendedProperties[0].Value -foregroundcolor $info;

    &{
    if ($folder.ExtendedProperties[1].Value -ne $null)
    {
    $byteA =[byte[]]($folder.ExtendedProperties[1].Value)
    $PolicyTagGUID = new-Object Guid(,$byteA);
    $PolicyTagString = $PolicyTagGUID.ToString();
    }
    Add-Content $LogFile ($MailboxName + "," +$folder.ExtendedProperties[0].Value +"," +$PolicyTagString+"," +$folder.ExtendedProperties[2].Value);
    }
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    continue;
    }
    }

    if ($oFindFolders.MoreAvailable -eq $false)
    {$MoreFolders = $false}

    if ($MoreFolders)
    {$FOffset += $FpageSize}

    }

    Write-host "Finished with Mailbox Name:" $MailboxName "Done!" -foregroundcolor $info
    Write-host "-------------------------------------------------" -foregroundcolor $info

    $service.ImpersonatedUserId = $null
    }

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("ServiceAccount","password","domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri("http://Your-CAS-Server/EWS/Exchange.asmx")

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    # Add headers to the log file
    Add-Content $LogFile ("Mailbox" + "," +"Folder Path" +"," +" Retention Tag GUID" +"," +"Retention Period");

    #Read data from the UserAccounts.txt
    import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    SearchFolders($WindowsEmailAddress)
    }


    UserAccounts.txt contains the list of the users mailboxes that need to be searched. This file should exist in the same directory as the .PS1 file.The First row denotes the Field Names. Format of the text file is as below:

    WindowsEmailAddress
    akasha@contoso.com
    akashb@contoso.com
    akashc@contoso.com

    To Run the script: 
    1)Open the Exchange Management Shell
    2)Navigate to the location where the script file is.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script
    Note: This script creates a log file in C:\Temp. Make sure that directory exists.
    Below is what the log file will look like:
    Mailbox,Folder Path, Retension Tag GUID,Retension Period
    akasha@contoso.com,\Deleted Items,,
    akasha@contoso.com,\Drafts,,
    akasha@contoso.com,\Inbox,,
    akasha@contoso.com,\Inbox\Inbox1,4528ac9a-35b6-454d-96cb-c6801795bbce,300
    akasha@contoso.com,\Inbox\Inbox1\Inbox2,4528ac9a-35b6-454d-96cb-c6801795bbce,300
    akasha@contoso.com,\Junk E-Mail,,
    akasha@contoso.com,\Outbox,,
    akasha@contoso.com,\Sent Items,,
    akasha@contoso.com,\Test,4528ac9a-35b6-454d-96cb-c6801795bbce,300
    akashb@contoso.com,\Deleted Items,,
    akashb@contoso.com,\Drafts,,
    akashb@contoso.com,\Inbox,,
    akashb@contoso.com,\Inbox\1,,
    akashb@contoso.com,\Junk E-Mail,,
    akashb@contoso.com,\Outbox,,
    akashb@contoso.com,\Sent Items,,
    Note the Folder Path and the GUID. What is this GUID? This is the Retention Policy Tag GUID!
    Where can I get it from?
    Open up Exchange Management Shell and run the command below:
    [PS] C:\>Get-RetentionPolicyTag | FL Name,GUID
    Output:

    Name : Personal 1 Year move to archive
    Guid : 797921d5-5c63-4a7b-b5d0-31e57a48f71b
    Name : 1 Year Delete
    Guid : 82139a14-cbf8-4467-9324-1c95c91bc12a
    Name : Corp-BusinessCritical
    Guid : 92186ff7-7f4d-4efa-a09b-bbdc5aee3908
    Name : 100 Days - Delete
    Guid : 3b7f5e7a-9016-4f87-a829-83f84c5adc62
    Name : 300 Days Delete
    Guid : 4528ac9a-35b6-454d-96cb-c6801795bbce



    Now you know which Policy Tag was applied to the folder. Enjoy!

  • Akash Blogging......

    All I know about Outlook 2010/2013 and SharePoint Synchronization

    • 1 Comments


    When does Outlook 2010/2013 synchronies SharePoint Lists and Document Libraries?

    By default, WSS lists can be synchronized once during each Send/Receive interval, and there are four basic ways to initiate this Send/Receive process in Outlook:

    · Send/Receive (automatic or manual)
    · Shift+F9 or F9
    · Folder focus change
    · Synchronize when Saving/Updating an Item

    The All Accounts Send/Receive group has SharePoint list synchronization enabled by default and an automatic send/receive interval of 30 minutes. If you want to customize the default Send/Receive settings for your Outlook profile, Click on Send/Receive Tab, Click on Send/Receive Groups and then click Define Send/Receive Groups. The Send/Receive Groups dialog box is displayed.

    clip_image001

    Then, in the Send/Receive Groups dialog box select the All Accounts group and click Edit.The Send/Receive Settings – All Accounts dialog box is displayed.

    All Accounts

    The Send/Receive Settings – All Accounts dialog box contains two settings related to SharePoint list synchronization.

    · Include Windows SharePoint lists in this Send/Receive group - With this checkbox enabled, every time the Send/Receive process is initiated for the Send/Receive group, the enabled SharePoint lists may be synchronized (depending on the Send/Receive update setting and the WSS automatic synchronization limit). If this checkbox is not enabled, then your SharePoint lists are not synchronized with Outlook during a Send/Receive operation. In this situation, you have to use different method for synchronizing the data.

    · Send/Receive Options - Every SharePoint list configured on the SharePoint Lists tab of the Account Settings dialog box is listed under Send/Receive Options in the Send/Receive Settings – All Accounts dialog box. Only lists whose checkbox is enabled are synchronized whenever the synchronization process for the parent Send/Receive group is initiated.

    Automatic Send/Receive Synchronization
    In the Send/Receive Groups dialog box is a setting for All Accounts called Schedule an automatic send/receive every x minutes. When this x-minute interval expires, Outlook performs an automatic send/receive operation based on the settings you have configured for the All Accounts group in the Send/Receive Settings – All Accounts dialog box. If you disable SharePoint list synchronization via the Include Windows SharePoint lists in this Send/Receive group checkbox, or synchronization of specific SharePoint lists via the Send/Receive Options checkbox for the lists, then an automatic send/receive operation does not synchronize the WSS lists.

    Manual Send/Receive Synchronization
    There are several ways to perform a manual send/receive operation.

    · Press F9
    · Click Send/Receive All Folders on the Send/Receive Tab

    If you have SharePoint synchronization enabled, then your SharePoint list should be synchronized whenever you use one of these manual send/receive operations.

    If you disable SharePoint list synchronization via the Include Windows SharePoint lists in this Send/Receive group checkbox or you disable synchronization of specific SharePoint lists via the Send/Receive Options checkboxes, then your SharePoint lists are not synchronized when you use one of these manual send/receive operations. This occurs because these methods honor the settings you specify for the Send/Receive group.

    Shift + F9
    Using Shift+F9 is not technically classified as a Send/Receive operation because it does not utilize the settings you have for your Send/Receive group(s). Even if you disable SharePoint list synchronization via the Include Windows SharePoint lists in this Send/Receive group checkbox or synchronization of specific SharePoint lists via the Send/Receive Options checkboxes, your SharePoint list for the selected folder is still synchronized.

    To initiate a manual synchronization of a SharePoint List folder, use the following steps.

    · Select the folder for the WSS list or document library in the Folder List module.
    · Press Shift + F9

    Folder Focus Change
    A third method of synchronizing a SharePoint list is to click off the folder for the list and then click back to it. This is another useful method if you disabled SharePoint list synchronization via the Include Windows SharePoint lists in this Send/Receive group checkbox or synchronization of specific SharePoint lists via the Send/Receive Options checkbox for the lists.

    Synchronize when Saving an Item
    To make collaboration a good user experience, Outlook automatically synchronizes your list if you add or edit a list item within the Outlook interface. Outlook does not wait for an automatic synchronization or a manual synchronization in this scenario and it only respects the server’s TTL value(We will look at what this is later in the article).

    Does the STSSyncInterval registry key for Outlook control synchronization on Outlook 2010/2013?

    The STSSyncInterval regkey corresponds to the "SharePoint folder sync interval" GPO which is included by default in the Outlook 2010/2013 adm template.

    Although this is present, it is no longer used by Outlook.

    How can we control the frequency of synchronization?

    The Outlook client makes a call to the SharePoint Server to returns changes made to the specified list since the date and time specified in the token, if specified, or to return all the list items in the list. It uses the GetListItemChangesSinceToken operation. More details can be found in the article below:

    Lists.GetListItemChangesSinceToken method
    http://msdn.microsoft.com/en-us/library/websvclists.lists.getlistitemchangessincetoken.aspx

    The following example shows a request that uses the change token extracted from a previous GetListItemChangesSinceTokenResponse:

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
    <GetListItemChangesSinceToken xmlns="http://schemas.microsoft.com/sharepoint/soap/">
    <listName>TestListItem</listName>
    <query><Query><OrderBy><FieldRef Name="ID"/></OrderBy></Query></query>
    <viewFields>
    <ViewFields>
    <FieldRef Name="ID" /><FieldRef Name="Title" />
    </ViewFields>
    </viewFields>
    <rowLimit>10</rowLimit>
    <changeToken> 1;3;e074e60a-0126-4446-a751-a5be7a55ed81;633374187828130000;243</changeToken>
    </GetListItemChangesSinceToken>
    </soap:Body>
    </soap:Envelope>


    The response we get:

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
    <GetListItemChangesSinceTokenResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
    <GetListItemChangesSinceTokenResult>
    <listitems MinTimeBetweenSyncs='0' RecommendedTimeBetweenSyncs='180' MaxBulkDocumentSyncSize='500' AlternateUrls='http://siteaddress/' EffectivePermMask='FullMask'
    xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882' xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882' xmlns:rs='urn:schemas-microsoft-com:rowset' xmlns:z='#RowsetSchema'>
    <Changes LastChangeToken="1;3;e074e60a-0126-4446-a751-a5be7a55ed81;633374187828130000;243"><List excluded content that matches GetList call> </List></Changes>
    <rs:data ItemCount="1">
    <z:row ows_ID='1' ows_Title='Test new entry' ows_MetaInfo='1;#' ows__ModerationStatus='0' ows__Level='1' ows_owshiddenversion='3' ows_UniqueId='1;#{078CAB5C-5FEA-43EA-B23A-FB93EF35397D}'
    ows_FSObjType='1;#0' ows_Created='2008-01-28 08:06:11' ows_FileRef='1;#Lists/TestListItem/1_.000' />
    </rs:data>
    </listitems>
    </GetListItemChangesSinceTokenResult>
    </GetListItemChangesSinceTokenResponse>
    </soap:Body>
    </soap:Envelope>

    Notice the MinTimeBetweenSyncs and the RecommendedTimeBetweenSyncs values returned, these values are used to control how synchronization works in Outlook.

    By default, a SharePoint list does not have a publishing limit configured and the Current provider limit value in the SharePoint List Options dialog box will show Not Published. Therefore, by default, you can manually synchronize your WSS list data with Outlook as often as you want. 
    Where can I see this in the Outlook UI? You can go to Account Settings, then to the SharePoint Lists Tab and then double click on the list you are interested in.
    xxx
    The MinTimeBetweenSyncs represents the minimum amount of time between user-initiated or automatic synchronization. The value represents a time in minutes.

    Clients should respect this value even if the user initiates synchronization manually. That is, if this is set to 5 minutes, clients should only send one request per 5 minutes even if the user repeatedly clicks "send/receive".

    This setting is also known TTL and the property name that needs to be set on the SharePoint server is “min-time-between-syncs”. The default value for this property is 0 minutes. How do we set it on the SharePoint Server?

    To set this value on the server, use the STSADM interface on the SharePoint server. The general syntax for setting a property is:
    stsadm -o setproperty –propertyname <property name> -propertyvalue <property value>
    So in this case:
    STSADM -o setproperty - propertyname "min-time-between-syncs" - propertyvalue "0"
    Users can prevent their clients from honoring the TTL value you specify by clearing the Update Limit checkbox in the SharePoint List Options dialog box for a list. However, you can also set policy key to force Outlook clients to respect the TTL value published by WSS.
    The policy is called “Override published sync interval”.

    Registry Key: software\policies\microsoft\office\14.0\outlook\options\wss
    Registry Value Name:IgnoreTTL
    Registry Value: 1 or 0
    Description: This policy setting allows you to prevent users from overriding the sync interval published by managed SharePoint lists. If you enable this policy setting, the ''Update Limit'' checkbox found under File tab | Info | Account Settings | SharePoint List | Change& is disabled, and the user's connected SharePoint lists will only sync as defined by the list's administrator. If you disable this policy setting, then individual users will be able to override the sync interval by unchecking the ''Update Limit'' checkbox in the SharePoint List's Options dialog.

    However, there is a different server synchronization limit that is used for automatic synchronizations initiated by an Outlook Send/Receive operation. The default limit for automatic synchronizations by Outlook is 180 minutes.
    This setting is also known ASI and the property name that needs to be set on the SharePoint server is “recommended-time-between-syncs”. The default value for this property is 180. How do we set it on the SharePoint Server?
    To set this value on the server, use the STSADM interface on the SharePoint server. The general syntax for setting a property is:
    stsadm -o setproperty –propertyname <property name> -propertyvalue <property value>
    So in this case:
    STSADM -o setproperty - propertyname "recommended-time-between-syncs" - propertyvalue "60"
    When the “recommended-time-between-syncs” is set on the server, Outlook will synchronize the lists once in 60 minutes(as set in the example above) even when automatic synchronizations is scheduled to happen in 30 minutes by default.
    If users synchronize too often with the SharePoint server or they complain that the automatic synchronization interval is too long, you can adjust the ASI and the TTL values on the server to better suit the needs of your users. 
    Enjoy!
  • Akash Blogging......

    Images sent using Snipping Tool(SnippingTool.exe) shows up as RED X in Outlook 2010

    • 0 Comments

    This was an interesting issue, the customer was using the Snipping Tool to capture images and mail them. He would simply get a capture and then do a File->Send To-> E-mail Recipient in the Snipping Tool, this opened an E-mail in Outlook and everything looked fine.

    image

    When the email was sent and received, the Image showed up as a Red X with the message “The linked image cannot be displayed. The file may have been moved, renamed, or deleted. Verify that the link points to the correct file and location.”

    image

    An Interesting fact was that this worked fine in the following scenarios:
    1) When Outlook was run in Safe mode
    2) If the image was copied and pasted in the e-mail instead of using the Snipping Tool

    We look at the HTML of the email and noticed that the img tag was pointing to a physical path to the Image file! This is never going to work since the same path+Image would never exist on the recipients machine. Below is what the body looked like:

    <body lang=EN-US link=blue vlink=purple>
    <div class=WordSection1><p class=MsoNormal>
    <span style='font-family:"Arial","sans-serif"'>
    <img border=0 width=866 height=306 id="_x0000_i1025" src="file:///C:\Users\AKASH\AppData\Local\Temp\SnipImage-%7b91EAD0DB-1DBE-4E4A-B24B-F9CAB854BE82%7d.PNG">
    </span><br></p>
    </div>
    </body>

    What should it have looked like?
    <body lang=EN-US link=blue vlink=purple style='tab-interval:.5in'>
    <div class=WordSection1><p class=MsoNormal>
    <img width=661 height=205 id="_x0000_i1025" src="cid:image001.png@01CDD4E9.F5F9A6E0">
    </p>
    </div>
    </body>
     
    In short, a link to the image was being sent instead of the image being sent with the mail. Why did this happen?

    Process Monitor finally came to the rescue. Reviewing the logs we found that the “Send Pictures With Document” registry key was set to 0. i.e Outlook would send a link instead of including the picture with the message. More details on the key:
    Key: HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Outlook\Options\Mail
    Value type: REG_DWORD
    Value name; Send Pictures With Document
    Value: 0
     
    If the Value is set to 1 or the key is not present, the picture is embedded as an hidden attachment pointed to by the Content-ID(cid).  Why did it work in Safe mode? In Safe mode Outlook ignores certain registry settings and sometimes takes a different code path!
     
    Enjoy!

  • Akash Blogging......

    Stamping Archive Policy Tag using EWS Managed API from PowerShell(Exchange 2010)

    • 0 Comments

    In a previous post I had explained how to stamp a Retention Policy Tag using EWS Managed API, In this post I will explain the changes you will need to make to stamp an Archive Policy Tag. What’s the difference between these two? Retention Policies consist of delete tags, i.e. retention tags with either Delete and Allow Recovery or Permanently Delete actions, or archive tags, i.e. retention tags with the Move To Archive action, which move items to the user's archive mailbox.

    The requirement in this case to prevent the items in the notes folder from being moved to the Archived folder, a detailed explanation of the issue can be found here. Why is the Notes folder part of the issue? In Exchange 2010 SP1, support was added for the Notes folder. In Exchange 2010 RTM, items in the Notes folder aren't processed. After upgrade to SP1, if the user's retention policy doesn't have a Retention Policy Tag for the Notes folder, the Default Policy Tags from the user's policy will apply to items in that folder.

    In short the workaround is to have users apply the Personal never move to archive personal tag (displayed as Never under Archive Policy in Outlook/OWA) to a default folder(Notes in this case). How do this archive personal tag look like? Below is a screenshot:

    image

    Now that we have the Archive Policy Tag in place, we have to make a few changes in the code to get this to work. Most of the need code is the same as in the Retention Policy Tag using EWS Managed API article. The change is in the “StampPolicyOnFolder” function which I have detailed below:

    function StampPolicyOnFolder($MailboxName)
    {
    Write-host "Stamping Policy on folder for Mailbox Name:" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Stamping Policy on folder for Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Search for the folder you want to stamp the property on
    $oFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
    $oSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$FolderName)
    $oFindFolderResults = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$oSearchFilter,$oFolderView)

    if ($oFindFolderResults.TotalCount -eq 0)
    {
    Write-host "Folder does not exist in Mailbox:" $MailboxName -foregroundcolor $warning
    Add-Content $LogFile ("Folder does not exist in Mailbox:" + $MailboxName)
    }
    else
    {
    Write-host "Folder found in Mailbox:" $MailboxName -foregroundcolor $info

    #PR_ARCHIVE_TAG 0x3018 – We use the PR_ARCHIVE_TAG instead of the PR_POLICY_TAG
    $ArchiveTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3018,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);

    #PR_RETENTION_FLAGS 0x301D
    $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    #PR_ARCHIVE_PERIOD 0x301E - We use the PR_ARCHIVE_PERIOD instead of the PR_RETENTION_PERIOD
    $ArchivePeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301E,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    #Change the GUID based on your policy tag
    $ArchiveTagGUID = new-Object Guid("{3b7f5e7a-9016-4f87-a829-83f84c5adc62}");
    $oFolder.SetExtendedProperty($ArchiveTag, $ArchiveTagGUID.ToByteArray())

    #Same as that on the policy - 16 specifies that this is a ExplictArchiveTag
    $oFolder.SetExtendedProperty($RetentionFlags, 16)

    #Same as that on the policy - Since this tag is disabled the Period would be 0
    $oFolder.SetExtendedProperty($ArchivePeriod, 0)

    $oFolder.Update()

    Write-host "Retention policy stamped!" -foregroundcolor $info
    Add-Content $LogFile ("Retention policy stamped!")
    }

    $service.ImpersonatedUserId = $null
    }
    #Change the name of the folder. This is the folder the properties will be stamped on.
    $FolderName = "Notes"


    With the above changes, you can now stamp an Archive Policy tag on multiple mailboxes and AFAIK there is no admin-controlled way of doing this. Once again, Thank you Vikas Soundade(Exchange) for helping me with the MRM piece!
    Enjoy!

  • Akash Blogging......

    Outlook displaying the Red X mark instead of the Image

    • 1 Comments

    This was a fun issue to troubleshoot! The problem : Compose a new mail in Outlook 2010, Insert a image and send it to yourself. The expected behavior is that when you open the received mail, you see the Image.

    In this case, instead of the Image we saw Red X mark with the message “The linked image cannot be displayed.  The file may have been moved, renamed, or deleted. Verify that the link points to the correct file and location.”

    image

    The interesting part was that everything worked fine in Safe mode! What could be causing this issue?
    In general, when we open an Item that has embedded images, the images are copied to a temp location which is then used for displaying the image. When the Item is closed the Images are deleted from that location. On further troubleshooting and debugging the issue, we noticed that in this case the Images never got created!

    Knowing this was a BIG help to track down this issue. The question: Where is the temp location picked up from and does it really exist? On debugging we found that Outlook was reading the value from the follow key:

    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Cache

    The value that was read in this case was %CACHEDIR%\Cache . Is this the right value? I guess NOT. In a working case it gives us the full path to the Temporary Internet Files, something like “C:\Documents and Settings\username\Local Settings\Temporary Internet Files”
    When I looked at a few machine in my environment the values were as below:
    On a Windows XP machine: %USERPROFILE%\Local Settings\Temporary Internet Files
    On a Windows 7 machine: %USERPROFILE%\AppData\Local\Microsoft\Windows\Temporary Internet Files
     
    In this case the problem machine was a Windows XP machine, we replaced %CACHEDIR%\Cache with %USERPROFILE%\Local Settings\Temporary Internet Files and then restarted Outlook.
    Now everything WORKS?

    Why did it work in Safe mode? In Safe mode, Outlook takes a different code path and a different API to get the temp folder location and does that successfully!
    Enjoy!

  • Akash Blogging......

    Exchange 2013 Preview is now AVAILABLE!

    • 0 Comments


    Download Microsoft Exchange Server 2013 Preview
    http://technet.microsoft.com/en-US/evalcenter/hh973395

    Developer Resources for Exchange 2013

    EWS client design overview for Exchange 2013
    http://msdn.microsoft.com/en-us/library/jj190904(EXCHG.150).aspx

    Exchange Web Services Managed API Version 2.0 - Technical Preview
    http://www.microsoft.com/en-us/download/details.aspx?id=30391

    New features in EWS in Exchange 2013
    http://msdn.microsoft.com/en-us/library/jj190903(EXCHG.150).aspx

    New features in the EWS Managed API
    http://msdn.microsoft.com/en-us/library/jj220500(EXCHG.80).aspx

    Exchange 2013 101 Code Samples
    http://code.msdn.microsoft.com/Exchange-2013-101-Code-3c38582c

    Get started with mail apps for Outlook 2013 and EWS in Exchange 2013
    http://msdn.microsoft.com/en-us/library/jj190901(EXCHG.150).aspx

    Enjoy!

  • Akash Blogging......

    Using–Filter, Select-Object, ForEach-Object in C# – Exchange PowerShell Automation

    • 0 Comments

    Have you ever tried migrating Exchange PowerShell script to be used from C# code?  I needed to convert the following script to C#

    Get-Mailbox -filter {ResourceType -ne "Room"}| Select-object Alias,DistinguishedName,Identity | foreach-object {Get-MailboxFolderStatistics $_.Alias}

    If you run the above script in Exchange 2010 Management Shell you might get the following error message:

    Pipeline not executed because a pipeline is already executing. Pipelines cannot be executed concurrently.
    + CategoryInfo : OperationStopped: (Microsoft.Power...tHelperRunspace:ExecutionCmdletHelperRunspace) [],
    PSInvalidOperationException
    + FullyQualifiedErrorId : RemotePipelineExecutionFailed

      This error is caused because the Windows PowerShell remoting feature doesn't support more than one pipeline running at the same time. To resolve this issue, store the output of the cmdlet earlier in the pipeline in a variable, and then pipe the data stored in the variable to the ForEach cmdlet as shown below:

    $Mailboxes = Get-Mailbox -filter {ResourceType -ne "Room"}| Select-object Alias,DistinguishedName, Identity 
    $Mailboxes | foreach-object {Get-MailboxFolderStatistics $_.Identity}

    Now, to get the code working in C#. How do I use the Filter ? How do I use the Select-Object ? How do I store the intermediate result and then pass it into the ForEach-Object? The below code snippet shows how this can be done. I you need a full example of using Exchange PowerShell from C#, you can find it here .

    //TODO: Create and Open Runspace, Create and Import Session
    //Create a pipline
    Pipeline pipe = runspace.CreatePipeline();

    //Add the first command Get-Mailbox with the filters
    Command CommandGetMailbox = new Command("Get-Mailbox");
    CommandGetMailbox.Parameters.Add("Filter", "ResourceType -ne \"Room\"");
    pipe.Commands.Add(CommandGetMailbox);

    // Do a Select-Object on the fields
    var props = new string[] { "Alias", "DistinguishedName", "Identity"};
    Command CommandSelect = new Command("Select-Object");
    CommandSelect.Parameters.Add("Property", props);
    pipe.Commands.Add(CommandSelect);

    //Invoke the First Pipeline and get the results
    resultMailboxes = pipe.Invoke();

    // Create a second Pipeline..
    Pipeline pipe2 = runspace.CreatePipeline();

    // Do a write-output of the results so that it can be piped to the foreach
    Command Command1 = new Command("write-output");
    Command1.Parameters.Add("InputObject", resultMailboxes);
    pipe2.Commands.Add(Command1);

    //Pipe into the ForEach
    Command CommandForEach = new Command("ForEach-Object");
    CommandForEach.Parameters.Add("Process", ScriptBlock.Create("Get-MailboxFolderStatistics -Identity $_.Identity"));
    pipe2.Commands.Add(CommandForEach);

    //Invoke the second Pipeline..
    resultMailboxes = pipe2.Invoke();
    //TODO: Display the results and clean up


    Enjoy!




  • Akash Blogging......

    EWS Managed API is lying to me!

    • 0 Comments

    I have been working with Exchange Web Services(EWS) for a long time and this is the first time I saw that the EWS was lying to me! The code was executing a FindItem call on a list of folders and all of a sudden it threw the following error: “Error occurred: The request failed. The remote server returned an error: (401) Unauthorized”.

    Why would that happen? If it was an authentication problem then why did my previous call work? The first step was to check the IIS Logs on the CAS server and below is what we find:

    7/11/2012  10.171.230.40    POST /EWS/Exchange.asmx    SoapAction=FindItem 443    ExchangeServicesClient/14.03.0067.001    200     63025
    7/11/2012 10.171.230.40 POST /EWS/Exchange.asmx SoapAction=FindItem 443 ExchangeServicesClient/14.03.0067.001 200 21809
    7/11/2012 10.171.230.40 POST /EWS/Exchange.asmx SoapAction=FindItem 443 ExchangeServicesClient/14.03.0067.001 302 196752
    7/11/2012 10.171.230.40 POST /EWS/Exchange.asmx SoapAction=FindItem 443 ExchangeServicesClient/14.03.0067.001 302 196643

    I chose to show only the relevant columns above. I you notice the highlighted values, the 302 is the HTTP Status code which means that the object has been temporarily moved. The last column is the time taken in milliseconds to execute the FindItem request. In the above case it was more than 3 minutes which is not good and means we have a LOTS of scope to optimize the code.

    But, why do I get the 302? I turned my attention to the Event Logs on the CAS box and I did not have to search too much. Below is what I saw:

    Log Name:      Application
    Source: ASP.NET 2.0.50727.0
    Date: 7/11/2012 12:58:05 PM
    Event ID: 1309
    Task Category: Web Event
    Level: Warning
    Keywords: Classic
    User: N/A
    Computer: myserver.contoso.com
    Description:
    Event code: 3001
    Event message: The request has been aborted.
    Event time: 7/11/2012 12:58:05 PM
    Event time (UTC): 7/11/2012 4:58:05 PM
    Event ID: 38a627eada914200923e244e9c83f7c8
    Event sequence: 6230694
    Event occurrence: 37
    Event detail code: 0

    Application information:
    Application domain: /LM/W3SVC/1/ROOT/EWS-1-129851299999841294
    Trust level: Full
    Application Virtual Path: /EWS
    Application Path: C:\Program Files\Microsoft\Exchange Server\ClientAccess\exchweb\EWS\
    Machine name: myserver

    Process information:
    Process ID: 1152
    Process name: w3wp.exe
    Account name: NT AUTHORITY\SYSTEM

    Exception information:
    Exception type: HttpException
    Exception message: Request timed out.

    Request information:
    Request URL: https://myserver.contoso.com:443/EWS/Exchange.asmx
    Request path: /EWS/Exchange.asmx
    User host address: 10.171.230.40
    User: DOMAIN\Username
    Is authenticated: True
    Authentication Type: Negotiate
    Thread account name: NT AUTHORITY\SYSTEM

    Thread information:
    Thread ID: 26
    Thread account name: NT AUTHORITY\SYSTEM
    Is impersonating: False
    Stack trace:

    Now the picture is clearer! Since the request was running for more that 110 seconds which is default timeout in ASP.NET 2.0, the request was being aborted, which resulted in a 302 in the IIS logs. Now why did EWS Managed API return 401, frankly I did not bother to find!

    To validate the cause we went ahead and increased the executionTimeout from 110 to 240 in the Web.Config for EWS and then we did not see the issue any more. This was done for TESTING only and is definitely not recommended.

    There are two possible solutions to this issue:
    1) Optimize your code and use filters and paging while doing FindItems (Recommended).
    2) Increase the executionTimeout setting in the Web.Config(Not Recommended – can be tried only If optimization does not work ).

    Enjoy!

  • Akash Blogging......

    Unable to send Bcc using System.Net.Mail when specifying a Pickup Directory(Exchange 2007/Exchange 2010) in code

    • 0 Comments

    Not sure developers using System.Net.Mail are not complaining about this issue? The issue is very easy to reproduce, all you need to do is create a new mail with someone on Bcc and drop it to the Pickup folder In Exchange. Most likely not to many people use Bcc or use Port instead of Pickup.

    MailMessage message = new MailMessage("User1@mycompany.com", "User2@mycompany.com", "Sent Using Pickup", "This Message was sent using Pickup");
    message.Bcc.Add(new MailAddress("User3@mycompany.com"));
    SmtpClient client = new SmtpClient();
    client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
    client.PickupDirectoryLocation = @"C:\Program Files\Microsoft\Exchange Server\TransportRoles\Pickup";
    client.Send(message);

    If we send a mail using the above code we would expect that the mail gets delivered to the Bcc user i.e User3@mycompany.com, but that does not happen! Why would that happen? I then tried to use CDOSYS to create a mail and added Bcc to it and this time the mail was delivered to User3@mycompany.com.

    What could the difference be? I stopped the Transport service on my Exchange 2007(installed on Windows 2003) so that the mail do not get delivered by the transport and then examined the .eml to check what the difference could be. Below is what I find:

    System.Net.Mail uses X-Headers to add the Recipient information to the mail.Below is what the MIME look like on my Windows 2003 box:

    X-Sender: User1@mycompany.com
    X-Receiver: User2@mycompany.com
    X-Receiver: User3@mycompany.com <- This is the BCC
    MIME-Version: 1.0
    From: User1@mycompany.com
    To: User2@mycompany.com
    Date: 19 Jul 2012 13:53:05 -0400
    Subject: Sent Using Pickup
    Content-Type: text/plain; charset=us-ascii
    Content-Transfer-Encoding: quoted-printable

    This Message was sent using Pickup

    Below is what the MIME looks like when I used CDOSYS on my Windows 2003 box:
    thread-index: Ac1l181oBFqzycOSRFaCD7mjVftgqw==
    Thread-Topic: This is a test CDOSYS message (Sent via Pickup)
    From: <User1@mycompany.com>
    To: <User2@mycompany.com>
    Bcc: <User3@mycompany.com>
    Subject: This is a test CDOSYS message (Sent via Pickup)
    Date: Thu, 19 Jul 2012 13:56:20 -0400
    Message-ID: <D76CF8D778AE43A096B10F2281A9914F@mycompany.com>
    MIME-Version: 1.0
    ...

    If you notice, the above MIME does not use X-Headers, it uses the Bcc header and it works. Unfortunately, this behavior of CDOSYS could be different on different environments. When I tested on a Windows 7 box, the MIME looks very similar to what we get from System.Net.Mail(uses X-Header).
    Based on my discussion with the Exchange guys, it turns out that when the mail is put in the Pickup folder, it does not look at the X-Headers and uses the From, To, Bcc headers if present.
     
    The simplest solution was to drop the mail in the Replay folder instead of the Pickup folder.
    MailMessage message = new MailMessage("User1@mycompany.com", "User2@mycompany.com", "Sent Using Pickup", "This Message was sent using Pickup");
    message.Bcc.Add(new MailAddress("User3@mycompany.com"));
    SmtpClient client = new SmtpClient();
    client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
    client.PickupDirectoryLocation = @"C:\Program Files\Microsoft\Exchange Server\TransportRoles\Replay";
    client.Send(message);
     
    Below is an article that talks about both the directories, although it does not mention that X-Headers are ignored in the pickup, it does explicitly say that Replay makes use of the X-Headers.

    Understanding the Pickup and Replay Directories
    http://technet.microsoft.com/en-us/library/bb124230.aspx

    I have tested this on Exchange 2007 installed on Windows 2003 and it works!
    Enjoy!


  • Akash Blogging......

    After installing KB 2553248, Outlook 2010 starts to crash when we open Meetings in the Calendar

    • 0 Comments

    Here's a scenario we recently had reported: A customer running Outlook 2010 installs KB 2553248, and now Outlook crashes when they try to open meetings that were created using Exchange Web Services (EWS). In fact there are a few other scenarios where Outlook crashes:

    1) Create a Meeting request using EWS and send it to yourself. When you receive it in Outlook 2010 as a No Response Required meeting request, just selecting the meeting request crashes Outlook.
    2) Opening the meeting in the Organizer's Calendar crashes Outlook.
    3) If the attendee sends an acceptance, selecting the acceptance in the Explorer crashes Outlook.
    4) Dismissing reminders crashes Outlook.

    The issue is happening because there was a change made in Outlook which caused it to crash when it encounters a Time Zone property on a meeting which does not have a name. If we use MFCMAPI to dump out the properties of a Meeting we can see the problem is with PidLidAppointmentTimeZoneDefinitionStartDisplay. Notice that szKeyName is null.

    <property tag = "0x80380102" type = "PT_BINARY">
    <NamedPropGUID>{00062002-0000-0000-C000-000000000046} = PSETID_Appointment</NamedPropGUID>
    <NamedPropName>id: 0x825E=33374 = PidLidAppointmentTimeZoneDefinitionStartDisplay, dispidApptTZDefStartDisplay</NamedPropName>
    <Value>cb: 76 lpb: 000000000000000000000000000000000000000000000000000000000000000000000000000000</Value>
    <AltValue><![CDATA[............>...A...............ð...........................................]]>
    </AltValue>
    <SmartView><![CDATA[Time Zone Definition:
    bMajorVersion = 0x02 (2)
    bMinorVersion = 0x01 (1)
    cbHeader = 0x0006 (6)
    wReserved = 0x0002 (2)
    cchKeyName = 0x0000 (0)
    szKeyName = (null)
    cRules = 0x0001 (1)

    TZRule[0x0].bMajorVersion = 0x02 (2)
    TZRule[0x0].bMinorVersion = 0x01 (1)
    TZRule[0x0].wReserved = 0x003E (62)
    TZRule[0x0].wTZRuleFlags = 0x0002 = TZRULE_FLAG_EFFECTIVE_TZREG
    <Value>04:00:00.000 PM 5/5/2012</Value>
    <AltValue>Low: 0x1FE4C000 High: 0x01CD2AD8</AltValue>
    </property>

    Fortunately, the fix is in the works and if everything falls in place should be out in the June 2012 CU for Office 2010.

    If you're running into this issue you have a couple options:
    1) Uninstall KB 2553248 (obviously).
    2) Fix the Exchange Web Services code so that new meetings that are created do not crash Outlook.

    What do I need to do to fix the existing code? The change is simple:

    For Exchange Web Services Proxy code, remember to specify the Time Zone Name along with the Base Offset:

    DateTime startTime = new DateTime(2012, 05, 05, 8, 00, 00, DateTimeKind.Unspecified);
    appointment.Start = startTime;
    appointment.End = startTime.AddHours(4);
    appointment.StartSpecified = appointment.EndSpecified = true;

    TimeZoneType tzUSMST = new TimeZoneType();
    tzUSMST.TimeZoneName = "Atlantic Standard Time";
    tzUSMST.BaseOffset = "PT4H";
    appointment.MeetingTimeZone = tzUSMST;

    For Exchange Web Services Managed API provide the TimeZone like below:
    appointment.StartTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Atlantic Standard Time");

  • Akash Blogging......

    Run-time error ‘-2147352567 (80020009) when trying to do SetColumns on a Items collection in Outlook

    • 0 Comments

    Problem: SetColumns is failing on non-english versions of windows with the error message below. In fact it was even failing on a English version of windows with English Office 2010 installed.

    image

    Why would this be happening? When I looked at the system where this code was failing, it looked as if the entire list of comma separated columns was be treaded a one. When I just gave any one of the columns for e.g “FullName” in the SetColumns call, the code seemed to work just fine.

    Digging deeper to find out what the problem could be, I looked at the Region and Language settings of the problem machine and found out that the Format was set to Russian (Russia). Could this be causing a problem?

    The answer is YES, when we clicked on the Additional Settings on the Format Tab in Regional and Language settings below is what we see:

    image

    The List separator was set to a semi-colon. I went back to the code and replaced the commas with semi-colon and tested and guess what? The code worked fine with out any issues. Outlook reads the value of List separator and uses it internally to split the string. In the non-working case, since we used a comma, the entire string was be treated as one column.

    The bigger question is, how can we make our code independent of the regional settings? The easiest way to get around this issue would be to read the value of List Separator from the registry and use that.

    The Separator value is stored under the following registry hive:
    [HKEY_CURRENT_USER\Control Panel\International]

    And the value you need to read is "sList". After reading the value replace the “,” in your Field list with the separator value read from the registry and then make the call to SetColumns and you should be good to go.

    Enjoy!

  • Akash Blogging......

    Stamping Retention Policy Tag on Items using EWS Managed API 1.1 from PowerShell(Exchange 2010)–Part 2

    • 0 Comments

    Yes, this is similar to the script in my previous post but is also different in the following ways:

    • This script searches the entire mailbox and looks for items in folders that contain mail items or in other words where the folders class is IPF.Note
    • This scripts also shows how to use the Not, Exists the AND logical operator to build up a Search Filter collection

    I would suggest you read my previous post as it covers some fundamental used in this post.

    To summarize what the script below does:

    • We start of by passing the SMTP address of the mailbox we want to search. We then do a deep folder traversal to get a list of all the folder where the FolderClass = “IPF.Note”.
    • We then pass to folder id to the StampPolicyOnItems function which then looks for Items which have a specific message class AND  does not have the PR_POLICY_TAG property stamped on the item. This is done using Search Filter Collection.
    • Finally we loop through all the items that meet the criteria above and stamp the PR_POLICY_TAG and the PR_RETENTION_PERIOD properties on the item and update it.

    Straight to code now. Create a .PS1 file with the code below:

    # The script requires the EWS managed API, which can be downloaded here:
    # http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1
    # This also requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$error = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt" # Path of the Log File

    function SearchFolders($MailboxName)
    {
    Write-host "Searching folders in Mailbox Name:" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Searching folders in Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Number of Items to Get
    $FpageSize =50
    $FOffset = 0
    $MoreFolders =$true

    while ($MoreFolders)
    {

    #Setup the View to get a limited number of Items at one time
    $folderView = new-object Microsoft.Exchange.WebServices.Data.FolderView($FpageSize,$FOffset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $folderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
    $folderView.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet(
    [Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly,
    [Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,
    [Microsoft.Exchange.WebServices.Data.FolderSchema]::FolderClass);

    #Create the Search Filter.
    $FoSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::FolderClass, "IPF.Note")

    $oFindFolders = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$FoSearchFilter,$folderView)

    foreach ($folder in $oFindFolders.Folders)
    {

    Write-host "Begining to stamp policy on Items in folder:" $folder.DisplayName -foregroundcolor $info;
    Add-Content $LogFile ("Begining to stamp policy on Items in folder:" + $folder.DisplayName);

    &{

    StampPolicyOnItems($folder.Id.UniqueId)

    Write-host "Retention policy stamped on Items in folder:" $folder.DisplayName -foregroundcolor $info
    Add-Content $LogFile ("Retention policy stamped on Items in folder:" + $folder.DisplayName)

    }
    trap [System.Exception]
    {
    $IsFailure = $true;
    Write-host ("Error in StampPolicyOnItems: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error in StampPolicyOnItems: " + $_.Exception.Message);
    Write-host "Failure in stamping Retention policy on Items in folder:" $folder.DisplayName -foregroundcolor $info
    Add-Content $LogFile ("Failure in stamping Retention policy on Items in folder:" + $folder.DisplayName)

    continue;
    }

    }


    if ($oFindFolders.MoreAvailable -eq $false)
    {$MoreFolders = $false}

    if ($MoreFolders)
    {$FOffset += $FpageSize}

    }

    Write-host "Finished with Mailbox Name:" $MailboxName "Done!" -foregroundcolor $info
    Write-host "-------------------------------------------------" -foregroundcolor $info

    Add-Content $LogFile ("Done!")
    Add-Content $LogFile ("-------------------------------------------------" )

    $service.ImpersonatedUserId = $null
    }



    function StampPolicyOnItems($fId)
    {
    #Number of Items to Get
    $pageSize =100
    $Offset = 0
    $MoreItems =$true
    $ItemCount=0

    #PR_POLICY_TAG 0x3019
    $PolicyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);

    #PR_RETENTION_PERIOD 0x301A
    $RetentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    while ($MoreItems)
    {
    #Setup the View to get a limited number of Items at one time
    $itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView($pageSize,$Offset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
    $itemView.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet(
    [Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly,
    [Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::ItemClass,
    [Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Subject);

    #Create the Search Filter.
    $oSearchFilters = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection(
    [Microsoft.Exchange.WebServices.Data.LogicalOperator]::And)


    $oSearchFilterEA1 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::ItemClass, "IPM.My-ARCHIVE")
    $oSearchFilterEA2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+Not(New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+Exists($PolicyTag))

    $oSearchFilters.add($oSearchFilterEA1)
    $oSearchFilters.add($oSearchFilterEA2)


    $uniqueId = new-object Microsoft.Exchange.WebServices.Data.FolderId($fId);

    $oFindItems = $service.FindItems($uniqueId,$oSearchFilters,$itemView)


    foreach ($Item in $oFindItems.Items)
    {
    $ItemCount++

    Write-host "Item Number:" $ItemCount
    Write-host "Message Class:" $Item.ItemClass
    Write-host "Subject:" $Item.Subject


    #Same as that on the policy
    $Item.SetExtendedProperty($RetentionPeriod, 1095)

    #Change the GUID based on your policy tag
    $PolicyTagGUID = new-Object Guid("{92186ff7-7f4d-4efa-a09b-bbdc5aee3908}");

    $Item.SetExtendedProperty($PolicyTag, $PolicyTagGUID.ToByteArray())

    $Item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
    Write-host "Retention policy stamped on " $Item.ID -foregroundcolor $info
    Add-Content $LogFile ("Retention policy stamped on " + $Item.ID)

    }



    if ($oFindItems.MoreAvailable -eq $false)
    {$MoreItems = $false}

    if ($MoreItems)
    {$Offset += $pageSize}

    }

    }

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("serviceAccount","Password","domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri("http://YOUR-CAS/EWS/Exchange.asmx")

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    #Read data from the UserAccounts.txt
    import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    SearchFolders($WindowsEmailAddress)
    }




    UserAccounts.txt contains the list of the users mailboxes in which the items needs to be stamped. This file should exist in the same directory as the .PS1 file.The First row denotes the Field Names. Format of the text file is as below:

    WindowsEmailAddress
    akasha@contoso.com
    akashb@contoso.com
    akashc@contoso.com

    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where the script file is.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script
    Note: This script creates a log file in C:\Temp. Make sure that directory exists.

    Enjoy!

  • Akash Blogging......

    Stamping Retention Policy Tag on Items using EWS Managed API 1.1 from PowerShell(Exchange 2010)

    • 0 Comments

    This post deals with a scenario where we need to stamp Retention Policy on specific items. In this case we wanted to stamp a policy on items with a specific message class. In my previous post I had explained about Messaging records management and how to stamp the policy on a folder. Please do read my previous post if you have not yet done so.

    The process of stamping Retention Policy on items is not much different, when applying the Retention Policy to an item, we need to stamp it with two important properties:

    • PR_POLICY_TAG – The data type for this property is binary . The value that it contains is the GUID of the retention tag that you create. Do a Get-RetentionPolicyTag YourTagName |fl and copy the value in the GUID property. This value will be uses in the script later on.
    • PR_RETENTION_PERIOD – The data type for this property is Integer. This is the same value that you specify for the AgeLimitForRetention while creating or updating the retention policy tag.

    Before you begin:
    You will need to download and install the EWS Managed API on the box you run this script on from the link below:
    Exchange Web Services Managed API
    http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1

    Read about setting up Impersonation in Exchange 2010 as the code below uses it.
    Configuring Exchange Impersonation (Exchange Web Services)
    http://msdn.microsoft.com/en-us/library/bb204095.aspx

    Using Exchange Impersonation XML Request/Response
    http://msdn.microsoft.com/en-us/library/bb204088.aspx

    Using Impersonation in EWS Managed API
    http://msdn.microsoft.com/en-us/library/dd633680(EXCHG.80).aspx

    We will stamp the properties using Exchange Web Services Managed API 1.1 in PowerShell 2.0 and below is how it is done. Create a .PS1 file with the code below:

    # This script requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$error = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt" # Path of the Log File

    function StampPolicyOnItems($MailboxName)
    {
    Write-host "Stamping Policy on Items for Mailbox Name:" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Stamping Policy on Items for Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate. Comment the line below if impersonation is not needed
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Number of Items to Get
    $pageSize =50
    $Offset = 0
    $MoreItems =$true
    $ItemCount=0

    while ($MoreItems)
    {
    #Setup the View to get a limited number of Items at one time
    $itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView($pageSize,$Offset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
    $itemView.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet(
    [Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly,
    [Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::ItemClass,
    [Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Subject);

    #Create the Search Filter and look for items with the message class IPM.My-ARCHIVE
    $oSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::ItemClass, "IPM.My-ARCHIVE")

    #Look for items in the Inbox


    $oFindItems = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$oSearchFilter,$itemView)

    foreach ($Item in $oFindItems.Items)
    {
    $ItemCount++

    Write-host "Item Number:" $ItemCount
    Write-host "Message Class:" $Item.ItemClass
    Write-host "Subject:" $Item.Subject

    #PR_POLICY_TAG 0x3019
    $PolicyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)

    #PR_RETENTION_PERIOD 0x301A
    $RetentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)

    #Same as that on the policy
    $Item.SetExtendedProperty($RetentionPeriod, 1095)

    #Change the GUID based on your policy tag
    $PolicyTagGUID = new-Object Guid("{92186ff7-7f4d-4efa-a09b-bbdc5aee3908}")

    $Item.SetExtendedProperty($PolicyTag, $PolicyTagGUID.ToByteArray())

    $Item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)

    Write-host "Retention policy stamped on " $Item.ID -foregroundcolor $info
    Add-Content $LogFile ("Retention policy stamped on " + $Item.ID)

    }


    if ($oFindItems.MoreAvailable -eq $false)
    {$MoreItems = $false}

    if ($MoreItems)
    {$Offset += $pageSize}

    }

    Write-host "Stamping Policy on Items for Mailbox Name:" $MailboxName "Done!" -foregroundcolor $info
    Add-Content $LogFile ("Done!")
    Add-Content $LogFile ("-------------------------------------------------" )

    #Comment the line below if impersonation is not needed

    $service.ImpersonatedUserId = $null
    }

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials,Use the account that has been granted Impersonation rights
    # If impersonation is not used, use the credentials required to access the mailbox
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("serviceAccount","Password","domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri(“http://Your-CAS-Server/EWS/Exchange.asmx”)

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    #Read data from the UserAccounts.txt
    import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    StampPolicyOnItems($WindowsEmailAddress)
    }


    UserAccounts.txt contains the list of the users mailboxes in which the items needs to be stamped. This file should exist in the same directory as the .PS1 file.The First row denotes the Field Names. Format of the text file is as below:
    WindowsEmailAddress
    akasha@contoso.com
    akashb@contoso.com
    akashc@contoso.com

    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where the script file is.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script

    Note: This script creates a log file in C:\Temp. Make sure that directory exists.

    Enjoy!

  • Akash Blogging......

    Searching contacts using EWS Managed API 1.1 from PowerShell (Impersonation, SearchFilterCollection, ContainsSubstring)

    • 0 Comments

    Here is another example of using EWS Managed API with PowerShell 2.0. In this case the requirement was to be able to find out all contacts that have Hotmail email address in any of the Email Address fields. This provided me to opportunity to demonstrate the use SearchFilterCollection and ContainsSubstring search filter from a PowerShell script. In addition to SearchFilterCollection and ContainsSubstring, this sample also shows the use of Impersonation. The use of Impersonation is necessary because the requirement was to search in multiple mailboxes.

    Details on how to configure Exchange Impersonation can be found in the links below:
    Exchange 2010:
    Configuring Exchange Impersonation (Exchange Web Services)
    http://msdn.microsoft.com/en-us/library/bb204095.aspx

    Using Exchange Impersonation XML Request/Response
    http://msdn.microsoft.com/en-us/library/bb204088.aspx

    Using Impersonation in EWS Managed API
    http://msdn.microsoft.com/en-us/library/dd633680(EXCHG.80).aspx

    Exchange 2007:
    Configuring Exchange Impersonation (Exchange Web Services)
    http://msdn.microsoft.com/en-us/library/bb204095(EXCHG.80).aspx

    Using Exchange Impersonation (Exchange Web Services)
    http://msdn.microsoft.com/en-us/library/bb204088(EXCHG.80).aspx

    You will also need to download and install the EWS Managed API on the box you run this script on from the link below:

    Exchange Web Services Managed API
    http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1

    Now for the code. Create a .PS1 file with the code below:

    # This script requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$error = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt" # Path of the Log File

    function SearchContacts($MailboxName)
    {
    Write-host "Searching Contacts in Mailbox :" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Searching Contacts in Mailbox :" + $MailboxName)

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId(
    [Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Number of Items to Get
    $pageSize =40
    $Offset = 0
    $MoreItems =$true
    $ItemCount=0

    while ($MoreItems)
    {
    #Setup the View to get a limited number of Items at one time
    $itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView($pageSize,$Offset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow

    $itemView.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.ContactSchema]::GivenName,
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::Surname,
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress1,
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress2,
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress3)

    #Create the Search Filter.
    $searchFilterEA1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring(
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress1,"@hotmail.com")

    $searchFilterEA2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring(
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress2,"@hotmail.com")

    $searchFilterEA3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring(
    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress3,"@hotmail.com")

    $oSearchFilters = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection(
    [Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or)

    $oSearchFilters.add($searchFilterEA1)
    $oSearchFilters.add($searchFilterEA2)
    $oSearchFilters.add($searchFilterEA3)

    $oFindItems = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts,$oSearchFilters,$itemView)

    foreach ($Item in $oFindItems.Items)
    {

    if ($Item.GetType().FullName -eq "Microsoft.Exchange.WebServices.Data.Contact")
    {
    Write-host "Given Name:" $Item.GivenName
    Write-host "Surname:" $Item.Surname
    Write-host "Email Address 1:" $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1]
    Write-host "Email Address 2:" $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress2]
    Write-host "Email Address 3:" $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress3]

    Add-Content $LogFile("Given Name :" + $Item.GivenName)
    Add-Content $LogFile("Surname :" + $Item.Surname)
    Add-Content $LogFile("Email Address 1:" + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1])
    Add-Content $LogFile("Email Address 2:" + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress2])
    Add-Content $LogFile("Email Address 3:" + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress3])

    }
    }

    if ($oFindItems.MoreAvailable -eq $false)
    {$MoreItems = $false}

    if ($MoreItems)
    {$Offset += $pageSize}

    }

    Write-host "Searching Contacts in Mailbox :" $MailboxName "Done!" -foregroundcolor $info
    Add-Content $LogFile ("Done!")
    Add-Content $LogFile ("-------------------------------------------------" )

    $service.ImpersonatedUserId = $null
    }

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"

    # This script also works on Exchange 2007. Just change the Version Header to Exchange2007_SP1
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("serviceAccount","Password","domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri(“http://Your-CAS-Server/EWS/Exchange.asmx”)

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    #Read data from the UserAccounts.txt
    import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    SearchContacts($WindowsEmailAddress)
    }

    UserAccounts.txt contains the list of the users mailboxes that need to be searched. This file should exist in the same directory as the .PS1 file.The First row denotes the Field Names. Format of the text file is as below:

    WindowsEmailAddress
    akasha@contoso.com
    akashb@contoso.com
    akashc@contoso.com

    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where the script file is.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script

    Note: This script creates a log file in C:\Temp. Make sure that directory exists.

    Enjoy!

  • Akash Blogging......

    Unable to set the Folder Property in Outlook View Control–Getting error 8002801d (Library Not Registered)

    • 0 Comments

    One of my customer ran into this issue with the Outlook View Control. All we were doing was trying to set the Folder property to the Contacts folder so that when the control loads it shows the contacts in that folder by default. In our case it gave the error 8002801d (Library Not Registered) and displayed the Inbox folder which is the default. This problem was not happening on all the machines.

    The strange thing was that it showed the Inbox in the View control, to me it means that the library is registered and that is why it was able to show Inbox. Why is it not showing the contacts? What could be going wrong? Which library are we talking about? After a little debugging, as expected we found out that the LoadLibrary call was failing. I was able to get the GUID it was looking for and it was {0006F062-0000-0000-C000-000000000046} which is for OUTLCTL.DLL.

    This is where Process Monitor came to our help. I decided to take a Process Monitor log on a non-working and a working machine. Below is what I see:

    On the non-working machine( looking for {0006F062-0000-0000-C000-000000000046} ) this is how it looked:

    OUTLOOK.EXE    RegQueryKey    HKCU\Software\Classes                                                        SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib SUCCESS
    OUTLOOK.EXE RegQueryKey HKCU\Software\Classes\TypeLib SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegQueryKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 SUCCESS
    OUTLOOK.EXE RegQueryKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0\409 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0\409 NAME NOT FOUND
    OUTLOOK.EXE RegQueryKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0\9 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0\9 NAME NOT FOUND
    OUTLOOK.EXE RegQueryKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0\0 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0\0 NAME NOT FOUND
    OUTLOOK.EXE RegCloseKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 SUCCESS
    OUTLOOK.EXE RegCloseKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegCloseKey HKCU\Software\Classes\TypeLib SUCCESS

    On the working machine( looking for {0006F062-0000-0000-C000-000000000046} ) this is how it looked:
    OUTLOOK.EXE    RegCloseKey    HKCU\Software\Classes                                                                SUCCESS
    OUTLOOK.EXE RegQueryKey HKCU\Software\Classes\TypeLib SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046} NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.0 NAME NOT FOUND
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046} NAME NOT FOUND
    OUTLOOK.EXE RegEnumKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegEnumKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046} NO MORE ENTRIES
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046} SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1 SUCCESS
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\409 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\409 NAME NOT FOUND
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\9 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\9 NAME NOT FOUND
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 SUCCESS
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 SUCCESS
    OUTLOOK.EXE RegCloseKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 SUCCESS
    OUTLOOK.EXE RegCloseKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 SUCCESS
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 SUCCESS
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 NAME NOT FOUND
    OUTLOOK.EXE RegOpenKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 SUCCESS
    OUTLOOK.EXE RegQueryKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 SUCCESS
    OUTLOOK.EXE RegOpenKey HKCU\Software\Classes\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 NAME NOT FOUND
    OUTLOOK.EXE RegQueryValue HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32\(Default) SUCCESS
    OUTLOOK.EXE RegCloseKey HKCR\TypeLib\{0006F062-0000-0000-C000-000000000046}\1.1\0\win32 SUCCESS
    OUTLOOK.EXE CreateFile C:\Program Files\Microsoft Office\Office14\OUTLCTL.DLL SUCCESS
    OUTLOOK.EXE ReadFile C:\Program Files\Microsoft Office\Office14\OUTLCTL.DLL SUCCESS
    OUTLOOK.EXE ReadFile C:\Program Files\Microsoft Office\Office14\OUTLCTL.DLL SUCCESS

    Now, comparing the entries of the working and non-working machine, we can clearly see:
    1) That the keys ending with 1.0 don’t exist on the working machine but they do on the non-working machine.
    2) On the working machine it continues to search for keys ending with 1.1 (Version 1.1 of OVC) and goes on to find the OUTLCTL.DLL, This does not happen on the non-working machine.
    So, I decided to take a look at the registry of the non-working machine and below is how it looks:
    image
     
    And on the working machine, this is how it looks:
    image

    What’s the difference? On the working machine there is no 1.0 key! If you look at the “PrimaryInteropAssemblyName” key on the non-working machine, it points to version 10.0.4504.0 which is a build from Office XP. This is a left over key from one of the previous installs of Office and the Interops.
    We decided to take a backup of the key and delete it. As soon as we deleted the 1.0 key and tested the code again, everything WORKED!

    Moral of the story? Comparing Process Monitor logs can give you answers Smile.

    Enjoy!

  • Akash Blogging......

    Stamping Retention Policy Tag using EWS Managed API 1.1 from PowerShell(Exchange 2010)

    • 5 Comments

    Exchange Web Services with PowerShell! In the last few days I have written quite a few scripts that use Exchange Web Services Managed API 1.1 with PowerShell, you will agree that it is not as easy as writing Exchange Web Services Managed API 1.1 with C# and it takes a bit of getting used to it. One of my customer wanted to stamp a Retention Policy Tag on a folder in the Archived mailbox in Exchange 2010. What is a Retention Policy Tag?

    Messaging records management (MRM) is the records management technology in Microsoft Exchange Server 2010 that helps organizations reduce the legal risks associated with e-mail. MRM makes it easier to keep the messages needed to comply with company policy, government regulations, or legal needs, and to remove content that has no legal or business value. This is accomplished through the use of retention policies or managed folders.

    Retention policies, use retention tags to apply retention settings. You create retention tags, and link them to a retention policy. Mailboxes that have a retention policy applied to them are processed by the Managed Folder Assistant, a mailbox assistant that runs on a schedule and provisions retention tags in mailboxes. More details about understanding and creating retention policies and tags can be found in the following articles on MSDN:

    Now that you understand what a Retention Policy Tag is, when applied to a folder stamps it with three important properties:

    • PR_POLICY_TAG – The data type for this property is binary . The value that it contains is the GUID of the retention tag that you create. Do a Get-RetentionPolicyTag YourTagName |fl and copy the value in the GUID property. This value will be uses in the script later on.
    • PR_RETENTION_FLAGS – The data type for this property is Integer. The easiest would be to get the values using MFCMAPI once the policy is applied to the Mailbox and the policy tag to the folder(screenshot below). “Test Policy” is the folder where this retention policy tag is applied. If you don’t see these properties that means that there is no policy tag on the folder.
    • PR_RETENTION_PERIOD – The data type for this property is Integer. This is the same value that you specify for the AgeLimitForRetention while creating or updating the retention policy tag.

    MFCMAPI

    We will stamp the same properties using Exchange Web Services Managed API 1.1 in PowerShell and this is how it is done:

    # The script requires the EWS managed API, which can be downloaded here:
    # http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1
    # This also requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$error = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt" # Path of the Log File

    function StampPolicyOnFolder($MailboxName)
    {
    Write-host "Stamping Policy on folder for Mailbox Name:" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Stamping Policy on folder for Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Search for the folder you want to stamp the property on
    $oFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
    $oSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$FolderName)

    #Uncomment the line below if the folder is in the regular mailbox
    #$oFindFolderResults = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$oSearchFilter,$oFolderView)

    #Comment the line below and uncomment the line above if the folder is in the regular mailbox
    $oFindFolderResults = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::ArchiveMsgFolderRoot,$oSearchFilter,$oFolderView)

    if ($oFindFolderResults.TotalCount -eq 0)
    {
    Write-host "Folder does not exist in Mailbox:" $MailboxName -foregroundcolor $warning
    Add-Content $LogFile ("Folder does not exist in Mailbox:" + $MailboxName)
    }
    else
    {
    Write-host "Folder found in Mailbox:" $MailboxName -foregroundcolor $info

    #PR_POLICY_TAG 0x3019
    $PolicyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);

    #PR_RETENTION_FLAGS 0x301D
    $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    #PR_RETENTION_PERIOD 0x301A
    $RetentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    #Bind to the folder found
    $oFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$oFindFolderResults.Folders[0].Id)

    #Same as the value in the PR_RETENTION_FLAGS property
    $oFolder.SetExtendedProperty($RetentionFlags, 137)

    #Same as the value in the PR_RETENTION_PERIOD property
    $oFolder.SetExtendedProperty($RetentionPeriod, 1095)

    #Change the GUID based on your policy tag
    $PolicyTagGUID = new-Object Guid("{92186ff7-7f4d-4efa-a09b-bbdc5aee3908}");

    $oFolder.SetExtendedProperty($PolicyTag, $PolicyTagGUID.ToByteArray())

    $oFolder.Update()

    Write-host "Retention policy stamped!" -foregroundcolor $info
    Add-Content $LogFile ("Retention policy stamped!")

    }

    $service.ImpersonatedUserId = $null
    }

    #Change the name of the folder. This is the folder the properties will be stamped on.
    $FolderName = "My Folder"

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("UserName","Password","Domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri(http://YOUR-CAS-SERVER/EWS/Exchange.asmx)

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    #Read data from the UserAccounts.txt.
    #This file must exist in the same location as the script.

    import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    StampPolicyOnFolder($WindowsEmailAddress)
    }

    UserAccounts.txt contains the list of the users mailboxes that contain the folder that is set in the $FolderName variable in the script above.The First row denotes the Field Names. Format of the text file is as below:

    WindowsEmailAddress
    akasha@contoso.com
    akashb@contoso.com
    akashc@contoso.com

    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where the script and the UserAccounts.txt file is.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script
     
    Thank you Vikas Soundade(Exchange) for helping me with the MRM piece!
     
    Enjoy!
  • Akash Blogging......

    How to get information on Database Copies using Managed code and Remote Powershell(Exchange 2010)

    • 2 Comments

    It all started with a customer who wanted to know which servers in the DAG setup have the copies for a specific Exchange database. There were other properties like Activation Preferences that he was interested in too.

    The easiest way to get the details about the copies for a database is by using the command (Get-MailboxDatabase "Database Name").DatabaseCopies. When I did this on the CAS Server using Exchange Management Shell, I got the following output:


    [PS] C:\Windows\system32>$copies = (Get-MailboxDatabase "Mailbox Database 1056078029").DatabaseCopies
    [PS] C:\Windows\system32>$copies

    DatabaseName : Mailbox Database 1056078029
    HostServerName : AKAS23474121
    ActivationPreference : 1
    ParentObjectClass : msExchPrivateMDB
    ReplayLagTime : 00:00:00
    TruncationLagTime : 00:00:00
    AdminDisplayName :
    ExchangeVersion : 0.10 (14.0.100.0)
    DistinguishedName : CN=AKAS23474121,CN=Mailbox Database 1056078029,CN=Databases,CN=Exchange Administrative Group (FY
    DIBOHF23SPDLT),CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,
    CN=Configuration,DC=DOM234741,DC=LOCAL
    Identity : Mailbox Database 1056078029\AKAS23474121
    Guid : 562cb271-7cee-4130-9582-e3ce0f08cb3d
    ObjectCategory : DOM234741.LOCAL/Configuration/Schema/ms-Exch-MDB-Copy
    ObjectClass : {top, msExchMDBCopy}
    WhenChanged : 7/21/2011 4:29:06 PM
    WhenCreated : 7/21/2011 4:29:06 PM
    WhenChangedUTC : 7/21/2011 10:59:06 AM
    WhenCreatedUTC : 7/21/2011 10:59:06 AM
    OrganizationId :
    OriginatingServer : AKAS23474118.DOM234741.LOCAL
    IsValid : True

    DatabaseName : Mailbox Database 1056078029
    HostServerName : AKAS23474120
    ActivationPreference : 2
    ParentObjectClass : msExchPrivateMDB
    ReplayLagTime : 00:00:00
    TruncationLagTime : 00:00:00
    AdminDisplayName :
    ExchangeVersion : 0.10 (14.0.100.0)
    ...


    The $copies variable contains live objects, we can then pipe it into a foreach-object and print out the selected properties we want to. The reason I am saying this is because there is a difference which I will talk about later. For now, I connect to the same CAS Server from a remote machine and run the exact same commands and following is the output:

    PS C:\Users\Superman> $copies=(Get-MailboxDatabase "Mailbox Database 1056078029").DatabaseCopies
    PS C:\Users\Superman> $copies
    Mailbox Database 1056078029\AKAS23474121
    Mailbox Database 1056078029\AKAS23474120

    Why is this difference? Why do I see just the Database name? What happened to all the other properties?

    I then did a Get-Member on the $copies variable first on the local CAS box and below is what I see


    [PS] C:\Windows\system32>$copies | Get-Member -MemberType Property


    TypeName: Microsoft.Exchange.Data.Directory.SystemConfiguration.DatabaseCopy

    Name MemberType Definition
    ---- ---------- ----------
    ActivationPreference Property System.Int32 ActivationPreference {get;}
    AdminDisplayName Property System.String AdminDisplayName {get;}
    DatabaseName Property System.String DatabaseName {get;}
    DistinguishedName Property System.String DistinguishedName {get;}
    ExchangeVersion Property Microsoft.Exchange.Data.ExchangeObjectVersion ExchangeVersion {get;}
    Guid Property System.Guid Guid {get;}
    HostServerName Property System.String HostServerName {get;}
    Identity Property Microsoft.Exchange.Data.ObjectId Identity {get;}
    IsValid Property System.Boolean IsValid {get;}
    ObjectCategory Property Microsoft.Exchange.Data.Directory.ADObjectId ObjectCategory {get;}

    ...

    When I do Get-Member on the $copies variable on the Remote machine below is what I see

    PS C:\Users\Superman> $copies | Get-Member -MemberType Properties


    TypeName: System.String

    Name MemberType Definition
    ---- ---------- ----------
    Length Property System.Int32 Length {get;}

    Notice the difference in TypeName. On the Local box it is “Microsoft.Exchange.Data.Directory.SystemConfiguration.DatabaseCopy” and on the Remote box it is “System.String”. Why does this happen?

    When you run remote commands that generate output, the command output is transmitted across the network back to the local computer. Because most live Microsoft .NET Framework objects (such as the objects that Windows PowerShell cmdlets return) cannot be transmitted over the network, the live objects are "serialized". In other words, the live objects are converted into XML representations of the object and its properties. Then, the XML-based serialized object is transmitted across the network. On the local computer, Windows PowerShell receives the XML-based serialized object and "deserializes" it by converting the XML-based object into a standard .NET Framework object. However, the deserialized object is not a live object. It is a snapshot of the object at the time that it was serialized. This is how an MSDN article defines serialization in .NET framework. More details can be found in the article “How objects are sent to and from remote sessions”. 

    Luckily the data that we needed was available buy just using the Get-MailboxDatabase cmd-let by accessing the DatabaseCopies Property. To prove what is said above and show how to do it in C#, below is the code that I wrote:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Management.Automation;
    using System.Management.Automation.Runspaces;
    using System.Management.Automation.Remoting;
    using System.Collections.ObjectModel;
    using System.Security;
    using System.Collections;

    namespace CallingPSCmdlet
    {
    class Program
    {
    static void Main(string[] args)
    {
    string password = "Passowrd";
    string userName = "Domain\\UserName";

    System.Uri uri = new Uri(“http://CAS-SERVER/powershell?serializationLevel=Full”);
    System.Security.SecureString securePassword = String2SecureString(password);

    System.Management.Automation.PSCredential creds = new System.Management.Automation.PSCredential(userName, securePassword);

    Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace();

    PowerShell powershell = PowerShell.Create();
    PSCommand command = new PSCommand();
    command.AddCommand("New-PSSession");
    command.AddParameter("ConfigurationName", "Microsoft.Exchange");
    command.AddParameter("ConnectionUri", uri);
    command.AddParameter("Credential", creds);
    command.AddParameter("Authentication", "Default");
    PSSessionOption sessionOption = new PSSessionOption();
    sessionOption.SkipCACheck = true;
    sessionOption.SkipCNCheck = true;
    sessionOption.SkipRevocationCheck = true;
    command.AddParameter("SessionOption", sessionOption);

    powershell.Commands = command;

    try
    {
    // open the remote runspace
    runspace.Open();

    // associate the runspace with powershell
    powershell.Runspace = runspace;

    // invoke the powershell to obtain the results
    Collection<PSSession> result = powershell.Invoke<PSSession>();

    foreach (ErrorRecord current in powershell.Streams.Error)
    Console.WriteLine("The following Error happen when opening the remote Runspace: " + current.Exception.ToString() +
    " | InnerException: " + current.Exception.InnerException);

    if (result.Count != 1)
    throw new Exception("Unexpected number of Remote Runspace connections returned.");

    // Set the runspace as a local variable on the runspace
    powershell = PowerShell.Create();

    command = new PSCommand();
    command.AddCommand("Set-Variable");
    command.AddParameter("Name", "ra");
    command.AddParameter("Value", result[0]);
    powershell.Commands = command;
    powershell.Runspace = runspace;
    powershell.Invoke();

    // First import the cmdlets in the current runspace (using Import-PSSession)
    command = new PSCommand();
    command.AddScript("Import-PSSession -Session $ra");
    powershell.Commands = command;
    powershell.Runspace = runspace;
    powershell.Invoke();

    // Now call the Get-MaiboxDatabase


    command = new PSCommand();
    command.AddCommand("Get-MailboxDatabase");

    //Change the name of the database

    command.AddParameter("Identity", "Mailbox Database XXXXXXXX");

    powershell.Commands = command;
    powershell.Runspace = runspace;

    Collection<PSObject> results = new Collection<PSObject>();
    results = powershell.Invoke();

    PSMemberInfo Member = null;

    foreach (PSObject oResult in results)
    {
    foreach (PSMemberInfo psMember in oResult.Members)
    {
    Member = psMember;
    DumpProperties(ref Member);
    }
    }

    results = null;
    Member = null;
    }
    finally
    {
    // dispose the runspace and enable garbage collection
    runspace.Dispose();
    runspace = null;

    // Finally dispose the powershell and set all variables to null to free up any resources.
    powershell.Dispose();
    powershell = null;
    }

    }

    //Method to Dump out the Properties
    public static void DumpProperties(ref PSMemberInfo psMember)
    {
    // Only look at Properties
    if (psMember.MemberType.ToString() == "Property")
    {
    switch (psMember.Name)
    {
    case "ActivationPreference":
    case "DatabaseCopies":
    if (psMember.Value != null)
    {
    PSObject oPSObject;
    ArrayList oArrayList;
    oPSObject = (PSObject)psMember.Value;
    oArrayList = (ArrayList)oPSObject.BaseObject;
    Console.WriteLine("Member Name:" + psMember.Name);
    Console.WriteLine("Member Type:" + psMember.TypeNameOfValue);
    Console.WriteLine("----------------------");
    foreach (string item in oArrayList)
    {
    Console.WriteLine(item);
    }
    Console.WriteLine("----------------------");
    }
    break;
    }
    }
    }

    private static SecureString String2SecureString(string password)
    {
    SecureString remotePassword = new SecureString();
    for (int i = 0; i < password.Length; i++)
    remotePassword.AppendChar(password[i]);

    return remotePassword;
    }

    }
    }


    Following is the output that I get from running the above code:
    Member Name:DatabaseCopies
    Member Type:Deserialized.Microsoft.Exchange.Data.Directory.SystemConfiguration.DatabaseCopy[]
    ----------------------
    Mailbox Database 1056078029\AKAS23474121
    Mailbox Database 1056078029\AKAS23474120
    ----------------------

    Member Name:ActivationPreference
    Member Type:Deserialized.System.Collections.Generic.KeyValuePair`2[[Microsoft.Exchange.Data.Directory.ADObjectId, Microsoft.Exchange.Data.Directory, Version=14.
    0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[System.Int32, mscorlib
    , Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]][]
    ----------------------
    [AKAS23474121, 1]
    [AKAS23474120, 2]
    ----------------------

    Notice the word “Deserialized” in the values of the “Member Type” field.
    Enjoy!

  • Akash Blogging......

    Another example of using EWS Managed API 1.1 from PowerShell (Impersonation, SearchFilter, FindItems, Paging)

    • 0 Comments

    In this case the customer wanted to know the Item count, size of all the item in the Inbox that were older than 5 years. As usual, the requirement was to run the script against multiple mailboxes. I could have easily written this in C# in no time but preferred to do this in PowerShell as:

    • I would not have to compile the code every time I make change
    • It would also be easier for the Administrator who don’t have access to Development Environment
    • Getting the list of mailboxes is easy in Exchange PowerShell

    The downside is that you do not get the IntelliSense that you get with Visual Studio. With all that said lets get down straight to code. This example shows the use of Impersonation, SearchFilter, FindItems, Paging in Exchange Web Services. Create a .PS1 as below:

    # The script requires the EWS managed API, which can be downloaded here:
    # http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1
    # This also requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$errorMessage = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt" # Path of the Log File

    function GetItemCount($MailboxName)
    {
    Add-Content $LogFile ("-------------------------------------------------" )
    Write-host "Searching Inbox for Mailbox Name:" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Searching Inbox for Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Number of Items to Get
    $pageSize =50
    $Offset = 0
    $MoreItems =$true
    $ItemCount=0
    $ItemSize=0

    while ($MoreItems)
    {
    #Setup the View to get a limited number of Items at one time
    $itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView($pageSize,$Offset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow

    #Get the Size of the Items
    $itemView.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size);


    #Create the Search Filter - Subtract 5 Years from todays date.
    $oSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,
    ([System.Datetime]::now).AddMonths(-60))

    $oFindItems = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$oSearchFilter,$itemView)


    $ItemCount += $oFindItems.Items.Count

    $i=0

    #Loop through the items returned and add up the size
    foreach ($Item in $oFindItems.Items)
    {
    $i++
    $ItemSize += $Item.Size
    Write-Progress -activity "Adding Item Sizes" -status "Percent added: " -PercentComplete (($i /$oFindItems.Items.Count) * 100)

    }

         if ($oFindItems.MoreAvailable -eq $false)
    {$MoreItems = $false}


    if ($MoreItems)
    {$Offset += $pageSize}

    }

    Write-host "Items Found in Inbox:" $ItemCount -foregroundcolor $info
    Add-Content $LogFile ("Items Found in Inbox:" + $ItemCount )

    Write-host "Size of Items:" ($ItemSize / 1Mb) "MB" -foregroundcolor $info
    Add-Content $LogFile ("Size of Items:" + ($ItemSize / 1Mb) + " MB" )
    Add-Content $LogFile ("-------------------------------------------------" )

    $service.ImpersonatedUserId = $null
    }

    #Change the path if needed
    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("serviceAccount","Password","Domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri(“http://CAS-SERVER/EWS/Exchange.asmx”)

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    Get-Mailbox | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $errorMessage;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    GetItemCount($WindowsEmailAddress)
    }


    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where the script file is.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script


    Enjoy!
  • Akash Blogging......

    Exchange 2010 SP1 Rollup 4 re-released

    • 0 Comments

    On Wednesday, 7/27, the Exchange Sustained Engineering team re-released Exchange 2010 SP1 RU4.  This release is being tracked by KB 2579150 and will contain a download file that is unique from the previous release. This updated rollup contains all of the changes that were in the original rollup with the exception of the regression because if which the previous RU4 was pulled back.

    For customers who have the previous version of RU4 and the IU 2581545 installed, there is technically no need to install this updated version of RU4 as the combination of those two releases is functionally equivalent to the updated RU4 dated Wednesday, 7/27.  The only thing a customer would gain by installing the updated rollup 4 would be performing the IU maintenance now that they would need to perform later when installing RU5.

    More details on the Exchange 2010 SP1 RU4 re-release can be found here and here. As for RU5, it is expected to be released by the end of August 2011.

    Enjoy!

  • Akash Blogging......

    Creating folder using EWS Managed API 1.1 from PowerShell

    • 4 Comments

    A customer of mine wanted to create a folder under Inbox for thousands of mailboxes. He also wanted to use PowerShell to do it. What better way that using Exchange Web Service(Managed API)  with Impersonation to do the job.

    The list of the users are available in a text file. The First row denotes the Field Names.The file is named UserAccounts.txt. Format of the text file is as below:

    WindowsEmailAddress
    akasha@contoso.com
    akashb@contoso.com
    akashc@contoso.com

    Create a .PS1 file as below:

    # The script requires the EWS managed API, which can be downloaded here:
    # http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1
    # This also requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White" # Color for informational messages
    [string]$warning = "Yellow" # Color for warning messages
    [string]$error = "Red" # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt" # Path of the Log File

    function CreateFolder($MailboxName)
    {
    Write-host "Creating Folder for Mailbox Name:" $MailboxName -foregroundcolor $info
    Add-Content $LogFile ("Creating Folder for Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

    #Create the folder object



    $oFolder = new-object Microsoft.Exchange.WebServices.Data.Folder($service)
    $oFolder.DisplayName = $FolderName

    #Call Save to actually create the folder
    $oFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)

    Write-host "Folder Created for " $MailboxName -foregroundcolor $warning
    Add-Content $LogFile ("Folder Created for " + $MailboxName)

    $service.ImpersonatedUserId = $null
    }

    #Change the name of the folder
    $FolderName = "My Folder"

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("serviceAccount","Password","Domain")

    # Change the URL to point to your cas server
    $service.Url= new-object Uri(http://CAS-Server/EWS/Exchange.asmx)

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    #Read data from the UserAccounts.txt
    import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
    Write-host "Autodiscovering.." -foregroundcolor $info
    $UseAutoDiscover = $false
    $service.AutodiscoverUrl($WindowsEmailAddress)
    Write-host "Autodiscovering Done!" -foregroundcolor $info
    Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    }
    #To catch the Exceptions generated
    trap [System.Exception]
    {
    Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
    Add-Content $LogFile ("Error: " + $_.Exception.Message);
    continue;
    }
    CreateFolder($WindowsEmailAddress)
    }


    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where you have stored the Data and the script file.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script

    Enjoy!

  • Akash Blogging......

    Use Windows PowerShell in Exchange Online

    • 0 Comments

    A few articles on working with Exchange Online (Office 365 for enterprises, Live@edu) with Windows Powershell.

    Install and Configure Windows PowerShell
    http://technet.microsoft.com/en-us/exchangelabshelp/cc952756

    Connect Windows PowerShell to the Service
    http://technet.microsoft.com/en-us/exchangelabshelp/cc952755

    Reference to Available PowerShell Cmdlets in Exchange Online
    http://technet.microsoft.com/en-us/exchangelabshelp/dd575549

    Administrator Role Groups in Exchange Online
    http://technet.microsoft.com/en-us/exchangelabshelp/ee441216

    Role Based Access Control in Exchange Online
    http://technet.microsoft.com/en-us/exchangelabshelp/dd207274

    Role Assignment Policies in Exchange Online
    http://technet.microsoft.com/en-us/exchangelabshelp/ee424427

    Enjoy!

  • Akash Blogging......

    Error: “Object reference not set to an instance of an object” when sending out mails using System.Net.Mail via Exchange 2007

    • 0 Comments

    This was a fun issue to work on! Some background, the customer was using System.Net.Mail to send out mails via Exchange 2007 using port 25 and port 587, all was working fine and then some updates were installed and he was no longer able to send out mails via port 587. He started to get the following exception:

    Exception:
    Object reference not set to an instance of an object.
    System.Net Error: at System.Net.Mail.SmtpConnection.GetConnection(String host, Int32 port)
       at System.Net.Mail.SmtpTransport.GetConnection(String host, Int32 port)
       at System.Net.Mail.SmtpClient.GetConnection()
       at System.Net.Mail.SmtpClient.Send(MailMessage message)

    The code was very simple:

    static void Main(string[] args)
    {
    SmtpClient _client = new SmtpClient();

    using (MailMessage _message = new MailMessage())
    {
    _message.Subject = "Test message";
    _message.Body = "Test message";

    _message.From = new MailAddress("support@xyz.com");
    _message.To.Add("akashb@xyz.com");

    _client.Send(_message);
    }
    }

    The host, port and the credentials were being picked up from the App.Config:

    <network host="EX-01.xyz.COM" port="587" userName="support@xyz.com" password="Password"/>

    What we were also told was that if in the code the SmtpClient.TargetName Property was set to blank then everything worked fine! What is this property? This property gets or sets the Service Provider Name (SPN) to use for authentication when using extended protection.

    We made sure we have the exact same setting on the Exchange 2007 Receive Connectors, updated the client with all the updates that were installed in the customer’s environment but we initially could not reproduce the issue. It did not matter if we set the TargetName to blank or not. We finally did reproduce the issue after I put the client machine where the code was running in the same domain as the Exchange Server and then setting the TargetName to blank did matter. Now the next step was to find out the update that caused the code to fail. We did get a hint from the SmtpClient.TargetName property and started to look for the patch that could cause the issue.

    Luckily, the search did not last long and we were able to identify the patch that caused the failure!

    Description of the rollup update for the .NET Framework 3.5 Service Pack 1 and the .NET Framework 2.0 Service Pack 2 on Windows XP and on Windows Server 2003 (976765 and 980773): June 8, 2010
    http://support.microsoft.com/kb/982167

    This updates the NLC on the system to support Extended Protection. When we uninstalling the update for KB 980773 from the system the code started to work. But now we had a few questions that we needed answer for:

    1)Is there something wrong with Exchange 2007?
    2)Does Exchange 2007 not understand the request for Authentication?
    3)Why does it still work with port 25 and fails only with port 587?

    We then decide to take a verbose log on the Exchange Receive connector and below is what we find:

    >,"220 EX-01.internal.xyz.com Microsoft ESMTP MAIL Service ready at Mon, 15 May 2011 08:59:37 -0500",
    <,EHLO akashb,
    >,250-EX-01.internal.xyz.com Hello [192.168.111.111],
    >,250-SIZE 10485760,
    >,250-PIPELINING,
    >,250-DSN,
    >,250-ENHANCEDSTATUSCODES,
    >,250-STARTTLS,
    >,250-AUTH GSSAPI NTLM LOGIN,
    >,250-8BITMIME,
    >,250-BINARYMIME,
    >,250 CHUNKING,
    <,AUTH gssapi,
    >,334 <authentication response>,
    >,334 <authentication response>,
    *,,Inbound Negotiate failed because of IllegalMessage
    >,535 5.7.3 Authentication unsuccessful,
    -,,Remote

    Looking at the verbose log it looks like Exchange does not understand the message, but why?

    Firstly the OS needs to be configured to support Extended Protection and secondly, support for Extended protection was introduced in Exchange 2007 SP 3. The best would be to have Exchange 2007 SP 3 RU 3 v2 installed. In this case it turns out that neither the customer nor I had Exchange 2007 SP 3 RU 3 v2. Make sure you have your SPN’s right. The below article talks about Extended Protection.

    Extended Protection for Authentication
    http://support.microsoft.com/kb/968389

    After Exchange 2007 SP 3 RU 3 v2 is installed you get a new property “ExtendedProtectionPolicy” for the Receive connector. You can then use the Set-ReceiveConnector  cmd-let and set the value for ExtendedProtectionPolicy to “Allow”. This also requires that RequireTLS is set to true on the connector and you must also set EnableSsl = true in the .Net code. So this answers the questions 1 & 2.

    Now for the question 3, why does it still work with port 25 and fails only with port 587?

    That was easy, the answer lies in EHLO. The Default receive connector (Port 25) did not have GSSAPI enabled and hence it was defaulting to NTLM(which works) instead using Negotiate. In the failing case, Negotiate was used with Extended protection, Exchange did not return the expected data and hence the error.

    -Enjoy

  • Akash Blogging......

    Pull Subscription request fails with error “The Mailbox database is temporarily unavailable” on Exchange 2010

    • 0 Comments

    This is the first time I ran into this issue with Exchange Web Services while sending out a Pull Subscription request! The confusing part was that apart from the subscribe request everything else was working as expected. We could connect to the mailbox using EWS Editor, browse the Inbox and other folders, Delete Items and so on…

    Below is the Request that was sent out:

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
    xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header>
    <t:RequestServerVersion Version="Exchange2010" />
    </soap:Header>
    <soap:Body>
    <m:Subscribe>
    <m:PullSubscriptionRequest>
    <t:FolderIds>
    <t:DistinguishedFolderId Id="inbox" />
    </t:FolderIds>
    <t:EventTypes>
    <t:EventType>CopiedEvent</t:EventType>
    <t:EventType>CreatedEvent</t:EventType>
    <t:EventType>DeletedEvent</t:EventType>
    <t:EventType>ModifiedEvent</t:EventType>
    <t:EventType>MovedEvent</t:EventType>
    <t:EventType>NewMailEvent</t:EventType>
    </t:EventTypes>
    <t:Timeout>90</t:Timeout>
    </m:PullSubscriptionRequest>
    </m:Subscribe>
    </soap:Body>
    </soap:Envelope>

    Below is the Response we got:
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
    <h:ServerVersionInfo MajorVersion="14" MinorVersion="1" MajorBuildNumber="289" MinorBuildNumber="6" Version="Exchange2010_SP1"
    xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:SubscribeResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
    <m:ResponseMessages>
    <m:SubscribeResponseMessage ResponseClass="Error">
    <m:MessageText>The mailbox database is temporarily unavailable.</m:MessageText>
    <m:ResponseCode>ErrorMailboxStoreUnavailable</m:ResponseCode>
    <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
    </m:SubscribeResponseMessage>
    </m:ResponseMessages>
    </m:SubscribeResponse>
    </s:Body>
    </s:Envelope>

    Looking at the ETL trace it was found that the underlying error was MAPI_E_NETWORK_ERROR (0x80040115) and we also got to the function that was failing. This helped in searching the Source Code for Exchange 2010, the intent was to find out if there was a way we could reproduce a similar error using the Exchange Management Shell. Luckily we discovered that Move-Mailbox cmd-let would also do something similar!  We discovered that the customer was unable to move mailboxes from one database to another on the same Mailbox Server and was getting the error MapiExceptionNetworkError: Unable to make connection to the server. (hr=0x800004005, ec=2423). Which server are we taking about?

    In this case it turns out that the CAS was not able to contact the Mailbox Server via NetBIOS name. Pinging the Mailbox with the NetBIOS name failed! Pinging the Mailbox with the FQDN worked! What was done to fix the problem?

    In this case a DNS suffix was added (DNS tab in the Advanced TCP/IP Settings) and now pinging the Mailbox with the NetBIOS name WORKED! Did the Move-Mailbox work? YES! Did the Subscribe Request work? YES!

    Enjoy!

  • Akash Blogging......

    Saving during send breaks relation between sent item and Voting responses in Outlook 2010–Updated

    • 0 Comments

    Do you use the voting feature in Outlook 2010? Do you “Save” the item in the Application_ItemSend event? Does everything work as expected?

    Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
    Item.Save
    End Sub

    I don’t think so. If you have not yet noticed, you loose the ability to track the voting responses that you have received. It also breaks the relationship between the Read Receipt(if you have requested one) and the sent mail message.

    We now have a hotfix that addresses this issue and it can now we download from:

    Description of the Outlook 2010 hotfix package (outlook-x-none)
    http://support.microsoft.com/kb/2547227

    However we have a scenario where this is still broken! If the user right clicks on a file and does a Send To – Mail Recipient and then adds the voting options, we loose the ability to track voting responses. We have identified the problem and the fix is in progress and should be out soon.

    Note: If the mail item is not saved OR you do not hook into the Application_ItemSend then don’t worry, everything will work as expected.

    Update:

    What we found out was that the Voting Response tracking was still broken when the user Right clicks on an file on the desktop/Hard drive and select "Send To" -> Mail Recipient and then added the voting options to the message. Fortunately this issue has also been resolved in the July 11th update.

    Description of the Outlook 2010 hotfix package (Outlook-x-none.msp): July 11, 2011
    http://support.microsoft.com/kb/2544027

    Enjoy!

  • Akash Blogging......

    ExpandGroup method returns no results for Dynamic Distribution Group in Exchange 2010

    • 5 Comments

    Strange but TRUE! This work in Exchange 2007 but does not in Exchange 2010. Below is the request and response that I get from a Exchange 2010 server for the ExpandGroup Exchange Web Services call:

    Request:

    <?xml version="1.0" encoding="utf-8"?>
     <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
    xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
       <soap:Header>
         <t:RequestServerVersion Version="Exchange2010" />
       </soap:Header>
       <soap:Body>
         <m:ExpandDL>
           <m:Mailbox>
             <t:EmailAddress>TestDDL@bhargava.com</t:EmailAddress>
           </m:Mailbox>
         </m:ExpandDL>
       </soap:Body>
     </soap:Envelope>

    Response:
    <?xml version="1.0" encoding="utf-8"?>
     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
       <s:Header>
         <h:ServerVersionInfo MajorVersion="14" MinorVersion="1" MajorBuildNumber="269" MinorBuildNumber="0" Version="Exchange2010_SP1" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
    xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
       </s:Header>
       <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
         <m:ExpandDLResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
           <m:ResponseMessages>
             <m:ExpandDLResponseMessage ResponseClass="Success">
               <m:ResponseCode>NoError</m:ResponseCode>
               <m:DLExpansion TotalItemsInView="0" IncludesLastItemInRange="true" />
             </m:ExpandDLResponseMessage>
           </m:ResponseMessages>
         </m:ExpandDLResponse>
       </s:Body>
     </s:Envelope>

    In this case the TestDDL group had members but we got no results. It turns out that this broke as a result of a change trying to synchronize behavior between various Mail Clients. Fortunately, we have decided to fix this and the fix should be available in one of the upcoming Rollup Updates for Exchange 2010 Service Pack 1.

    The only workaround I know is of using LDAP to get the membership of a Dynamic Distribution Group if it fits your requirements.

    Update:

    This issue has been fixed in Exchange 2010 SP 1 RU 5. Exchange 2010 SP 1 RU 5 should be available in August 2011.Unfortunately this has been pushed to Exchange 2010 SP 1 RU 6. However, in case you need an Interim Update for SP 1 RU 4 OR RU 5 when it becomes available, open up a Service Request with Microsoft Support to obtain one.

    Enjoy!

Page 1 of 3 (71 items) 123