I hope you find it useful.
Friday, 12 December 2014
A Real Comparison of SharePoint 2013 Versions
I hope you find it useful.
Friday, 1 February 2013
What's new with CRM 2011 Update Rollup 12
http://blogs.msdn.com/b/crm/archive/2013/01/29/10-things-i-love-about-dynamics-crm-2011-december-2012-service-update-or-polaris-update.aspx
Wednesday, 30 January 2013
CRM 2011 Update Rollup 12 is finally out!
You can catch up on the release exploits here.
If you'd rather download the update or read about what's new in it, go right ahead.
Just a friendly tip, I suggest waiting at least 3-4 weeks before deploying this update to any production/live environment.
The updated SDK is also available and is now at build 5.0.13.
Monday, 28 January 2013
Running SQL queries in PowerShell
Whenever I search for this information I come up with long code sinplets of various optimisation level.
On my last search for this I found an actual PowerShell function for executing SQL queries. I rejoiced but not for long. While this worked very well, it required the local installation of the SQL management tools. Quite a tall order for something that should work practically anywhere.
So I decided to write my own (simplified) SQL query execution function.
Here's the result.
Function Execute-SqlQuery {
[cmdletbinding()]
Param (
#SQL server name.
[parameter(Mandatory=$true)]
[validateNotNull()]
[String]
$Server,
#SQL query to execute.
[parameter(Mandatory=$true)]
[validateNotNull()]
[String]
$Query,
#Database name on the SQL server.
[parameter(Mandatory=$true)]
[validateNotNull()]
[String]
$Database
)
Process {
#Only basic connection string support implemented. Always use Integrated Security.
$ConnStr = "Server=$Server; Database=$Database; Integrated Security=SSPI;"
#Create SQL connection object and pass connection string.
$SqlConn = New-Object System.Data.SqlClient.SqlConnection
$SqlConn.ConnectionString = $ConnStr
$SqlConn.Open()
#Create SQL command object and apply the connection to it.
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $Query
$SqlCmd.Connection = $SqlConn
#Crate SQL adapter object to return query data to the end user.
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
#Fill the SQL query results into a temporary DataSet object.
$Results = New-Object System.Data.DataSet
$SqlAdapter.Fill($Results) | Out-Null
#Clean up
$SqlCmd.Dispose()
$SqlConn.Close()
$SqlConn.Dispose()
#Return
Return $Results.Tables[0]
}
}
I hope you all find this useful. It's a big time saver for sure.
Friday, 25 January 2013
Writing PowerShell functions properly
I've been using PowerShell for a few years now. Started out slowly with the odd command here and there and eventually got a big push forward when I forced myself to sit down and write my first actual script file. Happiest day of my scripting life!
Ever since then I've been trying to find various best practises and good examples for whatever I try to do. The most common search I perform is for proper PowerShell function declaration. To me, more than anything, this is the holy grail pinpointing the main difference between PowerShell and most other languages.
PowerShell is a language aimed at IT personnel and not developers. This target audience knows little of best practises, proper syntax or code hardening. They are usually under budgeted and do not have the time for proper coding. In addition, they usually also lack the expertise required to properly estimate a coding project.
The above audience make up the main bulk of PowerShell article and blog writers. And herein lies the problem. The blind are leading the blind.
Whenever I try searching for a properly written PowerShell function I usually find articles explaining how simple it is to write these in PowerShell. Those articles usually contain functions similar to my first example below.
The following function (and all those to follow) are simples ones that take in 3 string values and combine them together with some additional text. I deliberately picked a simple example.
First, this base function example.
Function WriteText ($a, $b, $c) {
Write-Host $a 'was first,' $b 'was second,' $c 'was third'
}
Some of you may be asking yourself what's wrong with this function. Well, quite a lot. I'll get to that in a moment. Let me start off by executing the above function:
WriteText 'ABC' 'DEF' 'GHI'
With the following output:
ABC was first, DEF was second, GHI was third
Great! That's what we wanted isn't it?
Yes, but only on a very limited basis.
What would happen if instead of providing 3 strings we only provided 2?
WriteText 'ABC' 'GHI'
With the following output:
ABC was first, GHI was second, was third
Now hang on there PowerShell window! That's not what I wanted you to do!
Clearly 'ABC' was supposed to be the first string and 'GHI' was the third. I don't even know what I expected you to do with the missing second one. That should have returned an error, right?
Wrong. All wrong.
Scripts (like other code) only do what you tell them to do. Nothing more and nothing less. So where did we go wrong with this function?
Oh so many places! Let's start listing some of them.
- Our function's name does not follow PowerShell's hyphened double worded function format. Examples of proper functions would be Get-Item, Set-ExecutionPolicy etc.
- Our variables are not typed hardened. This could cause all sorts of issues in longer code segments where we expect something to work which only works with specific types.
- Our general syntax uses the simplest possible PowerShell syntax. While this is fast to write, it is less readable if we later on go back and want to make alterations or explain it to others.
My next example is the very same function with some syntax and type hardening alterations. Still not a very robust function but at least it's readable. It now also has a meaningful function name which follows proper PowerShell best practises for naming. I even added my own 'La' prefix to the function so it would not clash with other similarly named functions in the future.
Function Get-LaStringConcat1 {
Param (
[String]$FirstString,
[String]$SecondString,
[String]$ThirdString
)
Process {
Write-Host $FirstString 'was first,' $SecondString 'was second,' $ThirdString 'was third'
}
}
I can now easily execute this function in the same way I executed the previous one but would like to show you additional changes to the way I like working. In order to avoid mishaps you should always strive to execute functions in a parametrised manner. This is demonstrated here (and will be the guideline for all the following examples).
Get-LaStringConcat1 -FirstString 'ABC' -SecondString 'DEF' -ThirdString 'GHI'
This returns the following output:
ABC was first, DEF was second, GHI was third
If I were to take out one of the strings, I would still get undesired results.
Example:
Get-LaStringConcat1 -FirstString 'ABC' -ThirdString 'GHI'
Output:
ABC was first, was second, GHI was third
Now let's move on and fix the undesired results. The first and easiest way to fix this is by requiring all 3 parameters contain a value. Simple enough to do, we just need to make the following changes to our function.
Function Get-LaStringConcat2 {
Param (
[parameter(Mandatory=$true)]
[validateNotNull()]
[String]
$FirstString,
[parameter(Mandatory=$true)]
[validateNotNull()]
[String]
$SecondString,
[parameter(Mandatory=$true)]
[validateNotNull()]
[String]
$ThirdString
)
Process {
Write-Host $FirstString 'was first,' $SecondString 'was second,' $ThirdString 'was third'
}
}
The above function implemented a very addition to all 3 parameters. The first option indicates to PowerShell that the parameter is mandatory so it cannot be omitted anymore. The second option tells PowerShell that even if the parameter is not omitted it must still contain a non-null value. It can be executed in the same way as before. I will skip the valid execution and go straight to one with missing parameters. Let's provide the first and second parameters but leave out the third.
Get-LaStringConcat2 -FirstString 'ABC' -SecondString 'CDE'
Output:
cmdlet Get-LaStringConcat2 at command pipeline position 1 Supply values for the following parameters: ThirdString:
That's good isn't it? PowerShell noticed I did not supply the ThirdString parameter and is now demanding it of me. What if I provide no value and just hit Enter? Well, this happens:
Get-LaStringConcat2 : Cannot bind argument to parameter 'ThirdString' because it is an empty string.
At line:1 char:1
+ Get-LaStringConcat2 -FirstString 'ABC' -SecondString 'CDE'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-LaStringConcat2], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Get-LaStringConcat2
Why is that? Because I also asked PowerShell to make sure any supplied values are not null.Right, 'are we done?' you may be asking. Far from it (and my apologies for the long post).
While the above example does harden our function so it can't be executed with missing parameters, I would prefer it be more flexible and forgiving. For this reason I came up with the following example.
Function Get-LaStringConcat3 {
Param (
[String]$FirstString,
[String]$SecondString,
[String]$ThirdString
)
Process {
[String]$OutStr = ''
If ($FirstString) {$OutStr += $FirstString + ' was first, '}
If ($SecondString) {$OutStr += $SecondString + ' was second, '}
If ($ThirdString) {$OutStr += $ThirdString + ' was third'}
If ($OutStr -eq '') {$OutStr = 'No input strings were provided'}
Write-Host $OutStr
}
}
Wait a second, the function just got a lot longer. Why? I made the following changes from the previous one:
- Removed the [parameter(Mandatory=$true)] and [validateNotNull()] options. I do want to allow running the function with missing parameters as this is a desired feature.
- As parameters can now be empty, I need to check each one and build up my output string accordingly. That's what the whole code block in Process{} does. The function will now create a blank output string variable and start adding to it for each non-null variable.
- If no parameters at all were provided the function will output the 'No input strings were provided' message.
Get-LaStringConcat3 -FirstString 'ABC' -ThirdString 'GHI'
ABC was first, GHI was third
Now that was good! Our function finally looks good even with missing parameters! How about no parameters at all?
Get-LaStringConcat3
No input strings were provided
Perfect! Now my function is ready to be shipped out so the entire company can use it!
Well, not quite yet. There's just one more adjustment I want to make to it. It's a very subtle one this time. Note how our function uses Write-Host for outputting the compiled string. The string isn't returned as the function's value but is just printed to screen as text. What if I wanted to store that text in a variable for later use? Not easily done with our current function. Here's what happens if I try.
$SomeString = Get-LaStringConcat3 -FirstString 'ABC' -ThirdString 'GHI'
ABC was first, GHI was third
Note how the variable value assigning output the string. But did it store it?
Write-Host $SomeString
Output:
No value. Our variable is empty because the function didn't return a value, it just printed to screen.
Now that's not good. I want to use this function in long scripts and must be able to store its output value.
So let's make a very small change to our function. In fact, it's only changing a single word. Can you spot it?
Function Get-LaStringConcat4 {
Param (
[String]$FirstString,
[String]$SecondString,
[String]$ThirdString
)
Process {
[String]$OutStr = ''
If ($FirstString) {$OutStr += $FirstString + ' was first, '}
If ($SecondString) {$OutStr += $SecondString + ' was second, '}
If ($ThirdString) {$OutStr += $ThirdString + ' was third'}
If ($OutStr -eq '') {$OutStr = 'No input strings were provided'}
Return $OutStr
}
}
Did you spot it? All I did was change the Write-Host command to a Return command. Return is a special command which can only be used inside a function and tells the function which data to return when executed. Let's have a look at the previous example again:
$SomeString = Get-LaStringConcat4 -FirstString 'ABC' -ThirdString 'GHI'
Now that's more like it. Storing a value in a variable shouldn't output any data unless I specifically ask for it. Now how about the actual variable? Does it have the string stored?
Write-Host $SomeString
Output:
ABC was first, GHI was third
Success!
Now the function can be shipped off and used in other scripts by anyone. I'd call this hardened enough for a more distributed use.
To be fair, we're not really done. There are still a lot of small bug fixed to perform with our function. Our 3 strings aren't put together smoothly enough, we haven't added any in-line help to our function, there is no proper documentation for what each line does (very important!) and many other small and annoying things to do. For the purpose of what I wanted to show you today I'll stop here. The rest you can do on your own. :)
***Update 30/01/2013
+Peter Kriegel suggested I also include the the [cmdletbinding()] option in this blog post. Seeing as it does not add too much complication, I decided to.
Instead of transposing other wonderful articles I decided to just link them here. I suggest a read through of these:
Wednesday, 16 January 2013
Picking apart CRM 2011's diagnostics page - Part 2
I am now going to dive deeper into the JavaScript sections of the diagnostics tests. In total there are 8 JavaScript performance tests with 5 of those nested together under the JavaScript Dom stress test. I call these stress tests as they are basically memory and CPU intensive performance tests designed to give an indication of client computer (and browser) performance.
When running these tests they produce the following output (numbers may differ):
=== Array Manipultaion Benchmark ===So what does all that mean? What does each of these tests actually do?
Time: 286 ms
Client Time: Wed, 16 Jan 2013 13:59:08 UTC
=== Morph Benchmark ===
Time: 334 ms
Client Time: Wed, 16 Jan 2013 13:59:08 UTC
=== Base 64 Benchmark ===
Time: 35 ms
Client Time: Wed, 16 Jan 2013 13:59:08 UTC
=== DOM Benchmark ===
Total Time: 561 ms
Breakdown:
Append: 20ms
Prepend: 25ms
Index: 465ms
Insert: 22ms
Remove: 29ms
Client Time: Wed, 16 Jan 2013 13:59:09 UTC
Now it all gets interesting. :)
I spent some time taking apart the exact JavaScript functions executed for each of these tests. I have detailed them below including the source snippet.
Array Manipulation Benchmark
function arrayBenchmark() {
for (var ret = [], tmp, n = 2e3, j = 0; j < n * 15; j++) {
ret = [];
ret.length = n
}
for (var j = 0; j < n * 10; j++)
ret = new Array(n);
ret = [];
for (var j = 0; j < n; j++)
ret.unshift(j);
ret = [];
for (var j = 0; j < n; j++)
ret.splice(0, 0, j);
for (var a = ret.slice(), j = 0; j < n; j++)
tmp = a.shift();
for (var a = ret.slice(), j = 0; j < n; j++)
tmp = a.splice(0, 1);
ret = [];
for (var j = 0; j < n; j++)
ret.push(j);
for (var a = ret.slice(), j = 0; j < n; j++)
tmp = a.pop()
}
This test builds up an array in memory and then performs various manipulations on it. The manipulations performed are (in order) unshift, splice, shift, splice, push, pop. Just simple memory manipulation games.Morph Benchmark
function morphBenchmark() {
var loops = 30, nx = 120, nz = 120;
function morph(a, f) {
for (var PI2nx = Math.PI * 8 / nx, sin = Math.sin, f30 = -(50 * sin(f * Math.PI * 2)), i = 0; i < nz; ++i)
for (var j = 0; j < nx; ++j)
a[3 * (i * nx + j) + 1] = sin((j - 1) * PI2nx) * -f30
}
for (var a = Array(), i = 0; i < nx * nz * 3; ++i) a[i] = 0;
for (var i = 0; i < loops; ++i) morph(a, i / loops)
}
This test performs mathematical manipulations in a loop on an array variable. This is again a memory and CPU stress test.Base 64 Benchmark
function base64Benchmark() {
var toBase64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", base64Pad = "=";
function toBase64(data) {
for (var result = "", length = data.length, i = 0; i < length - 2; i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 3) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[((data[i + 1] & 15) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[i + 2] & 63]
}
if (length % 3) {
i = length - length % 3;
result += toBase64Table[data[i] >> 2];
if (length % 3 == 2) {
result += toBase64Table[((data[i] & 3) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[(data[i + 1] & 15) << 2];
result += base64Pad
}
else {
result += toBase64Table[(data[i] & 3) << 4];
result += base64Pad + base64Pad
}
}
return result
}
var toBinaryTable = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1];
function base64ToString(data) {
for (var result = "", leftbits = 0, leftdata = 0, i = 0; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 127], padding = data[i] == base64Pad;
if (c == -1)
continue;
leftdata = leftdata << 6 | c;
leftbits += 6;
if (leftbits >= 8) {
leftbits -= 8;
if (!padding) result += String.fromCharCode(leftdata >> leftbits & 255);
leftdata &= (1 << leftbits) - 1
}
}
if (leftbits)
throw Components.Exception("Corrupted base64 string");
return result
}
for (var str = [], i = 0; i < 819; i++)
str.push(String.fromCharCode(25 * Math.random() + 97));
str = str.join("");
for (var base64, loops = 1, i = 0; i <= loops; i++)
base64 = toBase64(str);
for (var i = 0; i <= loops; i++)
base64ToString(base64)
}
A long and intimidating function that in essence converts values to and from base64 encoding. Once again a fairly simple memory and CPU stress test.DOM Benchmark
This test is actually a combination of 5 sub tests. I have broken up the main function below so to explain more clearly.
function domBenchmark() {
var count = 1500, divs = new Array(count);
This first JavaScript snippet creates the base structure within which the other functions operate. Basically creating the variables used by each sub function. Note that the 5 following functions are nested within the domBenchmark() function and are not root functions.Later in this function the HTML DOM structure is created. The DIV element all functions relate to is created in just a few lines.
DOM Benchmark - Append
function testAppend(div) {
for (var i = 0; i < count; i += 1) {
var add = document.createElement("div");
div.appendChild(add)
}
}
A function that creates 1500 DIV elements all nested under the source DIV.DOM Benchmark - Prepend
function testPrepend(div) {
for (var i = 0; i < count; i += 1) {
var add = document.createElement("div");
div.insertBefore(add, div.firstChild)
}
}
A function that creates an additional 1500 DIV elements but instead of appending them as before it prepends them. They are added to the top of the parenting DIV elements instead of the bottom.DOM Benchmark - Index
function testIndex(div) {
for (var i = 0; i < count; i += 1)
divs[i] = div.childNodes[count * 2 - i * 2 - 1]
}
A function that indexes 1500 of the div elements created in the testAppend() and testPrepend() functions. It only indexes every second DIV.This is considered indexing as it takes the DIV structure and stores it in an array which acts as an index table. Again, meant at stressing memory and CPU.
DOM Benchmark - Insert
function testInsert(div) {
for (var i = 0; i < count; i += 1) {
var add = document.createElement("div");
div.insertBefore(add, divs[i])
}
}
A function that takes the index table array variable and inserts each of its DIV elements into the HTTP DOM. A very similar test to the testPrepend() test but with array data instead of cleanly created.. A simple array insert test to stress memory and CPU.DOM Benchmark - Remove
function testRemove(div) {
for (var i = 0; i < count; i += 1)
div.removeChild(divs[i])
}
The final test function which removes 1500 of the DIV elements created earlier.DOM Benchmark - Run Tests
var div = document.createElement("div");
div.style.display = "none";
div.setAttribute("id", "domBenchmarkDiv");
document.body.appendChild(div);
var start, end;
start = new Date;
testAppend(div);
end = new Date;
var appendTime = end - start;
start = new Date;
testPrepend(div);
end = new Date;
var prependTime = end - start;
start = new Date;
testIndex(div);
end = new Date;
var indexTime = end - start;
start = new Date;
testInsert(div);
end = new Date;
var insertTime = end - start;
start = new Date;
testRemove(div);
end = new Date;
var removeTime = end - start;
document.body.removeChild(div);
var total = appendTime + prependTime + insertTime + indexTime + removeTime, results = "Breakdown:\r\n Append: " + appendTime + "ms\r\n Prepend: " + prependTime + "ms\r\n Index: " + indexTime + "ms\r\n Insert: " + insertTime + "ms\r\n Remove: " + removeTime + "ms\r\n";
return [total, results]
}
And finally, actually executing all the above functions. This is done in turn and by order right after creating the parent DIV elements as well as some time tracking variables used for duration reporting of the results.The final steps clean up the parent DIV element and return the stress test results as text.
As you can clearly see, these stress tests have very little to do with actual CRM performance on either the client or server. They are memory and CPU client stress tests designed to measure how browsers behave.
It is important to point out that these functions are rather short and simple and as such are not affected by antivirus scans (the dredded McAfee for example which can kill CRM through its ScriptScan feature).
I hope that sums it all up well enough.
I am open to any additional questions.
Tuesday, 15 January 2013
Picking apart CRM 2011's diagnostics page - Part 1
After going through the obvious culprits such as update rollups, installed antivirus software, client and server specifications etc. it eventually turned out to be a server I/O problem which was causing longer than expected load times. I used HTTP Watch and WireShark to point me in that direction and then had some discussions with the IT team.
All that, however, wasn't the interesting part. Not even close.
During this examination I used CRM 2011's diagnostics page (http://<server>/<org>/tools/diagnostics/diag.aspx or http://<server>/tools/diagnostics/diag.aspx). This was part of CRM Online that came to the on-premise version in Update Rollup 4. More about that in Rhett Clinton's article. The reason I started picking apart the diagnostics page was because I was getting fairly good results despite CRM behaving worse than expected. Seeing that, I decided to check what exactly the diagnostic pages does and measures. Below is a sample of this page from one of my test systems.
![]() |
| CRM 2011 Diagnostics Page |
The first thing I did was to understand what each of the tests stands for. These are as follows:
- Latency Test
Not exactly what I'd call a latency test but as close as you can get with HTTP. This calculates the average time taken over 20 downloads of a very small text file (only 12 bytes long).
The file downloaded is /_static/Tools/Diagnostics/smallfile.txt - Bandwidth Test
Performs downloads of image files in increasing size. Slightly similar in concept to many Internet based speed test sites. These download speeds are then averaged out (weighted of course) to give one average download speed value. - Browser Info
Basic JavaScript pull of the local browser details such as browser name, version, cookie status, platform (OS) and the user-agent string. - IP Address
Reports the IP address of the client computer as known to the server. This is passed as a variable in the diag.aspx file. The IP address is server-side dynamic and represents the IP address which was used to contact the server with. - JavaScript tests (there are 4)
Runs various JavaScript times loops and returns their execution time. This is basically a memory/CPU stress test on the client machine. I will elaborate more on thest in Part 2 of this article. - Organization Info
Basic server info such as organization name, time on server and url.
In the second part of this article I will dive into the JavaScript tests phase. If you're like me, that's the most interesting part. :)
Monday, 7 January 2013
CRM 2011 Update Rollup 12 imminent
There has just been a refresh on a lot of the CRM 2011 downloads on Microsoft's downloads page. If you click search and sort by 'newest to oldest' it'll bring up quite a few CRM 2011 downloads on the first 3 pages.
One of the downloads refreshed is the 5.0.13 SDK download. To me this points to an imminent Update Rollup 12 release which will, hopefully, also bring new Service Release functionality. This was previously meant to be released with the now non-existent Update Rollup 9 (as you may recall we jumped from Update Rollup 8 to 10).
I have my fingers crossed for this!
*** Update 14/01/2013
As of 07/01/2013 the Update Rollup 12 download link has been live with download links only for the client elements of the update. Going over the KB article linked from the download page it seems Microsoft was aiming at releasing the on-premise server update files on 10/01/2013. That was 4 days ago.
My personal estimate is that the client updates went live in order to support the server updates deployed on Microsoft's CRM Online hosting service. I am not sure why there's a delay in releasing the on-premise server updates and can only guess there have been some issues with the deployment to CRM Online. Hopefully we should be getting our much anticipated on-premise Update Rollup 12 very soon.
*** Update 15/01/2013
Going through the KB article this morning I noticed a slight change in wording. Microsoft are no longer promising the on-premise server components on 10 January as before but rather just in January 2013. The exact wording follows.
Update Rollup 12 for Microsoft Dynamics CRM 2011 will be available for on-premise customers in January 2013.
*** Update 21/01/2013
Yet more delays with the on premise server components of Update Rollup 12. It seems Microsoft found a critical bug with the Update Rollup 12 release just after publishing it and it was swiftly removed. The message about this went out on the 15th but has only found its way to my Google search feed today. The exact wording follows.
I'd like to give an update on the Microsoft Dynamics CRM 2011 Update Rollup (UR) 12 Release. We originally made UR12 available on Thursday, January 10th, to deliver multi-browser support for our On Premises customers. After discovering an issue that could potentially impact a customer's database, we withdrew the UR12 Server bits to ensure that no On Premises customers would be affected. Unfortunately, these bits were available on the Microsoft Download Center for a short period of time. If you downloaded the UR12 Server bits, please do not install them. We plan to repost the UR12 Server bits within the next week, and we will keep you informed as to when they are available. We have taken measures to improve our engineering processes and methodologies going forward, and we take your feedback very seriously. We apologize for any inconvenience this has caused.
** Update 30/01/2013
Surprisingly, Microsoft kept the vague (and once altered) release date of January 2013. Update Rollup 12 for CRM 2011 has been officially and fully released today.
Finally!
Wednesday, 5 December 2012
"Gamification"
What I came across on CodePlex was a CRM 2011 solution called "The Game for Microsoft Dynamics CRM 2011". Perplexed by this I kept reading.
Back to the new concept. I couldn't have put it better myself so I'll just quote the solution author on this.
The concept of ‘gamification’ is a simple one. To apply game design to non-game applications in order to make them more fun and engaging. Gamification has been used successfully across forums such as those maintained by Microsoft (http://social.microsoft.com/Forums) in order to encourage contributor participation and reward them for achieving a number of points and achievements. The result of this has been a flourishing forum community.Still puzzled and rather doubtful, I decided to try installing and configuring The Game on a test CRM organisation I had lying around. The installation itself was a simple CRM solution import and went by quite swiftly. Once complete I had a plethora of configuration entities all intertwined.
As I'm an active PlayStation 3 player (mostly spending my time on Dust 514 lately), things really started to look familiar now. I could set up games, define ranks and link them to games. I could even set up point thresholds at which users advance to the next level!
What really threw me back was the ability to also create and fully configure achievements. To anyone who's played even the simplest console game this should be quite clear. Using all the different configuration elements I could create a game based on my CRM system's existing business process.
Remember that boring old sales process? Moving through leads, opportunities and then the drudging quotes and orders?
After about half an hour's configuration (and learning curve) I was able to transform this whole sales process from a boring 100% business driven system into a system that still provides everything it did before but is also fun to use. Adding these gaming elements really livened up the system and I can easily imagine users being more enthusiastic about selling.
So what did I actually add? There were some workflows required (most of my learning curve was devoted to those) for the basic functionality of the game. Once those were in place I had the following structure for the game:
- New opportunities gained the user 5 points.
- New quotes gained the user 10 points.
- New orders gained the user 25 points.
- Closed opportunities gained the user 20 points.
- Opportunities opened and closed within 2 days granted users the "Swift Salesperson" achievement. This granted them an additional 40 points and a £50 cash reward (cash rewards went though accounting, naturally).
- With some additional workflow tools I granted every user on their first closed opportunity the "First timer" achievement. This granted an additional 50 points.
- With some additional workflow tools I was also able to count returning orders and users received the "Bulker" achievement for that. This also granted an additional 20 points per return order.
I can highly recommend this solution and method (obviously only where appropriate). I will be considering this for my future CRM implementations.
Tuesday, 4 December 2012
I now call England my home
To anyone considering a similar move, I can highly recommend it. :)
Friday, 11 April 2008
Join CRM Field Values Into A Single Field
Something I get asked for occasionally is to join a few CRM form field values into a single field. A possible example for this would be the joining of the first name and last name fields into the full name field on a contact form. This can, of course, be done by using a callout or plug-in (and to a limited extent, by using a workflow rule) but the easiest way to get this done is by using a form script.
I took the time and created a generic script that (I hope) covers all possible cases and irregularities in the data. Explanations are built into the script itself as remarks.
//Declare your world.
var strings = new Array();
var target = crmForm.all.some_nvarchar_field;
//A function that check whether there is a legitimate value further on.
function checkNext(a,i) {
if (i >= a.length) {
return false;
} else if (a[i] != null && a[i] != "") {
return true;
}
else {
return checkNext(a,i+1);
}
}
//A function that add a line to an array
function addLine(l,t) {
//If lookup field.
if (t == 1) {
if (l.DataValue) {
return l.DataValue[0].name;
} else {
return null;
}
//If text field.
} else if (t == 2) {
return l.DataValue;
//If picklist field.
} else if (t == 3) {
return l.SelectedText;
} else {
return null;
}
}
//Add each of the values in order to the array.
//As the second parameter, enter 1 for lookup fields, 2 for any text field and 3 for picklist fields.
strings[0] = addLine(crmForm.all.some_lookup_field,1);
strings[1] = addLine(crmForm.all.some_nvarchar_field,2);
strings[2] = addLine(crmForm.all.some_picklist_field,3);
//Loop through the array and chain its
values.
for (i=0;i<strings.length;i++) {
if (strings[i] != null && strings[i] != "" && checkNext(strings,i+1)) {
if (i==0){
target.DataValue = strings[i] + " - ";
} else {
target.DataValue += strings[i] + " - ";
}
} else if (strings[i] != null && strings[i]
!= "") {
if (i==0) {
target.DataValue = strings[i];
} else
{
target.DataValue += strings[i];
}
}
}
It seems there isn't much I can do about how badly the script comes out on the blog...
My apologies. I'll take it up with Google.
Tuesday, 1 April 2008
Hiding Users From The CRM (3.0 / 4.0) Service Calendar

Monday, 31 March 2008
MOSS 2007 / WSS 3.0 Search Indexer Gets Stuck
So what's the problem?
Some of you must have come across this before. The Shared Service Provider's crawler get stuck and you can't stop it. It also does not add any new items to the index.
I've found two great articles that deal with this issue.
The first one by Mike Hanley explains how to resolve this issue with clear instructions and good screenshots. It can be found here.
The second is a support KB by Microsoft which expands what is explained in Mike's article by adding more tables which need fixing. It can be found here under KB930887.
Sunday, 10 February 2008
Datetime field bugs
Update 15/02/08 - Partial solution provided by Microsoft Support. As this is not exactly a supported fix, please contact me privately for details or leave a comment and I'll contact you.
CRM 4.0 coming out just at the turn of a year and customers going from 2007 to 2008 in various date fields raised an interesting bug.
The problem is with saving forms where I try to enter a date at the beginning of any year like 01/01/2008 I get the following error:

This error basically states "Invalid Argument" for the newly entered date value. Which is odd, as this is a real date.
Turning on the DevError flag produces the following output when I attempted to open anything saved with a "bad" datetime value. This was followed by a long stack trace.
System.ArgumentOutOfRangeException: Specified argument was out of the range ofAfter studying this problem further, I came across an interesting behavior. I changed the datetime field so it would not just show the date but also the time values. I then started playing with the time values and attempting to save each time. This problem only arrose between 12:00am and 02:00am (02:00am was the first time value that allowed me to save). This seemed odd but then I remembered my user was configured to work in the GMT+2 time zone. I Then attempted this with the GMT+5 time zone and it behaved in the exact same way apart for it starting to work properly at 05:00am.
valid values.Parameter name: GetDaylightChanges actual year: 2007,
MinValue.Year: 2008, MaxValue.Year: 9999
This all struck me as being very odd. To try and narrow down the issue, I wanted to see if this only happens when moving between years or if it also arrises on the first day of any given month. I quickly attempted to enter a new date as the 01/02/2008 (first of feb) with different time values but they all worked. It appears the problems only happen when moving between years.
I'd just like to add that this happens with all date fields in the system. Default and custom entities with default or custom datetime fields. Any mix will do, all you need is a datetime field.
To sum up the results from my various checks, this problem only seems to happen when the value entered in any datetime field changes year values (ex. 2007 and 2008) when comparing the local time zone and the GMT time zone datetime values. This can be reproduced by entering a value on 01/01 of any year with a time value which is lower than your local GMT offset (ex. 01:30am for GMT+2 or 04:30am for GMT+5).
In addition, I have also performed the exact same tests on 3 different CRM 4.0 deployments in both clean installation and upgraded (from CRM 3.0) environments. They all performed in the exact same way.
Microsoft have provided me with an unsupported SQL script which temporarily fixes the issues described in this post. However, I would rather not make this fix publically available as it does involve directly editing the CRM databases.
A permenant hotfix will be made available for this matter shortly (I hope). I will post an update once this happens.
Sunday, 20 January 2008
CRM 4.0 language packs - some more
However, an interesting part of this document is a list of all languages expected to be released during Q1 2008 as both base installations and language packs.
The list is as follows:
- Arabic
- Brazilian Protuguese
- Chinese - Simplified
- Chinese - Traditional
- Chinese - Traditional (Hong Kong)
- Czech
- Danish
- Dutch
- English
- Finnish
- French
- German
- Greek
- Hebrew
- Hungarian
- Italian
- Japanese
- Korean
- Norwegian
- Polish
- Portuguese
- Russian
- Spanish
- Swedish
- Turkish
Thursday, 17 January 2008
CRM 4.0 language pack so far
The first is the server's ability to work in a multi-tethered configuration (Enterprise license only). This basically means you can run more than one CRM organization on the same server. Very cool for hosted servers or very large deployments where business units do not "cut it".
The second is, to some degree, even cooler. It's CRM's ability to provice a multi-lingual experience to end users. Once a new language pack is installed on the server (this is on a per-organization basis which corresponds with the multi-tethered part), each end user can configure the language he'll be using the system in. This is great!
So far, Microsoft have released language packs in the following languages:
- English
- Danish
- Dutch
- Finnish
- French
- German
- Spanish
- Hebrew (not really released, but I've already tested a very final pre-release version)
All these language packs are available at the following location:
Make sure you select the language you wish to download from the drop-down menu and press Change.
After downloading, it's a simple matter of executing the installer on the CRM server and then activating the new language pack for each of the organizations on each server. This can be done through the Settings section.
Tuesday, 15 January 2008
CRM 4.0, fetchXML and Lookup fields
Those of you out there dealing with Microsoft Dynamics CRM 3.0 are probably aware of Ronald Lemmen's wonderful post about using fetchXML and the additionalparams property to apply filters to lookup fields. If not, click here.
While this is indeed a most wonderful and amazing thing you can do with CRM 3.0, it does not work in CRM 4.0 (Titan). Many of you may already know this after a short search through Google. What I'd like to provide here is a slightly better explanation that the standard "It just doesn't work" complaint. Getting to the reason why it does not work took me about 3 days of searching through Google using different keywords and hoping for a magical answer (there is no magical answer btw).
As I stated earlier, you can no longer use the fetchXML parameter with the additionalparams property (or the AddParam method) in lookup fields. It is just not supported anymore in CRM 4.0. I have found 2 seperate reasons for this so far.
The first reason I came across stated that the fetchXML property in lookup fields was a feature introduced in CRM 1.2 and was supported in CRM 3.0 due to Microsoft's policy to provide backwards compatibility. However, as the fetchXML property was not officially supported with CRM 3.0 and was not part of the SDK, they were in no way obligated to keep it functional in CRM 4.0 as their backwards compatibility policy only requires them to support one version back. This was interesting to learn.
The second reason I found, and a much more interesting one at that, states the following. With the move to a multi-tenant architecture (the ability of a single CRM server to house multiple organizations), the dev's faced a problem with the say fetchXML was implemented in lookup fields. I do not know the exact extent of the problem but it had something to do with security and users being able to view stuff they weren't supposed to be able to. What was stated is that this may return to CRM 4.0 at a later date. Perhaps as part of an update rollup package.
I'm rooting for that!
I'm afraid I can't currently find the exact posts I found this information in but if I come across them, I'll stick a link up for you guys.
I hope this has been helpful to you (depite not providing a solution).

