Public/New-UpgradeInitializationScript.ps1

# https://patorjk.com/software/taag/#p=display&h=1&f=Big&t=GetVariables

enum UpgradeMode
{
  LS_14_174_18 = 1
  LS_14_18_TablesRenamingInV18 = 2
  LS_14_18_TablesRenamingInV14 = 3
}

function New-UpgradeInitializationScript {
    param (
        [string] $ConfigFile,
        [switch] $IncludeCustomizationAppPlaceholders
    )

    $loadFromConfigFile = $ConfigFile
    $includeCustomizationAppPlaceholders = $IncludeCustomizationAppPlaceholders

    $upgradeMode = [UpgradeMode]::LS_14_18_TablesRenamingInV14

    if ($upgradeMode -eq [UpgradeMode]::LS_14_174_18) {
        throw "Upgrading from LS 14.2 -> LS 17.4 -> LS 18 is not supported."
    }

    $loadingFromConfigFile = $loadFromConfigFile -ne ""
    $minimumToLSVersion = "21.0"
    $country = "w1"

    Clear-Host

    $pshost = Get-Host
    $pswindow = $pshost.UI.RawUI
    $minWidth = 150
    
    if (($pswindow.BufferSize) -and ($pswindow.WindowSize) -and ($pswindow.WindowSize.Width -lt $minWidth)) {
        $buffersize = $pswindow.BufferSize
        $buffersize.width = $minWidth
        try {
            $pswindow.buffersize = $buffersize
        }
        catch {}
        
        $newsize = $pswindow.windowsize
        $newsize.width = $minWidth
        try {
            $pswindow.windowsize = $newsize
        }
        catch {}
    }
    
    $errorActionPreference = "Stop"
    
    $script:wizardStep = 0
    $script:acceptDefaults = $false

    $Step = @{
        "GetVariables"              = 0
        "SqlServer"                 = 5
        "Database"                  = 10
        "FromLSVersion"             = 15
        "FromBCVersion"             = 20
        "FromBCVersion2"            = 22
        "FromServerInstanceName"    = 25
        "FromBCServerPath"          = 27
        "FromBCClientPath"          = 28
        "ToLSVersion"               = 30
        "ToBCVersion"               = 35
        "ToBCVersion2"              = 37
        "ToServerInstanceName"      = 40
        "ToBCServerPath"            = 45
        "CustomExtensions"         = 50
        "AddCustomExtensions"        = 52
        "AddCustomExtensionsManually" = 54
        "AddCustomExtensionsFromFile" = 56
        "AddCustomExtensionsFromFile2" = 58
        "RemoveCustomExtension"       = 60
        "Multitenancy"                = 65
        "Localization"                = 70
        "Final"                       = 100
    }

    $script:prevSteps = New-Object System.Collections.Stack
    $script:prevSteps.Push(1)

    if ($loadingFromConfigFile) {
        Write-Host "Loading configuration from $loadFromConfigFile." -ForegroundColor Green
    
        if (!(Test-Path -Path $loadFromConfigFile)) {
            Write-Host "Config file not found: $($loadFromConfigFile)" -ForegroundColor Red
            return
        }

        Get-UpgradeInitializationScriptConfig -configFile $loadFromConfigFile

        # Test "From Version"
        Write-Host "Testing FromBC Version config" -ForegroundColor Green
        if (!(Test-Version -Version $upgradeInitializationScriptConfig.FromBc.Version)) {
            return
        }
        Write-Host "FromBC Version config ($($upgradeInitializationScriptConfig.FromBc.Version)) is valid."

        # Test "To Version"
        Write-Host "Testing ToBC Version config" -ForegroundColor Green
        if (!(Test-Version -Version $upgradeInitializationScriptConfig.ToBc.Version)) {
            return
        }
        Write-Host "ToBC Version config ($($upgradeInitializationScriptConfig.ToBc.Version)) is valid."
        
        $script:wizardStep = $Step.Final
    }

    $customExtensions = @{}
    
    while ($script:wizardStep -le 100) {
    
        $script:thisStep = $script:wizardStep
        $script:wizardStep++
    
        switch ($script:thisStep) {
    
            $Step.SqlServer {
                $sqlServerInstance = ""

                $sqlServer = Enter-Value `
                    -title @'
                     _____ ____ _ _____
                    / ____|/ __ \| | / ____|
                   | (___ | | | | | | (___ ___ _ ____ _____ _ __
                    \___ \| | | | | \___ \ / _ | '__\ \ / / _ | '__|
                    ____) | |__| | |____ ____) | __| | \ V | __| |
                   |_____/ \___\_|______| |_____/ \___|_| \_/ \___|_|
                                                                                          
'@
 `
                    -description "Enter the name of the SQL Server (and the SQL Server Instance, when applicable).`n`nExample: localhost or localhost\MSSQLSERVER, where localhost would be the SQL Server and MSSQLSERVER would be the instance." `
                    -question "SQL Server name:" `
                    -default "localhost" `
                    -previousStep
    
                $splittedSqlServer = $sqlServer.Split('\')
                if ($splittedSqlServer.Count -eq 2) {
                    $sqlServer = $splittedSqlServer[0]
                    $sqlServerInstance = $splittedSqlServer[1]
                } elseif ($splittedSqlServer.Count -gt 2) {
                    Write-Host -ForegroundColor Red "Invalid SQL Server."
                }
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }
            }
    
            $Step.Database {
       
                $databaseName = Enter-Value `
                    -title @'
      _____ _ _
     | __ \ | | | |
     | | | | __ _| |_ __ _| |__ __ _ ___ ___
     | | | |/ _` | __/ _` | '_ \ / _` / __|/ _ \
     | |__| | (_| | |_ (_| | |_) | (_| \__ \ __/
     |_____/ \__,_|\__\__,_|_.__/ \__,_|___/\___|
'@
 `
                    -description "Enter the name of the database that's going to be upgraded." `
                    -question "Database Name:" `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }    
            }
    
            $Step.FromLSVersion {
    
                $lsVersions = @()
                Get-LSCentralAppInfo | ForEach-Object {
                    $lsVersions += $_.version
                }

                $default = "14.2"

                $fromLsVersion = Enter-Value `
                    -title @'
     ______ _ _____ __ __ _
    | ____| | | / ____|\ \ / / (_)
    | |__ _ __ ___ _ __ ___ | | | (___ \ \ / /___ _ __ ___ _ ___ _ __
    | __|| '__|/ _ \ | '_ ` _ \ | | \___ \ \ \/ // _ \| '__|/ __|| | / _ \ | '_ \
    | | | | | (_) || | | | | || |____ ____) | \ /| __/| | \__ \| || (_) || | | |
    |_| |_| \___/ |_| |_| |_||______||_____/ \/ \___||_| |___/|_| \___/ |_| |_|
'@
 `
                    -description "What version of LS Central are you upgrading from?" `
                    -options $lsVersions `
                    -question "From LS Version:" `
                    -default $default `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                $fromMajorLSVersion = [int]($fromLsVersion.Split('.')[0])
            }

            $Step.FromBCVersion {
    
                $predef = Select-Value `
                    -title @'
     ______ ____ _____ __ __ _
    | ____| | _ \ / ____|\ \ / / (_)
    | |__ _ __ ___ _ __ ___ | |_) || | \ \ / /___ _ __ ___ _ ___ _ __
    | __|| '__|/ _ \ | '_ ` _ \ | _ < | | \ \/ // _ \| '__|/ __|| | / _ \ | '_ \
    | | | | | (_) || | | | | || |_) || |____ \ /| __/| | \__ \| || (_) || | | |
    |_| |_| \___/ |_| |_| |_||____/ \_____| \/ \___||_| |___/|_| \___/ |_| |_|
'@
 `
                    -description "What version of Business Central are you upgrading from?" `
                    -options ([ordered]@{
                        "SpecificOnPrem"  = "Specific Business Central OnPrem build (requires version number)"
                    }) `
                    -question "From BC Version" `
                    -default "SpecificOnPrem" `
                    -writeAnswer `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }
            }
    
            $Step.FromBCVersion2 {
    
                $fullVersionNo = $false

                $type = $predef.Substring(8)
                $ok = $false
                do {
                    $fromVersion = Enter-Value `
                        -description "Specify version number.`nIf you specify a full version number (like 15.4.41023.41345), you will get the closest version.`nIf multiple versions matches the entered value, you will be asked to select" `
                        -question "Enter version number (format major[.minor[.build[.release]]])" `
                        -doNotClearHost `
                        -writeAnswer `
                        -previousStep
            
                    if ($fromVersion -eq "back") {
                        $ok = $true
                    }
                    else {
                        if ($fromVersion.indexOf('.') -eq -1) {
                            $verno = 0
                            $ok = [int32]::TryParse($fromVersion, [ref]$verno)
                            if (!$ok) {
                                Write-Host -ForegroundColor Red "Illegal version number"
                            }
                        }
                        else {
                            $verno = [Version]"0.0.0.0"
                            $ok = [Version]::TryParse($fromVersion, [ref]$verno)
                            if (!$ok) {
                                Write-Host -ForegroundColor Red "Illegal version number"
                            }
                            $fullVersionNo = $verno.Revision -ne -1
                        }
    
                        if ($ok) {

                            if ($fullVersionNo) {
                                $artifactUrl = Get-BCArtifactUrl -type $type -version $fromVersion -country 'w1' -select 'Closest'
                                if ($artifactUrl) {
                                    $foundVersion = $artifactUrl.split('/')[4]
                                    if ($foundVersion -ne $fromVersion) {
                                        Write-Host -ForegroundColor Yellow "The specific version doesn't exist, closest version is $foundVersion"
                                    }
                                }
                            }
                            else {
                                $fromVersions = @()
                                Get-BCArtifactUrl -type $type -version $fromVersion -country 'w1' -select All | ForEach-Object {
                                    $fromVersions += $_.Split('/')[4]
                                }
                                if ($fromVersions.Count -eq 0) {
                                    Write-Host -ForegroundColor Red "Unable to find a version matching the specified version"
                                    $ok = $false
                                }
                                elseif ($fromVersions.Count -gt 1) {
                                    $fromVersion = Enter-Value `
                                        -options $fromVersions `
                                        -question "Select specific version" `
                                        -doNotClearHost `
                                        -writeAnswer `
                                        -previousStep
    
                                    if ($fromVersion -eq "back") {
                                        $ok = $true
                                    }
                                    else {
                                        $fullVersionNo = $true
                                    }
                                }
                            }
                        }

                        if ($ok) {
                            $fromMajorVersion = [int]($fromVersion.Split('.')[0])
                            if ($fromMajorLSVersion -ne $fromMajorVersion) {
                                Write-Host -ForegroundColor Red "BC and LS major versions are not the same. Business Central version must be V$($fromMajorLSVersion)."
                                $ok = $false
                            }                                
                        }
                    }

                } while (!$ok)
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                if ($fromVersion -ne "back") {
                    $fromMajorVersion = [int]($fromVersion.Split('.')[0])
                }
            }
    
             $Step.FromServerInstanceName {
       
                $fromServerInstanceName = Enter-Value `
                    -title @'
     ______ _____ _____ _ _ _
    | ____| / ____| |_ _| | | | \ | |
    | |__ _ __ ___ _ __ ___ | (___ ___ _ __ __ __ ___ _ __ | | _ __ ___ | |_ __ _ _ __ ___ ___ | \| | __ _ _ __ ___ ___
    | __|| '__|/ _ \ | '_ ` _ \ \___ \ / _ \| '__|\ \ / // _ \| '__|| | | '_ \ / __|| __|/ _` || '_ \ / __|/ _ \| . ` | / _` || '_ ` _ \ / _ \
    | | | | | (_) || | | | | | ____) || __/| | \ V /| __/| | _| |_ | | | |\__ \| |_| (_| || | | || (__| __/| |\ || (_| || | | | | || __/
    |_| |_| \___/ |_| |_| |_||_____/ \___||_| \_/ \___||_| |_____||_| |_||___/ \__|\__,_||_| |_| \___|\___||_| \_| \__,_||_| |_| |_| \___|
'@
 `
                    -description "Enter the name of the NAV/BC server instance name for the original NAV/BC version." `
                    -question "Original NAV/BC version Service Instance Name:" `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }    
            }

            $Step.FromBCServerPath {
       
                $default = "C:\Program Files\Microsoft Dynamics 365 Business Central\$($fromMajorVersion)0"

                $fromBCServerPath = Enter-Value `
                    -title @'
     ______ ____ _____ _____ _____ _ _
    | ____| | _ \ / ____| / ____| | __ \ | | | |
    | |__ _ __ ___ _ __ ___ | |_) || | | (___ ___ _ __ __ __ ___ _ __ | |__) |__ _ | |_ | |__
    | __|| '__|/ _ \ | '_ ` _ \ | _ < | | \___ \ / _ \| '__|\ \ / // _ \| '__|| ___// _` || __|| '_ \
    | | | | | (_) || | | | | || |_) || |____ ____) || __/| | \ V /| __/| | | | | (_| || |_ | | | |
    |_| |_| \___/ |_| |_| |_||____/ \_____||_____/ \___||_| \_/ \___||_| |_| \__,_| \__||_| |_|
'@
 `
                    -description "Enter the path for the NAV/BC service version you're upgrading from. Example: $default" `
                    -question "From BC Server Path:" `
                    -default $default `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                # Skips the FromBCClientPath step if the major version is greater than v14.
                if ($fromMajorVersion -ne "14") {
                    $script:wizardStep = $Step.FromBCClientPath + 1
                }
            }

            $Step.FromBCClientPath {
       
                $default = "C:\Program Files (x86)\Microsoft Dynamics 365 Business Central\$($fromMajorVersion)0"

                $fromBCClientPath = Enter-Value `
                    -title @'
     ______ ____ _____ _____ _ _ _ _____ _ _
    | ____| | _ \ / ____|/ ____|| |(_) | | | __ \ | | | |
    | |__ _ __ ___ _ __ ___ | |_) || | | | | | _ ___ _ __ | |_ | |__) |__ _ | |_ | |__
    | __|| '__|/ _ \ | '_ ` _ \ | _ < | | | | | || | / _ \| '_ \ | __|| ___// _` || __|| '_ \
    | | | | | (_) || | | | | || |_) || |____| |____ | || || __/| | | || |_ | | | (_| || |_ | | | |
    |_| |_| \___/ |_| |_| |_||____/ \_____|\_____||_||_| \___||_| |_| \__||_| \__,_| \__||_| |_|
'@
 `
                    -description "Enter the path for the BC roletailored client version you're upgrading from. Example: $default" `
                    -question "From BC Roletailored Client Path:" `
                    -default $default `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }    
            }

            $Step.ToLSVersion {

                $lsVersions = @()
                Get-LSCentralAppInfo -version $minimumToLSVersion -versionFilterType 'ge' | ForEach-Object {
                    $lsVersions += $_.version
                }

                $default = Get-DefaultDestinationVersion
                
                $toLsVersion = Enter-Value `
                    -title @'
  _______ _ _____ __ __ _
 |__ __| | | / ____|\ \ / / (_)
    | | ___ | | | (___ \ \ / /___ _ __ ___ _ ___ _ __
    | | / _ \ | | \___ \ \ \/ // _ \| '__|/ __|| | / _ \ | '_ \
    | || (_) || |____ ____) | \ /| __/| | \__ \| || (_) || | | |
    |_| \___/ |______||_____/ \/ \___||_| |___/|_| \___/ |_| |_|
'@
 `
                    -description "What version of LS Central are you upgrading to?" `
                    -options $lsVersions `
                    -question "To LS Version:" `
                    -default $default `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                $toMajorLSVersion = [int]($toLsVersion.Split('.')[0])
            }
 
            $Step.ToBCVersion {
    
                $predef = Select-Value `
                    -title @'
  _______ ____ _____ __ __ _
 |__ __| | _ \ / ____|\ \ / / (_)
    | | ___ | |_) || | \ \ / /___ _ __ ___ _ ___ _ __
    | | / _ \ | _ < | | \ \/ // _ \| '__|/ __|| | / _ \ | '_ \
    | || (_) || |_) || |____ \ /| __/| | \__ \| || (_) || | | |
    |_| \___/ |____/ \_____| \/ \___||_| |___/|_| \___/ |_| |_|
'@
 `
                    -description "What version of Business Central are you upgrading to?" `
                    -options ([ordered]@{
                        "SpecificOnPrem"  = "Specific Business Central OnPrem build (requires version number)"
                    }) `
                    -question "To BC Version:" `
                    -default "SpecificOnPrem" `
                    -writeAnswer `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }
            }
    
            $Step.ToBCVersion2 {
    
                $fullVersionNo = $false

                $type = $predef.Substring(8)
                $ok = $false
                do {
                    $toVersion = Enter-Value `
                        -description "Specify version number.`nIf you specify a full version number (like 15.4.41023.41345), you will get the closest version.`nIf multiple versions matches the entered value, you will be asked to select" `
                        -question "Enter version number (format major[.minor[.build[.release]]])" `
                        -doNotClearHost `
                        -writeAnswer `
                        -previousStep
            
                    if ($toVersion -eq "back") {
                        $ok = $true
                    }
                    else {
                        if ($toVersion.indexOf('.') -eq -1) {
                            $verno = 0
                            $ok = [int32]::TryParse($toVersion, [ref]$verno)
                            if (!$ok) {
                                Write-Host -ForegroundColor Red "Illegal version number"
                            }
                        }
                        else {
                            $verno = [Version]"0.0.0.0"
                            $ok = [Version]::TryParse($toVersion, [ref]$verno)
                            if (!$ok) {
                                Write-Host -ForegroundColor Red "Illegal version number"
                            }
                            $fullVersionNo = $verno.Revision -ne -1
                        }
    
                        if ($ok) {

                            if ($fullVersionNo) {
                                $artifactUrl = Get-BCArtifactUrl -type $type -version $toVersion -country 'w1' -select 'Closest'
                                if ($artifactUrl) {
                                    $foundVersion = $artifactUrl.split('/')[4]
                                    if ($foundVersion -ne $toVersion) {
                                        Write-Host -ForegroundColor Yellow "The specific version doesn't exist, closest version is $foundVersion"
                                    }
                                }
                            }
                            else {
                                $toVersions = @()
                                Get-BCArtifactUrl -type $type -version $toVersion -country 'w1' -select All | ForEach-Object {
                                    $toVersions += $_.Split('/')[4]
                                }
                                if ($toVersions.Count -eq 0) {
                                    Write-Host -ForegroundColor Red "Unable to find a version matching the specified version"
                                    $ok = $false
                                }
                                elseif ($toVersions.Count -gt 1) {
                                    $toVersion = Enter-Value `
                                        -options $toVersions `
                                        -question "Select specific version" `
                                        -doNotClearHost `
                                        -writeAnswer `
                                        -previousStep
    
                                    if ($toVersion -eq "back") {
                                        $ok = $true
                                    }
                                    else {
                                        $fullVersionNo = $true
                                    }
                                }
                            }
                        }

                        if ($ok) {
                            $toMajorVersion = [int]($toVersion.Split('.')[0])
                            if ($toMajorLSVersion -ne $toMajorVersion) {
                                Write-Host -ForegroundColor Red "BC and LS major versions are not the same. Business Central version must be V$($toMajorLSVersion)."
                                $ok = $false
                            }                                
                        }
                    }
                } while (!$ok)
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                if ($toVersion -ne "back") {
                    $toMajorVersion = [int]($toVersion.Split('.')[0])
                }
            }

            $Step.ToServerInstanceName {
       
                $toServerInstanceName = Enter-Value `
                    -title @'
      _______ _____ _____ _ _ _
     |__ __| / ____| |_ _| | | | \ | |
        | | ___ | (___ ___ _ __ __ __ ___ _ __ | | _ __ ___ | |_ __ _ _ __ ___ ___ | \| | __ _ _ __ ___ ___
        | | / _ \ \___ \ / _ \| '__|\ \ / // _ \| '__|| | | '_ \ / __|| __|/ _` || '_ \ / __|/ _ \| . ` | / _` || '_ ` _ \ / _ \
        | || (_) |____) || __/| | \ V /| __/| | _| |_ | | | |\__ \| |_| (_| || | | || (__| __/| |\ || (_| || | | | | || __/
        |_| \___/|_____/ \___||_| \_/ \___||_| |_____||_| |_||___/ \__|\__,_||_| |_| \___|\___||_| \_| \__,_||_| |_| |_| \___|
'@
 `
                    -description "Enter the name of the BC server instance name for the new BC version" `
                    -question "New BC version Service Instance Name:" `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }    
            }    

            $Step.ToBCServerPath {

                $default = "C:\Program Files\Microsoft Dynamics 365 Business Central\$($toMajorVersion)0"

                $toBCServerPath = Enter-Value `
                    -title @'
      _______ ____ _____ _____ _____ _ _
     |__ __| | _ \ / ____| / ____| | __ \ | | | |
        | | ___ | |_) || | | (___ ___ _ __ __ __ ___ _ __ | |__) |__ _ | |_ | |__
        | | / _ \ | _ < | | \___ \ / _ \| '__|\ \ / // _ \| '__|| ___// _` || __|| '_ \
        | || (_) || |_) || |____ ____) || __/| | \ V /| __/| | | | | (_| || |_ | | | |
        |_| \___/ |____/ \_____||_____/ \___||_| \_/ \___||_| |_| \__,_| \__||_| |_|
'@
 `
                    -description "Enter the path for the NAV/BC service version you're upgrading to. Example: $default" `
                    -question "To BC Server Path:" `
                    -default $default `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }    
            }
 
            $Step.CustomExtensions {

                $CustomExtensionsDetailed = ""
                $customExtensions.GetEnumerator() | ForEach-Object {
                    $CustomExtensionsDetailed += "$($_.key): "
                    $CustomExtensionsDetailed += "$($_.value | ConvertTo-Json -Compress) `n"
                }
                $description = "If you have customization extensions then you can automatically add those to the migration script.`n`nCustom extensions:`n`n$($CustomExtensionsDetailed)"

                $predef = Select-Value `
                    -title @'
      _____ _ ______ _ _
     / ____| | | | ____| | | (_)
    | | _ _ ___ | |_ ___ _ __ ___ | |__ __ __| |_ ___ _ __ ___ _ ___ _ __ ___
    | | | | | |/ __|| __|/ _ \ | '_ ` _ \ | __| \ \/ /| __|/ _ \| '_ \ / __|| | / _ \ | '_ \ / __|
    | |____| |_| |\__ \| |_| (_) || | | | | || |____ > < | |_| __/| | | |\__ \| || (_) || | | |\__ \
     \_____|\__,_||___/ \__|\___/ |_| |_| |_||______|/_/\_\ \__|\___||_| |_||___/|_| \___/ |_| |_||___/
'@
 `
                    -description $description `
                    -options ([ordered]@{
                        "add"  = "Add custom extension"
                        "remove"  = "Remove custom extension"
                        "continue"  = "Continue to next step."
                    }) `
                    -question "Do you want to add or remove custom extensions to the script?" `
                    -default "continue" `
                    -writeAnswer `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                switch ($predef)
                {
                    "remove" { $script:wizardStep = $Step.RemoveCustomExtension }   # Go to the remove custom extensions menu.
                    "continue" { $script:wizardStep = $Step.Multitenancy }                 # Skips the next steps if not adding custom extensions.
                    Default {}
                } 
            }

            $Step.AddCustomExtensions {
    
                $predef = Select-Value `
                    -description "Choose if you want to enter the extension information (app id, name, publisher and version) manually or read it from the app.json file." `
                    -options ([ordered]@{
                        "readfromfile"  = "Read from app.json file."
                        "manually"  = "Enter manually."
                    }) `
                    -question "How do you want to enter the custom extensions information?" `
                    -default "readfromfile" `
                    -writeAnswer `
                    -doNotClearHost `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                # Skips the next step if not adding custom extensions.
                if ($predef -eq "readfromfile") {
                    $script:wizardStep = $Step.AddCustomExtensionsFromFile
                }                
            }

            $Step.AddCustomExtensionsManually {
    
                $ok = $false
                do {
                    $appId = Enter-Value `
                        -description "Specify the app id for the custom extension." `
                        -question "Enter the app id for the custom extension:" `
                        -doNotClearHost `
                        -writeAnswer `
                        -previousStep
            
                    if ($appId -eq "back") {
                        $ok = $true
                    }
                    else {
                        $appIdGuid = [System.Guid]::empty
                        $ok = [System.Guid]::TryParse($appId, [ref]$appIdGuid)
                        if (!$ok) {
                            Write-Host -ForegroundColor Red "Illegal app id for the custom extension"
                        }

                        if ($ok) {
                            $appName = Enter-Value `
                                -description "Specify the custom extension name." `
                                -question "Enter the custom extension name:" `
                                -doNotClearHost `
                                -writeAnswer `
                                -previousStep
                        }

                        if ($ok) {
                            $appPublisherName = Enter-Value `
                                -description "Specify the custom extension publisher name." `
                                -question "Enter the custom extension publisher name:" `
                                -doNotClearHost `
                                -writeAnswer `
                                -previousStep
                        }

                        if ($ok) {
                            $appVersion = Enter-Value `
                                -description "Specify the final version for the custom extension." `
                                -question "Enter the final version for the custom extension:" `
                                -doNotClearHost `
                                -writeAnswer `
                                -previousStep
    
                            if ($appVersion.indexOf('.') -eq -1) {
                                $verno = 0
                                $ok = [int32]::TryParse($appVersion, [ref]$verno)
                                if (!$ok) {
                                    Write-Host -ForegroundColor Red "Illegal version number"
                                }
                            }
                            else {
                                $verno = [Version]"0.0.0.0"
                                $ok = [Version]::TryParse($appVersion, [ref]$verno)
                                if (!$ok) {
                                    Write-Host -ForegroundColor Red "Illegal version number"
                                }
                                $fullVersionNo = $verno.Revision -ne -1
                            }
                        }
                    }
                } while (!$ok)
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                if ($appId -ne "back") {
                    $customExtension = [ordered]@{
                        "Id" = $appId
                        "Name" = $appName
                        "Publisher" = $appPublisherName
                        "Version" = $appVersion
                    }
                    $offset = $customExtensions.Count
                    $customExtensions.add($([char]($offset+97)), $customExtension)

                    # Go to the custom extensions main step.
                    $script:wizardStep = $Step.CustomExtensions
                }
            }

            $Step.AddCustomExtensionsFromFile {
    
                $ok = $false
                do {
                    $appJsonPath = Enter-Value `
                        -description "Specify the path for the custom extension app.json file.`nExample: C:\CustomExtensionProject\app.json." `
                        -question "Enter the path for the custom extension app.json file:" `
                        -doNotClearHost `
                        -writeAnswer `
                        -previousStep
            
                    if ($appJsonPath -eq "back") {
                        $ok = $true
                    }
                    else {
                        $appJsonPath = $appJsonPath.Trim('"')
                        $ok = Test-Path -Path $appJsonPath
                        if (!$ok) {
                            Write-Host -ForegroundColor Red "App.json file not found"
                        }

                        if ($ok) {
                            try {
                                $appJson = Get-Content $appJsonPath | ConvertFrom-Json
                                
                                $appId = $appJson.id
                                $appName = $appJson.name
                                $appPublisherName = $appJson.publisher
                                $appVersion = $appJson.version
                            }
                            catch {
                                $ok = $false
                                throw "Error reading configuration file $loadFromConfigFile, cannot execute function."
                            }                    
                        }
                    }
                } while (!$ok)
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                if ($appJsonPath -ne "back") {
                    $customExtension = [ordered]@{
                        "Id" = $appId
                        "Name" = $appName
                        "Publisher" = $appPublisherName
                        "Version" = $appVersion
                    }
                    $offset = $customExtensions.Count
                    $customExtensions.add($([char]($offset+97)), $customExtension)

                    # Go to the custom extensions main step.
                    $script:wizardStep = $Step.CustomExtensions
                }
            }

            $Step.RemoveCustomExtension {
    
                $customExtensionsOptions = @{}
                $customExtensions.GetEnumerator() | ForEach-Object {
                    $key = $_.key
                    $value = $_.value | ConvertTo-Json -Compress
                    $customExtensionsOptions.add($key, $value)
                }

                $predef = Select-Value `
                    -description "Choose the custom extension you want to remove." `
                    -options ($customExtensionsOptions) `
                    -question "What custom extension do you want to remove?" `
                    -writeAnswer `
                    -doNotClearHost `
                    -previousStep

                $customExtensions.remove($predef)
                
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                # Go to the custom extensions main step.
                $script:wizardStep = $Step.CustomExtensions
            }

            $Step.Multitenancy {
    
                $predef = Select-Value `
                    -title @'
     __ __ _ _ _ _
    | \/ | | || | (_)| |
    | \ / | _ _ | || |_ _ | |_ ___ _ __ __ _ _ __ ___ _ _
    | |\/| || | | || || __|| || __|/ _ \| '_ \ / _` || '_ \ / __|| | | |
    | | | || |_| || || |_ | || |_| __/| | | || (_| || | | || (__ | |_| |
    |_| |_| \__,_||_| \__||_| \__|\___||_| |_| \__,_||_| |_| \___| \__, |
                                                                     __/ |
                                                                    |___/
'@
 `
                    -description "Indicate if you're setting up a single or multitenant environment." `
                    -options ([ordered]@{
                        "yes"  = "Yes, it's a multi tenant environment."
                        "no"  = "No, it's a single tenant environment."
                    }) `
                    -question "Are you setting up a multitenancy environment?" `
                    -default "no" `
                    -writeAnswer `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }

                $multitenancy = $false
                if ($predef -eq "yes") {
                    $multitenancy = $true
                }
            }

            $Step.Localization {
    
                $default = "w1"

                $localization = Enter-Value `
                    -title @'
     _ _ _ _ _
    | | | |(_) | | (_)
    | | ___ ___ __ _ | | _ ____ __ _ | |_ _ ___ _ __
    | | / _ \ / __|/ _` || || ||_ // _` || __|| | / _ \ | '_ \
    | |____| (_) || (__| (_| || || | / /| (_| || |_ | || (_) || | | |
    |______|\___/ \___|\__,_||_||_|/___|\__,_| \__||_| \___/ |_| |_|
'@
 `
                    -description "Indicate if you are using any localization.`n`nEnter the country code with 2 digits like: is, fi, es, us, etc.`nMore Info: https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/compliance/apptest-countries-and-translations`n`nBase, System and Application files for this localization will be used." `
                    -question "What localization are you using?" `
                    -default $default `
                    -writeAnswer `
                    -previousStep
                if ($script:wizardStep -eq $script:thisStep + 1) {
                    $script:prevSteps.Push($script:thisStep)
                }
            }

            # ______ _ _
            # | ____(_) | |
            # | |__ _ _ __ __ _| |
            # | __| | | '_ \ / _` | |
            # | | | | | | | (_| | |
            # |_| |_|_| |_|\__,_|_|
            #
            $step.Final {
                $script:acceptDefaults = $false

                if (!$loadingFromConfigFile) {
                    if (!((Get-Variable -scope Script upgradeInitializationScriptConfig -ErrorAction SilentlyContinue) -and $upgradeInitializationScriptConfig)) {

                        $customExtensionsConfig = @()
                        $customExtensions.GetEnumerator() | ForEach-Object {
                            $customExtensionsConfig = $customExtensionsConfig + $_.Value
                        }
                        
                        Set-Variable -scope Script -Name upgradeInitializationScriptConfig -Value @{
                            "Environment" = @{
                                "Multitenant" = $multitenancy
                                "Localization" = $localization
                            }
                            "SQL" = @{
                                "Server" = "$sqlServer"
                                "Instance" = "$sqlServerInstance"
                                "Database" = "$databaseName"
                            }
                            "FromBC" = @{
                                "ServerInstance" = "$fromServerInstanceName"
                                "Version" = "$fromVersion"
                                "LSVersion" = "$fromLSVersion"
                                "ServerPath" = "$fromBCServerPath"
                                "ClientPath" = "$fromBCClientPath"
                            }
                            "ToBC" = @{
                                "ServerInstance" = "$toServerInstanceName"
                                "Version" = "$toVersion"
                                "LSVersion" = "$toLsVersion"
                                "ServerPath" = "$toBCServerPath"
                            }
                            "CustomExtensions" = $customExtensionsConfig
                        }
                        
                    }
                }
          
                $title = `
@"
     _____ _ _ _ _____ _ _
    | __ \ | | | || | / ____| (_) | |
    | |__) |___ __ __ ___ _ __ ___ | |__ ___ | || | | (___ ___ _ __ _ _ __ | |_ ___
    | ___// _ \\ \ /\ / // _ \| '__|/ __|| '_ \ / _ \| || | \___ \ / __|| '__|| || '_ \ | __|/ __|
    | | | (_) |\ V V /| __/| | \__ \| | | || __/| || | ____) || (__ | | | || |_) || |_ \__ \
    |_| \___/ \_/\_/ \___||_| |___/|_| |_| \___||_||_| |_____/ \___||_| |_|| .__/ \__||___/
                                                                                     | |
                                                                                     |_|
"@


                Write-Host -ForegroundColor Yellow $title

                $preparationScript = Get-PreparationScript
                $preparationScriptPath = Join-Path (Get-Location) "Preparation-Script.ps1"
                $preparationScript | Out-File $preparationScriptPath
                Write-Host "Preparation Powershell script file exported to $preparationScriptPath"

                $migrationScript = Get-MigrationScript
                $migrationScriptPath = Join-Path (Get-Location) "Migration-Script.ps1"
                $migrationScript | Out-File $migrationScriptPath
                Write-Host "Migration Powershell script file exported to $migrationScriptPath"

                if ($loadingFromConfigFile -eq $false) {
                    $exportConfigPath = Join-Path (Get-Location) "config.json"
                    $upgradeInitializationScriptConfig | ConvertTo-Json | Set-Content $exportConfigPath
                    Write-Host "Config file exported to $exportConfigPath"
                }
            }    
        }
    }
}

function Select-Value {
    Param(
        [Parameter(Mandatory = $false)]
        [string] $title,
        [Parameter(Mandatory = $false)]
        [string] $description,
        [Parameter(Mandatory = $true)]
        $options,
        [Parameter(Mandatory = $false)]
        [string] $default = "",
        [Parameter(Mandatory = $true)]
        [string] $question,
        [switch] $doNotClearHost = ($host.name -ne "ConsoleHost"),
        [switch] $writeAnswer = ($host.name -ne "ConsoleHost"),
        [switch] $previousStep
    )

    if (!$doNotClearHost) {
        Clear-Host
    }

    if ($title) {
        Write-Host -ForegroundColor Yellow $title
        Write-Host
    }
    if ($description) {
        Write-Host $description
        Write-Host
    }
    $offset = 0
    $defaultChr = -1
    $keys = @()
    $values = @()

    $options.GetEnumerator() | ForEach-Object {
        Write-Host -ForegroundColor Yellow "$([char]($offset+97)) " -NoNewline
        $keys += @($_.Key)
        $values += @($_.Value)
        if ($_.Key -eq $default) {
            Write-Host -ForegroundColor Yellow $_.Value
            $defaultAnswer = $offset
        }
        else {
            Write-Host $_.Value
        }
        $offset++     
    }
    Write-Host
    if ($script:thisStep -lt 100) {
        if (($default) -and !$script:acceptDefaults) {
            Write-Host -ForegroundColor Yellow "!" -NoNewline
            Write-Host " accept default answers for the remaining questions"
        }
        if ($previousStep) {
            Write-Host -ForegroundColor Yellow "x" -NoNewline
            Write-Host " start over"
            Write-Host -ForegroundColor Yellow "z" -NoNewline
            Write-Host " go back"
        }
        if (($default) -or ($previousStep)) {
            Write-Host
        }
    }
    $answer = -1
    do {
        Write-Host "$question " -NoNewline
        if ($defaultAnswer -ge 0) {
            Write-Host "(default $([char]($defaultAnswer + 97))) " -NoNewline
        }
        if ($script:acceptDefaults -and $defaultAnswer -ge 0) {
            $selection = ""
        }
        else {
            $selection = (Read-Host).ToLowerInvariant()
        }
        if ($selection -eq "!" -and ($default)) {
            $selection = ""
            $script:acceptDefaults = $true
            Write-Host $defaultAnswer
        }
        if ($previousStep) {
            if ($selection -eq "x") {
                if ($writeAnswer) {
                    Write-Host
                    Write-Host -ForegroundColor Green "Start over selected"
                    Write-Host
                }
                $script:acceptDefaults = $false
                $script:wizardStep = 0
                $script:prevSteps = New-Object System.Collections.Stack
                $script:prevSteps.Push(1)
                return "Back"
            }
            if ($selection -eq "z") {
                if ($writeAnswer) {
                    Write-Host
                    Write-Host -ForegroundColor Green "Back selected"
                    Write-Host
                }
                $script:acceptDefaults = $false
                $script:wizardStep = $script:prevSteps.Pop()
                return "Back"
            }
        }
        if ($selection -eq "") {
            if ($defaultAnswer -ge 0) {
                $answer = $defaultAnswer
            }
            else {
                Write-Host -ForegroundColor Red "No default value exists. " -NoNewline
            }
        }
        else {
            if (($selection.Length -ne 1) -or (([int][char]($selection)) -lt 97 -or ([int][char]($selection)) -ge (97 + $offset))) {
                Write-Host -ForegroundColor Red "Illegal answer. " -NoNewline
            }
            else {
                $answer = ([int][char]($selection)) - 97
            }
        }
        if ($answer -eq -1) {
            if ($offset -eq 2) {
                Write-Host -ForegroundColor Red "Please answer one letter, a or b"
            }
            else {
                Write-Host -ForegroundColor Red "Please answer one letter, from a to $([char]($offset+97-1))"
            }
        }
    } while ($answer -eq -1)

    if ($writeAnswer) {
        Write-Host
        Write-Host -ForegroundColor Green "$($values[$answer]) selected"
        Write-Host
    }
    $keys[$answer]
}

function Enter-Value {
    Param(
        [Parameter(Mandatory = $false)]
        [string] $title,
        [Parameter(Mandatory = $false)]
        [string] $description,
        [Parameter(Mandatory = $false)]
        $options,
        [Parameter(Mandatory = $false)]
        [string] $default = "",
        [Parameter(Mandatory = $true)]
        [string] $question,
        [switch] $doNotClearHost = ($host.name -ne "ConsoleHost"),
        [switch] $writeAnswer = ($host.name -ne "ConsoleHost"),
        [switch] $doNotConvertToLower,
        [switch] $previousStep
    )

    if (!$doNotClearHost) {
        Clear-Host
    }

    if ($title) {
        Write-Host -ForegroundColor Yellow $title
        Write-Host
    }
    if ($description) {
        Write-Host $description
        Write-Host
    }
    if ($script:thisStep -lt 100) {
        if (($default) -and !$script:acceptDefaults) {
            Write-Host -ForegroundColor Yellow "!" -NoNewline
            Write-Host " accept default answers for the remaining questions"
        }
        if ($previousStep) {
            Write-Host "Enter " -NoNewline
            Write-Host -ForegroundColor Yellow "x" -NoNewline
            Write-Host " to start over"
            Write-Host "Enter " -NoNewline
            Write-Host -ForegroundColor Yellow "z" -NoNewline
            Write-Host " to go back"
        }
        if (($default) -or ($previousStep)) {
            Write-Host
        }
    }
    $answer = ""
    do {
        Write-Host "$question " -NoNewline
        if ($options) {
            Write-Host "($([string]::Join(', ', $options))) " -NoNewline
        }
        if ($default) {
            Write-Host "(default $default) " -NoNewline
        }
        if ($script:acceptDefaults -and ($default)) {
            $selection = ""
            Write-Host $default
        }
        elseif ($doNotConvertToLower) {
            $selection = Read-Host
        }
        else {
            $selection = (Read-Host).ToLowerInvariant()
        }
        if ($selection -eq "!" -and ($default)) {
            $selection = ""
            $script:acceptDefaults = $true
        }
        if ($selection -eq "") {
            if ($default) {
                $answer = $default
            }
            else {
                Write-Host -ForegroundColor Red "No default value exists. "
            }
        }
        elseif ($selection -eq "x" -and $previousStep) {
            if ($writeAnswer) {
                Write-Host
                Write-Host -ForegroundColor Green "Exit selected"
                Write-Host
            }
            $script:acceptDefaults = $false
            $script:wizardStep = 0
            $script:prevSteps = New-Object System.Collections.Stack
            $script:prevSteps.Push(1)
            return "back"
        }
        elseif ($selection -eq "z" -and $previousStep) {
            if ($writeAnswer) {
                Write-Host
                Write-Host -ForegroundColor Green "Back selected"
                Write-Host
            }
            $script:acceptDefaults = $false
            $script:wizardStep = $script:prevSteps.Pop()
            return "back"
        }
        else {
            if ($options) {
                $answer = $options | Where-Object { $_ -like "$selection*" }
                if (-not ($answer)) {
                    Write-Host -ForegroundColor Red "Illegal answer. Please answer one of the options."
                }
                elseif ($answer -is [Array]) {
                    Write-Host -ForegroundColor Red "Multiple options match the answer. Please answer one of the options that matched the previous selection."
                    $options = $answer
                    $answer = $null
                }
            }
            else {
                $answer = $selection
            }
        }
    } while (-not ($answer))

    if ($writeAnswer) {
        Write-Host
        Write-Host -ForegroundColor Green "$answer selected"
        Write-Host
    }
    $answer
}

function Get-PreparationScript {

    $fromLSVersion = $upgradeInitializationScriptConfig.FromBC.LSVersion
    $fromLSMajorVersion = [int]($fromLSVersion.Split('.')[0])

    $script = @()
    switch ($fromLSMajorVersion)
    {
        "14" {
            $script = Get-PreparationScript_FromLS14
            break
        }
        default {
            $script = Get-PreparationScript_FromLS15OrLater
            break
        }
    }
    return $script
}

function Get-PreparationScript_FromLS14
{
    $fromServerInstanceName = $upgradeInitializationScriptConfig.FromBC.ServerInstance
    $fromLSVersion = $upgradeInitializationScriptConfig.FromBC.LSVersion
    $fromBCServerPath = $upgradeInitializationScriptConfig.FromBC.ServerPath
    $fromBCClientPath = $upgradeInitializationScriptConfig.FromBC.ClientPath
    $toVersion = $upgradeInitializationScriptConfig.ToBC.Version
    $toLSVersion = $upgradeInitializationScriptConfig.ToBC.LSVersion
    $multitenancy = $upgradeInitializationScriptConfig.Environment.Multitenant
    $customExtensions = $upgradeInitializationScriptConfig.CustomExtensions
    
    $fromVersion = $upgradeInitializationScriptConfig.FromBC.Version
    $fromMajorVersion = [int]($fromVersion.Split('.')[0])

    $script = @()
    $script += ""
    $script += "###################### Initialization Helper ######################"
    $script += ""
    $script += "`$ErrorActionPreference = `"Stop`""
    $script += ""
    $script += "Import-Module LSMigrationTools -Force"
    $script += ""    
    $script += "## Get ALCompiler files"
    $script += "Get-ALCompilerFromArtifacts -Version $($toVersion)"
    $script += ""
    $script += "## Get txt2al files"
    $script += "Get-Txt2AlFromArtifacts"
    $script += ""
    $script += "## Create migration extensions folders"
    $script += "New-UpgradeAppsStructure -ImportSymbolsApp"
    $script += ""
    $script += ""
    $script += "########## C/AL - Tables renaming in Business Central 14 ##########"
    $script += ""
    $script += "`$baseFolder = `"$(Get-Location)`""
    $script += "`$objectsOutputPath = Join-Path `$baseFolder `"Objects`""
    $script += ""
    $script += "`$fromServerInstanceName = `"$fromServerInstanceName`""
    if ($multitenancy) {
        $script += "### Update this with the right tenant name"
    }
    $script += "`$tenantId = `"default`""
    $script += ""

    $sqlServer = $upgradeInitializationScriptConfig.SQL.Server
    $sqlServerInstance = $upgradeInitializationScriptConfig.SQL.Instance    
    if ($sqlServerInstance -eq "") {
        $script += "`$fullDatabaseServer = `"$sqlServer`""
    } else {
        $script += "`$fullDatabaseServer = `"$sqlServer\$sqlServerInstance`""
    }
    $script += "`$databaseName = `"$($upgradeInitializationScriptConfig.SQL.Database)`""
    $script += ""
    $script += "## BC 14"
    $script += "Import-Module `"$((Join-Path $fromBCServerPath 'Service\NavAdminTool.ps1'))`" -Force"
    $script += "Import-Module `"$((Join-Path $fromBCClientPath 'Roletailored Client\NavModelTools.ps1'))`" -Force"
    $script += ". `"$((Join-Path $fromBCClientPath 'Roletailored Client\NavModelTools.ps1'))`" -NavIde `"$((Join-Path $fromBCClientPath 'Roletailored Client\finsql.exe'))`" -Force"
    $script += ""
    $script += "Export-NAVApplicationObject -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Path (Join-Path `$objectsOutputPath 'exported_bc14-cal-tables.txt') -Filter 'Type=Table;Id=1..1999999999'"
    $script += "Convert-BC14CALObjectsToTableSchemaOnly -ObjectsFilePath (Join-Path `$objectsOutputPath 'exported_bc14-cal-tables.txt') -OutputFilePath (Join-Path `$objectsOutputPath 'converted_bc14-cal-tables.txt')"
    $script += ""
    $script += "Import-NAVApplicationObject -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Path (Join-Path `$objectsOutputPath 'converted_bc14-cal-tables.txt')"
    $script += "Compile-NAVApplicationObject -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Filter 'Type=Table;Id=1..1999999999'"
    if ($fromMajorVersion -eq 14) {
        $script += "Delete-NAVApplicationObject -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Filter 'Type=Table;Id=99009070..99009074|99009077' -SynchronizeSchemaChanges Force"
    }
    $script += ""
    $script += "Sync-NAVTenant -ServerInstance `$fromServerInstanceName -Tenant `$tenantId -Mode Sync -Force"
    $script += ""
    $script += "Export-NAVApplicationObject -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Path (Join-Path `$objectsOutputPath 'exported_bc14-cal-tables_renamed.txt') -Filter 'Type=Table;Id=1..1999999999'"
    $script += "Convert-BC14CALToStrippedAL -ObjectsFilePath (Join-Path `$objectsOutputPath 'exported_bc14-cal-tables_renamed.txt') -OutputFilePath (Join-Path `$objectsOutputPath 'converted_bc14-al-tables_renamed.al')"
    $script += ""
    $script += "Copy-Item -Path (Join-Path `$objectsOutputPath 'converted_bc14-al-tables_renamed.al') (Join-Path `$baseFolder 'ExtensionsSourceCode\ls-upgrade-1st_version\src')"
    $script += ""
    $script += ""
    $script += "## Compile each one of the migration extensions"
    $script += ""
    $script += "`$baseFolder = `"$(Get-Location)`""
    $script += ""
    $script += "### ls-upgrade-1st_version"
    $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-1st_version`""
    $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\ls-upgrade-1st_version.app`""
    $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
    $script += ""
    $script += "### ls-upgrade-2nd_version-migration"
    $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-2nd_version-migration`""
    $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\ls-upgrade-2nd_version-migration.app`""
    $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
    $script += ""
    $script += "### ls-upgrade-base-empty"
    $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-base-empty`""
    $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\ls-upgrade-base-empty.app`""
    $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
    $script += ""
    $script += "### ls-upgrade-system-empty"
    $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-system-empty`""
    $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\ls-upgrade-system-empty.app`""
    $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
    $script += ""
    $script += "### ls-upgrade-lscentral-empty"
    $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-lscentral-empty`""
    $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\ls-upgrade-lscentral-empty.app`""
    $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
    $script += ""
    $script += "### ls-upgrade-lscentral_system_app-empty"
    $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-lscentral_system_app-empty`""
    $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\ls-upgrade-lscentral_system_app-empty.app`""
    $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
    $script += ""
    foreach ($extension in $customExtensions) {
        $appPackageName = Get-AppPackageName -Publisher $extension.Publisher -Name $extension.Name
        $appPackageName = "$($appPackageName)-empty"

        $script += "### $($appPackageName)"
        $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\$($appPackageName)`""
        $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\$($appPackageName).app`""
        $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
        $script += ""
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### ls-upgrade-customization_app placeholder"
        $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-customization_app`""
        $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\<Customization App_14.0.0.0.app>`""
        $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
        $script += ""
    }
    if ($upgradeMode -eq [UpgradeMode]::LS_14_18_TablesRenamingInV18) {
        $script += "### ls-upgrade-tables_renaming-1st_version-empty"
        $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-tables_renaming-1st_version-empty`""
        $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\LS Retail_ls-upgrade-tables_renaming_14.0.0.0.app`""
        $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
        $script += ""
        $script += "### ls-upgrade-tables_renaming-2nd_version-old_tables_names"
        $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-tables_renaming-2nd_version-old_tables_names`""
        $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\LS Retail_ls-upgrade-tables_renaming_18.0.0.0.app`""
        $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
        $script += ""
        $script += "### ls-upgrade-tables_renaming-3rd_version-new_tables_names"
        $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-tables_renaming-3rd_version-new_tables_names`""
        $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\LS Retail_ls-upgrade-tables_renaming_18.0.1.0.app`""
        $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
        $script += ""
        $script += "### ls-upgrade-tables_renaming-4th_version-migration"
        $script += "`$appProjectFolder = Join-Path `$baseFolder `"ExtensionsSourceCode\ls-upgrade-tables_renaming-4th_version-migration`""
        $script += "`$appOutputFile = Join-Path `$baseFolder `"MigrationFiles\LS Retail_ls-upgrade-tables_renaming_18.0.2.0.app`""
        $script += "New-CompiledALProjectApp -appProjectFolder `$appProjectFolder -appOutputFile `$appOutputFile -appSymbolsFolder (Join-Path `$appProjectFolder `".alpackages`")"
        $script += ""
    }

    $script += "### Task 6: Prepare the existing database (Only applicable if there's any AL extension in the database)"
    $script += "Get-NAVAppInfo -ServerInstance `$fromServerInstanceName"
    $script += "Get-NAVAppInfo -ServerInstance `$fromServerInstanceName | % { Uninstall-NAVApp -ServerInstance `$fromServerInstanceName -Name `$_.Name -Version `$_.Version -Force }"
    $script += "Get-NAVAppInfo -ServerInstance `$fromServerInstanceName -SymbolsOnly | % { Unpublish-NAVApp -ServerInstance `$fromServerInstanceName -Name `$_.Name -Version `$_.Version }"
    $script += ""

    return $script
}

function Get-PreparationScript_FromLS15OrLater
{
    $fromServerInstanceName = $upgradeInitializationScriptConfig.FromBC.ServerInstance
    $fromBCServerPath = $upgradeInitializationScriptConfig.FromBC.ServerPath
    $multitenancy = $upgradeInitializationScriptConfig.Environment.Multitenant

    $fromVersion = $upgradeInitializationScriptConfig.FromBC.Version
    $fromMajorVersion = [int]($fromVersion.Split('.')[0])

    $script = @()
    $script += ""
    $script += "###################### Initialization Helper ######################"
    $script += ""
    $script += "`$ErrorActionPreference = `"Stop`""
    $script += ""
    $script += "## Create migration extensions folders"
    $script += "New-UpgradeAppsStructure -ImportSymbolsApp"
    $script += ""
    $script += ""
    $script += "`$fromServerInstanceName = `"$fromServerInstanceName`""
    $script += ""    
    if ($multitenancy) {
        $script += "### Update this with the right tenant name"
        $script += "`$tenantId = `"default`""
        $script += ""
    }

    $script += "## BC $($fromMajorVersion)"
    $script += "Import-Module `"$((Join-Path $fromBCServerPath 'Service\NavAdminTool.ps1'))`" -Force"
    $script += ""
    $script += "### Task 3: Prepare the existing database"
    $script += "Get-NAVAppInfo -ServerInstance `$fromServerInstanceName"
    $script += "Get-NAVAppInfo -ServerInstance `$fromServerInstanceName | % { Uninstall-NAVApp -ServerInstance `$fromServerInstanceName -Name `$_.Name -Version `$_.Version -Force }"
    $script += "Get-NAVAppInfo -ServerInstance `$fromServerInstanceName -SymbolsOnly | % { Unpublish-NAVApp -ServerInstance `$fromServerInstanceName -Name `$_.Name -Version `$_.Version }"
    $script += ""
    $script += "Stop-NAVServerInstance -ServerInstance `$fromServerInstanceName"
    $script += ""

    return $script
}

function Get-MigrationScript {

    $fromLSVersion = $upgradeInitializationScriptConfig.FromBC.LSVersion
    $fromLSMajorVersion = [int]($fromLSVersion.Split('.')[0])

    $script = @()
    switch ($fromLSMajorVersion)
    {
        "14" {
            $script = Get-MigrationScript_FromLS14
            break
        }
        default {
            $script = Get-MigrationScript_FromLS15OrLater
            break
        }
    }
    return $script
}

function Get-MigrationScript_FromLS14
{
    $sqlServer = $upgradeInitializationScriptConfig.SQL.Server
    $sqlServerInstance = $upgradeInitializationScriptConfig.SQL.Instance
    $databaseName = $upgradeInitializationScriptConfig.SQL.Database

    $fromVersion = $upgradeInitializationScriptConfig.FromBc.Version
    $fromLSVersion = $upgradeInitializationScriptConfig.FromBc.LSVersion

    $toServerInstanceName = $upgradeInitializationScriptConfig.ToBC.ServerInstance
    $toBCServerPath = $upgradeInitializationScriptConfig.ToBC.ServerPath
    $toVersion = $upgradeInitializationScriptConfig.ToBC.Version
    $toFullVersion = Get-ApplicationVersionFromArtifacts -version $upgradeInitializationScriptConfig.ToBC.Version

    $toLSVersion = $upgradeInitializationScriptConfig.ToBC.LSVersion
    $toLSApp = Get-LSCentralAppInfo -version $toLsVersion -country $country
    
    $multitenancy = $upgradeInitializationScriptConfig.Environment.Multitenant

    $customExtensions = $upgradeInitializationScriptConfig.CustomExtensions

    $script = @()
    $script += ""

    $script += ""
    $script += "######################################################### Step 1 - Going from LS Central $fromLSVersion (on BC $fromVersion) to LS Central $toLSVersion (on BC $toVersion) #############################################################"
    $script += "############# WARNING: Please make sure that you close and open a new Powershell console window otherwise Business Central Powershell modules might not load properly (Invoke-NAVApplicationDatabaseConversion will not be found) #########"
    $script += ""
    $script += "## BC Folders"
    $script += "`$toBCServerPath = `"$toBCServerPath`""
    $script += ""
    $script += "## BC Server Instances"
    $script += "`$toServerInstanceName = `"$toServerInstanceName`""
    $script += ""
    $script += "## SQL"
    if ($sqlServerInstance -eq "") {
        $script += "`$fullDatabaseServer = `"$sqlServer`""
    } else {
        $script += "`$fullDatabaseServer = `"$sqlServer\$sqlServerInstance`""
    }
    $script += "`$databaseServerOnly = `"$sqlServer`""
    $script += "`$databaseInstance = `"$sqlServerInstance`""
    $script += "`$databaseName = `"$databaseName`""
    $script += ""
    $script += "## Base folder"
    $script += "`$baseFolder = `"$(Get-Location)`""
    $script += ""    
    $script += "# Internal variables builder"
    $script += "`$migrationFilesPath = Join-Path `$baseFolder `"MigrationFiles`""
    $script += "`$licenseFile = Join-Path `$baseFolder `"License\DEV.bclicense`""
    $script += ""
    if ($multitenancy) {
        $script += "### Update this with the right tenant name"
        $script += "`$tenantId = `"default`""
        $script += ""
    }
    $script += "`$ErrorActionPreference = `"Stop`""
    $script += ""
    $script += ""
    $script += "# LS Central $toLsVersion (BC $toVersion)"
    $script += "Import-Module (Join-Path `$toBCServerPath 'Service\NavAdminTool.ps1') -Force"
    $script += ""
    $script += "###################################################################################################################################"
    $script += ""
    $script += "# Task 7: Convert version 14 database"
    $script += "Invoke-NAVApplicationDatabaseConversion -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Force"
    $script += ""
    $script += "# Task 8: Configure version 19 server for DestinationAppsForMigration"
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DatabaseServer -KeyValue `$databaseServerOnly"
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DatabaseName -KeyValue `$databaseName"
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DatabaseInstance -KeyValue `$databaseInstance"
    $script += "Set-NavServerConfiguration -ServerInstance `$toServerInstanceName -KeyName EnableTaskScheduler -KeyValue false"
    $script += ""
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DestinationAppsForMigration -KeyValue '[{`"appId`":`"73dac250-f355-4b89-9b27-f98837f2d1f2`", `"name`":`"ls-upgrade`", `"publisher`": `"LS Retail`"}]'"
    $script += ""
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += "# Task 9: Import License"
    $script += Add-ImportNAVServerLicenseToScript -LicenseFile `$licenseFile  -IsMultitenant $multitenancy
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += "# Task 10: Publish DestinationAppsForMigrations extensions"
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'ls-upgrade-1st_version.app')" -IsMultitenant $multitenancy -SkipVerification
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'LS Retail_ls-upgrade-tables_renaming_14.0.0.0.app')" -IsMultitenant $multitenancy -SkipVerification
    }
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'ls-upgrade-system-empty.app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'ls-upgrade-base-empty.app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'ls-upgrade-lscentral-empty.app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'ls-upgrade-lscentral_system_app-empty.app')" -IsMultitenant $multitenancy -SkipVerification
    foreach ($extension in $customExtensions) {
        $appPackageName = Get-AppPackageName -Publisher $extension.Publisher -Name $extension.Name
        $appPackageName = "$($appPackageName)-empty"
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath '$($appPackageName).app')" -IsMultitenant $multitenancy -SkipVerification
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Customization App_14.0.0.0.app')" -IsMultitenant $multitenancy -SkipVerification
    }
    $script += Add-GetNAVAppInfoToScript
    $script += ""
    $script += "# Task 11: Synchronize tenant"
    if ($multitenancy) {
        $script += "Mount-NAVTenant -ServerInstance `$toServerInstanceName -DatabaseName `$databaseName -DatabaseServer `$fullDatabaseServer -Tenant `$tenantId -AllowAppDatabaseWrite -Force"
    }
    $script += Add-SyncNAVTenantToScript -IsMultitenant $multitenancy -Mode Sync -Force
    $script += Add-SyncNAVAppToScript -Name "ls-upgrade" -Version 1.0.0.0
    $script += Add-SyncNAVAppToScript -Name "System Application" -Version 14.0.0.0
    $script += Add-SyncNAVAppToScript -Name "Base Application" -Version 14.0.0.0
    $script += Add-SyncNAVAppToScript -Name "LS Central System App" -Version 14.0.0.0
    $script += Add-SyncNAVAppToScript -Name "LS Central" -Version 14.0.0.0
    
    foreach ($extension in $customExtensions) {
        $script += Add-SyncNAVAppToScript -Name $($extension.Name) -Version 0.0.0.1
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-SyncNAVAppToScript -Name "<Customization app name>" -Version 14.0.0.0
    }
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += Add-SyncNAVAppToScript -Name "ls-upgrade-tables_renaming" -Version 14.0.0.0
    }
    $script += ""
    $script += "# Task 12: Install DestinationAppsForMigration and move tables"
    $script += Add-StartNAVDataUpgradeToScript -FunctionExecutionMode Serial -SkipAppVersionCheck -Force -IsMultitenant $multitenancy
    $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Progress)
    $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Detailed)
    $script += (Add-GetNAVTenantToScript -IsMultitenant $multitenancy) + " # State must be Operational when the data upgrade is finished."

    $script += ""
    $script += Add-InstallNAVAppToScript -Name "System Application" -Version 14.0.0.0
    $script += Add-InstallNAVAppToScript -Name "Base Application" -Version 14.0.0.0
    $script += Add-InstallNAVAppToScript -Name "LS Central System App" -Version 14.0.0.0
    $script += Add-InstallNAVAppToScript -Name "LS Central" -Version 14.0.0.0

    foreach ($extension in $customExtensions) {
        $script += Add-InstallNAVAppToScript -Name $($extension.Name) -Version 0.0.0.1
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-InstallNAVAppToScript -Name "<Customization app name>" -Version 14.0.0.0
    }
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += Add-InstallNAVAppToScript -Name "ls-upgrade-tables_renaming" -Version 14.0.0.0
    }
    $script += ""
    if ($multitenancy) {
        $script += "### REPEAT THESE STEPS FOR EACH TENANT"
    }
    $script += ""
    $script += "# Task 13: Publish final extensions"
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'ls-upgrade-2nd_version-migration.app')" -IsMultitenant $multitenancy -SkipVerification
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {    
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'LS Retail_ls-upgrade-tables_renaming_18.0.0.0.app')" -IsMultitenant $multitenancy -SkipVerification
    }
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Microsoft_System Application_$($toFullVersion).app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Microsoft_Base Application_$($toFullVersion).app')" -IsMultitenant $multitenancy -SkipVerification
    $script += "`$appInfo = (Get-NAVAppInfo -ServerInstance `$toServerInstanceName -Name `"Application`" -SymbolsOnly)[0]"
    $script += Add-UnpublishNAVAppToScript -Name "Application" -Version "`$appInfo.Version.ToString()" -IsMultitenant $multitenancy
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Microsoft_Application_$($toFullVersion).app')" -IsMultitenant $multitenancy -SkipVerification
    $script += ""
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath `"$($toLSApp.systemFile)`")" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath `"$($toLSApp.appFile)`")" -IsMultitenant $multitenancy -SkipVerification
    if ($customExtensions.Count -gt 0) {
        $script += "## UPDATE TO THE APPROPRIATE CUSTOM EXTENSION APP FILE PATH AND NAME."
        foreach ($extension in $customExtensions) {
            $appFilename = Get-AppFileName -Publisher $extension.Publisher -Name $extension.Name -Version $extension.Version
            $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath '$($appFilename)')" -IsMultitenant $multitenancy -SkipVerification
        }
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath `"<customization final app file>`")" -IsMultitenant $multitenancy -SkipVerification
    }
    $script += Add-GetNAVAppInfoToScript
    $script += ""
    $script += "# Task 14: Synchronize final extensions"
    $script += Add-SyncNAVAppToScript -Name "System Application" -Version $($toFullVersion)
    $script += Add-SyncNAVAppToScript -Name "Base Application" -Version $($toFullVersion)
    $script += Add-SyncNAVAppToScript -Name "Application" -Version $($toFullVersion)
    $script += Add-SyncNAVAppToScript -Name "$($toLSApp.systemAppName)" -Version $($toLSApp.systemAppVersion)
    $script += Add-SyncNAVAppToScript -Name "$($toLSApp.appName)" -Version $($toLSApp.appVersion)

    foreach ($extension in $customExtensions) {
        $script += Add-SyncNAVAppToScript -Name "$($extension.Name)" -Version $($extension.Version)
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-SyncNAVAppToScript -Name "<Customization app name>" -Version "<Customization final app version>"
    }
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += Add-SyncNAVAppToScript -Name "ls-upgrade-tables_renaming" -Version "18.0.0.0"
    }
    $script += "Sync-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade`" -Version 2.0.0.0 # -Mode ForceSync # -> Only if needed."
    $script += ""
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DestinationAppsForMigration -KeyValue '[{`"appId`":`"04e836a7-2c96-4bed-9651-a5c57572bb22`", `"name`":`"ls-upgrade-renamed-tables`", `"publisher`": `"LS Retail`"}]'"
        $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
        $script += ""
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'LS Retail_ls-upgrade-tables_renaming_18.0.1.0.app')" -IsMultitenant $multitenancy
        $script += "Sync-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade-tables_renaming`" -Version 18.0.1.0 -Mode ForceSync -Force"
        $script += ""
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'LS Retail_ls-upgrade-tables_renaming_18.0.2.0.app')" -IsMultitenant $multitenancy
        $script += Add-SyncNAVAppToScript -Name "ls-upgrade-tables_renaming" -Version "18.0.2.0"
    }
    $script += ""
    $script += "# Task 15: Upgrade empty table migration extension"
    $script += Add-StartNAVAppDataUpgradeToScript -Name "ls-upgrade" -Version "2.0.0.0" -IsMultitenant $multitenancy
    $script += ""
    $script += "# Task 16: Clean sync and unpublish table migration extensions"
    $script += "Uninstall-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade`" -Version 2.0.0.0"
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += "Uninstall-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade-renamed-tables`" -Version 18.0.2.0"
    }
    $script += "Sync-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade`" -Version 2.0.0.0 -Mode clean"
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += "Sync-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade-renamed-tables`" -Version 18.0.2.0 -Mode clean"
    }
    $script += Add-UnpublishNAVAppToScript -Name "ls-upgrade" -Version "1.0.0.0" -IsMultitenant $multitenancy
    $script += Add-UnpublishNAVAppToScript -Name "ls-upgrade" -Version "2.0.0.0" -IsMultitenant $multitenancy
    if ($upgradeMode -eq [UpgradeMode]::BC14_18_TablesRenamingInBC18) {
        $script += Add-UnpublishNAVAppToScript -Name "ls-upgrade-renamed-tables" -Version "14.0.0.0" -IsMultitenant $multitenancy
        $script += "Unpublish-NAVApp -ServerInstance `$toServerInstanceName -Tenant `$tenantId -Name `"ls-upgrade-renamed-tables`" -Version 18.0.0.0"
        $script += Add-UnpublishNAVAppToScript -Name "ls-upgrade-renamed-tables" -Version "18.0.0.0" -IsMultitenant $multitenancy
        $script += Add-UnpublishNAVAppToScript -Name "ls-upgrade-renamed-tables" -Version "18.0.1.0" -IsMultitenant $multitenancy
        $script += Add-UnpublishNAVAppToScript -Name "ls-upgrade-renamed-tables" -Version "18.0.2.0" -IsMultitenant $multitenancy
    }
    $script += ""
    $script += ""
    $script += "# Task 17: Upgrade and install final extensions"
    $script += Add-StartNAVAppDataUpgradeToScript -Name "System Application" -Version $($toFullVersion) -IsMultitenant $multitenancy
    $script += Add-StartNAVAppDataUpgradeToScript -Name "Base Application" -Version $($toFullVersion) -IsMultitenant $multitenancy
    $script += Add-InstallNAVAppToScript -Name "Application" -Version $($toFullVersion)
    $script += Add-StartNAVAppDataUpgradeToScript -Name "$($toLSApp.systemAppName)" -Version $($toLSApp.systemAppVersion) -IsMultitenant $multitenancy
    $script += Add-StartNAVAppDataUpgradeToScript -Name "$($toLSApp.appName)" -Version $($toLSApp.appVersion) -IsMultitenant $multitenancy
    foreach ($extension in $customExtensions) {
        $script += Add-StartNAVAppDataUpgradeToScript -Name "$($extension.Name)" -Version $($extension.Version) -IsMultitenant $multitenancy
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-StartNAVAppDataUpgradeToScript -Name "<Customization app name>" -Version "<Customization final app version>" -IsMultitenant $multitenancy
    }
    $script += ""
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += "# Task 18: Upgrade control add-ins"
    $script += "`$servicesAddinsFolder = Join-Path `$toBCServerPath 'Service\Add-ins'"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.BusinessChart' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'BusinessChart\Microsoft.Dynamics.Nav.Client.BusinessChart.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.FlowIntegration' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'FlowIntegration\Microsoft.Dynamics.Nav.Client.FlowIntegration.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.OAuthIntegration' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'OAuthIntegration\Microsoft.Dynamics.Nav.Client.OAuthIntegration.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.PageReady' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'PageReady\Microsoft.Dynamics.Nav.Client.PageReady.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.PowerBIManagement' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'PowerBIManagement\Microsoft.Dynamics.Nav.Client.PowerBIManagement.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.RoleCenterSelector' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'RoleCenterSelector\Microsoft.Dynamics.Nav.Client.RoleCenterSelector.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.SatisfactionSurvey' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'SatisfactionSurvey\Microsoft.Dynamics.Nav.Client.SatisfactionSurvey.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.SocialListening' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'SocialListening\Microsoft.Dynamics.Nav.Client.SocialListening.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.VideoPlayer' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'VideoPlayer\Microsoft.Dynamics.Nav.Client.VideoPlayer.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.WebPageViewer' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'WebPageViewer\Microsoft.Dynamics.Nav.Client.WebPageViewer.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.WelcomeWizard' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'WelcomeWizard\Microsoft.Dynamics.Nav.Client.WelcomeWizard.zip')"
    $script += ""
    $script += "# Task 19: Install upgraded permissions sets"
    $script += ""
    $script += "# Task 20: Change application version"
    $script += "Set-NAVApplication -ServerInstance `$toServerInstanceName -ApplicationVersion $($toFullVersion) -Force"
    $script += Add-SyncNAVTenantToScript -IsMultitenant $multitenancy -Mode Sync -Force
    $script += ""
    $script += Add-StartNAVDataUpgradeToScript -FunctionExecutionMode Serial -Force -IsMultitenant $multitenancy
    $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Progress)
    $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Detailed)
    $script += (Add-GetNAVTenantToScript -IsMultitenant $multitenancy) + " # State must be Operational when the data upgrade is finished."
    $script += ""
    $script += "# Post-upgrade tasks"
    $script += Add-UnpublishNAVAppToScript -Name "System Application" -Version "14.0.0.0" -IsMultitenant $multitenancy
    $script += Add-UnpublishNAVAppToScript -Name "Base Application" -Version "14.0.0.0" -IsMultitenant $multitenancy
    $script += Add-UnpublishNAVAppToScript -Name "$($toLSApp.appName)" -Version "14.0.0.0" -IsMultitenant $multitenancy
    $script += Add-UnpublishNAVAppToScript -Name "$($toLSApp.systemAppName)" -Version "14.0.0.0" -IsMultitenant $multitenancy
    foreach ($extension in $customExtensions) {
        $script += Add-UnpublishNAVAppToScript -Name "$($extension.Name)" -Version "0.0.0.1" -IsMultitenant $multitenancy
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-UnpublishNAVAppToScript -Name "<Customization app name>" -Version "14.0.0.0" -IsMultitenant $multitenancy
    }
    $script += Add-GetNAVAppInfoToScript
    $script += ""
    $script += "Set-NavServerConfiguration -ServerInstance `$toServerInstanceName -KeyName EnableTaskScheduler -KeyValue true"
    $script += ""
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += ""

    return $script
}

function Get-MigrationScript_FromLS15OrLater
{
    $sqlServer = $upgradeInitializationScriptConfig.SQL.Server
    $sqlServerInstance = $upgradeInitializationScriptConfig.SQL.Instance
    $databaseName = $upgradeInitializationScriptConfig.SQL.Database

    $fromVersion = $upgradeInitializationScriptConfig.FromBc.Version
    $fromLSVersion = $upgradeInitializationScriptConfig.FromBc.LSVersion

    $toServerInstanceName = $upgradeInitializationScriptConfig.ToBC.ServerInstance
    $toBCServerPath = $upgradeInitializationScriptConfig.ToBC.ServerPath
    $toVersion = $upgradeInitializationScriptConfig.ToBC.Version
    $toFullVersion = Get-ApplicationVersionFromArtifacts -version $upgradeInitializationScriptConfig.ToBC.Version

    $toLSVersion = $upgradeInitializationScriptConfig.ToBC.LSVersion
    $toLSApp = Get-LSCentralAppInfo -version $toLsVersion -country $country
    
    $fromLSMajorVersion = [int]($fromLSVersion.Split('.')[0])
    $fromLSMajorMinorVersion = (($fromLSVersion.Split('.')[0])+ "." + ($fromLSVersion.Split('.')[1]))

    $multitenancy = $upgradeInitializationScriptConfig.Environment.Multitenant

    $customExtensions = $upgradeInitializationScriptConfig.CustomExtensions

    $script = @()
    $script += ""

    $script += ""
    $script += "######################################################### Step 1 - Going from LS Central $fromLSVersion (on BC $fromVersion) to LS Central $toLSVersion (on BC $toVersion) #############################################################"
    $script += "############# WARNING: Please make sure that you close and open a new Powershell console window otherwise Business Central Powershell modules might not load properly (Invoke-NAVApplicationDatabaseConversion will not be found) #########"
    $script += ""
    $script += "## BC Folders"
    $script += "`$toBCServerPath = `"$toBCServerPath`""
    $script += ""
    $script += "## BC Server Instances"
    $script += "`$toServerInstanceName = `"$toServerInstanceName`""
    $script += ""
    $script += "## SQL"
    if ($sqlServerInstance -eq "") {
        $script += "`$fullDatabaseServer = `"$sqlServer`""
    } else {
        $script += "`$fullDatabaseServer = `"$sqlServer\$sqlServerInstance`""
    }
    $script += "`$databaseServerOnly = `"$sqlServer`""
    $script += "`$databaseInstance = `"$sqlServerInstance`""
    $script += "`$databaseName = `"$databaseName`""
    $script += ""
    $script += "## Base folder"
    $script += "`$baseFolder = `"$(Get-Location)`""
    $script += ""    
    $script += "# Internal variables builder"
    $script += "`$migrationFilesPath = Join-Path `$baseFolder `"MigrationFiles`""
    $script += "`$licenseFile = Join-Path `$baseFolder `"License\DEV.bclicense`""
    $script += ""
    if ($multitenancy) {
        $script += "### Update this with the right tenant name"
        $script += "`$tenantId = `"default`""
        $script += ""
    }
    $script += "`$ErrorActionPreference = `"Stop`""
    $script += ""
    $script += ""
    $script += "# LS Central $toLsVersion (BC $toVersion)"
    $script += "Import-Module (Join-Path `$toBCServerPath 'Service\NavAdminTool.ps1') -Force"
    $script += ""
    $script += "###################################################################################################################################"
    $script += ""
    $script += "# Task 4: Convert application database"
    $script += "Invoke-NAVApplicationDatabaseConversion -DatabaseServer `$fullDatabaseServer -DatabaseName `$databaseName -Force"
    $script += ""
    $script += "# Task 5: Configure server"
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DatabaseServer -KeyValue `$databaseServerOnly"
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DatabaseName -KeyValue `$databaseName"
    $script += "Set-NAVServerConfiguration -ServerInstance `$toServerInstanceName -KeyName DatabaseInstance -KeyValue `$databaseInstance"
    $script += "Set-NavServerConfiguration -ServerInstance `$toServerInstanceName -KeyName EnableTaskScheduler -KeyValue false"
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += "# Task 6: Import license"
    $script += Add-ImportNAVServerLicenseToScript -LicenseFile `$licenseFile  -IsMultitenant $multitenancy
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += "# Task 7: Publish extensions"
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Microsoft_System Application_$($toFullVersion).app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Microsoft_Base Application_$($toFullVersion).app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath 'Microsoft_Application_$($toFullVersion).app')" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath `"$($toLSApp.systemFile)`")" -IsMultitenant $multitenancy -SkipVerification
    $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath `"$($toLSApp.appFile)`")" -IsMultitenant $multitenancy -SkipVerification
    if ($customExtensions.Count -gt 0) {
        $script += "## UPDATE TO THE APPROPRIATE CUSTOM EXTENSION APP FILE PATH AND NAME."
        foreach ($extension in $customExtensions) {
            $appFilename = Get-AppFileName -Publisher $extension.Publisher -Name $extension.Name -Version $extension.Version
            $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath '$($appFilename)')" -IsMultitenant $multitenancy -SkipVerification
        }
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-PublishNAVAppToScript -Path "(Join-Path `$migrationFilesPath `"<customization final app file>`")" -IsMultitenant $multitenancy -SkipVerification
    }
    $script += Add-GetNAVAppInfoToScript
    $script += ""
    $script += "# Task 8: Synchronize tenant"
    if ($multitenancy) {
        $script += "Mount-NAVTenant -ServerInstance `$toServerInstanceName -DatabaseName `$databaseName -DatabaseServer `$fullDatabaseServer -Tenant `$tenantId -AllowAppDatabaseWrite -Force"
    }
    $script += Add-SyncNAVTenantToScript -IsMultitenant $multitenancy -Mode Sync -Force
    $script += Add-SyncNAVAppToScript -Name "System Application" -Version $($toFullVersion)
    $script += Add-SyncNAVAppToScript -Name "Base Application" -Version $($toFullVersion)
    $script += Add-SyncNAVAppToScript -Name "Application" -Version $($toFullVersion)
    $script += Add-SyncNAVAppToScript -Name "LS Central System App" -Version $($toLSApp.systemAppVersion)
    
    if ($fromLSMajorMinorVersion -le 17.4) {
        $script += Add-SyncNAVAppToScript -Name "LS Central" -Version $($toLSApp.appVersion) -Mode ForceSync -Force
    } else {
        $script += Add-SyncNAVAppToScript -Name "LS Central" -Version $($toLSApp.appVersion)
    }
    
    foreach ($extension in $customExtensions) {
        $script += Add-SyncNAVAppToScript -Name $($extension.Name) -Version $($extension.Version)
    }
    if ($includeCustomizationAppPlaceholders) {
        $script += "### This is a placeholder"
        $script += Add-SyncNAVAppToScript -Name "<Customization app name>" -Version "<Customization app version>"
    }
    $script += ""
    $script += "# Task 9: Upgrade data"
    if ($multitenancy) {
        $script += "### REPEAT THESE STEPS FOR EACH TENANT"
        $script += Add-StartNAVDataUpgradeToScript -FunctionExecutionMode Serial -SkipAppVersionCheck -Force -IsMultitenant $multitenancy
        $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Progress)
        $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Detailed)
        $script += (Add-GetNAVTenantToScript -IsMultitenant $multitenancy) + " # State must be Operational when the data upgrade is finished."
        $script += ""
    } else {
        $script += Add-StartNAVAppDataUpgradeToScript -Name "System Application" -Version $($toFullVersion) -IsMultitenant $multitenancy
        $script += Add-StartNAVAppDataUpgradeToScript -Name "Base Application" -Version $($toFullVersion) -IsMultitenant $multitenancy
        $script += Add-StartNAVAppDataUpgradeToScript -Name "Application" -Version $($toFullVersion) -IsMultitenant $multitenancy
        if ($fromLSMajorVersion -ge "18") {
            $script += Add-StartNAVAppDataUpgradeToScript -Name "$($toLSApp.systemAppName)" -Version $($toLSApp.systemAppVersion) -IsMultitenant $multitenancy
        } else {
            $script += Add-InstallNAVAppToScript -Name "$($toLSApp.systemAppName)" -Version $($toLSApp.systemAppVersion) -IsMultitenant $multitenancy
        }
        $script += Add-StartNAVAppDataUpgradeToScript -Name "$($toLSApp.appName)" -Version $($toLSApp.appVersion) -IsMultitenant $multitenancy
        foreach ($extension in $customExtensions) {
            $script += Add-StartNAVAppDataUpgradeToScript -Name "$($extension.Name)" -Version $($extension.Version) -IsMultitenant $multitenancy
        }
        if ($includeCustomizationAppPlaceholders) {
            $script += "### This is a placeholder"
            $script += Add-StartNAVAppDataUpgradeToScript -Name "<Customization app name>" -Version "<Customization final app version>" -IsMultitenant $multitenancy
        }
        $script += ""
    }
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    
    $script += "# Task 11: Upgrade control add-ins"
    $script += "`$servicesAddinsFolder = Join-Path `$toBCServerPath 'Service\Add-ins'"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.BusinessChart' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'BusinessChart\Microsoft.Dynamics.Nav.Client.BusinessChart.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.FlowIntegration' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'FlowIntegration\Microsoft.Dynamics.Nav.Client.FlowIntegration.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.OAuthIntegration' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'OAuthIntegration\Microsoft.Dynamics.Nav.Client.OAuthIntegration.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.PageReady' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'PageReady\Microsoft.Dynamics.Nav.Client.PageReady.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.PowerBIManagement' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'PowerBIManagement\Microsoft.Dynamics.Nav.Client.PowerBIManagement.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.RoleCenterSelector' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'RoleCenterSelector\Microsoft.Dynamics.Nav.Client.RoleCenterSelector.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.SatisfactionSurvey' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'SatisfactionSurvey\Microsoft.Dynamics.Nav.Client.SatisfactionSurvey.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.SocialListening' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'SocialListening\Microsoft.Dynamics.Nav.Client.SocialListening.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.VideoPlayer' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'VideoPlayer\Microsoft.Dynamics.Nav.Client.VideoPlayer.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.WebPageViewer' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'WebPageViewer\Microsoft.Dynamics.Nav.Client.WebPageViewer.zip')"
    $script += "Set-NAVAddIn -ServerInstance `$toServerInstanceName -AddinName 'Microsoft.Dynamics.Nav.Client.WelcomeWizard' -PublicKeyToken 31bf3856ad364e35 -ResourceFile (Join-Path `$servicesAddinsFolder 'WelcomeWizard\Microsoft.Dynamics.Nav.Client.WelcomeWizard.zip')"
    $script += ""
    $script += "# Task 12: Install upgraded permissions sets"
    $script += ""
    $script += "# Task 13: Change application version"
    $script += "Set-NAVApplication -ServerInstance `$toServerInstanceName -ApplicationVersion $($toFullVersion) -Force"
    $script += Add-SyncNAVTenantToScript -IsMultitenant $multitenancy -Mode Sync -Force
    $script += ""
    $script += Add-StartNAVDataUpgradeToScript -FunctionExecutionMode Serial -Force -IsMultitenant $multitenancy
    $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Progress)
    $script += " " + (Add-GetNavDataUpgradeToScript -IsMultitenant $multitenancy -Detailed)
    $script += (Add-GetNAVTenantToScript -IsMultitenant $multitenancy) + " # State must be Operational when the data upgrade is finished."
    $script += ""
    $script += "#Task Post"
    $script += "Set-NavServerConfiguration -ServerInstance `$toServerInstanceName -KeyName EnableTaskScheduler -KeyValue true"
    $script += ""
    $script += "Set-NAVServerInstance -ServerInstance `$toServerInstanceName -Restart"
    $script += ""
    $script += ""

    return $script
}

function Add-PublishNAVAppToScript {
    param (
        [Parameter(Mandatory)]
        [string] $Path,
        [switch] $Force,
        [switch] $SkipVerification,
        [bool] $IsMultitenant
    )
    $result = "Publish-NAVApp -ServerInstance `$toServerInstanceName -Path $($Path)"
    if ($Force.IsPresent) { $result += " -Force" }
    if ($SkipVerification.IsPresent) { $result += " -SkipVerification" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-UnpublishNAVAppToScript {
    param (
        [bool] $IsMultitenant,
        [string] $Name,
        [string] $Version
    )
    $result = "Unpublish-NAVApp -ServerInstance `$toServerInstanceName"
    if ($Name -ne "") { $result += " -Name `"$($Name)`"" }
    if ($Version -ne "") { $result += " -Version $($Version)" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-SyncNAVAppToScript {
    param (
        [Parameter(Mandatory)]
        [string] $Name,
        [string] $Version,
        [switch] $Force,
        [ValidateSet("Add","Clean","Development","ForceSync","None")]
        [string] $Mode,
        [bool] $IsMultitenant
    )
    $result = "Sync-NAVApp -ServerInstance `$toServerInstanceName -Name `"$($Name)`""
    if ($Version -ne "") { $result += " -Version $($Version)" }
    if ($Mode -ne "") { $result += " -Mode $($Mode)" }
    if ($Force.IsPresent) { $result += " -Force" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-InstallNAVAppToScript {
    param (
        [bool] $IsMultitenant,
        [string] $Name,
        [string] $Version
    )
    $result = "Install-NAVApp -ServerInstance `$toServerInstanceName -Name `"$($Name)`""
    if ($Version -ne "") { $result += " -Version $($Version)" }
    if ($Force.IsPresent) { $result += " -Force" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-StartNAVAppDataUpgradeToScript {
    param (
        [bool] $IsMultitenant,
        [string] $Name,
        [string] $Version
    )
    $result = "Start-NAVAppDataUpgrade -ServerInstance `$toServerInstanceName -Name `"$($Name)`""
    if ($Version -ne "") { $result += " -Version $($Version)" }
    if ($Force.IsPresent) { $result += " -Force" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-SyncNAVTenantToScript {
    param (
        [switch] $Force,
        [ValidateSet("ForceSync","Sync","CheckOnly")]
        [string] $Mode,
        [bool] $IsMultitenant
    )
    $result = "Sync-NAVTenant -ServerInstance `$toServerInstanceName"
    if ($Mode -ne "") { $result += " -Mode $($Mode)" }
    if ($Force.IsPresent) { $result += " -Force" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-GetNAVAppInfoToScript {
    param (
        [bool] $IsMultitenant
    )
    $result = "Get-NAVAppInfo -ServerInstance `$toServerInstanceName"
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-ImportNAVServerLicenseToScript {
    param (
        [string] $LicenseFile,
        [bool] $IsMultitenant
    )
    $result = "Import-NAVServerLicense -ServerInstance `$toServerInstanceName -LicenseFile `$licenseFile"
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-StartNAVDataUpgradeToScript {
    param (
        [Parameter(Mandatory)]
        [ValidateSet("Serial","Paralel")]
        [string] $FunctionExecutionMode,
        [switch] $SkipAppVersionCheck,
        [switch] $Force,
        [bool] $IsMultitenant
    )

    $result = "Start-NAVDataUpgrade -ServerInstance `$toServerInstanceName -FunctionExecutionMode $($FunctionExecutionMode)"
    if ($SkipAppVersionCheck.IsPresent) { $result += " -SkipAppVersionCheck" }
    if ($Force.IsPresent) { $result += " -Force" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-GetNAVTenantToScript {
    param (
        [bool] $IsMultitenant
    )

    $result = "Get-NAVTenant -ServerInstance `$toServerInstanceName"
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Add-GetNAVDataUpgradeToScript {
    param (
        [switch] $Progress,
        [switch] $Detailed,
        [bool] $IsMultitenant
    )

    $result = "Get-NAVDataUpgrade -ServerInstance `$toServerInstanceName"
    if ($Progress.IsPresent) { $result += " -Progress" }
    if ($Detailed.IsPresent) { $result += " -Detailed" }
    if ($IsMultitenant) { $result += " -Tenant `$tenantId" }
    return $result
}

function Get-DefaultDestinationVersion {
    $app = Get-LSCentralAppInfo -DefaultVersion
    return $app.version
}

Export-ModuleMember -Function New-UpgradeInitializationScript
Export-ModuleMember -Function Add-PublishNAVAppToScript
Export-ModuleMember -Function Add-UnpublishNAVAppToScript
Export-ModuleMember -Function Add-SyncNAVAppToScript
Export-ModuleMember -Function Add-InstallNAVAppToScript
Export-ModuleMember -Function Add-StartNAVAppDataUpgradeToScript
Export-ModuleMember -Function Add-SyncNAVTenantToScript
Export-ModuleMember -Function Add-GetNAVAppInfoToScript
Export-ModuleMember -Function Add-ImportNAVServerLicenseToScript
Export-ModuleMember -Function Add-StartNAVDataUpgradeToScript
Export-ModuleMember -Function Add-GetNAVDataUpgradeToScript
Export-ModuleMember -Function Add-GetNAVTenantToScript
Export-ModuleMember -Function Get-DefaultDestinationVersion