Security change on December 1, 2026: TLS 1.2 will become the minimum standard for 365 business Print Agent. Details and system requirements
365 business development
Concepts

Architecture & modules

ALbuild is a single PowerShell package, businessdev.ALbuild, composed internally of use-case nested modules that ship and version together as one package. The Azure DevOps extension, the VS Code extension, the CLI and the MCP server are thin shells on top of it.

Code
┌────────────────┐ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ Azure DevOps │ │ VS Code │ │ albuild CLI │ │ MCP server │ │ tasks │ │ extension │ │ (cross-platf.) │ │ (AI agents) │ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ │ │ │ │ └──────────────────┴─────────┬────────┴──────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ businessdev.ALbuild (PowerShell) │ │ Core · Containers · Apps · Feeds · RuntimePackages · │ │ Marketplace · OnPrem · Environments · Pipeline │ └─────────────────────────────────────────────────────────┘

All four surfaces are thin shells over the same module. The CLI and the MCP server added the cross-platform and AI-agent entry points, but none of them re-implement container, compile or coverage logic. The behaviour is identical wherever you drive it from.

The one deliberate exception: AL Probe

albuild probe is not a shell over the PowerShell module. It is an AL interpreter written in C# and hosted inside the CLI: a lexer, parser and tree-walking evaluator that reads your .al source directly and evaluates expressions and procedure calls in-process.

That is the whole point of it. Every other surface answers "what does Business Central do?" by asking Business Central, which costs a container and minutes. Probe answers a narrow slice of the same question in well under a second, with no Docker and no server, so an agent can check a piece of logic as often as it likes.

The cost of a second engine is the risk of the two disagreeing, so probe is built to refuse rather than guess: anything it cannot model exactly (Commit, Random, unsupported CalcFormula kinds, Format format strings) returns a refusal, not an approximation. A differential harness runs the same AL through both engines and turns any disagreement into a failing test. Probe is a fast pre-check, never a replacement for running the real tests.


The nested modules

Area (nested module)PurposeTier
businessdev.ALbuild.CoreConfig, logging, telemetry, licensing, helpers, version math, project config, build order, version stampingFree
businessdev.ALbuild.ContainersArtifacts, Docker container lifecycle, Traefik v3, certificatesFree
businessdev.ALbuild.AppsCompile (al tool / container), publish/install/sign/test, XLIFFFree (except AppSource validation)
businessdev.ALbuild.FeedsFeed providers + the transitive dependency resolver, NuGet packagingFree / Licensed
businessdev.ALbuild.RuntimePackagesBatched / parallel / incremental runtime-package engineLicensed
businessdev.ALbuild.MarketplaceAppSource / Partner Center ingestionLicensed
businessdev.ALbuild.OnPremOn-prem publish/install/upgrade, PTE, DevLicensed
businessdev.ALbuild.EnvironmentsDemo/dev environment provisioningLicensed
businessdev.ALbuild.PipelineLocal pipeline runnerFree

Licensing tiers are enforced at runtime (via Assert-ALbuildLicensed), independent of packaging, every cmdlet ships in the one package, and the licensed ones check on entry. See Licensing & tiers.


Naming conventions

  • Cmdlet nouns use the Bc prefix for Business Central operations (New-BcContainer, Invoke-BcCompiler) and ALbuild for ALbuild's own tooling, configuration, logging and licensing (Get-ALbuildConfig, Write-ALbuildLog, Assert-ALbuildLicensed).
  • Every public cmdlet ships comment-based help; the cmdlet reference pages are generated from it.

Two kinds of configuration

ALbuild deliberately separates per-repository settings from per-machine settings:

albuild.json (project config)Get-/Set-ALbuildConfig (machine config)
ScopePer repository / per app folderPer machine / per user
Committed to source control?YesNo
ControlsCountry, artifact type, BC version, selection, test runner, dependency feedsArtifact/package cache folders, retry, telemetry, licensing
ReferenceProject configurationGet-ALbuildConfig / Set-ALbuildConfig

The machine configuration file (config.json)

The per-machine settings live in a JSON file the module reads automatically (so the CLI, the Azure DevOps tasks and the MCP server all honour it). You normally manage it with cmdlets rather than editing it by hand:

PowerShellCode
Get-ALbuildConfig # the effective configuration Get-ALbuildConfig -Name BcArtifactCacheFolder # a single setting Set-ALbuildConfig -Settings @{ BcArtifactCacheFolder = 'D:\bcartifacts.cache' } -Persist

-Persist writes the file; without it the change lasts only for the current session.

These are machine settings, so they are stored machine-wide by default, in two layers:

LayerFilePrecedence
Machine (-Scope Machine, the default)C:\ProgramData\ALbuild\config.jsonWins
User (-Scope User)%APPDATA%\ALbuild\config.jsonBelow the machine file

Writing the machine file requires an elevated session on Windows, which is deliberate: a build agent's cache location should not be something one account can change out from under another. Without elevation you get an explicit message naming the scope, and can fall back to -Scope User for a developer box.

Why the machine layer exists

The per-user file used to be the only store, and it caused a silent split on build servers: the agent account had one cache folder in its own profile while an administrator on the same machine read the built-in default and saw a completely different cache. Nothing announced the divergence. If you still have a per-user file from that era, remember it now ranks below the machine file.

ALBUILD_CONFIG still names one explicit file and then replaces the layering entirely. It is meant for tests and for pinning a configuration in an isolated run, not as the normal way to configure an agent:

PowerShellCode
$env:ALBUILD_CONFIG = 'C:\ProgramData\ALbuild\config.json' # replaces both layers Set-ALbuildConfig -Settings @{ BcArtifactCacheFolder = 'D:\bcartifacts.cache' } -Persist

It is plain JSON, so you can also drop the file in place directly:

JSONCode
{ "BcArtifactCacheFolder": "D:\\bcartifacts.cache", "ArtifactCacheFolder": "D:\\ALbuild\\artifacts" }

Settings and their defaults:

SettingDefaultPurpose
BaseFolderC:\alb on Windows, ~/.local/share/ALbuild elsewhereRoot for the cache folders below. Windows deliberately uses a short path at the system-drive root: paths inside a BC artifact reach ~245 characters on their own, and the Azure DevOps task runs under Windows PowerShell 5.1, which enforces the 260-character MAX_PATH limit hard. A deeper per-user base intermittently pushed artifact extraction over it.
ArtifactCacheFolder…\ALbuild\artifactsHost-side extracted BC artifacts (compile symbols).
PackageCacheFolder…\ALbuild\packagesDownloaded NuGet / dependency packages.
BcArtifactCacheFolderC:\bcartifacts.cachePersistent cache shared into every container at C:\dl, so the BC artifact is downloaded once per host and reused by later containers. BcContainerHelper-compatible.
ProcessRetryCount / ProcessRetryDelaySeconds3 / 5Retry policy for external processes.
TelemetryEnabled / TelemetryConnectionStringfalse / (empty)Opt-in usage telemetry.
LicensingBaseUrl / LicenseAppId / LicenseGraceDays(service) / (app id) / 14Licensing service for the paid tier (offline grace window in days).

Self-hosted agents: stop re-downloading the artifact

Container creation otherwise downloads the full BC artifact (multiple GB) inside every new container. Set the artifact cache to a roomy, persistent drive once — as a machine environment variable:

Code
ALBUILD_BCARTIFACT_CACHE = D:\bcartifacts.cache

or in config.json (BcArtifactCacheFolder). The first container on the host fills it; every later container reuses it. Precedence: New-BcContainer -ArtifactCacheFolder → ALBUILD_BCARTIFACT_CACHE → the config value (-NoArtifactCache disables it). Microsoft-hosted agents are ephemeral, so there's nothing to reuse — leave the default.

Readable errors everywhere

Every surface reduces failures to a single, clean message via Format-BcErrorMessage (the VS Code extension mirrors it as an ALBUILD-ERROR: summary), instead of dumping a raw PowerShell error. Full detail is always available in the underlying log / output channel.


Platform support

  • Container operations (*-BcContainer, test toolkit, in-container compile/test) require a Windows host with a running Docker engine. They fail fast with a clear message on other platforms, enforced by Test-BcPlatform and Test-BcDocker.
  • Container-less operations: compilation via Microsoft's cross-platform al dotnet tool, dependency resolution, signing, Marketplace/SaaS REST and XLIFF, run on any platform with PowerShell 7.
  • albuild probe needs neither Docker nor PowerShell nor a Business Central installation. It is pure .NET inside the CLI and runs anywhere the CLI runs.
Last modified on