AX & Azure Series: AIF Service as RESTful API App


Error message

Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of D:\home\site\wwwroot\webapp\includes\

AX is a platform for integration.  The ease at which you can fire up a web service and call it from external clients makes this a premium ERP system to be sought after.  I strongly suspect this will become even better in future versions of the AX product. 

While the AIF and custom WCF services have been out for some time, and even others have shown how to mask these endpoints with a RESTful wrapper – today I’d like to demonstrate how you can turn your services in to plug n’ play components for Azure Logic and other apps.

To start, we first need a service in AX to work with.  Let’s create a new Inbound Port and call it CustomerService:

Next, let’s add some service operations for customers, such as the following and then we’ll activate the port:

 Now we should have a WSDL that we can work with locally.  Let’s create a new API App by opening Visual Studio 2013 and browsing to a new ASP .NET Web Application:

If you don’t have the latest Azure SDK, you can download it from Visual Studio by going to File->New Project->Visual C#->Cloud->Download SDK:

I’ll call the new solution RestfulCustomer and then select Azure API App (Preview):

The key difference between a WebAPI and Azure API App is that the authentication is disabled.  This is because you’ll use auth options from Azure such as Azure Active Directory, or another Identity Provider such as Google, Facebook, or a good ol’ Microsoft Live account.

Now let’s add our WSDL as a Service Reference:

Let’s call the reference AXCustomerService and finish with the dialog. 

Now, let’s create our Customer Model by right-clicking and adding a New Class to the Models folder in our project.  I’ll call my model Customer.cs and it is defined as follows:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using RestfulCustomer.AXCustomerService;
  7. namespace RestfulCustomer.Models
  8. {
  9. public class Customer
  10. {
  11. public string accountNum {get; set;}
  12. public string name {get; set;}
  13. public string custGroup { get; set; }
  15. public static Customer[] GetCustomersFromAX(int page, int objectsPerPage)
  16. {
  17. List<Customer> customerList = new List<Customer>();
  19. CustomerServiceClient axClient = new CustomerServiceClient();
  20. axClient.ClientCredentials.Windows.ClientCredential.Domain = "IMMGLOBAL.local";
  21. axClient.ClientCredentials.Windows.ClientCredential.Password = "Pass@Word!";
  22. axClient.ClientCredentials.Windows.ClientCredential.UserName = "lswenka";
  23. axClient.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
  25. CallContext ctx = new CallContext();
  26. ctx.LogonAsUser = "IMMGLOBAL\\lswenka";
  27. ctx.Company = "IG";
  29. QueryCriteria qc = new QueryCriteria();
  30. qc.CriteriaElement = new CriteriaElement[1];
  31. qc.CriteriaElement[0] = new CriteriaElement();
  32. qc.CriteriaElement[0].DataSourceName = "CustTable";
  33. qc.CriteriaElement[0].FieldName = "AccountNum";
  34. qc.CriteriaElement[0].Operator = Operator.NotEqual;
  35. qc.CriteriaElement[0].Value1 = "";
  37. EntityKey[] keys = axClient.findKeys(ctx, qc);
  39. int counter = page * objectsPerPage;
  40. while (counter <= (page+1 * objectsPerPage) && counter <= (keys.Length-1))
  41. {
  42. EntityKey[] keyArray = new EntityKey[1];
  43. keyArray[0] = keys[counter];
  44. AxdCustomer document =, keyArray);
  45. customerList.Add(Customer.ConvertDocumentToCustomer(document.CustTable[0]));
  47. counter++;
  48. }
  51. return customerList.ToArray();
  52. }
  54. public static Customer ConvertDocumentToCustomer(AxdEntity_CustTable doc)
  55. {
  56. Customer ret = new Customer();
  58. ret.accountNum = doc.AccountNum;
  59. ret.custGroup = doc.CustGroup;
  60. = doc.DirParty[0].Name;
  62. return ret;
  63. }
  64. }
  67. }

You’ll need to update this with the appropriate credentials you wish to connect to AX, as well as the legal entity in the CallContext.  Of course, if you wish to use Claims Users, you can pass along an email address from your calling application as long as that user exists in AX.  See Joris’ blog entry regarding Trusted Intermediaries.

Next, let’s right-click our Controllers folder and select Add New Controller.  Select WebAPI 2 with read/write actions.

I’ll call mine CustomerController.cs and it is defined as follows:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Http;
  6. using System.Web.Http;
  7. using RestfulCustomer.Models;
  9. namespace RestfulCustomer.Controllers
  10. {
  11. public class CustomerController : ApiController
  12. {
  14. // GET: api/Customer
  15. public IEnumerable<Customer> Get()
  16. {
  17. return Customer.GetCustomersFromAX(0, 5);
  18. }
  20. // GET: api/Customer/5
  21. public string Get(int id)
  22. {
  23. return "value";
  24. }
  26. // POST: api/Customer
  27. public void Post([FromBody]string value)
  28. {
  29. }
  31. // PUT: api/Customer/5
  32. public void Put(int id, [FromBody]string value)
  33. {
  34. }
  36. // DELETE: api/Customer/5
  37. public void Delete(int id)
  38. {
  39. }
  40. }
  41. }

Naturally, you’ll want to populate the other service operations in the controller if you wish to edit, create, or delete new customers via this restful API.  For now, we should have enough to hit F5 and debug our new API.

What did we do wrong?  Well, nothing actually.  The API Apps template does not provide a welcome or help page.  Instead, let’s add /swagger/ to the URI and we should be greated with the Swashbuckle implementation of our API’s metadata:

Let’s drill down in to the Customer API and open up the Get /api/Customer operation:

This is the metadata behind the operation.  Swagger allows Azure Apps platform to understand the inputs of the API, and what it returns.  Let’s switch the response content to “application/xml” and run it to see the output:

Which matches AX:

This is great.  Now, let’s setup our Azure API App in .  Click on New->Web & Mobile->API App.  I’ll call mine AzureRestCustomer:

Be sure to select the Standard pricing tier, as this will be important later.

As that is being deployed, let’s setup an Azure Virtual Network for our AOS host.  To do this, follow the tutorials here.  After that is completed and successfully connected to the VNET, or if you already have a VNET, note down the IP assigned to your AOS via ipconfig:

Now, back in the Azure Portal let’s select our App Service Plan from the Browse All blade:

From there scroll down til you see the Network option and select your new Virtual Network created earlier. 

This connects your APIs back to your on-premises network so that they can communicate with internal resources.

Back in Visual Studio, it’s time to publish our application.  You’ll want to either have configured your web.config file appropriate for Debug vs Release, or as I have lazily done just modified the web.config to point from my AOS local DNS name to its Azure VNET IP of

Then, select the project in your solution and right-click and choose Publish…:

Select the API App (Preview) option and then choose your API App from the list.  Mine was called AzureRestCustomer. Keep all of the defaults and then publish your API to the cloud.

Again, we run in to a lack of an index.html:

As before, add the /swagger/ to your URI and you should still be able to retrieve your customers from on-premises a-la-cloud over HTTPS:

Next post, we’ll make better use of this API in some of the other fun features available in the Azure App Services suite!