Microsoft Dynamics AX 2012 – Developing Apps [Not Secure]
Purpose: The purpose of this document is to illustrate how to integrate Microsoft Dynamics AX 2012 with external applications and explain different approaches to authentication and development.
Challenge: For the purposes of demonstration or Proof of Concept it may be needed to quickly develop external application which is integrated with Microsoft Dynamics AX 2012. There're also multiple development approaches you may want to choose and multiple authentication strategies you may want to use.
Solution: For the purposes of this walkthrough I'll use Microsoft Dynamics AX 2012 R2 deployed in Windows Azure and Microsoft Dynamics AX 2012 R2 Demo VM which can be downloaded from Customer/Partner Source. I'm going to develop external application which will integrate with Microsoft Dynamics AX 2012 using Document and Custom Web Service. Also I'll illustrate the use of Windows authentication and Claims-based authentication in integration scenarios. Please note that in this walkthrough I'm focusing on developing apps for POC [Not Secure], please refer to another article in this series about Developing Apps [Secure] leveraging Windows Azure Service Bus.
Task: Console Application which I'll develop in this walkthrough will display the info about Cases retrieved from Microsoft Dynamics AX 2012. I used Console Application for the sake of simplicity and this material will also be applicable in case you develop Windows 8, Windows Phone8 or Web apps integrated with Microsoft Dynamics AX 2012.
Walkthrough:
First off I'll deploy Microsoft Dynamics AX 2012 in Windows Azure by creating Windows Azure VM(s)
Endpoints
And exposing HTTP/HTTPS endpoint for the VM in order to establish connection to Microsoft Dynamics AX 2012 from external application [Not Secure]
Add Endpoint – Add an endpoint to a virtual machine
Once we chose Add standalone endpoint we can now select endpoint name HTTP
Add Endpoint – Specify the details of the endpoint
As the result HTTP endpoint will be exposed for the VM
Endpoints
Now we have to install Web Server Role on the VM in order to expose Web Services through HTTP/HTTPS
Add Roles – Web Server (IIS)
Add Roles – Web Server (IIS)
Add Roles – Web Server (IIS)
After successful installation of Web Server (IIS) Role we can successfully access the following URL from the local machine: http://azureax.cloudapp.net/
IIS - Port 80
The next step is to install Web Services on IIS component for Microsoft Dynamics AX 2012
Microsoft Dynamics AX 2012 - Web Services on IIS
During the installation you may encounter the following error which means that not all prerequisites have been installed on the machine before installation of Web Services on IIS
Prerequisites details
In my case the problem was caused by the absence of Health and Diagnostics – Request Monitor component which is not installed by default with Web Server (IIS) Role
Add Roles – Web Server (IIS)
That's why I simply added Health and Diagnostics – Request Monitor component on the VM in order to avoid installation error
Now we can continue with Web Services on IIS installation process
The next step would be to specify Business Connector Proxy account information
Microsoft Dynamics AX Setup – Specify Business Connector Proxy account information
After that we have to specify hosting Web site details as depicted below
Microsoft Dynamics AX 2012 Setup – Configure IIS for Web Services
And before we complete installation we will specify AOS account
Microsoft Dynamics AX 2012 Setup – Specify an AOS account
After you install Web Server (IIS) Role you may also need to configure it. Please refer to the following article about how to Configure IIS: http://technet.microsoft.com/en-us/library/gg731848.aspx
By now we completed required installations and can think about authentication. For the sake of simplicity I'll enable Windows Authentication for Web Server (IIS) on Windows Azure VM
Add Roles – Web Server (IIS)
Please note that Windows authentication is not appropriate for use in an Internet environment because that environment does not require or encrypt user credentials. Please refer to the following article about Configuring Windows authentication on IIS: http://technet.microsoft.com/en-us/library/cc754628(v=ws.10).aspx
However for the purposes of POC this is the quickest way for me to authenticate. That's why now I'll check that Windows authentication is enabled for Default Web Site > MicrosoftDynamicsAXAif60 in IIS Manager
IIS Manager
In Microsoft Dynamics AX 2012 I'll validate Web Site URL
Microsoft Dynamics AX 2012 – Web Sites (Validate)
Microsoft Dynamics AX 2012 – Web Sites (Validate Result)
And then create Enhanced Inbound port
Enhanced port / HTTP
To expose standard CaseWebDetailService Document Web Service in Microsoft Dynamics AX 2012
Select Service Operations
CaseWebDetailService Document Service will allow me to retrieve info about Cases in Microsoft Dynamics AX 2012 from external application
Document Data Policies
Once Enhanced Inbound port has been activated I can now access respective WSDL URI using the following URL: http://azureax/MicrosoftDynamicsAXAif60/CaseManagement/xppservice.svc
In order to access WSDL URI from external application I will use the full machine name as: http://azureax.cloudapp.net/MicrosoftDynamicsAXAif60/CaseManagement/xppservice.svc
Here's how WSDL URI looks like
RoutingService Service
RoutingService Service
RoutingService Service
You have created a service.
To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:
svcutil.exe http://azureax.azureax0.local/MicrosoftDynamicsAXAif60/CaseManagement/xppservice.svc?wsdl
This will generate a configuration file and a code file that contains the client class. Add the two files to your client application and use the generated client class to call the Service. For example:
C#
class Test {
static void Main() {
RequestReplyRouterClient client = new RequestReplyRouterClient();
// Use the 'client' variable to call operations on the service.
// Always close the client.
client.Close();
}
}
Visual Basic
Class Test Shared Sub Main() Dim client As RequestReplyRouterClient = New RequestReplyRouterClient() ' Use the 'client' variable to call operations on the service.
' Always close the client.
client.Close()
End Sub
End Class |
Now let's develop a simple client application (Console application) to retrieve the info about Cases from Microsoft Dynamics AX 2012
For these purpose I quickly installed Visual Studio Express 2012 for Windows Desktop on my local machine and on Windows Azure VM from here: http://www.microsoft.com/visualstudio/eng/downloads. After that I'm going to create a project
New Project
And add Service Reference using Web Service WSDL URI
Add Service Reference
Once Service Reference has been added all necessary Proxy classes will be generated inside the project
Proxy Classes
Please note that CaseWebDetailService Web Service is a Document Web Service Microsoft Dynamics AX 2012, that's why the system generated all necessary AIF classes (Axd, etc)
The source code for client application is presented below
Source code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApplication1.ServiceReference1;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
CaseWebDetailServiceClient client = new CaseWebDetailServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = "azureax0";
client.ClientCredentials.Windows.ClientCredential.UserName = "Administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "pass@word1";
CallContext context = new CallContext();
context.Company = "DAT";
EntityKey[] entityKey = new EntityKey[1];
entityKey[0] = new EntityKey();
entityKey[0].KeyData = new KeyField[1];
entityKey[0].KeyData[0] = new KeyField();
entityKey[0].KeyData[0].Field = "RecId";
entityKey[0].KeyData[0].Value = "5637144576";
AxdCaseWebDetail caseItem = client.read(context, entityKey);
if (caseItem != null)
{
Console.WriteLine(String.Format("CaseId: {0}; Description: {1}", caseItem.CaseDetail[0].CaseId, caseItem.CaseDetail[0].Description));
}
Console.WriteLine("Done!");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Error!" + ex.Message);
Console.ReadLine();
}
}
}
}
|
Please note that for the sake of simplicity I hardcoded user credentials. I'll also need to change endpoint address in App.config appropriately from "azureax.azureax0.local" to "azureax.cloudapp.net" to be able to resolve the name of Windows Azure VM from my local machine where I create my client application
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="reqReplyEndpoint">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
<binding name="BasicHttpBinding_CaseWebDetailService">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://azureax.cloudapp.net/MicrosoftDynamicsAXAif60/CaseManagement/xppservice.svc"
binding="basicHttpBinding" bindingConfiguration="reqReplyEndpoint"
contract="ServiceReference1.IRequestReplyRouter" name="reqReplyEndpoint" />
<endpoint address="http://azureax.cloudapp.net/MicrosoftDynamicsAXAif60/CaseManagement/xppservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_CaseWebDetailService"
contract="ServiceReference1.CaseWebDetailService" name="BasicHttpBinding_CaseWebDetailService" />
</client>
</system.serviceModel>
</configuration>
|
As the result I'll be able to retrieve info about Cases from Microsoft Dynamics AX 2012 in my client application which I ran from my local machine as well as from the same Windows Azure VM
Microsoft Dynamics AX 2012 - Cases
Microsoft Dynamics AX 2012 – Table Browser
As you can see I ran client application from my local machine as well as from the same Windows Azure VM
Result - Azure VM
Result - Local machine
Okay, we got the result! However there's another way to authenticate against Microsoft Dynamics AX 2012 using AIF Intermediary proxy. The idea is very simple, you can use AIF Intermediary proxy in the scenarios when your external application has already taken care on authentication (using Claims-based authentication), so when you authenticate against Microsoft Dynamics AX 2012 you simply use a static integration account credentials (impersonation of AX integration user) also adding logon user information (email address which is already linked to AX integration user)
For example, I say that alex@outlook.com is a Claims user which is linked to AX integration user Admin. Please see below the setup required for this scenario
User – Claims user
Then I will have to specify the association between AX Claims user and AX integration user in Trusted intermediary users form for Inbound port
Inbound port – Security (Trusted Intermediary users)
Please note that before you define the association you have to mark "Allow trusted intermediary to impersonate" checkbox [V] for Inbound port
Trusted intermediaries
Please note that I associated alex@outlook.com Claims user with trusted intermediary user Admin in Microsoft Dynamics AX 2012
Now I'll change the source code little bit to call Web Services on behalf of external user
Source code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApplication1.ServiceReference1;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
CaseWebDetailServiceClient client = new CaseWebDetailServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = "azureax0";
client.ClientCredentials.Windows.ClientCredential.UserName = "Administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "pass@word1";
CallContext context = new CallContext();
context.Company = "DAT";
context.LogonAsUser = "external\\alex@outlook.com";
EntityKey[] entityKey = new EntityKey[1];
entityKey[0] = new EntityKey();
entityKey[0].KeyData = new KeyField[1];
entityKey[0].KeyData[0] = new KeyField();
entityKey[0].KeyData[0].Field = "RecId";
entityKey[0].KeyData[0].Value = "5637144576";
AxdCaseWebDetail caseItem = client.read(context, entityKey);
if (caseItem != null)
{
Console.WriteLine(String.Format("CaseId: {0}; Description: {1}", caseItem.CaseDetail[0].CaseId, caseItem.CaseDetail[0].Description));
}
Console.WriteLine("Done!");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Error!" + ex.Message);
Console.ReadLine();
}
}
}
}
|
Please note that if you call this code as System Administrator you can successfully get the info about Case from Microsoft Dynamics AX 2012
However if you call this code NOT as System Administrator your Web Service call will fail with authentication error
By now we discussed how to use standard Document Service to retrieve information about Cases in Microsoft Dynamics AX 2012. Document Services in Microsoft Dynamics AX 2012 use AIF infrastructure/framework, that's why after we added corresponding Service Reference the system generated a number of AIF Proxy classes for us (Axd, etc)
Please note that you can also apply "pure WCF" approach by using Custom Services in Microsoft Dynamics AX 2012 when you don't use AIF, and define Data Contract and Service Contract in X++. Please find the reference on how to use Custom Services in Microsoft Dynamics AX 2012 here: http://technet.microsoft.com/en-us/library/hh509052.aspx
For Custom Service scenario I'll use local Microsoft Dynamics AX 2012 R2 Demo VM
Please see below how we can define Data Contact and Service Contract for Custom Web Service which will help us retrieve information about Cases in Microsoft Dynamics AX 2012
Source code – Data Contract Class
[DataContractAttribute]
public class AlexCase
{
str alexCaseId;
str alexCaseDescription;
}
|
[DataMemberAttribute]
public str caseId(str _alexCaseId = alexCaseId)
{
alexCaseId = _alexCaseId;
return alexCaseId;
}
|
[DataMemberAttribute]
public str description(str _alexCaseDescription = alexCaseDescription)
{
alexCaseDescription = _alexCaseDescription;
return alexCaseDescription;
}
|
Please note that in Data Contract I defined 2 fields (Id and Name) for the sake of simplicity
Source code – Service Contract Class
class AlexCaseService
{
}
|
[SysEntryPointAttribute(true)]
public AlexCase getCase()
{
AlexCase alexCase;
;
alexCase = new AlexCase();
alexCase.caseId('1');
alexCase.description('AlexCase');
return alexCase;
}
|
Please note that in Service Contract Class I implemented just one operation to retrieve Case info for the sake of simplicity
Once Data Contract and Service Contract have been implemented we can create Service, associate Service Contract Class to newly created Service, add Service to Service Group
Project
And finally deploy Service group which will create and activate Inbound port
Inbound port
Please note that I created Enhanced Inbound port using HTTP adapter to expose Custom Service
Select Service Operations
Please note that I selected getCase operation from the list to finalize Inbound port setup
Now I can create a simple client application to consume Custom Service I exposed, so I'll go ahead create a project and add Service Reference using WSDL URI
Add Service Reference
Now let's take a look at the list of proxy classes
Proxy Classes
In this walkthrough I implemented two options of how to call Custom Service: Synchronous and Asynchronous
Please see the source code for Synchronous scenario below
Source code - Sync
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApplication2.ServiceReference1;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
AlexCaseServiceClient client = new AlexCaseServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = "contoso";
client.ClientCredentials.Windows.ClientCredential.UserName = "Administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "pass@word1";
CallContext context = new CallContext();
context.Company = "USMF";
try
{
AlexCase alexCase = client.getCase(context);
if (alexCase != null)
{
Console.WriteLine(alexCase.caseId + ":" + alexCase.description);
}
Console.WriteLine("Done!");
}
catch (Exception e)
{
Console.WriteLine(e.InnerException.Message);
Console.WriteLine("Error!");
}
Console.ReadLine();
}
}
}
|
Please see the source code for Asynchronous scenario below
Source code – Async
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ConsoleApplication2.ServiceReference1;
namespace ConsoleApplication2
{
class Program
{
public static async Task DoAsync()
{
AlexCaseServiceClient client = new AlexCaseServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = "contoso";
client.ClientCredentials.Windows.ClientCredential.UserName = "Administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "pass@word1";
CallContext context = new CallContext();
context.Company = "USMF";
try
{
AlexCaseServiceGetCaseResponse result = await client.getCaseAsync(context);
if (result != null)
{
Console.WriteLine(result.response.caseId + ":" + result.response.description);
}
Console.WriteLine("Success");
}
catch (Exception e)
{
Console.WriteLine(e.InnerException.Message);
Console.WriteLine("Failure");
}
}
static void Main(string[] args)
{
var Task = DoAsync();
Console.ReadLine();
}
}
}
|
Before I will execute this code I have to come back to authentication setup and make sure Windows Authentication is enabled for IIS Web Site
IIS Web site Authentication settings
I also had to change ClientCredentialsType = Windows to ClientCredentialType = Ntml in App.config to avoid the following error: The remote server returned an error: (401) Unauthorized. The HTTP request is unauthorized with client authentication scheme 'Negotiate'.
App config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="serviceEndpoint">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
<binding name="BasicHttpBinding_AlexCaseService">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://ax2012r2a.contoso.com/MicrosoftDynamicsAXAif60/AlexServiceExt/xppservice.svc"
binding="basicHttpBinding" bindingConfiguration="serviceEndpoint"
contract="ServiceReference1.IRequestReplyRouter" name="serviceEndpoint" />
<endpoint address="http://ax2012r2a.contoso.com/MicrosoftDynamicsAXAif60/AlexServiceExt/xppservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_AlexCaseService"
contract="ServiceReference1.AlexCaseService" name="BasicHttpBinding_AlexCaseService" />
</client>
</system.serviceModel>
</configuration>
|
As the result I can successfully retrieve info about Case in Microsoft Dynamics AX 2012 from external application using Custom Service, just the same way I did this using Document Service
Please note that Custom Services in Microsoft Dynamics AX 2012 can offer you greater flexibility and performance, development process for Custom Services resembles the way you develop for .NET/WCF and Custom Services are not dependent of AIF infrastructure/framework
This article is a logical continuation of: http://ax2012aifintegration.blogspot.com/2013/04/microsoft-dynamics-ax-2012-windows-8.html
Summary: In this walkthrough I demonstrated how to quickly develop external application which is integrated with Microsoft Dynamics AX 2012 using Document Services and Custom Services. Please note that in this walkthrough for the sake of simplicity I used impersonation and Windows Integrated authentication [Not Secure] as well as outlined other authentication scenarios such as AIF Intermediary proxies. I provided a comparison Document Services and Custom Services in Microsoft Dynamics AX 2012 and outlined some benefits of Custom Services which may offer greater flexibility (.NET like, sync/async calls) and performance (WCF).
Tags: Microsoft Dynamics ERP, Microsoft Dynamics AX 2012, Visual Studio 2012, AIF, Application Integration Framework, Web Services, Document Services, Custom Services, .NET, WCF, Synchronous, Asynchronous
Note: This document is intended for information purposes only, presented as it is with no warranties from the author. This document may be updated with more content to better outline the concepts and describe the examples.