tirsdag den 3. maj 2011

Disable and Enable systemuser in CRM2011

I’ve spend way to many hours goggling this. Hopefully someone in need this will find this post before wasting 9 hours on figuring out how to do something this simple, like I had too.

Function DisableUser(user As SystemUser) As SystemUser
    SetEntityState(user, 1, -1)
    Return getEntity(user.LogicalName, "domainname", ConditionOperator.Equal, user.DomainName)
End Function

Function EnableUser(user As SystemUser) As SystemUser
    SetEntityState(user, 0, -1)
    Return getEntity(user.LogicalName, "domainname", ConditionOperator.Equal, user.DomainName)
End Function

Sub SetEntityState(entity As Entity, state As Integer, Status As Integer)
    Dim request As New SetStateRequest
    request.EntityMoniker = New EntityReference()
    request.EntityMoniker.Id = entity.Id
    request.EntityMoniker.LogicalName = entity.LogicalName
    request.State = New OptionSetValue(state)
    request.Status = New OptionSetValue(Status)
    Dim response As SetStateResponse = cliOrganization.Execute(request)
End Sub

What I did to get to this, was fire up SQL Profiler and look at the SQL being called while disabling and enabling a system user. After messing around inside sdkmessage and sdkmessageproccessingsteps I finally got a few hints and while goggling I stumbles over this page, and gave it a try. And it seemed to work.

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

lørdag den 30. april 2011

New-CrmOrganization The Deployment Service cannot process the request because one or more validation checks failed.

I’m playing around with scripting and implementing CRM 2011 into my provisioning and kept banging my head trying to figure out why the !”¤# I constantly would get

New-CrmOrganization : The Deployment Service cannot process the request because one or more validation checks failed.

I could create new Organization fine in the Deployment Manager MMC, so I smacked together a VB console app calling the deployment web service and got same result. No luck.

I gave up for a while and spend some time reading up and creating the environment for running ADFS 2.0 for Claims-based Authentication. ( I can recommend reading the MS guide and this YouTube video is a MUST! )

So after struggling abet with certificates a went back to the original problem. I must admit I have NO idea why, but after I managed to get ADFS working seemingly internally and externally, suddenly both PowerShell and calling the deployment web service directly worked again. So I guess the lesson learned is. Get that up and running BEFORE trying to script the rest Winking smile

onsdag den 27. april 2011

Remote Desktop RemoteAPP not running logon scripts

This one was giving my some headaches. Sometimes people would see there network drives, and sometimes people could only see there home drive when launching applications directely instead of starting a desktop. Found tons of people who had “smart” hacks messing with RDP properties and runonce keys, but not anything I really liked. Was talking with a friend about it and he mentioned trying to use USRLOGON.CMD to just call the normal logon script. That didn’t quite work for me, but creating C:\Windows\System32\usrlogn1.cmd and then copy’ing the logon script localy and executing it, works seamlessly for me.

C:\Windows\System32\usrlogn1.cmd containing
xcopy /S /Y \\domain.fqdn\NETLOGON %temp%
C:\Windows\System32\cscript.exe %temp%\logon.vbs

onsdag den 13. april 2011

Getting list of XenApp hotfixes, installing hotfix’es unattended from powershell

Searched high and low for this, and couldn’t find anything, so here is my version.

# Regiser Addins if missing
$snapin = Get-PSSnapin | where {$_.name -eq 'Citrix.Common.Commands'}
if($snapin -eq $null){ Add-PSSnapin Citrix.Common.Commands }
$snapin = Get-PSSnapin | where {$_.name -eq 'Citrix.XenApp.Commands'}
if($snapin -eq $null){ Add-PSSnapin Citrix.XenApp.Commands }

function installXenappHotfix([string]$HotfixName, [string]$hotfixpath ){
# Get current computername and XenServer object
$computername = get-content env:computername

$isInstalled = $false;
foreach($hotfix in (Get-XAServerHotFix -ServerName $computername)){
if($hotfix.HotfixName -eq $HotfixName){ $isInstalled = $true; }
}
if($isInstalled -eq $false){
Write
-Host ('Missing ' + $HotfixName)
$exitcode = (Start-Process -FilePath "msiexec.exe" -ArgumentList "/qb /passive /norestart /p `"$hotfixpath`"" -Wait -Passthru).ExitCode
if($exitcode -eq 3010){
$exitcode = 1604
Write
-Host 'Restart needed, so restarting. doh!'
restart
-computer -force -throttlelimit 10; exit $exitcode
exit
$exitcode
}
if($exitcode -ne 0){
Write
-Host ('Unknown exitcode: ' + $exitcode)
exit
$exitcode
}
}
else { Write-Host ($HotfixName + ' ok') }
}

installXenappHotfix 'XA600W2K8R2X64001' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64001.msp'
installXenappHotfix 'XA600W2K8R2X64002' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64002.msp'
installXenappHotfix 'XA600W2K8R2X64003' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64003.msp'
installXenappHotfix 'XA600W2K8R2X64004' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64004.msp'
installXenappHotfix 'XA600W2K8R2X64006' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64006.msp'
installXenappHotfix 'XA600W2K8R2X64007' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64007.msp'
installXenappHotfix 'XA600W2K8R2X64008' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64008.msp'
installXenappHotfix 'XA600W2K8R2X64009' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64009.msp'
installXenappHotfix 'XA600W2K8R2X64010' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64010.msp'
installXenappHotfix 'XA600W2K8R2X64011' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64011.msp'
installXenappHotfix 'XA600W2K8R2X64012' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64012.msp'
installXenappHotfix 'XA600W2K8R2X64013' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64013.msp'
installXenappHotfix 'XA600W2K8R2X64015' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64015.msp'
installXenappHotfix 'XA600W2K8R2X64016' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64016.msp'
installXenappHotfix 'XA600W2K8R2X64017' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64017.msp'
installXenappHotfix 'XA600W2K8R2X64018' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64018.msp'
installXenappHotfix 'XA600W2K8R2X64019' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64019.msp'
installXenappHotfix 'XA600W2K8R2X64020' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64020.msp'
installXenappHotfix 'XA600W2K8R2X64021' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64021.msp'
installXenappHotfix 'XA600W2K8R2X64023' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64023.msp'
installXenappHotfix 'XA600W2K8R2X64026' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64026.msp'
installXenappHotfix 'XA600W2K8R2X64027' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64027.msp'
installXenappHotfix 'XA600W2K8R2X64028' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64028.msp'
installXenappHotfix 'XA600W2K8R2X64029' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64029.msp'
installXenappHotfix 'XA600W2K8R2X64030' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64030.msp'
installXenappHotfix 'XA600W2K8R2X64031' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64031.msp'
installXenappHotfix 'XA600W2K8R2X64033' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64033.msp'
installXenappHotfix 'XA600W2K8R2X64035' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64035.msp'
installXenappHotfix 'XA600W2K8R2X64036' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64036.msp'
installXenappHotfix 'XA600W2K8R2X64037' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64037.msp'
installXenappHotfix 'XA600W2K8R2X64038' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64038.msp'
installXenappHotfix 'XA600W2K8R2X64039' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64039.msp'
installXenappHotfix 'XA600W2K8R2X64041' 'X:\XenApp
6 hotfixes\XA600W2K8R2X64041.msp'

torsdag den 7. april 2011

PSCmdlet with several ParameterSets containing same Parameter

One thing I kept running into when creating CmdLet’s was when I want to give the user the ability to pass in several parameters as both object or by ID. Troubleshooting passing in this parameters can be a bit annoying so here's a few pointers.

Imagine you have a PSCmdlet that needs a customer and a plan. lets call it New-CustomerService and the parameters Customer or CustomerID and ServicePlan or ServicePlanID this will give a total of 4 different ways of passing parameters hence 4 ParameterSetName names.

 

Private _Customer As New List(Of wsCloudAPI.extCustomer)
<Parameter(Position:=0, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set1")> _
<Parameter(Position:=0, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set2")> _
<ValidateNotNullOrEmpty()> _
Public Property Customer() As List(Of wsCloudAPI.extCustomer)
    Get
        Return _Customer
    End Get
    Set(ByVal value As List(Of wsCloudAPI.extCustomer))
        _Customer = value
        For Each o In _Customer
            _CustomerID.Add(o.CustomerID)
        Next
    End Set
End Property

Private _CustomerID As New List(Of Integer)
<Parameter(Position:=0, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set3")> _
<Parameter(Position:=0, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set4")> _
<ValidateNotNullOrEmpty()> _
Public Property CustomerID() As List(Of Integer)
    Get
        Return _CustomerID
    End Get
    Set(ByVal value As List(Of Integer))
        _CustomerID = value
    End Set
End Property

Private _ServicePlan As New List(Of wsCloudAPI.extServicePlan)
<Parameter(Position:=1, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set1")> _
<Parameter(Position:=1, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set3")> _
<ValidateNotNullOrEmpty()> _
Public Property ServicePlan() As List(Of wsCloudAPI.extServicePlan)
    Get
        Return _ServicePlan
    End Get
    Set(ByVal value As List(Of wsCloudAPI.extServicePlan))
        _ServicePlan = value
        For Each o In _ServicePlan
            _PlanID.Add(o.PlanID)
        Next
    End Set
End Property

Private _PlanID As New List(Of Integer)
<Parameter(Position:=1, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set2")> _
<Parameter(Position:=1, Mandatory:=True, ValueFromPipeline:=True, ValueFromPipelineByPropertyName:=True, ParameterSetName:="set4")> _
<ValidateNotNullOrEmpty()> _
Public Property PlanID() As List(Of Integer)
    Get
        Return _PlanID
    End Get
    Set(ByVal value As List(Of Integer))
        _PlanID = value
    End Set
End Property

Testing if this work, can then be done with

trace-command parameterbinding -pshost { New-CustomerService $customer -ServicePlan $plan }

But there is another cute way of checking the different parameter sets


(get-command New-CustomerService).ParameterSets | foreach {
$ps = $_.name
$_.Parameters | Select @{n="ParameterName";e={$_.name}},@{n="Mandatory";e={$_.IsMandatory}},@{n="Position";e={$_.Position}},@{n="HelpMessage";e={$_.HelpMessage}},@{n="ParameterSet";e={$ps}} | Format-Table
}

mandag den 4. april 2011

SCOM 2007 R2–Clearing Console Cache

Note to self.
Go to C:\Users\<Username>\AppData\Local\Microsoft\Microsoft.Mom.UI.Console and delete momcache.mdb