Containers/New-Container.ps1
<# .Synopsis Creates a new NAV/BC container .Description Creates a new NAV/BC container .Parameter ContainerName Name of the container. Can be provided in the settings.json .Parameter ImageName Not used anymore. Kept for backwards capability .Parameter Version Version to create .Parameter LicenseFile Path to license file. It can be an Uri and then the license will be downloaded .Parameter Credential Credentials to be used for login to the container. Can be provided in settings.json .Parameter Country Country version of the container. Can be provided in settings.json .Parameter SourcePath Defines the source path of the container definitions .Parameter databaseBackup Defines the BAK or bacpac of a database to use to restore into the container .Parameter singleTenant Defines, if the database is single tenant .Parameter alwaysPull Always pull latest version of the docker image .Parameter SetupTestUsers Creates test users in the container after it is created .Parameter SkipTestTool Add this switch if you do not want to install the test tool .Parameter includeCSide Include this switch if you want to have Windows Client and CSide development environment available on the host. This switch will also export all objects as txt for object handling functions unless doNotExportObjectsAsText is set. .Parameter skipBackup Add this switch if you do not want to use database backups from previous containers .Parameter useHyperVIsolation Add this switch if you want to force Hyper V isolation when creating the container .Parameter includeClickOnce Specify the clickonce switch if you want to have a clickonce version of the Windows Client created .Parameter enableTaskScheduler Include this switch if you want to do Enable the Task Scheduler .Parameter isLocal Specify this switch, if you want to use a larger memory limit .Example New-Container -SetupTestUsers #> function New-Container { [CmdletBinding(SupportsShouldProcess, ConfirmImpact="low")] Param( [Parameter(Mandatory = $false)] [string] $ContainerName, [Parameter(Mandatory = $false)] [string] $ImageName, [Parameter(Mandatory = $false)] [string] $Version, [Parameter(Mandatory = $false)] [string] $LicenseFile, [Parameter(Mandatory = $false)] [pscredential] $Credential, [Parameter(Mandatory = $false)] [string] $Country = "", [Parameter(Mandatory = $false)] [string] $SourcePath = (Get-Location), [Parameter(Mandatory = $false)] [string] $ArtifactUrl = "", [Parameter(Mandatory = $false)] [ValidateSet('', 'OnPrem','Sandbox')] [string] $target = "", [Parameter(Mandatory = $false)] [string] $saasToken = "", [Parameter(Mandatory = $false)] [string] $databaseBackup = "", [switch] $singleTenant, [switch] $alwaysPull, [switch] $SetupTestUsers, [switch] $SkipTestTool, [switch] $includeCSide, [switch] $skipBackup, [switch] $useHyperVIsolation, [switch] $includeClickOnce, [switch] $enableTaskScheduler, [switch] $isLocal ) if ($PSCmdlet.ShouldProcess("Container", "This will create a new ")) { Write-Output @' _____ _ _____ _ | __ \ (_) | __ \ | | | |__) | __ ___ _ __ __ _ _ __ _ _ __ __ _ | |__) |_ _ _ __ __ _ _ __ ___ ___| |_ ___ _ __ ___ | ___/ '__/ _ \ '_ \ / _` | '__| | '_ \ / _` | | ___/ _` | '__/ _` | '_ ` _ \ / _ \ __/ _ \ '__/ __| | | | | | __/ |_) | (_| | | | | | | | (_| | | | | (_| | | | (_| | | | | | | __/ || __/ | \__ \ |_| |_| \___| .__/ \__,_|_| |_|_| |_|\__, | |_| \__,_|_| \__,_|_| |_| |_|\___|\__\___|_| |___/ | | __/ | |_| |___/ '@ $context = Get-AzContext if ($context.Subscription.Name -ne "NAV-X") { try { Connect-AzAccount -Identity } catch { if ($null -ne (Get-AzContext -ListAvailable | Where-Object { $_.Subscription.Name -eq "NAV-X" })) { Set-AzContext -Subscription "NAV-X" } else { throw "Cannot connect to the 'NAV-X' Azure subscription" } } } # Image name not used anymore - not changing due to compatibility for now $imageName = $imageName $ContainerName = Get-NewContainerName -SourcePath $SourcePath -ContainerName $ContainerName if ($null -eq $Credential) { $NewCredential = Get-CredentialFromEnvironmentJson -SourcePath $SourcePath $Credential = $NewCredential } $Password = $Credential.Password if ($Country -eq "") { $Country = Get-EnvironmentKeyValue -KeyName "locale" -SourcePath $SourcePath } if ($null -eq $Version -or $Version -eq "") { $Version = Get-AppKeyValue -SourcePath $SourcePath -KeyName "platform" try { $Version = "{0}.{1}" -f ([version]$version).Major, ([version]$Version).Minor if ($Version -lt "12") { $Version = "2018" } } catch { $version = "" } } # there is a bug in the 17.0 container images, so always using 17.1 then if ($Version -eq "17.0") { $Version = "17.1" } if ($Version -eq "NextMinor" -or $Version -eq "NextMajor") { try { $saasToken = Get-Secret -vaultName "nav-x" -secretName "insiderToken" if ($Country -eq "") { $Country = "us" } $artifactUrl = Get-BCArtifactUrl -country $Country -select $Version -sasToken "$saasToken" } catch { throw "Could not retrieve SaaSToken" } } if ($target -eq "") { $tempTarget = Get-AppKeyValue -KeyName "Target" -SourcePath $SourcePath if ($tempTarget -eq "Cloud" -or $tempTarget -eq "" -or $tempTarget -eq "Extension") { $target = 'Sandbox' } else { $target = 'OnPrem' } } if ($target -eq "Sandbox") { $EnvironmentType = "Sandbox" } else { $EnvironmentType = "OnPrem" } if ($EnvironmentType -ne "Sandbox" -and $Version -eq "NextMajor") { $EnvironmentType = "Sandbox" } $dockerImageName = "navx" if ($Version -in @("2016","2017","2018")) { $cu = Get-EnvironmentKeyValue -KeyName "cu" -SourcePath $SourcePath if ($ArtifactUrl -eq "") { $ArtifactUrl = Get-NavArtifactUrl -nav $Version -cu $cu -select Latest -country $Country.ToLower() } if (!$Version.StartsWith('navx:')) { $dockerImageName = "${dockerImageName}:$Version-$cu-$country" } else { $dockerImageName = $Version } if ($LicenseFile -eq "") { $LicenseFile = Get-LicenseFile -Publisher (Get-AppKeyValue -SourcePath $SourcePath -KeyName 'Publisher') -Version $Version } Write-Output "Version: $Version" Write-Output "Cumulative Update: $cu" Write-Output "Country: $Country" } else { $parameters = @{} if ($null -ne $saasToken -and $saasToken -ne "") { $parameters.Add("sasToken", $saasToken) } if ($ArtifactUrl -eq "") { $artifactUrl = Get-BCArtifactUrl -version $Version -select Latest -country $Country.ToLower() -Type $EnvironmentType @parameters } if (!$Version.StartsWith('navx:')) { if ($Version -eq "NextMinor" -or $Version -eq "NextMajor") { if ($artifactUrl.StartsWith("https://bcinsider.azureedge.net/sandbox/")) { $tempVersion = [version]$artifactUrl.Substring(40).SubString(0, $artifactUrl.Substring(40).IndexOf("/")) $Version = ("{0}.{1}" -f $tempVersion.Major, $tempVersion.Minor) } } $dockerImageName = "${dockerImageName}:$Version-$country-$EnvironmentType" } else { $dockerImageName = $Version } Write-Output "Version: $Version" Write-Output "Country: $Country" if ($LicenseFile -eq "") { $LicenseFile = Get-LicenseFile -Publisher (Get-AppKeyValue -SourcePath $SourcePath -KeyName 'Publisher') -Version $Version } } Write-Output "Artifact Url: $artifactUrl" Write-Output "Image: $dockerImageName" [version]$platform = Get-VersionAppJson -SourcePath $SourcePath -ImageName $Version if ($platform.Major -lt 15) { $skipBackup = $true } #disable using backup, since this is currently possibly causing issues as well $skipBackup = $true if (!$skipBackup.IsPresent) { if (!(Test-Path "C:\.backups")) { New-Item -Path "C:\.backups" -ItemType Directory | Out-Null } if ($Country -eq "") { $Country = Get-EnvironmentKeyValue -KeyName "locale" -SourcePath $SourcePath } $bakFolder = (Join-Path "C:\.backups" $platform) $bakFolder = (Join-Path $bakFolder $Country) if (Test-Path (Join-Path $bakFolder "*")) { Write-Output "Backups: Yes" } } else { Write-Output "Backups: No" } $customLicenseFile = Get-FileFromStorage -fileName $LicenseFile $startParameters = @{ accept_eula = $true accept_outdated = $true updateHosts = $true useBestContainerOS = $true doNotExportObjectsToText = $true auth = "NavUserPassword" containerName = $ContainerName Credential = $Credential artifactUrl = $artifactUrl imageName = $dockerImageName licenseFile = $customLicenseFile } $localBackup = "" if ($databaseBackup -ne "") { $localBackup = Get-FileFromStorage -fileName $databaseBackup if ((Split-Path $localBackup -Leaf).Split('.')[-1].ToLower() -eq 'bak') { $startParameters.Add('bakFile', $localBackup) } } if ($useHyperVIsolation.IsPresent) { $startParameters.Add('isolation', 'HyperV') } if (Get-EnvironmentKeyValue -KeyName "usePremium" -SourcePath $SourcePath) { $startParameters.Add('assignPremiumPlan', $true) } $isStandardTesting = Get-UseStandardTest -MajorVersion $platform.Major -SourcePath $SourcePath if (!($SkipTestTool.IsPresent)) { if ($isStandardTesting) { $startParameters.Add('includeTestToolkit', $true) } } if ($isLocal.IsPresent -or $databaseBackup -ne "") { $startParameters.Add('memoryLimit', '12GB') } else { $startParameters.Add('memoryLimit', '8GB') } if ($platform.Major -le 14) { $startParameters.Add('shortcuts', 'DesktopFolder') # Turn off symbol loading for now # $startParameters.Add('enableSymbolLoading', $false) } else { $startParameters.Add('shortcuts', 'None') $startParameters.Add('enableSymbolLoading', $false) } if ($alwaysPull.IsPresent) { $startParameters.Add('alwaysPull', $true) } if ($includeCSide.IsPresent -or $includeClickOnce.IsPresent) { $startParameters.Add('includeCSide', $true) $startParameters.Add('clickonce', $true) } if (!$skipBackup.IsPresent) { $startParameters.Add('bakFolder', $bakFolder) $startParameters.Add('additionalParameters', @("-v C:\.backups:C:\.backups")) } if ($enableTaskScheduler.IsPresent) { $startParameters.Add('enableTaskScheduler', $true) } else { $startParameters.Add('enableTaskScheduler', $false) } if ($singleTenant.IsPresent) { $startParameters.Add('multitenant', $false) } # turn off health check for CPU usage $startParameters.Add('doNotCheckHealth', $true) # DNS fix $startParameters.Add('dns', '8.8.8.8') # Containers are now created as multitenant. Turn this off for anything but the newer versions (greater or equal BC15) if ($platform.Major -lt 15) { $startParameters.Add('multitenant', $false) } if ($SetupTestUsers.IsPresent) { $startParameters.Add('finalizeDatabasesScriptBlock', {Setup-BcContainerTestUsers -containerName $ContainerName -password $Password -credential $Credential}) } $startParameters.GetEnumerator() | ForEach-Object { Write-Output ("{0}: {1}" -f $_.Key, $_.Value) } Write-Output @' _____ _ _ _____ _ _ / ____| | | (_) / ____| | | (_) | | _ __ ___ __ _| |_ _ _ __ __ _ | | ___ _ __ | |_ __ _ _ _ __ ___ _ __ | | | '__/ _ \/ _` | __| | '_ \ / _` | | | / _ \| '_ \| __/ _` | | '_ \ / _ \ '__| | |____| | | __/ (_| | |_| | | | | (_| | | |___| (_) | | | | || (_| | | | | | __/ | \_____|_| \___|\__,_|\__|_|_| |_|\__, | \_____\___/|_| |_|\__\__,_|_|_| |_|\___|_| __/ | |___/ '@ Flush-ContainerHelperCache -cache bcartifacts -keepDays 7 New-BcContainer @startParameters if ($localBackup -ne "") { if ((Split-Path $localBackup -Leaf).Split('.')[-1].ToLower() -eq 'bacpac') { $containerPath = Join-Path "C:\Run\My" (Split-Path $localBackup -Leaf) Copy-FileToBcContainer -containerName $ContainerName -localPath $localBackup -containerPath $containerPath Invoke-ScriptInBcContainer -containerName $ContainerName -scriptblock { Param($containerPath, $database) Invoke-SqlCmd ` -Query "EXEC sp_configure 'contained', 1;RECONFIGURE;" ` -ServerInstance "localhost\SQLEXPRESS" Restore-BacpacWithRetry ` -bacpac $containerPath ` -databasename $database ` -maxattempts 1 } -argumentList $containerPath, $ContainerName $tenantId = 'default' if ($singleTenant.IsPresent) { Invoke-ScriptInBcContainer -containerName $ContainerName -scriptblock { Param($database, $serverInstance) Set-NAVServerConfiguration ` -ServerInstance $serverInstance ` -KeyName DatabaseName ` -KeyValue $database Restart-NAVServerInstance ` -ServerInstance $serverInstance Sync-NavTenant ` -ServerInstance $serverInstance ` -Force } -argumentList $ContainerName, ((Get-BcContainerServerConfiguration -ContainerName $ContainerName).ServerInstance) } else { $tenantId = $ContainerName Invoke-ScriptInBCContainer -containerName $containerName -scriptblock { Param($database, $tenantId, $serverInstance) Mount-NavTenant ` -ServerInstance $serverInstance ` -id $tenantId ` -databasename $database ` -databaseserver localhost ` -databaseinstance SQLEXPRESS ` -EnvironmentType Sandbox ` -OverwriteTenantIdInDatabase ` -Force Sync-NavTenant ` -ServerInstance $ServerInstance ` -Tenant $tenantId ` -Force } -argumentList $ContainerName, $tenantId, ((Get-BcContainerServerConfiguration -ContainerName $ContainerName).ServerInstance) } New-BcContainerBcUser ` -containerName $containerName ` -Credential $Credential ` -ChangePasswordAtNextLogOn:$false ` -PermissionSetId SUPER ` -tenant $tenantId } } # Write-Output @' # # _____ _ _ _ _ # |_ _| | | (_) | | (_) # | | _ __ ___ _ __ ___ _ __| |_ _ _ __ __ _ | | _ ___ ___ _ __ ___ ___ # | | | '_ ` _ \| '_ \ / _ \| '__| __| | '_ \ / _` | | | | |/ __/ _ \ '_ \/ __|/ _ \ # _| |_| | | | | | |_) | (_) | | | |_| | | | | (_| | | |____| | (_| __/ | | \__ \ __/ # |_____|_| |_| |_| .__/ \___/|_| \__|_|_| |_|\__, | |______|_|\___\___|_| |_|___/\___| # | | __/ | # |_| |___/ # #'@ # # Import-BcContainerLicense -containerName $ContainerName -licenseFile $customLicenseFile -restart if ($platform.Major -le 11) { Write-Output @' _ _ _ _ _ _ _ _____ | | | | | | | | (_) /\ | | | | |_ _| | | | |_ __ __| | __ _| |_ _ _ __ __ _ / \ __| | __| |______| | _ __ ___ | | | | '_ \ / _` |/ _` | __| | '_ \ / _` | / /\ \ / _` |/ _` |______| | | '_ \/ __| | |__| | |_) | (_| | (_| | |_| | | | | (_| | / ____ \ (_| | (_| | _| |_| | | \__ \ \____/| .__/ \__,_|\__,_|\__|_|_| |_|\__, | /_/ \_\__,_|\__,_| |_____|_| |_|___/ | | __/ | |_| |___/ '@ Invoke-ScriptInBcContainer -containerName $ContainerName -scriptblock { $sharedPath = "C:\navpfiles\" + $args[0] + "0\RoleTailored Client\" $programPath = "C:\Program Files (x86)\Microsoft Dynamics NAV\" + $args[0] + "0\RoleTailored Client\" Write-Output "Copying add-in files to shared files" Copy-Item "C:\Program Files\Microsoft Dynamics NAV\*\Service\Add-Ins\" $sharedPath -recurse -Force Write-Output "Copying add-in files to program files" Copy-Item "C:\Program Files\Microsoft Dynamics NAV\*\Service\Add-Ins\" $programPath -Recurse -Force } -ArgumentList $platform.Major } if (!($SkipTestTool.IsPresent)) { if (!$isStandardTesting) { Write-Output @' _____ _ _______ _ _ |_ _| | | |__ __| | | (_) | | _ __ ___ _ __ ___ _ __| |_ | | ___ ___| |_ _ _ __ __ _ | | | '_ ` _ \| '_ \ / _ \| '__| __| | |/ _ \/ __| __| | '_ \ / _` | _| |_| | | | | | |_) | (_) | | | |_ | | __/\__ \ |_| | | | | (_| | |_____|_| |_| |_| .__/ \___/|_| \__| |_|\___||___/\__|_|_| |_|\__, | | | __/ | |_| |___/ '@ Import-Testing -containerName $ContainerName } } if ($platform.Major -ge 15) { Wait-ForTenantReady -containerName $ContainerName -tenant "default" } } } Export-ModuleMember New-Container |