Do you expect that?
For previous months I was involved in developing a new app for Business Central. That’s what I love most. You have white (actually black) canvas, take the brush and begin to create.
The end-user had to start using the application as quickly as possible. So I’ve populated all setup data during the installation process: app setup, no-series, templates and so on.
Everything worked as expected until I’ve installed 2-nd version of my app on production tenant. I’ve noticed that after 1-st installation user changed some setup configuration and 2-nd version returned everything to default.
That was not expected, cos everything was tested on a sandbox and worked fine.
So I decided to dive into that.
Start of blog series
I’ve decided to check what is going on when you
- First-time install
- Upgrade
- Re-install
- Uninstall and Install
- Unpublish and Install
an app on 3 different environments
- Container Sandbox
- Online Sandbox
- Online Production Tenant
Today I will describe the First-time install process.
But before some prehistory, to show you the evolution of the installation process.
Old classic customizations “installation” approach
Even 2-3 years ago development process looked more or less like in the next picture:

Figure 1. Install customizations on Production
We just developed something very useful in the development database, exported changes as a .fob or .txt, and imported them into the production database. Some developers changed the code in Production directly… ufff… NEVER do that.
Extensions 1.0 installation approach
Then we’ve got Extensions 1.0, where the process became a little more difficult:

Figure 2. Install Extensions 1.0 on Production
First, we needed to create 2 databases: original from NAV DVD, and development. Then we developed something very useful in the development database. After exporting all objects from each database, run through PowerShell scripts to get Deltas and generated .navx file from these deltas. At the end, we published .navx file on Production database using PowerShell command.
All this is very well described in the official documentation and in various blogs, so I will go further.
Note! If you have NAV2016 or NAV2017 – this is how you should make changes
Extensions 2.0 installation approach
With Extensions 2.0 we’ve got Visual Studio Code as a new development platform. So, we don’t develop inside the database, we develop outside.
The process could be illustrated on next picture

Figure 3. Install Extensions 2.0 on Production
The process seems much easier than in Extensions 1.0 approach.
First, you create a project in VSCode with AL extension pre-installed. Then you download symbols of your destination platform (NAV2018 or Business Central). Then you develop something very useful in .al files. After packaging your solution in a .app file you publish it in Production Database. Packaging and publishing run in one click.
I’m sure you know everything this very well.
But the Devil is in details.
- What’s going on during the Installation process?
- How correctly add some new features to the already Installed app?
And, what is more, important how to install an app to an Online Business Central Production Tenant?
So, let’s start with the first question.
What’s going on during the Installation process?
Generally, the publishing process should look like that.

Figure 4. Publish Extensions 2.0 on Production
But this depends on:
- How do you run Publish command?
- What is your destination: Container Sandbox, Online Sandbox or Online Production Tenant?
So, let’s create a simple app and try to publish it differently on different databases.
Simple app to test the installation process
To check what is going on we will create a table with auto increment primary key and a list page for it. And during the installation process will fill this table with some messages.
table 50101 "AIR Check install process"
{
DataPerCompany = false;
fields
{
field(1; "Primary Key"; Integer)
{
AutoIncrement = true;
}
field(2; "Trigger"; Text[250])
{ }
field(3; "Version Installing"; Text[250])
{ }
field(4; "Company Name"; Text[250])
{ }
field(5; "Version Installed"; Text[250])
{ }
}
keys
{
key(PK; "Primary Key")
{
Clustered = true;
}
}
procedure InsertRecord(MessageFromTrigger: Text)
var
begin
Init();
"Trigger" := MessageFromTrigger;
"Company Name" := CompanyName();
InsertVersionNo();
Insert();
end;
local procedure InsertVersionNo()
var
AppInfo: ModuleInfo;
begin
NavApp.GetCurrentModuleInfo(AppInfo);
"Version Installing" := Format(AppInfo.AppVersion());
"Version Installed" := Format(AppInfo.DataVersion());
end;
Notice, that I’ve added property DataPerCompany = False. This will allow us to record triggers related to database, also.
Upgrade and Install Codeunits
First, we will create Upgrade Codeunit. And on each trigger will fill our table.
codeunit 50101 "AIR Upgrade codeunit"
{
Subtype = Upgrade;
trigger OnCheckPreconditionsPerDatabase()
begin
//if you will add insert any code that insert data to a table,
//where DataPerCompany = True, you will receive an error during publish process
AddMessageToOurTable('OnCheckPreconditionsPerDatabase');
end;
trigger OnCheckPreconditionsPerCompany()
begin
AddMessageToOurTable('OnCheckPreconditionsPerCompany');
end;
trigger OnUpgradePerDatabase()
begin
AddMessageToOurTable('OnUpgradePerDatabase');
end;
trigger OnUpgradePerCompany()
begin
AddMessageToOurTable('OnUpgradePerCompany');
end;
trigger OnValidateUpgradePerDatabase()
begin
AddMessageToOurTable('OnValidateUpgradePerDatabase');
end;
trigger OnValidateUpgradePerCompany()
begin
AddMessageToOurTable('OnValidateUpgradePerCompany');
end;
local procedure AddMessageToOurTable(MessageFromTrigger: Text)
var
CheckInstallProcess: Record "AIR Check install process";
begin
CheckInstallProcess.InsertRecord(MessageFromTrigger);
end;
}
codeunit 50102 "AIR Install codeunit"
{
Subtype = Install;
trigger OnInstallAppPerDatabase()
begin
AddMessageToOurTable('OnInstallAppPerDatabase');
end;
trigger OnInstallAppPerCompany()
begin
AddMessageToOurTable('OnInstallAppPerCompany');
end;
local procedure AddMessageToOurTable(MessageFromTrigger: Text)
var
CheckInstallProcess: Record "AIR Check install process";
begin
CheckInstallProcess.InsertRecord(MessageFromTrigger);
end;
}
Let’s publish this app in different ways.
SCENARIO 1. INSTALL APP FOR THE FIRST TIME
Container Sandbox
VSCode > AL:Publish without debugging (Ctrl+F5)
The first-time app install. Summary
- Upgrade codeunit was not triggered at all. That’s because we publish our app for the first time.
- Install codeunit was triggered.
The result of this scenario can be displayed in the next picture
Figure 5. Install app for the first time
What’s next
Next blog will be about the upgrade process. Stay tuned.