Don’t fall victim to PTE ransomware in Business Central

What happens if you lose access to the source code for your PTEs? Can you recreate a PTEs without the source? That the topic of this video, check it out:

https://youtu.be/VvHjSehV9Gc

In this video, Erik demonstrates a critical risk that Business Central customers face: losing control of Per-Tenant Extension (PTE) source code. He walks through why this situation is dangerous, how to identify if you’re at risk, and provides a step-by-step recovery method to regain control of your data when you no longer have access to the original source code.

The Problem: PTE “Ransomware”

In the old days of NAV (Navision), there was only one version of objects — the FOB/text files that lived inside the system. With Business Central, things changed fundamentally. AL code lives outside the system in Visual Studio Code, gets compiled into an .app file, and then gets uploaded to the cloud or an on-prem server.

Depending on the extension’s settings, what’s running in your Business Central environment might not be accessible to you. Unless you actually control your source code — the AL code sitting in a repository somewhere — you can end up in a situation where you have critical functionality running in your system that you do not have full control over.

The Worst-Case Scenario

Here’s how things can go wrong:

  1. A Microsoft update comes along and your PTE is no longer compatible with Business Central
  2. The people who made the customization are no longer available — either physically or economically
  3. You have a component blocking updates that Microsoft wants to remove
  4. You need to run your business, and you’re stuck

This is what Erik calls “PTE ransomware” — someone else is effectively holding your ERP system hostage because you don’t have access to your own source code.

Prevention: Don’t Get Into This Situation

The most important takeaway from this video is prevention. Make sure that whatever customizations are made to your system:

  • The source code is available and stored on your file server or in a repository you control
  • You have access to the code repository directly
  • There is a contract between you and whoever writes the code that guarantees access to the source
  • If a partner is coming up with shady schemes where the code isn’t available — go somewhere else

How to Identify the Risk

Erik shows how to check whether you’re at risk. In Business Central, navigate to Extension Management and find the extension in question. Click the three dots on the extension and look for the option to download the source code.

If you can download the source code, you’re in good shape — you can put it into Visual Studio Code, modify it, and publish new versions. If you cannot download the source code, you have a problem. That extension is not under your control.

This behavior is controlled by the resourceExposurePolicy section in the extension’s app.json. Here’s what a locked-down extension looks like:

"resourceExposurePolicy": {
    "allowDebugging": false,
    "allowDownloadingSource": false,
    "includeSourceInSymbolFile": false
}

When all three of these are set to false, you have zero visibility into what that extension is doing, and no way to retrieve the code.

The Recovery Process: Regaining Control

If you find yourself in the worst-case scenario, here’s the process Erik demonstrates to regain control of your data.

Step 1: Extract the Symbols

Even without source code access, you can still download the symbols for an extension. Create an empty AL project in Visual Studio Code and add a dependency to the locked extension in your app.json:

"dependencies": [
    {
        "id": "5a2bd722-18cb-499c-bf60-4ac4ac828f01",
        "name": "Ransomware",
        "publisher": "Default publisher",
        "version": "1.0.0.2"
    }
]

Then run the Download Symbols command. Open the .alpackages folder and you’ll find the symbol file for the extension. Using the AL Developer Tools (or the AZ AL Dev Tools extension), double-click on the symbols to see what objects exist in the extension.

What you get is essentially “debug AL” (DAL) — pseudo-AL that shows the bare minimum: field definitions, keys, page controls. You won’t see properties, business logic, triggers, or any internal code. But you get enough to understand the data structure.

Step 2: Create a Replacement App

Create a new AL project that will become the replacement for the locked extension. The critical step is to make sure the id, name, and publisher in your new app.json match the original extension exactly — and then increment the version number:

{
    "id": "5a2bd722-18cb-499c-bf60-4ac4ac828f01",
    "name": "Ransomware",
    "publisher": "Default publisher",
    "version": "1.0.0.3",
    ...
}

And this time, make it a friendly app that you won’t lose control of again:

"resourceExposurePolicy": {
    "allowDebugging": true,
    "allowDownloadingSource": true,
    "includeSourceInSymbolFile": true
}

Step 3: Recreate the Objects

Using the symbol information as a guide, recreate the table and page objects. Copy the field definitions from the symbols and fill in the missing properties that symbols don’t expose (like PageType, SourceTable, ApplicationArea, and UsageCategory).

Here’s the recreated table:

table 50148 ImportantTable
{
    Caption = 'ImportantTable';
    DataClassification = ToBeClassified;
    
    fields
    {
        field(1; PKey; Code[20])
        {
            Caption = 'PKey';
            DataClassification = ToBeClassified;
        }
        field(2; Data1; Text[50])
        {
            Caption = 'Data1';
            DataClassification = ToBeClassified;
        }
        field(3; Date2; Date)
        {
            Caption = 'Date2';
            DataClassification = ToBeClassified;
        }
        field(4; Amount; Decimal)
        {
            Caption = 'Amount';
            DataClassification = ToBeClassified;
        }
    }
    keys
    {
        key(PK; PKey)
        {
            Clustered = true;
        }
    }
}

And the recreated page:

page 50148 "Important Data"
{
    Caption = 'Important Data';
    PageType = List;
    SourceTable = ImportantTable;
    UsageCategory = Lists;
    ApplicationArea = all;

    layout
    {
        area(content)
        {
            repeater(General)
            {
                field(PKey; Rec.PKey)
                {
                    ToolTip = 'Specifies the value of the PKey field.';
                    ApplicationArea = All;
                }
                field(Amount; Rec.Amount)
                {
                    ToolTip = 'Specifies the value of the Amount field.';
                    ApplicationArea = All;
                }
                field(Data1; Rec.Data1)
                {
                    ToolTip = 'Specifies the value of the Data1 field.';
                    ApplicationArea = All;
                }
                field(Date2; Rec.Date2)
                {
                    ToolTip = 'Specifies the value of the Date2 field.';
                    ApplicationArea = All;
                }
            }
        }
    }
}

Step 4: Deploy Carefully

This is the most critical part of the entire process. In your launch.json, make absolutely sure that schema sync mode is set so that it will not force anything:

"schemaUpdateMode": "Synchronize"

You do not want anything set to "ForceSync" or "Recreate". You want the deployment to sync nicely without destroying any existing data. If the schema doesn’t match, you want it to fail rather than wipe your tables.

Important: Before doing this on a production environment, use the Admin Center to make a copy of your production environment into a sandbox, and test the entire process there first.

Step 5: Verify

After deploying, go to Extension Management and verify that the old versions are no longer installed but your new version (in this case, version 3) is installed. Then navigate to the page and confirm your data is still there and accessible.

The data that was previously locked inside an app you had no access to is now available in an extension you fully control.

Limitations of This Approach

Erik is upfront about the limitations: if the original extension had a million tables, a million pages, and tons of business logic, this method is extremely labor-intensive and potentially not feasible. You only get the bare, raw, public visualization of whatever has been exposed as symbols. All internal business logic, triggers, and private code are invisible and will never be recoverable.

As Erik puts it: “This is a crappy solution to a crappy problem — but it is a solution.” The data can be recovered, even if the business logic needs to be rebuilt from scratch.

Conclusion

The key message is clear: don’t get into this situation in the first place. Always ensure you have access to the source code for every PTE running in your Business Central environment. Whether that means storing the code on your own file server, having access to the repository, or having contractual guarantees with your development partner — protect yourself. If your partner won’t give you access to your own code, find a different partner. You don’t want someone else holding your ERP system for ransom.

If you do find yourself in this unfortunate position, the symbol-based recovery method shown here can help you regain control of your data — but rebuilding the business logic will be a significant effort that grows with the complexity of the original extension.