Business Central (BC) is a powerful ERP solution, and running it in a Docker container can simplify deployment and management. This guide provides a PowerShell script to create a Business Central container with various configurations.
Prerequisites
- Docker installed and running
- PowerShell with administrative privileges
- Business Central Container Helper module installed
- A valid Business Central license file
- Access to the internet for downloading images
PowerShell Script
Microsoft.PowerShell_profile.ps1
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
# Admin check with module loading logic
$IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator
)
if ($IsAdmin) {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Write-Host "ADMIN MODE ⚡" -ForegroundColor Green
Import-Module BcContainerHelper -Force
} else {
Write-Host "User Mode 🔒" -ForegroundColor Yellow
# Load limited functionality here if needed
}
<# Create-Instance Function Improvements #>
function Add-BcContainer {
param (
[Parameter(Mandatory)]
[string]$name,
[Parameter(Mandatory)]
[ValidateScript({ Test-Path $_ -PathType Leaf })]
[string]$license,
[ValidateSet('Latest', 'First', 'All', 'Closest')]
[string]$select = 'Latest',
[ValidateSet('OnPrem', 'SandBox')]
[string]$type = 'OnPrem',
[ValidateScript({ Test-Path $_ -PathType Leaf })]
[string]$bakFile = $null,
[ValidateScript({ $_ -match '^\d{1,2}\.\d{1,2}\.\d{1,2}\.\d{1,2}$' })]
[string]$version = $null,
[string]$country = 'w1',
[ValidateSet('Customer', 'Internal')]
[string]$artifactType = 'Internal',
# Features
[switch]$includeTest = $false,
[switch]$ssl = $false,
[switch]$expose = $false,
# External Database
[string]$db_server = 'host.docker.internal',
[string]$db_name = $null,
[string]$db_user = 'DockerUser',
[string]$db_password = 'P@@sword1',
[string]$db_instance = $null,
# Ports
[int]$webPort = $null,
[int]$devPort = $null,
# Network
[string]$network = $null
)
# Check Docker is running
if ((Get-Service -Name "Docker" -ErrorAction SilentlyContinue).Status -ne 'Running') {
Write-Host "Docker is not running" -ForegroundColor Red
return
}
$defaultPorts = $null
if ($artifactType -eq 'Internal') {
$defaultPorts = @{
web = 8020
dev = 7020
}
} elseif ($artifactType -eq 'Customer') {
$defaultPorts = @{
web = 9020
dev = 6020
}
}
function Get-UsedPorts {
try {
docker ps --format '{{.Ports}}' | ForEach-Object {
if ($_ -match '(\d+)->\d+/tcp') { [int]$matches[1] }
} | Where-Object { $_ }
} catch {
Write-Host "Error checking used ports: $_" -ForegroundColor Red
return @()
}
}
function Find-AvailablePorts {
param (
[int]$initialWebPort,
[int]$initialDevPort,
[int]$increment = 5,
[int]$maxAttempts = 20
)
$usedPorts = Get-UsedPorts
$portDelta = $initialWebPort - $initialDevPort
for ($i = 0; $i -lt $maxAttempts; $i++) {
$currentWeb = $initialWebPort + ($i * $increment)
$currentDev = $currentWeb - $portDelta
if ($currentWeb -notin $usedPorts -and $currentDev -notin $usedPorts) {
return @{ Web = $currentWeb; Dev = $currentDev }
}
}
return $null
}
if (-not $IsAdmin) {
Write-Host "You need to run this function as an Administrator" -ForegroundColor Red
return
}
# Check if BC Container Helper module is loaded
if (-not (Get-Module -Name BcContainerHelper)) {
Write-Host "BC Container Helper module is not loaded" -ForegroundColor Red
return
}
$db_credentials = @{
server = $db_server
database = $db_name
user = $db_user
password = $db_password
instance = $db_instance
}
$artifactUrl = @{
country = $country
select = $select
type = $type
}
if ($version) {
$artifactUrl['version'] = $version
}
$containerParams = @{
accept_eula = $true
updateHosts = $true
multitenant = $false
containerName = $name.ToLower()
credential = New-Object pscredential 'admin', (ConvertTo-SecureString 'P@@sword1' -AsPlainText -Force)
auth = 'UserPassword'
artifactUrl = Get-BcArtifactUrl @artifactUrl
imageName = $name.ToLower()
licenseFile = $license
isolation = 'hyperv'
}
if ($network) {
$containerParams += @{
network = $network
}
}
$usedPorts = Get-UsedPorts
# Find available ports
$availablePorts = Find-AvailablePorts -initialWebPort $defaultPorts['web'] -initialDevPort $defaultPorts['dev']
if ($null -eq $availablePorts) {
Write-Host "No available ports found" -ForegroundColor Red
return
}
if ($expose) {
$webServicePort = $availablePorts['web']
$developerServicePort = $availablePorts['dev']
if ($webPort) {
$webServicePort = $webPort
}
if ($devPort) {
$developerServicePort = $devPort
}
$soapPort = $webServicePort + 2
$odataPort = $webServicePort + 3
$containerParams += @{
webClientPort = $webServicePort
developerServicesPort = $developerServicePort
soapServicesPort = $soapPort
odataServicesPort = $odataPort
publishPorts = @($webServicePort, $developerServicePort, $soapPort, $odataPort)
}
}
if ($bakFile) {
$containerParams['bakFile'] = $bakFile
}
if ($includeTest) {
$containerParams['includeTestToolkit'] = $true
}
if ($ssl) {
$containerParams['useSSL'] = $true
}
# Validate DB credentials when one is set
if ($db_credentials['database']) {
if ($containerParams['bakFile']) {
$containerParams['bakFile'] = $null
Write-Host "Bak file is not supported when using external database" -ForegroundColor Yellow
}
$containerParams += @{
databaseServer = $db_credentials['server']
databaseInstance = $db_credentials['instance']
databaseName = $db_credentials['database']
databaseCredential = New-Object pscredential $db_credentials['user'], (ConvertTo-SecureString $db_credentials['password'] -AsPlainText -Force)
}
}
# Display all container parameters
Write-Host "Container parameters:" -ForegroundColor Yellow
foreach ($key in $containerParams.Keys) {
Write-Host "$key : $($containerParams[$key])" -ForegroundColor Cyan
}
# Ask for confirmation
if ((Read-Host "`nProceed with creation? (Y/N)") -eq 'Y') {
try {
New-BcContainer @containerParams
Write-Host "Container $name created successfully!" -ForegroundColor Green
} catch {
Write-Host "Creation failed: $_" -ForegroundColor Red
}
} else {
Write-Host "Operation cancelled" -ForegroundColor Yellow
}
}