How to create an Automated TFS Build for Dynamics CRM Solutions?

In this post I will show you how to fully automate the build of your Dynamics CRM Solutions using TFS and the xRM CI Framework. Creating an automated build when you start a new project is probably the least things on your mind when you have so many requirements to worry about. For this reason I tried to make this process easy and intuitive so you can get this up and running with a few clicks. This will definitely save you time down the line and make your overall process more consistent and reliable.

For this post I am going to show you how to do a Dynamics CRM Online Automated Build. I do not mean the cloud version of Dynamics CRM in this case. I mean creating CRM Solutions by exporting them from an available CRM environment. In this scenario the development team have decided that they want to use Dynamics CRM as their golden source of customizations as opposed to source control. I will assume that you are using the CRM Developer Toolkit to develop your custom assemblies and web resources.

Before you proceed. Follow the instructions in my previous posts to learn more about the xRM CI Framework for CRM 2011 & 2013. Also make sure you have completed the setup instructions detailed in this post.

The first thing is to create a TFS Build Definition. This provides all the configurations values that define what takes place once your build is triggered. The main difference is that instead of using the default build template we are using the customised build template that comes with the xRM CI Toolkit. This will give you the additional options that will allow to build your CRM solution as I will describe below. The default parameters are still available. These form the basis of all builds. If you need more information around these I suggest you read this msdn article. Below is a screen shot of the customised template so you can see what I mean.
BuildProcess

Check my previous post to learn about the flow of this build. Below are step by step instructions to create your first Dynamics CRM Build definition.

  1. Enter your build name. This should take into consideration the CRM Solution name, the source control branch, the build type and any other criteria you want to include.
    BuildName
  2. Select the trigger. I suggest you start with a Manual trigger until your build is working as expected, you can then change it to run on check-in or scheduled basis.
    BuildTrigger
  3. Select the source. This would be the folder containing the visual studio solution that contains the projects for your plug-ins etc…Ideally this should have been created using the CRM Developer Toolkit. But this is not a requirement.
    BuildSource
  4. Set the build controller. TFS online comes with a default build controller. For on premise version you will need to have this setup. Make sure you have configured the build controller with the custom toolkit assemblies as detailed in the installation guide. Also set the Drop Folder. This is where all the outputs will be placed including the exported CRM solutions.
    BuildDefaults
  5. Process. This is where the build workflow is configured. Select the Dynamics CRM Build template from the toolkit as per the installation guide.
    BuildProcess
  6. Set the items to build and configurations. In this case this is your visual studio solution contains all the things you want to build. If you only have customizations in CRM, you can skip this step.
    ItemToBuildSolutionFile
  7. Configure your test settings if you want any unit tests to run as part of your build. Then set the build number format. Use a format similar to the below if you are planning to use this number for your CRM Solution versioning as well. You can update this later if needed to increment your major or minor versions.
    BuildBasic
  8. Set the CRM connection string. The format follow the CRM Sdk connection string. Note if you click on the dots in the field you will be able to provide this using a wizard. Don’t worry you can still update the field manually later to add things such as timeout settings. Then provide the unique solution name. These settings will be used in the following sections for connecting to the Dynamics CRM Server.
    DynamicsCRM
  9. Complete this section if you are using the CRM Developer toolkit and you want to perform the equivalent of deploy from visual studio to ensure that the target CRM environment is inline with your source code before doing the export. The parameters are self explanatory except for the table below.
    CrmPackage

    Parameter Name Description
    Check-In Register File When you perform a deploy on the CrmPackage project using the CRM Developer Toolkit this updates the RegisterFile.crmregister file with the assembly and step GUIDs from CRM. Set this to true to check-in these changes post deployment or false to discard.
    Copy Dependent Packages If you add any dependent CRM Solutions to your package project, setting this to true will copy all of these solutions to the drop folder.
    Enable Deploy If this is set to False then this section will be skipped and no deployment will take place.
  10. Final and most important step. This export the CRM Solution from the specified environment into the drop location. You can optionally publish customizations and increment the Solution version number inline with the build number.
    SolutionExport

    Parameter Name Description
    Enable Solution Export Setting this to False will inform the build to skip this section completely
    Publishes Customizations Publishes all the customizations prior to the export operation
    Update Solution Version to Build Setting this to True will inform the build to update the version number of the CRM Solution in the specified CRM Environment to the Build version number (the part after the ‘_’). This happens before the export operation.
  1. Save the build definition. Trigger a new build and wait until this complete. If everything went ok you should see something similar to the below. Notice that I also get my unit test results and the code coverage.
    BuildCompleted
  2. Finally go to the drop location for that build. You should see all the outputs including the exported CRM Solutions. Notice in this case my solutions have the same version as the build number.
    BuildOutput

By now you should have a fully automated build. That you can re-use. Off course not all builds are simple as this. But this will cater for many scenarios.

Watch out for the up coming posts. I will show you how you can use the CRM Solution packager to do your builds and implement Continuous Integration including extending your builds to deal with more complex scenarios.

About these ads

Posted on November 25, 2013, in CRM 2011, CRM 2013, CRM Developer Toolkit, Dynamics CRM, xRM CI Framework and tagged , , , , , , , , . Bookmark the permalink. 29 Comments.

  1. Great work. Could save us a lot of time. We’ve tried following your instructions to the letter but when we attempt a Build, we are immediately presented with TF215097: An error occurred while initializing a build for build definition \Aston\Allegro: Exception Message: Cannot create unknown type ‘{clr-namespace:TfsBuildExtensions.Activities.FileSystem;assembly=TfsBuildExtensions.Activities}File’. (type XamlObjectWriterException)Exception

    • John, which TFS version are you using? This error normally comes up if either the TfsBuildExtensions.Activities.dll or one of its dependencies (TfsBuildExtensions.TfsUtilities.dll,Ionic.Zip.dll) is not present in the custom assemblies folder in source control or the build controller is not configured to use that folder. Let me know how it goes…

      • Hi Wael. Thanks for the response. We managed to overcome this error. The controller was not configured to use the correct folder. But now we are getting the following… ‘The type ‘InArgument(xfcc:SolutionPackageType)’ of property ‘SolutionPackPackageType’ could not be resolved.’ Line number ’82’ and line position ’48’ at System.Activities.XamlIntegration.DynamicActivityXamlReader.BufferedPropertyList.ActivityPropertyHolder..ctor(DynamicActivityXamlReader parent, XamlReader reader)… This occurs on the queue of a build and when we attempt to view the xaml through Visual Studio. We’re using TFS 2013 and the CRM 2013 version of your framework.

      • Ignore that previous comment. Your original response was correct. We compared what was in the CRM 2011 CI Framework install and what was in the CRM 2013 CI Framework install and pulled in the differences (about 4 assemblies). The build now runs. It fails after 17 mins, but at least it runs. Now to figure out why it’s failing…

      • Hi Wael. We have successfully run builds. One where the source is exported from CRM and another where Enable Solution Export = False and the SolutionPackager builds the solution from TFS (our preferred). So this has saved us some time. Thanks! Another question though… I thought I would have a try at editing the xaml file to see if we could modify to suit our situation a bit more but I get the same error as I reported above (The type ‘InArgument(xfcc:SolutionPackageType)…etc). Do you have any pointers to resolving that?

      • John, Glad you got it working and it is saving you time. The best way to extend this template is:
        1- Add any extra logic you want to execute in the post build PowerShell Script
        2- Edit the template in visual studio – You will have to a create a workflow activity project, add the template file to that project and any references from the custom assemblies folder and visual studio build assemblies. I will be sharing the source at some point on codeplex to give you a head start.
        What kind of extensions are you thinking of by the way?
        It would be good if you can rate the download as based on your experience.

  2. Hi i want to deploy files in drop folder to iis server location please help

    • This post is mainly for Dynamics CRM components. For web projects you might want to consider using things like MSDeploy or PowerShell to deploy your websites.

  3. Hi Wael

    We are successfully building the solutions from the items in TFS. Thanks. A good place to be. What we’re struggling with now is the Powershell side of things. How is it possible to pass into the Powershell script, the name of the managed solution that has been created and sits in the drop location? In fact, passing anything in other than constants is proving problematic.

    We’re fairly new to TFS build so I’m sure we’re just missing something obvious.

    • Hi John,

      Good question. The framework takes care of that. This is something I am still in the process of documenting. But basically the template passes a list of parameters to the PowerShell script. So just define the parameters below in your script and use them. Feel free to add more such as target CRM connection string etc…

      param([string]$buildDirectory,
      [string]$sourcesDirectory,
      [string]$binariesDirectory, #This is where the output of the build will be that will contain your zip files
      [string]$testResultsDirectory,
      [string]$buildNumber,
      [string]$connectionString, #The one supplied in the template
      [string]$exportUnManagedFileName,
      [string]$exportManagedFileName,
      [string]$packUnmanagedFileName,
      [string]$packManagedFileName)

      Good luck :)

      • Thanks Wael. That’s what we were missing. We can successfully read those parameters. We are, however, having difficulty calling the methods in Xrm.Framework.CI.PowerShell.dll through the build process. We can run a script locally on the build machine successfully, but the same script being referenced in the build template gives us the following in the log…

        “The term ‘Import-XrmSolution’ is not recognized as the name of a cmdlet, function, script file, or operable program.”

        The script is running. We Write-Host all the parameters we’re using and we see them in the build log. We created a very simple script with Get-XrmSolution and that errors in the same way. Again, when run through powershell on the build machine under the identity of the build user, it runs fine. It is only through the build process that we have the problem.

        Is this something you have come across before? This is our last hurdle. So close to getting this wrapped up!

      • Figured it out. In the Advanced section of the Build process parameters, we had to change the MSBuildPlatform from Auto to X86. We are now building a solution from TFS and successfully deploying to our QA environment.

      • Excellent glad you got it working.

  4. Hi,

    Could you help me with this error, can’t figure out what does cause it.
    I get this exception in :
    Solution Version Update
    UpdateSolution

    Inputs
    Version: TEST2_20140210.8
    CrmConnectionString: ServiceUri=http://example:123/MyOrg/XRMServices/2011/Organization.svc;
    UniqueSolutionName: MyOrg

    Exception Message: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs. (type FaultException) Exception Stack Trace: Server stack trace: at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter) at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at Microsoft.Xrm.Sdk.IOrganizationService.Execute(OrganizationRequest request) at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.ExecuteCore(OrganizationRequest request) at Microsoft.Xrm.Client.Services.OrganizationService.InnerOrganizationService.UsingService[TResult](Func`2 action) at Microsoft.Xrm.Sdk.Client.OrganizationServiceContext.Execute(OrganizationRequest request) at Microsoft.Xrm.Sdk.Linq.QueryProvider.RetrieveEntityCollection(OrganizationRequest request, NavigationSource source) at Microsoft.Xrm.Sdk.Linq.QueryProvider.Execute(QueryExpression qe, Boolean throwIfSequenceIsEmpty, Boolean throwIfSequenceNotSingle, Projection projection, NavigationSource source, List`1 linkLookups, String& pagingCookie, Boolean& moreRecords) at Microsoft.Xrm.Sdk.Linq.QueryProvider.Execute[TElement](QueryExpression qe, Boolean throwIfSequenceIsEmpty, Boolean throwIfSequenceNotSingle, Projection projection, NavigationSource source, List`1 linkLookups) at Microsoft.Xrm.Sdk.Linq.QueryProvider.Execute[TElement](Expression expression) at Microsoft.Xrm.Sdk.Linq.QueryProvider.System.Linq.IQueryProvider.Execute[TResult](Expression expression) at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source) at Xrm.Framework.CI.Common.UpdateSolution.Update(String solutionName, String version) at Xrm.Framework.CI.TeamFoundation.Activities.UpdateSolution.Execute(CodeActivityContext context) at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)

  5. Hi Wael,

    The Template gives an error when trying to open it as earlier mentioned.
    How can I change the Template for example changing the Binary outputdirectory.

  6. Hi Wael,

    I would like to have the powershell output also included in the mail send when a build has succeeded or failed.

    If an error has occured in the powershell script it is included in the Summary section of the mail. In the script I also have the ErrorActionPreference variable set to stop.

    However in the script I also do some Write-Host (also checked with Write-Output) logging but I cant find this information anywhere. Even in the build log it isn’t displayed.

    What can I do to get logging information done in the powershell sciprt?

    Fabian

    • Hi Fabian,

      The Write-Output normally does the trick for me. As the PowerShell activity will write all the output from the PowerShell execution to the build output. Another thing that worked for me is setting the built logging to diagnostics but this generates lots of information.

      I will try to see if the PowerShell activity from TFS 2013 behaves a bit better and will update accordingly once we have full VS 2013 support.

      Hope this helps

  7. Hi Wael,

    Is there some documentation where I can setup CI with source depot?

  8. Hi Wael,

    I am getting the following error during build. Requesting your help

    The build controller cannot deserialize the process parameters of the build. Make sure the process parameters are compatible with the assemblies on the build machine. Details: Cannot create unknown type ‘{clr-namespace:Xrm.Framework.CI.Common;assembly=Xrm.Framework.CI.Common}SolutionPackageType’

    Thanks,
    Arun.

    • Sounds like you have something missing assemblies. What dlls do you have in the custom assemblies folder in source control that is linked to the build controller?

  9. Hi.

    I am having some issues with a certificat and I am getting the following error: When accessing the https://crm.server.local/BGUCRM/XRMServices/2011/Organization.svc using Internet Explorer gives no error, however, I have installed a self-signed certificat issues to *.server.local. So the big question is if I can add anything to the “CRM Connection Details” field (connection string) to tell the system that the connection is to be trusted???

    Exception Message: Metadata contains a reference that cannot be resolved: ‘https://crm.server.local/BGUCRM/XRMServices/2011/Organization.svc?wsdl’. (type InvalidOperationException)
    Exception Stack Trace: at System.ServiceModel.Description.MetadataExchangeClient.MetadataRetriever.Retrieve(TimeoutHelper timeoutHelper)
    at System.ServiceModel.Description.MetadataExchangeClient.ResolveNext(ResolveCallState resolveCallState)
    at System.ServiceModel.Description.MetadataExchangeClient.GetMetadata(MetadataRetriever retriever)
    at Microsoft.Xrm.Sdk.Client.ServiceMetadataUtility.RetrieveServiceEndpointMetadata(Type contractType, Uri serviceUri, Boolean checkForSecondary)
    at Microsoft.Xrm.Sdk.Client.ServiceConfiguration`1..ctor(Uri serviceUri, Boolean checkForSecondary)
    at Microsoft.Xrm.Sdk.Client.OrganizationServiceConfiguration..ctor(Uri serviceUri)
    at Microsoft.Xrm.Sdk.Client.ServiceConfigurationFactory.CreateConfigurationTService
    at Microsoft.Xrm.Client.Services.OrganizationService.CreateServiceConfiguration(CrmConnection connection)
    at Microsoft.Xrm.Client.Services.OrganizationService.GetServiceConfiguration(CrmConnection connection)
    at Microsoft.Xrm.Client.Services.OrganizationService.ToOrganizationServiceProxy(CrmConnection connection)
    at Microsoft.Xrm.Client.Services.OrganizationService.ToOrganizationService(CrmConnection connection)
    at System.Lazy`1.CreateValue()
    — End of stack trace from previous location where exception was thrown —
    at Microsoft.Xrm.Client.Services.OrganizationService.InnerOrganizationService.get_Value()
    at Microsoft.Xrm.Client.Services.OrganizationService.Dispose()
    at Microsoft.Xrm.Client.CrmOrganizationServiceContext.Dispose(Boolean disposing)
    at Microsoft.Xrm.Sdk.Client.OrganizationServiceContext.Dispose()
    at Xrm.Framework.CI.TeamFoundation.Activities.GetSolutionDetails.Execute(CodeActivityContext context)
    at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
    at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)

    Inner Exception Details:

    Exception Message: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. (type WebException)Exception Stack Trace: at System.Net.HttpWebRequest.GetResponse()
    at System.ServiceModel.Description.MetadataExchangeClient.MetadataLocationRetriever.DownloadMetadata(TimeoutHelper timeoutHelper)
    at System.ServiceModel.Description.MetadataExchangeClient.MetadataRetriever.Retrieve(TimeoutHelper timeoutHelper)

    Inner Exception Details:

    Exception Message: The remote certificate is invalid according to the validation procedure. (type AuthenticationException)
    Exception Stack Trace: at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
    at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
    at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
    at System.Net.ConnectStream.WriteHeaders(Boolean async)

  1. Pingback: Backup Dynamics CRM solution with powershell » Mind The Cloud

  2. Pingback: Microsoft Dynamics CRM 2013 Application Lifetime Management - Part 1 - TSG R&D - Microsoft Dynamics CRM Blog - Microsoft Dynamics CRM - Microsoft Dynamics Community

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 44 other followers

%d bloggers like this: