Dynamics CRM 2011は後方互換性のためCRM4.0のエンドポイント ( /mscrmservices/2007/crmservice.asmx ) を使用するCrmService を使用できます。AD認証の場合は滞りなく使えます。IFD認証の場合どうなのよと思って、試してみました。結果から先に記述すると、DiscoveryService にアクセスした時点で 下図のように "HTTP ステータス 401: Unauthorized エラーで要求が失敗しました" が発生し動作させることができませんでした。

ちなみに、下記フォーラムの内容によると、Unauthorizedエラーは仕様通りで、一度ブラウザでフォーム認証されていれば動作するという回答をサポートから受けたようです。ってことはコンソールアプリでCrmServiceを使用している場合、IFD構成では、後方互換性が担保されないってことだと思われます。

Cannot access CRM 4.0 Discovery services on a CRM 2011 installation
http://social.microsoft.com/Forums/en-US/crmdeployment/thread/d92924d8-5982-4a11-ac66-602feb4542c8

1. 検証プログラム

参考までに、検証に使用したプログラムを掲載します。最初に、CrmService と DiscoveryService 用のプロキシを作成します。以下ではIFD用のURLでアクセスすると、wsdlを取得しプロキシを作成できなかったので、AD認証用のURLでプロキシを作成します。

コンソールプログラムを作成し、プロジェクトを右クリック→サービス参照の追加を行います。

サービス参照の追加画面が表示されます。下部の詳細設定ボタンをクリックします。

サービス参照設定ダイアログが表示されます。互換性セクションの Web 参照の追加 ボタンをクリックします。

Web 参照の追加画面が表示されます。 URL に CRM4.0 用の CrmServiceWsdl.aspx の URL を入力し、右の矢印ボタンをクリックします。本例では、http://localhost/mscrmservices/2007/CrmServiceWsdl.aspx?wsdl=dyn と入力しています。環境に合わせて変更してください。

Webサービスの検索に成功したら、 Web 参照名に適当な名前(本例では Crm4Sdk) を入力して 参照の追加ボタンをクリックします。

同様の手順で CrmDiscoveryService のプロキシを作成します。 URLは本例では、 http://localhost/MSCRMServices/2007/AD/CrmDiscoveryService.asmx?wsdl としています。環境によってホスト名などのURLを変更してください。IFDの場合 本来、パスの /AD/ の部分は /SPLA/ なのですが、wsdlを取得できなかったので、 AD認証用のURLを使用してプロキシを作成しています。

以上でプロキシの準備ができました。

あとは、Program.cs を次のように変更して実行します。プログラムは CrmDiscoveryService からチケットを取得し、作成したチケットを利用して CrmService のインスタンスを作成します。そのあとは WhoAmIRequest を呼び出しています。といっても、DiscoveryService にアクセスした時点でエラーになってしまうのですが。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LegacyCrmSeviceIfdSample.Crm4Sdk;
using LegacyCrmSeviceIfdSample.Crm4Sdk1;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

namespace LegacyCrmSeviceIfdSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 自己証明書対応用。証明書に問題があっても続行するようにする。   
            System.Net.ServicePointManager.ServerCertificateValidationCallback += RemoteCertValidation;

            CrmService service = GetService("https://dyn.dcrm.local", "dyn", "dcrm\\testuser", "password");
            WhoAmIRequest request = new WhoAmIRequest();
            WhoAmIResponse response = service.Execute(request) as WhoAmIResponse;
            Console.WriteLine(response.UserId);
        }

        static CrmService GetService(string serverurl, string organizationName, string username, string password)
        {
            UriBuilder uriBuilder = new UriBuilder(serverurl);
            uriBuilder.Path = "/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx";
            CrmDiscoveryService disco = new CrmDiscoveryService();
            disco.Url = uriBuilder.Uri.ToString();

            disco.Credentials = System.Net.CredentialCache.DefaultCredentials;
            disco.UseDefaultCredentials = true;

            RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
            orgRequest.UserId = username;
            orgRequest.Password = password;
            RetrieveOrganizationsResponse orgResponse =
                (RetrieveOrganizationsResponse)disco.Execute(orgRequest);

            bool found = false;
            string serviceUrl = string.Empty;
            foreach (OrganizationDetail orgdetail in orgResponse.OrganizationDetails)
            {
                if (orgdetail.OrganizationName == organizationName)
                {
                    found = true;
                    serviceUrl = orgdetail.CrmServiceUrl;
                }
            }
            if (found == false) return null;

            RetrieveCrmTicketRequest ticketRequest = new RetrieveCrmTicketRequest();
            ticketRequest.OrganizationName = organizationName;
            ticketRequest.UserId = username;
            ticketRequest.Password = password;
            RetrieveCrmTicketResponse ticketResponse =
                (RetrieveCrmTicketResponse)disco.Execute(ticketRequest);   

            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.AuthenticationType = 2; // IFD, ADの場合 0
            token.OrganizationName = organizationName;
            token.CrmTicket = ticketResponse.CrmTicket;

            UriBuilder bi = new UriBuilder(serverurl);
            bi.Path = "/mscrmservices/2007/crmservice.asmx";

            CrmService service = new CrmService();
            //service.Credentials = new System.Net.NetworkCredential(username, password);
            service.Credentials = System.Net.CredentialCache.DefaultCredentials;
            service.CrmAuthenticationTokenValue = token;
            service.UseDefaultCredentials = true;
            service.Url = bi.Uri.ToString();

            return service;
        }
        static bool RemoteCertValidation(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
        {
            if (error != SslPolicyErrors.None)
            {
                Console.WriteLine("証明書に問題があります。理由:" + chain.ChainStatus[0].StatusInformation);
                Console.Write("処理を続行しますか? y:続行:");
                string input = Console.ReadLine();
                if (input.ToLower() == "y")
                    return true;
                else
                    return false;
            }
            return true;
        }  
    }
}

実行すると、 RetrieveOrganizationsResponse = (RetrieveCrmTicketResponse)disco.Execute(ticketRequest);   の部分で以下の例外(HTTP ステータス 401: Unauthorized で要求が失敗しました。)が発生します。

2.まとめ

今回の説明は以上です。間違っていたらご指摘お願いします。

個人的には予想通り、CRM2011用のIFD認証では、CRM4.0用のコンソールプログラムはうまく動作しませんでした。プログラムを作成しなおすか、AD認証にするかの変更が必要みたいです。.NET3.5でどうしても動作させる必要がある場合は、 [Dynamics CRM 2011] WSDL Endpoint を使用するコンソールアプリケーション(認証方式はIFDクレーム認証)を作成する の方法で一般的なWCFクライアントを作成して対応する方法もあります。

CRM4のIFD認証を使用するプログラムを作成したので、プログラムが間違っているんじゃないかという懸念もしますが、AD認証の場合は動作したので問題ないはずです。