Install Codeunits in Business Central: `SubType = Install`

There are a bunch of things you want to happen when an extension is installed in a tenant: Seed some setup, create default records, or run a process.

That’s what install codeunits are for.

This post is specifically about codeunits with SubType = Install; and how (and when) Business Central runs them.

What Is an Install Codeunit?

An install codeunit is a codeunit whose SubType is set to Install and includes AL methods for performing operations with the extension code itself:

  • When an extension is installed for the first time
  • When an uninstalled version is installed again (reinstall)

Install code is not part of an upgrade. If you publish a newer version and run a data upgrade, that’s upgrade codeunit territory.

Learn more about writing extension install code here.

The SubType Property (And What It Really Means)

The SubType property tells the platform what kind of codeunit you’re creating.

For install logic, the important bit is:

codeunit 50100 "DVLPR Install"
{
    SubType = Install;
}

Microsoft’s reference page for the property is here.

The Two Install Triggers: Per-Company vs. Per-Database

Install codeunits have two system triggers:

  • OnInstallAppPerCompany()
  • OnInstallAppPerDatabase()

The names are telling:

  • Per-company runs once for each company.
  • Per-database runs once for the whole tenant/database as part of the install.

A simple skeleton:

codeunit 50100 "DVLPR Install"
{
    SubType = Install;

    trigger OnInstallAppPerCompany()
    begin
        // Company-specific setup
    end;

    trigger OnInstallAppPerDatabase()
    begin
        // Tenant-wide setup
    end;
}

You Can Have More than One Install Codeunit (But Don’t Assume Order)

You can have multiple install codeunits in the same extension version.

But there’s an important catch from the docs: There’s no guaranteed execution order between different install codeunits.

So if you split install logic across multiple codeunits, design them so they can run independently (or at least safely if they run in any order).

Fresh Install vs. Reinstall: Use ModuleInfo.DataVersion()

A really practical pattern is to detect whether this is:

  • A true first-time install (no prior data)
  • A reinstall (data exists from a previous install)

The docs call out a convention: In install code, a DataVersion of 0.0.0.0 means “fresh/new install”.

You can access this using NavApp.GetCurrentModuleInfo and ModuleInfo:

codeunit 50100 "DVLPR Install"
{
    SubType = Install;

    trigger OnInstallAppPerDatabase()
    var
        CurrentModuleInfo: ModuleInfo;
    begin
        NavApp.GetCurrentModuleInfo(CurrentModuleInfo);

        if CurrentModuleInfo.DataVersion() = Version.Create(0, 0, 0, 0) then
            HandleFreshInstall()
        else
            HandleReinstall();
    end;

    local procedure HandleFreshInstall()
    begin
        // First install of this app in this tenant
        // e.g. create default setup, seed templates, etc.
    end;

    local procedure HandleReinstall()
    begin
        // App was installed before, removed, and installed again
        // e.g. validate/patch baseline records
    end;
}

If you want to go deeper into what you can read from ModuleInfo, the NavApp datatype docs are a good jumping-off point: Learn more about NavApp and ModuleInfo here.

What Belongs in Install Code?

My rule of thumb: Install code should be idempotent and fast.

Good candidates:

  • Create extension setup records if missing
  • Initialize default values (like templates, configurations, default dimensions)
  • Seed reference data that your app expects to exist
  • Register things that prevent “first upgrade surprises” (more on that below)

Less-than-ideal candidates:

  • Long-running data migrations (that’s upgrade code)
  • Anything that depends on user interaction (install runs in a system context)
  • Anything that can’t be safely retried

Install Code + Upgrade Tags (So Your First Upgrade Doesn’t Re-run Old Steps)

If you use upgrade tags to guard upgrade steps, a best practice is to register the tag(s) during install.

That way, when the customer upgrades later, your upgrade code doesn’t run a migration step just because the tag table is empty.

I covered upgrade tags (and version gating) in a separate post:

Wrapping Up

Install codeunits are one of those features you don’t think about much—until you need them.

Use them to make your extension feel polished on day one: Create the defaults, seed the data you know you’ll need, and put guard rails in place so later upgrades stay predictable.

Learn more:

Note: This content is for informational and demonstration purposes only. Always test installation logic in a sandbox and make sure it’s safe to run repeatedly.

Permanent link to this article: https://www.dvlprlife.com/2026/01/install-codeunits-in-business-central-subtype-install/

Leave a Reply

Your email address will not be published.