onprem-remote-deploy
Skill28 starsChanged 7 months agoMITView on GitHub
AL app deployment to remote Business Central OnPrem servers. Use when deploying .app files from VS Code to a BC server via WinRM, SSH, SMB, or RDP tsclient-pull when network access is restricted.
In this repository38 other files it gives its agents.
- bc-agent-instructions-debuggerSkill
- bc-al-bug-fixSkill
- bc-al-code-reviewerSkill
- bc-al-project-contextSkill
- bc-api-page-generatorSkill
- bc-api-query-generatorSkill
- bc-approval-workflow-generatorSkill
- bc-attachments-generatorSkill
- skill-batch-processingSkill
- bc-business-events-generatorSkill
- bc-cds-page-generatorSkill
- bc-dataverse-entity-generatorSkill
- bc-dataverse-mapping-generatorSkill
- bc-field-propagationSkill
- bc-install-codeunit-generatorSkill
- bc-isolated-storage-generatorSkill
- bc-job-queueSkill
- bc-manifest-handoff-generatorSkill
- bc-matrix-page-generatorSkill
- bc-number-series-generatorSkill
- bc-pricing-refactorSkill
- bc-report-selection-generatorSkill
- bc-reportsSkill
- bc-setup-table-generatorSkill
- bc-setup-wizard-generatorSkill
- bc-table-dimensions-generatorSkill
- bc-telemetry-generatorSkill
- bc-test-codeunit-generatorSkill
- bc-upgrade-codeunit-generatorSkill
- documentation-bc-analysis-generatorSkill
- documentation-bc-architecture-generatorSkill
- documentation-bc-ccn-generatorSkill
- documentation-bc-md-to-docx-converterSkill
- documentation-bc-phase-plan-generatorSkill
- documentation-bc-release-note-generatorSkill
- documentation-bc-technical-spec-generatorSkill
- documentation-bc-user-story-generatorSkill
- skill-contribution-assistantSkill
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 5 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 29
Ask the user these diagnostic questions and select the strategy:
- Distinctive: line 202
This is the strategy for when EVERYTHING is blocked: Only requires RDP access with drive redirection.
- Very specific: line 252
Use the reusable script template in scripts/Setup-DeployAgent.Template.ps1.
- Distinctive: line 255
All script output messages are in English.
- Very specific: line 289
Use the compact patterns in references/onprem-remote-deploy-reference.md:
- Distinctive: line 292
Add extra package cache for dependent apps
- Distinctive: line 363
When adapting these scripts for a new project, update:
- Very specific: line 381
Never: use -Mode ForceSync on production without explicit approval — it can delete data from removed table fields
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Skill: Deploy to BC OnPrem
Purpose
Deploy AL extension .app files from a local VS Code workspace to a remote Business Central OnPrem server. This skill covers multiple deployment strategies ranked by network access level, from fully blocked (tsclient-pull via RDP) to fully open (WinRM/SSH).
When to Load
This skill should be loaded when:
- A developer needs to publish
.appfiles to a remote BC OnPrem server - WinRM, SSH, or SMB access to the server is blocked or restricted
- A deploy automation script needs to be created or adapted
- The developer has RDP access to the server but no remote PowerShell access
- A deploy agent or scheduled task needs to be configured on a BC server
Architecture Overview
The Problem
Deploying AL extensions to BC OnPrem requires running Publish-NAVApp, Sync-NAVApp, and Start-NAVAppDataUpgrade on the server. When the developer's PC cannot reach the server via WinRM (5985), SSH (22), or SMB admin shares (445/d$), traditional push-based deploys fail.
Decision Matrix: Choose Your Strategy
Ask the user these diagnostic questions and select the strategy:
| Question | How to Test |
| Can you reach port 5985 (WinRM)? | Test-NetConnection -ComputerName <SERVER> -Port 5985 |
| Can you reach port 22 (SSH)? | Test-NetConnection -ComputerName <SERVER> -Port 22 |
| Can you reach port 445 (SMB)? | Test-NetConnection -ComputerName <SERVER> -Port 445 |
Can you access \\SERVER\d$? | Test-Path "\\<SERVER>\d$" |
| Are you in the same domain? | (Get-WmiObject Win32_ComputerSystem).Domain |
| Do you have RDP access? | User knows this |
| Scenario | Strategy | Section |
| WinRM open (5985) + same domain | Strategy A: WinRM Direct | Simplest, no agent needed |
| SSH open (22) | Strategy B: SSH Direct | Needs OpenSSH on server |
| SMB open (445) + admin share or named share works | Strategy C: SMB Push + Remote Trigger | Good if auth works |
| Only RDP access, all other ports blocked | Strategy D: tsclient-pull (RDP Agent) | Works with zero network access |
| None of the above | Strategy E: Manual Deploy | Fallback |
Strategy A: WinRM Direct (Best Case)
Requirements: Port 5985 open, same domain or trusted domain, WinRM enabled on server.
Prerequisites (ask server admin)
# ON THE SERVER (as admin):
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force # or specific IPsDeploy Script Pattern
param(
[string]$ServerName,
[string]$ServerInstance,
[string]$AppPath, # local .app file
[string]$RemotePath, # path on server to copy to
[PSCredential]$Credential
)
# 1. Copy .app to server via SMB
$session = New-PSSession -ComputerName $ServerName -Credential $Credential
Copy-Item $AppPath -Destination $RemotePath -ToSession $session
# 2. Execute BC admin commands remotely
Invoke-Command -Session $session -ScriptBlock {
param($inst, $appPath, $appName, $appVersion, $forceSync)
Import-Module 'C:\Program Files\Microsoft Dynamics 365 Business Central\*\Service\NavAdminTool.ps1' -DisableNameChecking
Publish-NAVApp -SkipVerification -ServerInstance $inst -Path $appPath
if ($forceSync) {
Sync-NAVApp -ServerInstance $inst -Name $appName -Version $appVersion -Mode ForceSync -Force
} else {
Sync-NAVApp -ServerInstance $inst -Name $appName -Version $appVersion
}
$current = Get-NAVAppInfo -ServerInstance $inst -Name $appName |
Where-Object { $_.Version.ToString() -ne $appVersion } |
Sort-Object Version -Descending | Select-Object -First 1
if ($current) {
Start-NAVAppDataUpgrade -ServerInstance $inst -Name $appName -Version $appVersion
Unpublish-NAVApp -ServerInstance $inst -Name $appName -Version $current.Version
} else {
Install-NAVApp -ServerInstance $inst -Name $appName -Version $appVersion
}
} -ArgumentList $ServerInstance, $RemotePath, $AppName, $AppVersion, $ForceSync
Remove-PSSession $sessionAdmin Request Template
We need to enable WinRM on server
<SERVER>to deploy Business Central extensions remotely from VS Code. Please run these commands as administrator on the server:Enable-PSRemoting -ForceRequired inbound port: 5985/TCP from the developer network.
Strategy B: SSH Direct
Requirements: Port 22 open, OpenSSH Server installed on the BC server.
Prerequisites (ask server admin)
# ON THE SERVER (as admin):
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType AutomaticDeploy Script Pattern
param(
[string]$ServerName,
[string]$User, # domain\user
[string]$ServerInstance,
[string]$LocalAppPath,
[string]$RemoteAppDir
)
# 1. Copy .app via SCP
$remotePath = "${User}@${ServerName}:${RemoteAppDir}"
scp $LocalAppPath $remotePath
# 2. Execute BC commands via SSH
$appFile = Split-Path $LocalAppPath -Leaf
$remoteFullPath = "$RemoteAppDir\$appFile"
ssh "${User}@${ServerName}" "powershell -ExecutionPolicy Bypass -Command `"
Import-Module 'C:\Program Files\Microsoft Dynamics 365 Business Central\*\Service\NavAdminTool.ps1' -DisableNameChecking;
Publish-NAVApp -SkipVerification -ServerInstance '$ServerInstance' -Path '$remoteFullPath';
Sync-NAVApp -ServerInstance '$ServerInstance' -Name '$AppName' -Version '$AppVersion';
Start-NAVAppDataUpgrade -ServerInstance '$ServerInstance' -Name '$AppName' -Version '$AppVersion'
`""Admin Request Template
We need to enable SSH on server
<SERVER>to deploy Business Central extensions. Please run these commands as administrator:Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service -Name sshd -StartupType AutomaticRequired inbound port: 22/TCP from the developer network.
Strategy C: SMB Push + Remote Trigger
Requirements: Port 445 open, SMB authentication works (same domain or NTLM accepted).
Diagnostic: Why SMB Might Fail
| Symptom | Cause | Solution |
Test-Path hangs forever | PC in WORKGROUP, server in domain → NTLM auth hangs | Use Strategy D |
| Error 64 "red no disponible" | Admin shares (d$) blocked by GPO | Create a named share |
| Error 5 "acceso denegado" | No permissions on share | Ask admin to grant FullControl |
Works with net use but not Test-Path | Credential caching issue | Map drive first: net use Z: \\server\share /user:domain\user |
Create Named Share (on server)
# ON THE SERVER (as admin):
New-Item -ItemType Directory -Path "D:\BCDeploy" -Force
New-SmbShare -Name "BC_DEPLOY" -Path "D:\BCDeploy" -ChangeAccess "Domain\DeveloperUser"Deploy Pattern
- Copy compiled
.appto\\SERVER\BC_DEPLOY\<Entorno>\via SMB - Copy a trigger script to the same share
- A scheduled task on the server detects the trigger and runs the deploy
This is a hybrid of push (files) and pull (execution). If SMB works for files but not for WinRM, this is the best option.
Strategy D: tsclient-pull (RDP Agent) — Zero Network Access
This is the strategy for when EVERYTHING is blocked. Only requires RDP access with drive redirection.
How It Works
┌─────────────────┐ ┌─────────────────┐
│ Developer PC │ RDP + drives │ BC Server │
│ │ ──────────────────> │ │
│ VS Code │ │ Scheduled Task │
│ Deploy script │ │ _DeployAgent.ps1 │
│ │ │ │
│ _deploy_output/ │ <── tsclient ───── │ reads trigger │
│ TRIGGER.txt │ │ copies .app │
│ *.app │ │ runs deploy │
│ RESULT.txt │ <── writes back ── │ writes result │
└─────────────────┘ └─────────────────┘Flow:
- VS Code compiles
.appfiles locally →_deploy_output/<Entorno>/ - Creates
_DEPLOY_TRIGGER.txtwith metadata - Polls for
_DEPLOY_RESULT.txtevery 5 seconds - Developer connects to server via RDP with drive redirection enabled
- Server agent (scheduled task, runs every 1 min) scans
\\tsclient\c\...\_deploy_output\for trigger - Agent reads trigger, copies
.appfrom tsclient to server local disk - Agent executes
Publish → Sync → DataUpgrade → Unpublish - Agent writes
OKorERRORto_DEPLOY_RESULT.txtvia tsclient - VS Code sees the result → shows LISTO!
RDP Drive Redirection Setup
The developer MUST connect to the server with drive redirection enabled:
Windows Remote Desktop (mstsc.exe):
- Open mstsc → "Show Options" / "Mostrar opciones"
- Tab "Local Resources" / "Recursos locales"
- Click "More..." / "Más..."
- Check "Drives" / "Unidades" ✓
- Connect
Remote Desktop Manager (RDM):
- Right-click connection → Edit / Properties
- Section "Local Resources" → "Drives" → Enable ✓
- Save and reconnect
Royal TS / mRemoteNG / other RDP clients: Similar option under Local Resources → Drive Redirection.
One-Time Setup: Server Agent
Use the reusable script template in scripts/Setup-DeployAgent.Template.ps1.
- It uses generic paths and environment names (
Entorno1,Entorno2). - All script output messages are in English.
- It creates a scheduled task and an agent script without project-specific names.
For full examples and optional variants, read references/onprem-remote-deploy-reference.md.
Deploy Script (runs on developer PC)
The deploy script on the developer PC performs these steps:
- Find alc.exe — searches
~/.vscode/extensions/ms-dynamics-smb.al-*/bin/alc.exe - Bump version — increments 4th component in
app.json(e.g.,22.0.8.180→22.0.8.181) - Compile — runs
alc.exe /project:<AppDir> /packagecachepath:<cache> /out:<output> - Generate server script — creates
_RunDeploy_<Entorno>.ps1with Publish → Sync → DataUpgrade → Unpublish - Write trigger — creates
_DEPLOY_TRIGGER.txtwith metadata (instance, paths, apps) - Poll for result — checks
_DEPLOY_RESULT.txtevery 5 seconds, up to 15 minutes
Key parameters:
-Entorno Entorno1|Entorno2— target environment-SoloAppBase— deploy only base app, skip secondary apps-ForzarSync— useSync-NAVApp -Mode ForceSync -Force(DANGEROUS: can cause data loss)
Trigger format and generated deploy script examples:
See references/onprem-remote-deploy-reference.md for:
- Trigger file schema (
_DEPLOY_TRIGGER.txt) - Generated server script pattern (
_RunDeploy_<Entorno>.ps1) - Generic sample values without customer-specific naming
Compilation: alc.exe Patterns
Finding and running alc.exe
Use the compact patterns in references/onprem-remote-deploy-reference.md:
- Discover latest
alc.exefrom AL extension folder - Build argument arrays with multiple
/assemblyprobingpaths - Add extra package cache for dependent apps
Sync Modes: When to Use What
| Mode | Command | Use When | Risk |
| Normal (default) | Sync-NAVApp -ServerInstance $inst -Name $name -Version $ver | Routine deploys, no breaking schema changes | None — fails safely if incompatible |
| ForceSync | Sync-NAVApp ... -Mode ForceSync -Force | Broken environment, schema pending, destructive schema changes | Can delete table data if fields are removed |
When the Environment is Broken
Error example (localized): environment requires schema synchronization.
Diagnosis:
Get-NAVTenant -ServerInstance '<Instance>' | Format-Table Id, State
# If State = "OperationalWithSyncPending" → needs Sync-NAVTenantFix:
Sync-NAVTenant -ServerInstance '<Instance>' -Mode ForceSync -ForceVersion Downgrade Error
Error: "Cannot synchronize extension X because a newer version has already been synchronized"
Cause: Server has version N+1, you're trying to deploy version N.
Fix: Bump local version higher than server version, then deploy normally.
Troubleshooting
Common Errors and Solutions
| Error | Cause | Fix |
OperationalWithSyncPending | Sync failed mid-deploy | Sync-NAVTenant -Mode ForceSync -Force |
newer version has already been synchronized | Local version < server version | Bump local version above server |
Cannot upgrade because version must be newer | DataUpgrade with same or lower version | Bump version, don't downgrade |
| Trigger not detected by agent | Drive redirection not enabled in RDP | Enable "Drives" in RDP Local Resources |
Test-Path \\server\share hangs forever | PC in WORKGROUP, server in domain | Use Strategy D (tsclient-pull) |
Agent runs but _DEPLOY_RESULT.txt never appears | Scheduled task not running | Check Task Scheduler, verify user and logon trigger |
.Trim() on null | Result file is empty when read | Add null check: if ($raw) { $result = $raw.Trim() } |
Diagnostic Commands (run on server)
# Check all installed apps
Get-NAVAppInfo -ServerInstance '<Instance>' | Sort-Object Name, Version | Format-Table Name, Version, Status -AutoSize
# Check tenant state
Get-NAVTenant -ServerInstance '<Instance>' | Format-Table Id, State
# Check scheduled task
Get-ScheduledTask -TaskName "BC-DeployAgent" | Format-List TaskName, State, LastRunTime, LastTaskResult
# Check if tsclient drives are visible
Test-Path "\\tsclient\c"
Get-ChildItem "\\tsclient\c" -ErrorAction SilentlyContinueCustomization Checklist
When adapting these scripts for a new project, update:
| Setting | Where | Example |
$ServerInstance / $Instancia | Deploy script param | BC-Entorno1 |
$environments | Setup agent script | @('Entorno1', 'Entorno2', 'PROD') |
$deployBasePath | Setup agent script | D:\BCDeploy |
$repoNames | Agent script | @('Al_MyProject', 'Al_OtherProject') |
$taskUser | Setup agent script | DOMAIN\Username |
| NavAdminTool path | Deploy script config | C:\Program Files\Microsoft Dynamics 365 Business Central\220\Service\NavAdminTool.ps1 |
ValidateSet for -Entorno | Deploy script param | 'Entorno1', 'Entorno2', 'PROD' |
| App publisher name | Filename pattern | MyPublisher |
candidatos paths in agent | Agent script | Add your common repo locations |
Security Notes
- Never use
-Mode ForceSyncon production without explicit approval — it can delete data from removed table fields - The scheduled task runs as the logged-in user with Highest privileges — ensure this is a dedicated service account or trusted admin user
- Drive redirection (
tsclient) is only available during an active RDP session — files are NOT accessible when disconnected - The agent does NOT store credentials — it runs in the context of the interactive logon session
.appfiles are copied to a local folder on the server before being published — clean up old.appfiles periodically
References and Scripts
references/onprem-remote-deploy-reference.md: extended examples, trigger schema, and command snippets.scripts/Setup-DeployAgent.Template.ps1: reusable setup script with generic names and English output messages.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

