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

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
Clean up your copied environments

Clean up your copied environments

One of the most important things every developer should handle is to clean up the environment when the environment (or a company) is copied. Especially if the environment is managed directly by a client and they can create new copies anytime. Similarly, for copied...

read more
7 git commands you should know

7 git commands you should know

For many years, developers in C/AL did not need to know anything about Git or other versioning tools. That has changed with Business Central, or more specifically with AL Language. Today, we will look at the most important (and basic) git commands any developer should...

read more
How to define the source for item reservations?

How to define the source for item reservations?

It's not uncommon to have a customer's request to limit from which source items could be reserved. For example, customers may not want to reserve items from return orders. How can we achieve this goal? It's really simple. All the magic is done in the procedure...

read more
NavigationAction for ErrorInfo data type

NavigationAction for ErrorInfo data type

One more article about ErrorInfo data type. Have you already read my previous posts about ErrorInfo and Collectible Errors? ErrorInfo data type & Collectible Errors | MSDyn365 Business Central - Ing. Tomáš Kapitán (kepty.cz) Collectible Errors?! | MSDyn365...

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