Modules/businessdev.ALbuild.Containers/Public/New-BcContainer.ps1
|
function New-BcContainer { <# .SYNOPSIS Creates a Business Central Docker container from an artifact. .DESCRIPTION Pulls the Business Central generic image (if needed) and runs a container configured for the given artifact URL using the generic image's environment contract (accept_eula, artifactUrl, Auth, username, password, licenseFile). Requires a Windows host with a running Docker engine; on other platforms it fails with a clear message. After starting, it waits for the container to report ready unless -NoWait is specified. .PARAMETER Name Container name. .PARAMETER ArtifactUrl The artifact URL (see Find-BcArtifactUrl). Required unless -Type/-Country/-Version are used. .PARAMETER Credential Credential for the container's admin user (used with UserPassword auth). .PARAMETER Auth Authentication model: UserPassword (default), NavUserPassword, Windows or AAD. .PARAMETER ImageName The generic image to base the container on. .PARAMETER MemoryLimit Optional memory limit (e.g. '8G'). Defaults to '8G' when not specified: Business Central's start script requires at least 3 GB, and under hyperv isolation (the Docker Desktop default on Windows client hosts) this value sizes the container VM, so leaving it unset makes the container exit immediately with "At least 3Gb memory needs to be available to the Container". .PARAMETER Isolation Container isolation: process or hyperv (default: let Docker decide). .PARAMETER LicenseFile Optional license file (path or URL) passed to the image. .PARAMETER Labels Additional Docker labels (hashtable). .PARAMETER PublishPorts Ports to publish (docker --publish values). .PARAMETER EnvironmentVariables Additional environment variables (hashtable) merged into the image contract. .PARAMETER AdditionalArguments Extra raw 'docker run' arguments. .PARAMETER NoWait Do not wait for the container to become ready. .PARAMETER DockerExecutable The Docker executable to use (default 'docker'). .EXAMPLE $cred = Get-Credential New-BcContainer -Name bld -ArtifactUrl (Find-BcArtifactUrl -Country w1 -Select Latest) -Credential $cred .OUTPUTS PSCustomObject describing the container. #> [CmdletBinding(SupportsShouldProcess)] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Name, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $ArtifactUrl, [pscredential] $Credential, [ValidateSet('UserPassword', 'NavUserPassword', 'Windows', 'AAD')] [string] $Auth = 'UserPassword', [string] $ImageName = 'mcr.microsoft.com/businesscentral:ltsc2022', [string] $MemoryLimit, [ValidateSet('', 'process', 'hyperv')] [string] $Isolation = '', [string] $LicenseFile, [hashtable] $Labels = @{}, [string[]] $PublishPorts = @(), [hashtable] $EnvironmentVariables = @{}, [string[]] $AdditionalArguments = @(), [switch] $NoWait, [string] $DockerExecutable = 'docker' ) Write-ALbuildLog "Creating Business Central container '$Name' from artifact '$ArtifactUrl' with image '$ImageName'..." Test-BcPlatform -Require | Out-Null Test-BcDocker -DockerExecutable $DockerExecutable -Require | Out-Null # BC's start script enforces a >= 3 GB minimum; default to 8 GB so the container starts under # any isolation mode (under hyperv this value sizes the VM, not just a cap). Overridable. if ([string]::IsNullOrWhiteSpace($MemoryLimit)) { $MemoryLimit = '8G' } $containerEnv = @{ 'accept_eula' = 'Y' 'artifactUrl' = $ArtifactUrl 'Auth' = $Auth } if ($Credential) { $containerEnv['username'] = $Credential.UserName $containerEnv['password'] = $Credential.GetNetworkCredential().Password } foreach ($key in $EnvironmentVariables.Keys) { $containerEnv[$key] = $EnvironmentVariables[$key] } if (-not $PSCmdlet.ShouldProcess($Name, "Create Business Central container from $ArtifactUrl")) { return } # Remove any pre-existing container of the same name so a re-run does not fail with a name # conflict ('The container name "/X" is already in use'). 'docker container inspect' exits 0 when # the container exists and 1 when it does not. $existing = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -SuccessExitCodes @(0, 1) -Arguments @('container', 'inspect', $Name) if ($existing.ExitCode -eq 0) { Write-ALbuildLog "Container '$Name' already exists; removing it before recreating." Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -Arguments @('rm', '--force', $Name) | Out-Null } # Bind-mount a host folder to C:\run\my so files (apps, packages) can be shared into the # container by writing to the host side - 'docker cp' is not supported against a running # hyperv-isolated container (the Docker Desktop default on Windows client hosts). $hostShare = Get-BcContainerHostShare -Name $Name if (Test-Path -LiteralPath $hostShare) { Remove-Item -LiteralPath $hostShare -Recurse -Force -ErrorAction SilentlyContinue } New-Item -ItemType Directory -Force -Path $hostShare | Out-Null $volumes = @("$($hostShare):C:\run\my") # A local license file must be made available *inside* the container - the host path (e.g. a # OneDrive folder) does not exist in the container, so the start script reports 'License File not # found'. Stage it into the bind-mounted share and reference the in-container path (URLs pass # through unchanged for the container to download). if ($LicenseFile) { $containerEnv['licenseFile'] = Resolve-BcContainerLicense -LicenseFile $LicenseFile -HostShare $hostShare } Write-ALbuildLog "Pulling image $ImageName ..." Invoke-BcDocker -DockerExecutable $DockerExecutable -RetryCount 5 -RetryDelaySeconds 15 -Arguments @('pull', $ImageName) | Out-Null $runArgs = Get-BcContainerRunArguments -ImageName $ImageName -Name $Name -EnvironmentVariables $containerEnv ` -MemoryLimit $MemoryLimit -Isolation $Isolation -PublishPorts $PublishPorts -Labels $Labels ` -Volumes $volumes -AdditionalArguments $AdditionalArguments -Detach $true Write-ALbuildLog "Creating container $Name ..." Invoke-BcDocker -DockerExecutable $DockerExecutable -Arguments $runArgs | Out-Null if (-not $NoWait) { Write-ALbuildLog "Waiting for container $Name to become ready (first run downloads the artifact; this can take several minutes)..." Wait-BcContainerReady -Name $Name -DockerExecutable $DockerExecutable | Out-Null } return (Get-BcContainer -Name $Name -DockerExecutable $DockerExecutable) } |