functions/apphandling/Invoke-NavAppUpgrade.ps1

function Invoke-NavAppUpgrade {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$ServerInstance,

        [Parameter(Mandatory)]
        [string]$AppPath,

        [string]$PlatformVersion,

        [switch]$Force
    )
    if ([string]::IsNullOrWhiteSpace($PlatformVersion))
    {
        # Determine platform version
        $PlatformVersion = Get-NAVServerPlatform -ServerInstance $ServerInstance
        if (-not $PlatformVersion) {
            Write-Error "Could not auto-detect platform version! Please use -PlatformVersion parameter."
            return $false
        }

        Write-Verbose "Auto-detect platform version: $PlatformVersion"
    } else {
        Write-Verbose "Using -PlatformVersion $PlatformVersion"
    }

    if ([string]::IsNullOrWhiteSpace($PlatformVersion))
    {
        Write-Error "Unable to determine platform version! Please use -PlatformVersion parameter."
        return $false
    }

    if (-not ((Get-Command -Name Get-NAVAppInfo) -and (Get-Command -Name Get-NAVServerInstance)))
    {
        # Import management module
        & (Get-DynamicsNavModule -PlatformVersion $PlatformVersion -ModuleType NavAdminTool)
    }

    # kann Get-NAVServerInstance schon -KeyName ?
    $cmd = Get-Command Get-NAVServerConfiguration -ErrorAction SilentlyContinue
    $hasKeyName = $cmd.Parameters.ContainsKey('KeyName')

    # Multitenant?
    if ($hasKeyName)
    {
        [bool]$isMultitenant = ((Get-NAVServerConfiguration -ServerInstance $ServerInstance -KeyName Multitenant).ToLower() -eq 'true')
    } else {
        $config = Get-NAVServerConfiguration -ServerInstance $ServerInstance
        $multitenantNode = $config | Where-Object {
            $_.Name -eq 'key' -and $_.Attributes['name'].Value -eq 'Multitenant'
        }

        if ($multitenantNode) {
            [bool]$isMultitenant = ([string]($multitenantNode.Attributes['value'].Value).ToLower() -eq 'true')
            Write-Verbose "Multitenant: $isMultitenant"
        } else {
            Write-Warning "Multitenant setting not found! Assuming 'false'"
            $isMultitenant = $false
        }
    }

    Write-Verbose "Multitenant auto-detect: $isMultitenant"

    # AppInfo and handle AppId
    try {
        $AppInfo = Get-NAVAppInfo -Path $AppPath
    }
    catch {
        if ($_.Exception.Message -like '*contains corrupted data*') {
            Write-Error "App is not compatible with the server or app file is corrupted!"
            return $false
        } else {
            Write-Error $_
            return $false
        }
    }

    if ($AppInfo.PSObject.Properties['AppId']) {
        $AppId = $AppInfo.AppId.Value.Guid
        Write-Verbose "Found AppId: $($AppId.ToString())"
    } elseif ($AppInfo.PSObject.Properties['Id']) {
        $AppId = $AppInfo.Id
        Write-Verbose "Found Id: $($AppId)"
    } else {
        Write-Error "AppInfo has no 'AppId' or 'Id'."
        return $false
    }

    # find published versions
    $publishedVersions = Get-NAVAppInfo -ServerInstance $ServerInstance -Id $AppInfo.AppId
    $isPublished = ($publishedVersions.Version -contains $AppInfo.Version)
    Write-Verbose "IsPublished: $isPublished"
    if ($isPublished) {
        Write-Verbose "Published versions: $($publishedVersions.Count)"
    }

    # find installed versions/tenants
    $installedOnTenants = Get-NAVAppTenant -ServerInstance $ServerInstance -Id $AppId
    if (-not $installedOnTenants)
    {
        if (-not $Force) {
            Write-Error "App is not installed! Cannot upgrade!"
            return $false
        } else {
            $installedOnTenantsCount = 0
        }
    } else {
        $installedOnTenantsCount = $installedOnTenants.Count
        Write-Verbose "App is installed on tenants: $installedOnTenantsCount"
    }

    if (-not ($isMultitenant) -and ($installedOnTenantsCount -gt 1)) {
        Write-Warning "Mutlitenant detection failed! Found app installed on $($installedOnTenants.Count) tenants. Assuming Mutlitenant 'true'"
        $isMultitenant = $true
    }

    # Publish new version
    if (-not $isPublished)
    {
        try {
            Write-Verbose "Publish $($AppPath)"
            Publish-NAVApp -ServerInstance $ServerInstance -Path $AppPath -ErrorAction Stop
        } 
        catch {
            if ($_.Exception.Message -like '*not been code signed*') {
                Write-Warning "Extension app has not been code signed!"
                Publish-NAVApp -ServerInstance $ServerInstance -Path $AppPath -SkipVerification -ErrorAction Stop
            } else {
                Write-Error $_
                return $false
            }
        }
    } else {
        Write-Warning "App was already published!"
    }

    # Sync and upgrade tenants
    if ($isMultitenant) {
        Write-Verbose "Start upgrade in multitenant mode"
        [string[]]$upgradeTenants = @()
        if (-not $installedOnTenants -and $Force) {
            # This is the case when an app was installed but currently not is.
            # Usually we should only upgrade installed apps. This is what -Force stands for.
            # In this case we re-install apps with this upgrade.
            # So we only process tenants that have ExtensionDataVersion -ne $null
            $tenants = Get-NAVTenant -ServerInstance $ServerInstance
            foreach($tenant in $tenants)
            {
                $extensionDataVersion = (Get-NAVAppInfo -ServerInstance $ServerInstance -Tenant $tenant.Id -TenantSpecificProperties -Id $AppInfo.AppId)[0].ExtensionDataVersion
                if ($null -ne $extensionDataVersion) {
                    $upgradeTenants += $tenant.Id
                }
            }
        } else {
            # Only tenants that currently have installed the app
            foreach($tenant in $installedOnTenants) {
                $upgradeTenants += $tenant.Id
            }
        }
        foreach($tenantId in $upgradeTenants)
        {
            Write-Verbose "Start upgrade process for tenant $tenantId"
            $extensionDataVersion = (Get-NAVAppInfo -ServerInstance $ServerInstance -Tenant $tenantId -TenantSpecificProperties -Id $AppInfo.AppId)[0].ExtensionDataVersion
            Write-Verbose "Current tenant ExtensionDataVersion: $extensionDataVersion"
            $isSynchronized = ($extensionDataVersion -eq $AppInfo.Version)
            if (-not $isSynchronized) {
                try {
                    Write-Verbose "Synchronize $tenantId"
                    Sync-NAVApp -ServerInstance $ServerInstance -Tenant $tenantId -Publisher $AppInfo.Publisher -Name $AppInfo.Name -Version $AppInfo.Version
                    $isSynchronized = $true
                }
                catch {
                    Write-Error "Sync failed for tenant $($tenantId): $_"
                }
            } else {
                Write-Information "Tenant $tenantId already synchronized."
            }
            if ($isSynchronized)
            {
                $needsUpgrade = (Get-NAVAppInfo -ServerInstance $ServerInstance -Tenant $tenantId -TenantSpecificProperties -Publisher $AppInfo.Publisher -Name $AppInfo.Name -Version $AppInfo.Version).NeedsUpgrade
                if ($needsUpgrade) {
                    try {
                        Write-Verbose "Upgrade $tenantId to $($AppInfo.Name) $($AppInfo.Version)"
                        Start-NAVAppDataUpgrade -ServerInstance $ServerInstance -Tenant $tenantId -Publisher $AppInfo.Publisher -Name $AppInfo.Name -Version $AppInfo.Version
                    }
                    catch {
                        Write-Error "Upgrade failed for tenant $($tenantId): $_"
                    }
                } else {
                    Write-Information "Tenant $tenantId already upgraded."
                }
            }
        }
    } else {
        Write-Verbose "Start upgrade in single-tenant mode"
        $isSynchronized = $false
        try {
            Write-Verbose "Synchronize"
            Sync-NAVApp -ServerInstance $ServerInstance -Publisher $AppInfo.Publisher -Name $AppInfo.Name -Version $AppInfo.Version
            $isSynchronized = $true
        }
        catch {
            Write-Error "Sync failed: $_"
            return $false
        }
        if ($isSynchronized)
        {
            try {
                Write-Verbose "Upgrade to $($AppInfo.Name) $($AppInfo.Version)"
                Start-NAVAppDataUpgrade -ServerInstance $ServerInstance -Publisher $AppInfo.Publisher -Name $AppInfo.Name -Version $AppInfo.Version
            }
            catch {
                Write-Error "Upgrade failed: $_"
                return $false
            }
        }
    }

    # Unpublish all old versions
    foreach ($oldApp in $publishedVersions)
    {
        # Failsave
        if ($oldApp.Version -eq $AppInfo.Verion)
        {
            continue
        }
        try
        {
            Write-Verbose "Unpublish $($oldApp.Publisher) $($oldApp.Name) $($oldApp.Version)"
            Unpublish-NAVApp -ServerInstance $ServerInstance -Publisher $oldApp.Publisher -Name $oldApp.Name -Version $oldApp.Version   
        }
        catch {
            Write-Warning "Unpublish failed: $_"
        }
    }

    return $true
}