Functions/AppManagement/Publish-BcSystem.ps1
<#
.Synopsis Installs Microsoft System app and (optional) Microsoft System Application into the Microsoft Dynamics 365 Business Central database. .Description Installs Microsoft System app and (optional) Microsoft System Application into the Microsoft Dynamics 365 Business Central database. Proces flow: Loads Business Central Powershell modules corresponding to ServiceTier version Converts the Business Central database Starts the Business Central Server Instance (optional) Unpublishes previous Microsoft System Symbols Publishes the System Symbols for the new platform release Syncs the changes to SQL with Sync-NAVTenant (optional) Uninstalls previous Microsoft System Application (optional) Publish the new System Application Unpublish previous System Application(s) Install the new System Application .Example Publish-BcSystem -ServerInstance 'BC170' -SystemPath 'C:\System\Microsoft_System.app' .Example Publish-BcSystem -ServerInstance 'BC170' -SoftwarePath (Join-Path $PSScriptRoot 'System') -RemovePreviousVersions .Example Publish-BcSystem ` -ServerInstance 'BC170' ` -SystemPath 'C:\System\Microsoft_System.app' ` -SystemApplicationPath 'C:\System\Microsoft_System Application.app' ` -RemovePreviousVersions #> function Publish-BcSystem { [CmdletBinding(DefaultParameterSetName='SoftwarePath')] Param( # Publishes the symbols and extensions into the BC database attached to the supplied serverinstance. # E.g. 'BC170' or @('BC170Test','BC170Dev') [Parameter(Mandatory=$true)] [string[]] $ServerInstance, # The path to the folder containing the system app and system application. # The folder should contain max one system app and one system application. # E.g. 'C:\system\' [Parameter(ParameterSetName='SoftwarePath', Mandatory = $true)] [string] $SoftwarePath, # FullName path to the Microsoft System you want to publish # E.g. 'C:\System\Microsoft_System.app' [Parameter(ParameterSetName='DirectPath', Mandatory = $true)] [string] $SystemPath, # FullName path to the Microsoft System Application you want to publish # E.g. 'C:\System\Microsoft_System_Application.app' [Parameter(ParameterSetName='DirectPath', Mandatory = $false)] [string] $SystemApplicationPath, # Location to write the logfile to. Default location is '?:\ProgramData\4ps\bcsystemdeployment'. # E.g. 'C:\BcInstallation\Log' [string] $LogFilePath = (Join-Path -Path $env:ProgramData -ChildPath '4ps\bcsystemdeployment'), # The customers Business Central software license file. E.g. 'C:\License\CustomerLicense.flf' # Important note for multi-tenant environments: When there are multiple tenants mounted on the serverinstance # and with the $Tenants parameter there are zero, two or more tenants specified; the license will be installed in # multiple tenants. If every tenant has it's own license, don't use this functionality. Upload it seperately. [string] $LicensePath, # When enabled it will removes all previous system symbols and system applications published in the database [switch] $RemovePreviousVersions ) " _____ _ _ _ _ ____ _____ _ | __ \ | | | (_) | | | _ \ / ____| | | | |__) | _| |__ | |_ ___| |__ | |_) | ___ | (___ _ _ ___| |_ ___ _ __ ___ | ___/ | | | '_ \| | / __| '_ \ | _ < / __| \___ \| | | / __| __/ _ \ '_ `` _ \ | | | |_| | |_) | | \__ \ | | | | |_) | (__ ____) | |_| \__ \ || __/ | | | | | |_| \__,_|_.__/|_|_|___/_| |_| |____/ \___| |_____/ \__, |___/\__\___|_| |_| |_| __/ | 4PS v{0} |___/ " -f $MyInvocation.MyCommand.Module.Version | Write-Host # Validate PowerShell version if (-not $PSVersionTable.PSVersion.Major -ge '5') { $msg = 'Powershell 5.0 or higher required to continue. Current version is {0}.{1}' -f ` $PSVersionTable.PSVersion.Major, $PSVersionTable.PSVersion.Minor throw $msg } 'PowerShell version {0} is compatible.' -f $PSVersionTable.PSVersion.ToString() | Write-Host # Validate if PowerShell is started as administrator $windowsIdentity = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()) $elevated = ($windowsIdentity.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) if (-not $elevated) { $msg = 'You need administrative privileges to deploy Business Central system (application). ' $msg += 'Start the script in a Powershell session launched as administrator.' throw $msg } 'Script is executed as administrator.' | Write-Host # Validate if script is executed in a 64 bit process. if(-not [Environment]::Is64BitProcess){ $msg = 'This script needs to be executed as a 64 bit process. Current process is x86 (32bit).' throw $msg } 'Session is 64 bit.' | Write-Host try{ Stop-Transcript }catch{} # Create logfile $logFile = Join-Path -Path $LogFilePath -ChildPath ('{0}_{1}.log' -f (Get-Date).ToString('yyyy-MM-dd_HH.mm.ss'), $ServerInstance[0]) New-Item -Path $logFile -ItemType File -Force | Out-Null # Remove old logfiles (keep max 10 logfiles) $LogFiles = Get-ChildItem $LogFilePath -File -Filter ('*_{0}.log' -f $ServerInstance[0]) if($LogFiles.Count -gt 10){ $Exclude = $LogFiles | Sort-Object -Property CreationTime -Descending | Select-Object -First 10 Get-ChildItem (Split-Path $logFile -Parent) -Exclude $Exclude | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } Start-Transcript -path $logFile -append ### Script starts from here $SystemId = '8874ed3a-0643-4247-9ced-7a7002f7135d' $SystemAppId = '63ca2fa4-4f03-4f2b-a480-172fef340d3f' $TaskStartTime = Write-StartProcessLine -StartLogText 'Importing Business Central PowerShell modules..' Import-BcModule ` -ServerInstance $ServerInstance[0] ` -ManagementModule ` -AppManagementModule ` -Force Write-EndProcessLine -TaskStartTime $TaskStartTime $TaskStartTime = Write-StartProcessLine -StartLogText 'Validating software path..' if([string]::IsNullOrEmpty($SoftwarePath) -eq $false){ if((Test-Path $SoftwarePath)){ 'Software path exists: ''{0}''' -f $SoftwarePath | Write-Host $AppFiles = Get-ChildItem -Path $SoftwarePath -Filter '*.app' $AppFiles | ForEach-Object { $AppGuid = (Get-NAVAppInfo -Path $_.FullName).AppId.Value.Guid if($AppGuid -eq $SystemId){ $SystemPath = $_.FullName 'System symbols found on path: {0}' -f $_.FullName | Write-Host } if($AppGuid -eq $SystemAppId){ $SystemApplicationPath = $_.FullName 'System Application found on path: {0}' -f $_.FullName | Write-Host } } } } # Write app details to host Get-NAVAppInfo -Path $SystemPath | Select-Object -Property Name, AppId, Version | Format-List if([string]::IsNullOrEmpty($SystemApplicationPath) -eq $false){ Get-NAVAppInfo -Path $SystemApplicationPath | Select-Object -Property Name, AppId, Version | Format-List } Write-EndProcessLine -TaskStartTime $TaskStartTime foreach ($ServerInstanceName in $ServerInstance){ $TaskStartTime = Write-StartProcessLine -StartLogText ('Starting deployment to ServerInstance ''{0}''..' -f $ServerInstanceName) $ServerInstanceConfig = Get-BCServerInstance -ServerInstance $ServerInstanceName $SubTaskStartTime = Write-StartProcessLine -StartLogText 'Convert the Business Central database..' "Invoke-NAVApplicationDatabaseConversion -DatabaseServer '{0}' -DatabaseName '{1}' -Force" -f $ServerInstanceConfig.SqlInstance, $ServerInstanceConfig.DatabaseName | Write-Host Invoke-NAVApplicationDatabaseConversion ` -DatabaseServer $ServerInstanceConfig.SqlInstance ` -DatabaseName $ServerInstanceConfig.DatabaseName ` -Force 'Starting ServerInstance ''{0}''' -f $ServerInstanceName | Write-Host Set-NAVServerInstance ` -ServerInstance $ServerInstanceName ` -Start ` -ErrorAction Stop Wait-BcServerInstanceMountingTenants ` -ServerInstance $ServerInstanceName Write-EndProcessLine -TaskStartTime $SubTaskStartTime $SubTaskStartTime = Write-StartProcessLine -StartLogText 'Deploy Microsoft System Symbols..' if([string]::IsNullOrEmpty($LicensePath) -eq $false ){ $TaskStartTime = Write-StartProcessLine -StartLogText 'Importing Business Central license file..' Import-NAVServerLicense ` -ServerInstance $ServerInstanceName ` -LicenseFile $LicensePath ` -Database 'NavDatabase' ` -WarningAction SilentlyContinue Write-EndProcessLine -TaskStartTime $TaskStartTime } if($RemovePreviousVersions){ "Get-NAVAppInfo -ServerInstance '{0}' -SymbolsOnly" -f $ServerInstanceName | Write-Host Get-NAVAppInfo ` -ServerInstance $ServerInstanceName ` -SymbolsOnly | Unpublish-NAVApp } "Publish-NAVApp -ServerInstance '{0}' -Path '{1}' -PackageType SymbolsOnly" -f $ServerInstanceName, $SystemPath | Write-Host Publish-NAVApp ` -ServerInstance $ServerInstanceName ` -Path $SystemPath ` -PackageType SymbolsOnly "Sync-NAVTenant -ServerInstance '{0}' -Mode Sync -Force" -f $ServerInstanceName | Write-Host Sync-NAVTenant ` -ServerInstance $ServerInstanceName ` -Mode Sync ` -Force Write-EndProcessLine -TaskStartTime $SubTaskStartTime if([string]::IsNullOrEmpty($SystemApplicationPath) -eq $false){ $SubTaskStartTime = Write-StartProcessLine -StartLogText 'Deploy Microsoft System Application..' 'Uninstall previous Microsoft System Application' | Write-Host Get-NAVTenant -ServerInstance $ServerInstanceName | ForEach-Object { Get-NAVAppInfo ` -ServerInstance $_.ServerInstance ` -Id $SystemAppId ` -Publisher 'Microsoft' | Uninstall-NAVApp -Tenant $_.Id -Force } 'Publish the new Microsoft System Application' | Write-Host "Publish-NAVApp -ServerInstance '{0}' -Path '{1}' -SkipVerification" -f $ServerInstanceName, $SystemApplicationPath | Write-Host Publish-NAVApp ` -ServerInstance $ServerInstanceName ` -Path $SystemApplicationPath ` -SkipVerification if($RemovePreviousVersions){ 'Unpublish previous System Application(s)' | Write-Host Get-NAVAppInfo ` -ServerInstance $ServerInstanceName ` -Id $SystemAppId ` -Publisher 'Microsoft' | Where-Object -Property Version -ne $(Get-NAVAppInfo -Path $SystemApplicationPath).Version | Unpublish-NAVApp } 'Install the new published System Application' Get-NAVTenant -ServerInstance $ServerInstanceName | ForEach-Object { Get-NAVAppInfo ` -ServerInstance $_.ServerInstance ` -Id $SystemAppId | Sync-NAVApp -Mode ForceSync -Tenant $_.Id -Force Get-NAVAppInfo ` -ServerInstance $_.ServerInstance ` -Id $SystemAppId | Start-NAVAppDataUpgrade -Tenant $_.Id } Write-EndProcessLine -TaskStartTime $SubTaskStartTime } Write-EndProcessLine -TaskStartTime $TaskStartTime } } Export-ModuleMember -Function Publish-BcSystem |