tirsdag den 3. maj 2011

ADFS / CRM2011 making my head hurt.

so here is a few things I learn after a week of playing with CRM 2011 with IFD configures.

First thing I did was watch this you tube clip, and do the same except use some other domain names. What I had to watch several times and still not quite understand was when he was using external domain names and internal domain names. That was lesson number 1 learn.

There IS no internal domains. CRM 2011 does not support having more than 1 HTTP and 1 HTTPS binding and since you need a unique IP address per certificate you are kind of forced to use external domain names for everything. So just to make I get this right, I’m going to post the real domain names I am using in this setup.

First off, I created 3 DNS entries.

adfs.wingu.dk A record pointing to my ADFS server (http/https published in TMG)
crm2011.wingu.dk A record pointing to my CRM2011 server (http/https published in TMG)
*.crm2011.wingu.dk CNAME record pointing to crm2011.wingu.dk

I am using 2 certificates. *.wingu.dk and *.crm2011.wingu.dk ( the first doesn’t need to be * but since I already had it, it was just easier to use that )

After installing CRM2011 I when to Deployment Manager and chanced the Web Address’ to internal.crm2011.wingu.dk ( this will now be the internal URL meaning this can be used to logon on to CRM with Kerberos. That means going to an org internally will now be internal.crm2011.wingu.dk/ORGName but coming externally with be ORGName.crm2011.wingu.dk . This one took me a while to get right. )

image
Update 23-09-2011: Don’t use different domain names here. It will make web service requests go nuts. so for me, that would be internal.crm2011.wingu.dk in all fields.

Next we want to setup Claim Based authentication on CRM. fire up the wizard an type

https://adfs.wingu.dk/FederationMetadata/2007-06/federationmetadata.xml

As encryption certificate you need to use a certificate that is common for all CRM servers (I think ? at least that gave me some issues when deploying more than one. Ill need to get back on this later, but for now I just choose the *.crm2011.wingu.dk certificate
Update 23-09-2011: True. When CRM receives a claim from ADFS it needs to use this certificate to decrypt the tokens. This has to be the same on all frontend servers. )

Jump to the ADFS server and “Add Relying Party Trust” and type in

https://internal.crm2011.wingu.dk/FederationMetadata/2007-06/FederationMetadata.xml

Note, we are using the “internal” link here, after that add the Claim rules from the video.
Pass Through or Filter and Incoming Claim –> Pass Primary Sid –> “Primary SID”
Pass Through or Filter and Incoming Claim –> Pass UPN –> “UPN”
Transform an Incoming Claim –> Transform Windows Account Name –> Windows Account name –> Name
( Update 23-09-2011: Actually it only needs UPN )

image

if this is the first time, also go to Claims Provider Trusts and add Claim rule to Active Directory –> Send LDAP Attributes as Claim –> Send UPN from AD to Claim –> "Active Directory” –> userPrincipalName –> UPN

image

Head back to CRM2011 and now configure Internet-Facing Deployment. Domains is crm2011.wingu.dk. Using dev.crm2011.wingu.dk or crm2011.wingu.dk under Discovery Web service didn’t really seem to make much difference for me, but just to be safe I did that.

image

External domain. Again I still don’t get this one. if you type the name for an organization that already exists you will get a ton of errors and problems. If you use the default “auth.crm2011.wingu.dk” and go this is URL after you get an “404” so I can add that to my “to read up on” list, but for now I just accepted the default. auth.crm2011.wingu.dk

image

After all this testing the internal URL will properly fail. at least it did for me. Tried adding internal.crm2011.wingu.dk and adfs.wingu.dk to “trust sites” in IE, but still didn’t work. after google’ing for a while I tried adding it to “Local Intranet Zone” and then everything worked. It felt wrong but hey, if it works it must be right, right ?

WRONG! .. but took a while before I found out why and how.

Update 23-09-2011: Two things went wrong here regarding powershell. 1) on CRM properties I had not typed the same domain name in all fields. 2) UPN mappings was setup wrong.

One thing that gave me problems was the PowerShell commands and calling the CRM web services. I n the beginning I would constantly get “The Deployment Service cannot process the request because one or more validation checks failed”. You can’t see it in PowerShell but if you do it from visual studio against the web service you can see the inner exception, and I could there see the problem was authentication. If I added a username and password everything would go though, but I wanted a solution that would accept Kerberos.

I created 2 console applications. One where I had added a Service Reference and in the other I would use the CRM 2011 SDK. I need this for provisioning the service in the hosting I’m building so its critical for me to be able to point to a specific server and not “hardcoded” to a specific URL. I also need to be able to use this from a DLL and as some people might now, that can be a bit trouble some. I cant remember/reproduce all the weird and annoying errors I got, trying tons of different approaches regarding connecting to CRM. Basically what I found was I could connect if I supplied username and password, but Kerberos would only work connection go the Deployment Service if using the SDK. Everything would work from the application where I had added the URL’s as Service References but when trying to create a ConfigurationChannelFactory that loaded the app.config everything would just start failing miserably. While paying around with all this and messing around with SPN’s, I suddenly got these 2 errors.

The NetworkCredentials provided were unable to create a Kerberos credential

SOAP security negotiation with 'https://adfs.wingu.dk/adfs/services/trust/2005/kerberosmixed' for target 'https://adfs.wingu.dk/adfs/services/trust/2005/kerberosmixed' failed.

and that got me thinking that I might have screwed up something in my SPN’s. I delete them all and recreated the following SPN’s

I added a SPN for the ADFS server
setspn –A HOST/adfs.wingu.dk ADFS01
setspn –A FQDN/adfs.wingu.dk ADFS01

setspn –A HOST/internal.wingu.wingu.dk CRM201101
setspn –A FQDN/internal.wingu.wingu.dk CRM201101

(Updated 07-05. I had chosen to install ADFS as standalone. If you choose the high availability doing setup, the installer set’s the SPN for you, but instead of setting it on the computer account the SPN’s get added to the service account ADFS runs under. )

And voila, everything just worked like a charm. If testing from a browser, make sure to add adfs.wingu.dk to “Local intranet” zone. The reason for this is that internet explore thinks (and is correct) that adfs.wingu.dk is an external URL and refuses to send your credentials to the site, and hence you will see a “popup” asking for username and password. Maybe there’s a better way around this, if so I haven’t found it. I don’t care, I can control this though policy’s on the RD/XenApp servers and the MS CRM 2011 SDK works, so I’m happy.

And here is a few goodies regarding connecting to CRM though code, and using LINQ to query data in CRM too.

Imports Microsoft.Crm
Imports Microsoft.Xrm
Imports Microsoft.Xrm.Sdk

Module Module1

    Private cliDeployment As Deployment.DeploymentServiceClient
    Private cliDiscovery As Discovery.IDiscoveryService
    Private cliOrganization As Microsoft.Xrm.Client.Services.OrganizationService
    Private cliContext As Microsoft.Xrm.Client.CrmOrganizationServiceContext
    'Private linqCRM As CrmQueryProvider

    Private Sub InitializeClients(Host As String, UniqueName As String)
        InitializeClients(Host, UniqueName, "", "")
    End Sub
    Private Sub InitializeClients(Host As String, UniqueName As String, Username As String, password As String)
        Try
            Dim DeploymentURI As Uri = New Uri("https://" & Host & "/XRMDeployment/2011/Deployment.svc")
            cliDeployment = Deployment.Proxy.ProxyClientHelper.CreateClient(DeploymentURI)

            Dim Credentials As ServiceModel.Description.ClientCredentials = Nothing
            If Username <> "" And password <> "" Then
                Credentials = New ServiceModel.Description.ClientCredentials
                Credentials.UserName.UserName = Username
                Credentials.UserName.Password = password
            End If
            Dim discoveryServiceUri As Uri = New Uri("https://" & Host & "/XRMServices/2011/Discovery.svc")
            cliDiscovery = New Microsoft.Xrm.Sdk.Client.DiscoveryServiceProxy(discoveryServiceUri, Nothing, Credentials, Nothing)
            If UniqueName <> "" Then
                Dim CrmConnection As New Microsoft.Xrm.Client.CrmConnection
                If Username <> "" And password <> "" Then
                    CrmConnection = Microsoft.Xrm.Client.CrmConnection.Parse("Url=https://" & Host & "/" & UniqueName & "; Username=" & Username & "; Password=" & password)
                Else
                    CrmConnection = Microsoft.Xrm.Client.CrmConnection.Parse("Authentication Type=Integrated; Url=https://" & Host & "/" & UniqueName)
                End If
                cliContext = New Microsoft.Xrm.Client.CrmOrganizationServiceContext(CrmConnection)
                cliOrganization = New Microsoft.Xrm.Client.Services.OrganizationService(CrmConnection)
            End If
        Catch ex As Exception
            Throw ex
        End Try
    End Sub

    Sub Main()
        Dim UniqueName As String = "test1"
        InitializeClients("internal.crm2011.wingu.dk", UniqueName)

        Dim request1 As New Deployment.RetrieveRequest
        request1.EntityType = Deployment.DeploymentEntityType.Organization
        request1.InstanceTag = New Deployment.EntityInstanceId
        request1.InstanceTag.Name = UniqueName
        Dim response1 As Deployment.RetrieveResponse = cliDeployment.Execute(request1)
        Dim org1 As Deployment.Organization = response1.Entity
        Console.WriteLine("Deployment Service: " & org1.UniqueName & " " & org1.FriendlyName)

        Dim Request2 As New Discovery.RetrieveOrganizationRequest
        Request2.UniqueName = UniqueName
        Dim Response2 As Discovery.RetrieveOrganizationResponse = cliDiscovery.Execute(Request2)
        Dim org2 As Discovery.OrganizationDetail = Response2.Detail
        Console.WriteLine("Discovery Service: " & org2.UniqueName & " " & org2.FriendlyName)

        Dim response3 = cliOrganization.Execute(New Microsoft.Crm.Sdk.Messages.WhoAmIRequest())
        Console.WriteLine("Organization (WhoAmI)")
        For Each res In response3.Results
            Console.WriteLine(res.Key & ": " & res.Value.ToString)
        Next

        Dim contacts = cliContext.CreateQuery(Of Contact)().ToList
        For Each c In contacts
            Console.WriteLine(c.FirstName & " " & c.LastName)
        Next

        Console.WriteLine(vbCrLf & "done.")
        Console.ReadLine()
    End Sub

End Module

Ingen kommentarer:

Send en kommentar