Monthly Archives: January 2010

BlackBerry Enterprise Server and Exchange Server 2010 Throttling Policies

One of the new features in Exchange Server 2010 is the concept of Client Throttling Policies. In summary, Client Throttling Policies are designed to limit the amount of system resources a given user can consume and in turn impact performance for other Exchange users. Out of the box there is a default throttling policy (use the Get-ThrottlingPolicy cmdlet) applied to all users:

RunspaceId                     : ba3cdf92-fc9f-4a70-a912-2cf225e6d573
IsDefault                      : True
EASMaxConcurrency              : 10
EASPercentTimeInAD             :
EASPercentTimeInCAS            :
EASPercentTimeInMailboxRPC     :
EWSMaxConcurrency              : 10
EWSPercentTimeInAD             :
EWSPercentTimeInCAS            :
EWSPercentTimeInMailboxRPC     :
EWSMaxSubscriptions            :
EWSFastSearchTimeoutInSeconds  : 60
EWSFindCountLimit              :
IMAPMaxConcurrency             :
IMAPPercentTimeInAD            :
IMAPPercentTimeInCAS           :
IMAPPercentTimeInMailboxRPC    :
OWAMaxConcurrency              : 5
OWAPercentTimeInAD             :
OWAPercentTimeInCAS            :
OWAPercentTimeInMailboxRPC     :
POPMaxConcurrency              : 20
POPPercentTimeInAD             :
POPPercentTimeInCAS            :
POPPercentTimeInMailboxRPC     :
PowerShellMaxConcurrency       : 18
PowerShellMaxCmdlets           :
PowerShellMaxCmdletsTimePeriod :
ExchangeMaxCmdlets             :
PowerShellMaxCmdletQueueDepth  :
RCAMaxConcurrency              : 20
RCAPercentTimeInAD             :
RCAPercentTimeInCAS            :
RCAPercentTimeInMailboxRPC     :
MessageRateLimit               :
RecipientRateLimit             :
ForwardeeLimit                 :
CPUStartPercent                : 75
AdminDisplayName               :
ExchangeVersion                : 0.10 (14.0.100.0)
Name                           : DefaultThrottlingPolicy_f017f530-3edf-4c59-9955-d94bb7892fb0
DistinguishedName              : CN=DefaultThrottlingPolicy_f017f530-3edf-4c59-9955-d94bb7892fb0,CN=Global Settings,CN=
                                 GreenOrg,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=green,DC=briandesmond,D
                                 C=net
Identity                       : DefaultThrottlingPolicy_f017f530-3edf-4c59-9955-d94bb7892fb0
Guid                           : af1aeaac-4d88-43da-92df-24c0924d4ad8
ObjectCategory                 : green.briandesmond.net/Configuration/Schema/ms-Exch-Throttling-Policy
ObjectClass                    : {top, msExchGenericPolicy, msExchThrottlingPolicy}
WhenChanged                    : 10/10/2009 5:44:29 PM
WhenCreated                    : 10/10/2009 5:44:11 PM
WhenChangedUTC                 : 10/10/2009 10:44:29 PM
WhenCreatedUTC                 : 10/10/2009 10:44:11 PM
OrganizationId                 :
OriginatingServer              : BRIAN-GRDC02.green.briandesmond.net
IsValid                        : True

 

As you can see, most of the valuesa re null, however each of the services has a *MaxCurrency property. These define the maximum number of connections a given user can have to that service. For example, the EASMaxConcurrency value limits a given user to a maximum of ten Exchange ActiveSync connections. Each of the values is documented in the Set-ThrottlingPolicy cmdlet documentation. Of particular note to this discussion is the RCAMaxConcurrency value which defines the maximum number of concurrent connections a given user can have to the RPC Client Access service. The RPC Client Access service is new in Exchange 2010 and it handles all MAPI connections to Exchange.

BlackBerry Enterprise Server (BES) uses a single service account to proxy all of the connections to Exchange on behalf of BlackBerry users. The side effect of this is that it’s quite likely that BES will need to have more than twenty (default limit) connections open to Exchange at a given time. If you review the documentation from RIM, they recommend setting the RCAMaxConcurrency value to null (equivalent to unlimited) for all users. This is really not a great idea at all.

Instead, what you can do is define a new Client Throttling Policy without an RCAMaxConcurrency value and apply it directly to the BES service account. The PowerShell script below does just that. The script assumes that your BES service account is called “BESAdmin”. If it isn’t modify the script accordingly.

New-ThrottlingPolicy "BES Throttling Policy" -RCAMaxConcurrency:$null
Set-Mailbox besadmin -ThrottlingPolicy "BES Throttling Policy"


You can easily confirm that the new policy is applied to the BESAdmin account by inspecting the properties of BESAdmin mailbox:



Get-Mailbox besadmin | fl Name,ThrottlingPolicy


You should see results similar to the following:



Name             : BES Admin
ThrottlingPolicy : BES Throttling Policy

Do you have any idea about end of life/end of support for SBS 2003 sp2?

Steve asked “Do you have any idea about end of life/end of support for SBS 2003 sp2?  I’m not finding it anywhere?


And another Steve answered this — Microsoft Product Support Lifecycle for SBS 2003 – The other steveb – Steve Banks’ Blog on SBS, EBS, and other Small Business Technology Topics:
http://msmvps.com/blogs/steveb/archive/2010/01/11/microsoft-product-support-lifecycle-for-sbs-2003.aspx


But let’s make this real clear —


“SBS 2003 SP1 will be supported until 2014 at least for extended support.”


That said, I’m not a fan of being the first on a platform, nor am I recommending that you be the last.  What is pushing most upgrades are when the hardware changes out.  I can’t accept/don’t like installing old code on new hardware either.

CWR on SBS 08

For some time I have really loved CWR Mobility’s CWR Mobile CRM client for the Windows Mobile Smartphone. It worked great for me, until I upgraded my network to Small Business Server 2008 (SBS 08). From then (back in April) to today, I have longed to have the CWR client back on my phone. But every time I attempted access the server, I would get an authentication error and it would go no further. The kind folks from CWR were stumped as well. I even spent an hour and a half with Erik van Hoof, chief owner of CWR, in person, trying to figure it out. Then today Jeffry from CWR made a suggestion that started me in the right direction.


I started looking at the authentication methods for the CWR web site on my server. Everything looked as it should be. Then I drilled a bit further. Of the possible authentication methods, only Windows Authentication is enabled. We tried enabling other methods to no avail.


image 


Then I noticed the Advanced Settings button. Trying this for the Windows Authentication method I saw there was a single check box to Enable Kernel-mode authentication. It was unchecked. I looked at a couple of other web sites and it was also unchecked. But the verbiage that went along with it suggested that it might resolve authentication problems and is on by default, except in my system. And I would assume other SBS 2008 servers as well. I simply selected this check box. This resolved my problem. I am now able to access CWR Mobile CRM from my phone and carry a fully functional Microsoft Dynamics CRM client with me anywhere I go.


image


ASP.NET: ListBox Tooltip

The ASP.NET ListBox does not display a horizontal scroll bar by default, which can be a problem if any of your list items are too long to fit. One solution to this problem is to use a tooltip. As the user moves the mouse over the ListBox entries, the full text appears in a tooltip.

image

The code to accomplish this is as follows:

In C#:

private void LB_PreRender(object sender, System.EventArgs e)
{
    foreach (ListItem item in LB.Items) {
        item.Attributes.Add("title", item.Text);
}

In C#, you also need to set up the event handler. In this example, the event handler is set up in the Page_Load event, but you could put it where it makes sense for your application.

LB.PreRender += LB_PreRender;

In VB:

Private Sub LB_PreRender(ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles LB.PreRender
    For Each item As ListItem In LB.Items
        item.Attributes.Add("title", item.Text)
    Next
End Sub

The ListBox, named LB in this example, has a PreRender event. In the PreRender event the code loops through the ListBox items and sets the title attribute to the text of the item.

Use this technique any time you want to display a tooltip over your ListBox items.

Enjoy!

How to Create an ActiveSync Device Report

Exchange logs quite a bit of info about ActiveSync device partnerships and you can use this to create reports about the utilization of mobility features in your organization. Getting this data requires a couple of intermediate steps before you can export it to a CSV for processing in something like Excel (or another script). The PowerShell script below will export all of the ActiveSync device relationships in your organization. Keep in mind that this will include old relationships which are no longer active. Depending on how large your organization is and the number of device relationships out there, it may take a little while for the script to run.

Note: If you have a mixed version organization (e.g. Exchange 2007 and Exchange 2010), you’ll need to run the script twice. Once in the Exchange 2007 Management Shell and once in the Exchange 2010 Management Shell. The cmdlets used here are not backwards (or forward compatible). I’ve provided two versions of the script – one for Exchange 2007 and one for Exchange 2010.

Exchange 2007 Version

$devices = @()
$mailboxes = Get-CASMailbox -ResultSize:Unlimited | Where-Object {$_.HasActiveSyncDevicePartnership -eq $true -and $_.ExchangeVersion.ExchangeBuild -ilike "8*"}

foreach ($m in $mailboxes) 
{
	$devices += Get-ActiveSyncDeviceStatistics -Mailbox $m.Identity
}

$devices | Export-Csv DeviceStats.csv




Exchange 2010 Version



$devices = @()
$mailboxes = Get-CASMailbox -ResultSize:Unlimited | Where-Object {$_.HasActiveSyncDevicePartnership -eq $true -and $_.ExchangeVersion.ExchangeBuild -ilike "14*"}

foreach ($m in $mailboxes) 
{
	$devices += Get-ActiveSyncDeviceStatistics -Mailbox $m.Identity
}

$devices | Export-Csv DeviceStats.csv




You can open the exported CSV in Excel from here and generate reports based on that. There is quite a bit of information in the report including some personally identifiable information (PII) for the devices so keep that in mind before redistributing the raw data file.

Web Site Globalization With ASP.NET Routing

For those who don’t know, I have this web site http://PauloMorgado.NET/ that I use both as a web presence besides my blogs and a playfield.

Because I write both in English and Portuguese, I wanted the web site to have both English and Portuguese versions. This is easily accomplished by using ASP.NET Globalization and Localization.

But I wanted to do more than guessing the user’s language form her/his web browser languages. I wanted something like the MSDN and TechNet web sites have where the culture is embedded in the URL which makes it easy for the user to choose in which language she/he wants to see the web site.

With the release of the ASP.NET Routing, this is as easy as writing a custom route handler that sets the culture for the request and returns the requested page handler.

Something like this:

public class GlobalizationRouteHandler : global::System.Web.Routing.IRouteHandler
{
    System.Globalization.CultureInfo culture;
    System.Globalization.CultureInfo uiCulture;

    public GlobalizationRouteHandler(System.Globalization.CultureInfo culture)
        : this(culture, culture)
    {
    }

    public GlobalizationRouteHandler(CultureInfo culture, CultureInfo uiCulture)
    {
        if (culture == null)
        {
            throw new ArgumentNullException("cultureInfo", "cultureInfo is null.");
        }

        if (uiCulture == null)
        {
            throw new ArgumentNullException("uiCulture", "uiCulture is null.");
        }

        this.culture = culture;
        this.uiCulture = uiCulture;
    }

    private GlobalizationRouteHandler()
    {
    }

    #region IRouteHandler Members

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        Thread.CurrentThread.CurrentCulture = this.culture;
        Thread.CurrentThread.CurrentUICulture = this.uiCulture;

        string path = "~/" + (requestContext.RouteData.Values["path"] as string);

        var physicalPath = requestContext.HttpContext.Server.MapPath(path);
        if (System.IO.Directory.Exists(physicalPath))
        {
            path = VirtualPathUtility.Combine(path, "Default.aspx");
        }

        var httpHandler = BuildManager.CreateInstanceFromVirtualPath(path, typeof(IHttpHandler)) as IHttpHandler;

        return httpHandler;
    }

    #endregion
}


And now it’s only a matter of registering the handled cultures:



routes.Add("en", new Route("en/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("en-US"))));
routes.Add("pt", new Route("pt/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("pt-PT"))));

Small Update to Redirection Blog

Last week, I posted about how to redirect HTTP connects to Exchange 2010 OWA to HTTPS. There was a small issue in the post which I’ve now corrected. If you explicitly disabled HTTP Redirection for the OWA virtual directory, you would break the /exchange, /public, and /exchweb virtual directories which redirect to /owa.

If you browse to https://owa.customer.com/exchange, you might see the following event in the Application log of your CAS server:

Log Name:      Application
Source:        ASP.NET 2.0.50727.0
Date:          1/31/2010 2:20:16 PM
Event ID:      1310
Task Category: Web Event
Level:         Warning
Keywords:      Classic
User:          N/A
Computer:      CAS01.green.briandesmond.net

Description:
Event code: 3008
Event message: A configuration error has occurred.
Event time: 1/31/2010 2:20:16 PM
Event time (UTC): 1/31/2010 10:20:16 PM
Event ID: 1dd0ff95241040a48b5acc09bff2e3ad
Event sequence: 32
Event occurrence: 31
Event detail code: 0 
Application information:    
Application domain: /LM/W3SVC/1/ROOT-2-129092586348966635    
Trust level: Full    
Application Virtual Path: /    
Application Path: C:inetpubwwwroot    
Machine name: CAS01
Process information:    
Process ID: 2268    
Process name: w3wp.exe    
Account name: IIS APPPOOLDefaultAppPool 
Exception information:    
Exception type: ConfigurationErrorsException    
Exception message: It is an error to use a section registered as allowDefinition=’MachineToApplication’ beyond application level.  This error can be caused by a virtual directory not being configured as an application in IIS. (C:ExchangeClientAccessowaweb.config line 31) 
Request information:    
Request URL: http://localhost/exchange/default.aspx    
Request path: /exchange/default.aspx    
User host address: 127.0.0.1    
User:     
Is authenticated: False    
Authentication Type:     
Thread account name: IIS APPPOOLDefaultAppPool 
Thread information:    
Thread ID: 19    
Thread account name: IIS APPPOOLDefaultAppPool    
Is impersonating: False    
Stack trace:   
   at System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
   at System.Web.Configuration.RuntimeConfig.GetSectionObject(String sectionName)
   at System.Web.Configuration.RuntimeConfig.GetSection(String sectionName, Type type, ResultsIndex index)
   at System.Web.Configuration.RuntimeConfig.get_Identity()
   at System.Web.HttpContext.SetImpersonationEnabled()
   at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)

To resolve this, open the web.config file for OWA. The path is in the event. I highlighted it in red boldface font above so you know where to look. Inside of the web.config file, search for and remove this line:

<httpRedirect enabled="false" />

Warning: Improperly editing the web.config file for OWA could render it entirely inoperable. I highly recommend that you save a backup prior to making any changes to the file.

Finding a Node in an XML String

A common requirement with an XML file is to find a particular node. If you are targeting the .NET framework 3.5 or higher, you can find the node using Linq to XML or by using Lambda expressions.

As with many of my prior XML examples, the XML string is as follows:

<States>
  <State name="Wisconsin">
    <Regions>
      <Region name="Milwaukee">
        <Area name="Mukwanago"/>
        <Area name="Germantown"/>
      </Region>
      <Region name="Fox Valley">
        <Area name="Oshkosh" />
        <Area name="Appleton" />
      </Region>    
    </Regions>
  </State>
</States>

The code to find the node for the Milwaukee region is as follows:

In C#:

// Be sure to set a reference to System.Core and System.Xml.Linq
XElement states  = XElement.Load("testXML.xml");

// Using LINQ
XElement foundNode;
var query = from XElement r in states.Descendants("Region")
                   where r.Attribute("name").Value == "Milwaukee"
                   select r;
foundNode = query.FirstOrDefault();

// Using Lambda expressions
foundNode = states.Descendants("Region").
     Where(r => r.Attribute("name").Value ==
                         "Milwaukee").FirstOrDefault();

In VB:

‘ Be sure to set a reference to System.Core and System.Xml.Linq
Dim states As XElement = XElement.Load("testXML.xml")

‘ Using LINQ
Dim foundNode As XElement
Dim query = From r As XElement In states…<Region> _
                  Where r.@<name> = "Milwaukee"
foundNode = query.FirstOrDefault()

‘ Using Lambda expression
foundNode = states…<Region>.Where(Function(r) r.@<name> =  _ 
                                 "Milwaukee").FirstOrDefault

This code first loads the XML file containing the XML. The next set of code can be done using LINQ or using Lambda expressions. Use either one, but not both. :-)

The C# code uses the XElement properties and methods. The VB code uses XML literals.

NOTE: The XElement properties and methods work in VB as well.

Enjoy!

NOTE: This post was created based on a prior post that included both finding a node and adding new nodes. This post separates the first step to provide a more straightforward example.

XML Documentation Comments

You can document your classes, properties, methods, and so on using XML tags. I’m sure all developers know this at this point, but did you know that you can modify the set of valid tags?

If you are not familiar with XML Documentation Comments (or just XML Comments), you create them differently depending on the language you are using.

In C#:

On the line above the class, property, method, or whatever you are documenting, type three forward slashes.

///
public List<Customer> Retrieve(int Id)

Visual Studio automatically inserts the appropriate XML tags:

/// <summary>
///
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public List<Customer> Retrieve(int Id)

In VB:

On the line above the class, property, method, or whatever you are documenting, type three apostrophes.

”’
Public Function Retrive(ByVal id As Integer) As List(Of Customer)

Visual Studio automatically inserts the appropriate XML tags:

”’ <summary>
”’
”’ </summary>
”’ <param name="id"></param>
”’ <returns></returns>
”’ <remarks></remarks>
Public Function Retrive(ByVal id As Integer) As List(Of Customer)

You can then build technical documentation of your API from the XML comments.

In C#:

1. Double-click on Properties for the project in the Solution Explorer.

2. Click on the Build tab.

3. Check the "XML document file" checkbox and ensure  that a file name is specified.

4. Build your project.

All of the XML comments are generated into one XML file.

In VB:

Generating the XML documentation is on by default. To check it:

1. Double-click on My Project for the project in the Solution Explorer.

2. Click on the Compile tab.

3. Check the "Generate XML documentation file" checkbox.

The XML file is generated in the binDebug folder for the project.

You can use a product such as SandCastle to generate your technical documentation from these comments.

But what if you want more or different tags than those that are provided? IF YOU ARE USING VB.NET you can do just that.

NOTE: As far as I have seen, this technique is not available in C#.

In VB:

Locate your XML Comment tag file. Mine was here:

C:Documents and SettingsDeborahApplication DataMicrosoftVisualStudio9.0VBXMLDoc.xml

If you don’t have this file, you can create it following the instructions defined here.

This file contains the definition of the valid XML tags used in XML Comments. You can then add to the contents of this file or edit it as you desire.

For one of my projects, we wanted to add an edit history to each class that included the date, the developer’s name, and a description. So I added the following to the Class code element:

    <CodeElement type="Class">
        <Template>
            <summary/>
            <remarks/>
        <editHistory date="" developer=""/>
        </Template>
        <CompletionList>
            <include file="" path=""/>
            <permission cref=""/>
            <remarks/>
            <summary/>
        <editHistory date="" developer=""/>
        </CompletionList>
    </CodeElement>

We could then create XML comments as follows:

”’ <summary>
”’ Manages a customer class
”’ </summary>
”’ <remarks></remarks>
”’ <editHistory  date="9/14/09" developer="DJK">
”’ Added customer type.
”’ </editHistory>
”’ <editHistory date="10/17/09" developer="DJK">
”’ Added an Email address.
”’ </editHistory>
Public Class Customer

Use this technique any time you want to enhance your XML comment documentation in VB.NET.

Enjoy!

“Someone should invent a machine to do that”

Lately, I find myself saying this a whole lot.

The conversations in which I’m saying this follow the pattern of these examples:

Me: “Let me understand your problem – tell me why you don’t want to have expiring passwords on this service account?”

Other guy: “Because when we change passwords, we’d have to change the password at the domain controller, then log on to each machine that runs the service, change the passwords, and stop and restart the service on those machines. Then we’d have to take note of which machines had issues with the process, so that we could work on troubleshooting those.”

Me: “Someone should invent a machine to do that … they could call it something like … a komputor. I’m sure they’d sell millions.”

Yes, it’s a little sarcastic when I put it that way, and for the most part that last piece is in my head. It usually comes out as “that could be done by a simple script”, or “can we justify the cost of buying one of the many programs out there that manages that entire process automatically?”

When you take a boring and mundane manual process and turn it into an automated process, whether through scripting or purpose-built applications, you significantly reduce the likelihood of error, and you can take your staff and turn them to other tasks that require human thought and are interesting.

Recent Comments

Archives