Connect to Azure Function in BC 2022 wave 2 (v21)

by Oct 15, 2022AL Language

Home 9 Development 9 AL Language 9 Connect to Azure Function in BC 2022 wave 2 (v21)

The new version of the Microsoft Dynamics 365 Business Central brought a new system module “Azure Functions” that makes integration with Azure Functions much easier and straightforward. The typical scenario, why to use Azure Functions together with Business Central, is if we need to use the .NET interop component (for example custom DLL) from AL. Some examples of why I use Azure Functions are creating integration with SFTP (to send warehouse data to old-school shipping agents), loading data from machines in manufacturing, or just signing documents using SignPads.

The new module contains three (public) codeunits.

  • Azure Functions (codeunit 7804)
  • Azure Functions Authentication (codeunit 7800)
  • Azure Functions Response (codeunit 7805)

Authentication

Let’s start with authentication. There are two possible ways how to use this module from the point of authentication. If your Azure Function is secured at function-level only (= without user identification), we can use the method CreateCodeAuth(…) from Codeunit 7800 “Azure Functions Authentication”. This method accepts two parameters, Azure Functions endpoint (URL) and authentication code that is defined for your Azure Function. This approach is far from ideal as anyone who knows this code can access your Azure Functions.

The second method is CreateOAuth2(…) which allows creating a connection with both OAuth2 and the Authentication code for your function.

Both these methods return an instance of codeunit that implements an interface “Azure Functions Authentication” which is later used for API requests.

Create own integration

Once we know the authentication methods, we know everything necessary to build our integration. To send a GET request to our Azure Function we can use the method SendGetRequest(…) available in Codeunit 7804 “Azure Functions”.

This method has two parameters, the first one is the implementation of Azure Functions Authentication that we discussed earlier. The second one is a Dictionary of Texts that will be translated to URL parameters (the dictionary key = parameter name, dictionary value = parameter value).

Method SendGetRequest(…) returns an instance of another codeunit, Codeunit 7805 “Azure Functions Response”. This object contains all information about the API response, including whether the API call was successful (method IsSuccessful()). You can also get error information (GetError()), response content (GetResultAsText(…), GetResultAsStream(…)) or whole instance of HttpResponseMessage (GetHttpResponse(…)).

procedure SendAzureFunctionGetRequest()
var
    AzureFunctions: Codeunit "Azure Functions";
    AzureFunctionsResponse: Codeunit "Azure Functions Response";
    AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
    IAzureFunctionsAuthentication: Interface "Azure Functions Authentication";
    QueryDictinary: Dictionary of [Text, Text];
begin
    QueryDictinary.Add('param1', 'value1');
    QueryDictinary.Add('param2', 'value2');
    QueryDictinary.Add('param3', 'value3');

    IAzureFunctionsAuthentication := AzureFunctionsAuthentication.CreateCodeAuth('AZURE FUNCTION ENDPOINT', 'AUTHENTICATION CODE');
    AzureFunctionsResponse := AzureFunctions.SendGetRequest(IAzureFunctionsAuthentication, QueryDictinary);
    if AzureFunctionsResponse.IsSuccessful() then
        Message('Get request successful.')
    else
        Error('Get request failed.\Details: %1', AzureFunctionsResponse.GetError());
end;

The POST request could be created similarly – we just need to use the SendPostRequest(…) method instead of SendGetRequest(…). This time, the method does not accept Dictionary, but RequestBody (only as Text) and Content-Type header.

Unfortunately, I really miss something like SendPostRequest(AzureFunctionAuthentication: Interface “Azure Functions Authentication”; Body: JsonObject) as it will be much easier to use…

procedure SendAzureFunctionPostRequest()
var
    AzureFunctions: Codeunit "Azure Functions";
    AzureFunctionsResponse: Codeunit "Azure Functions Response";
    AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
    IAzureFunctionsAuthentication: Interface "Azure Functions Authentication";
    RequestBody: Text;
    JsonObject: JsonObject;
begin
    JsonObject.Add('param1', 'value1');
    JsonObject.Add('param2', 'value2');
    JsonObject.Add('param3', 'value3');

    JsonObject.WriteTo(RequestBody);
    IAzureFunctionsAuthentication := AzureFunctionsAuthentication.CreateCodeAuth('AZURE FUNCTION ENDPOINT', 'AUTHENTICATION CODE');
    AzureFunctionsResponse := AzureFunctions.SendPostRequest(IAzureFunctionsAuthentication, RequestBody, 'application/json');
    if AzureFunctionsResponse.IsSuccessful() then
        Message('Post request successful.')
    else
        Error('Post request failed.\Details: %1', AzureFunctionsResponse.GetError());
end;

There is also an available universal method Send(…), which allows using any HTTP Request Type and defining both Header & Body content.

procedure SendAzureFunctionUniversalRequest()
var
    AzureFunctions: Codeunit "Azure Functions";
    AzureFunctionsResponse: Codeunit "Azure Functions Response";
    AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
    IAzureFunctionsAuthentication: Interface "Azure Functions Authentication";
    HttpRequestType: Enum "Http Request Type";
    QueryDictinary: Dictionary of [Text, Text];
    RequestBody: Text;
    JsonObject: JsonObject;
begin
    QueryDictinary.Add('urlParam1', 'urlValue1');
    QueryDictinary.Add('urlParam2', 'urlValue2');
    JsonObject.Add('bodyParam1', 'bodyValue1');
    JsonObject.Add('bodyParam2', 'bodyValue2');

    JsonObject.WriteTo(RequestBody);
    IAzureFunctionsAuthentication := AzureFunctionsAuthentication.CreateCodeAuth('AZURE FUNCTION ENDPOINT', 'AUTHENTICATION CODE');
    AzureFunctionsResponse := AzureFunctions.Send(IAzureFunctionsAuthentication, HttpRequestType::/*METHOD*/, QueryDictinary, RequestBody, 'application/json');
    if AzureFunctionsResponse.IsSuccessful() then
        Message('Universal request successful.')
    else
        Error('Universal request failed.\Details: %1', AzureFunctionsResponse.GetError());
end;

In my opinion, this new system module made the integration with Azure Functions a bit easier as we do not longer need to set all headers manually and also we do not have to think about the implementation of authentication.

What do you think? Let me know by sharing this article on Twitter/LinkedIn.

Recent Articles from the category

BC Open Source? How to start?

BC Open Source? How to start?

BC Open Source? How to start? One of the most exciting news introduced last month in Lyon during Directions EMEA 2023 was the changes to the open-source initiative. This means that you can now contribute to the source code of the Base app and the System app, which are...

read more
Validate a FlowField Field. Wait? What?

Validate a FlowField Field. Wait? What?

Validate a FlowField Field. Wait? What? There are not many things in the AL Language that surprised me. However, last week, I found one such thing - I reviewed customizations made by another partner and had to analyze the OOTB code of the Demand Forecast matrix. I run...

read more
Dynamics NAV 2013 & Expired Cronus License

Dynamics NAV 2013 & Expired Cronus License

We found an interesting problem - we were not able to run the development environment for Dynamics NAV 2013. Whenever we tried to run the development client, we got the following error message: "Your program license has expired" and the development client has closed...

read more
Indirect Dependencies and Access Modifiers

Indirect Dependencies and Access Modifiers

Last week, there was a discussion on Yammer on how to get values from the "Sent Email" record when all fields are marked as Internal. I was surprised that many people do not know what can/can't access modifiers (such as local, protected, or internal) be used for. I...

read more
AL Extensions: Replace Document Attachment

AL Extensions: Replace Document Attachment

I have published a new simple, open-source extension that allows replacing existing document attachments in all master entities as well as in open documents. The source code as well as the app file that can be installed in your environment is available on my GitHub...

read more

Sign Up for News

Certifications

Highest certification
Microsoft Data Management and
also in D365 Business Central

Microsoft Certified: Dynamics 365 Business Central Functional Consultant Associate

See other certifications here