Publish/Publish-BcAppsUsePowerShellNew.ps1
function Publish-BcAppsUsePowerShellNew { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]]$appPaths, [Parameter(Mandatory = $true)] [string]$ServerInstance, [switch]$force, [switch]$uninstallApps, [switch]$ScopeTenant ) Write-Host "Force mode: $force" $appInfo = @{} $installApps = [ordered]@{} $removeApps = [ordered]@{} $targetApps = @() $DependentAppNames = @{} $DependentAppNamesWithoutTarget = @{} $UnpublishApps = @{} $maxAttempts = 5 $attempt = 0 Get-BcManagementModule -ServerInstance $ServerInstance $installedExtensions = Get-NAVAppInfo -ServerInstance $ServerInstance $sortApps = Sort-AppFilesByDependencies -appFiles $appPaths 3> $null foreach ($appPath in $appPaths) { $appJson = Get-AppJsonFromAppFile $appPath $appInfo[$appJson.name] = $appJson.version } foreach ($app in $appInfo.Keys) { $targetApps += $app } foreach ($appPath in $sortApps) { $appJson = Get-AppJsonFromAppFile $appPath $appName = $appJson.name $appVersion = $appJson.version $installApps[$appName] = @{ Path = $appPath; Version = $appVersion } } echo "Found dependencies apps" $DependentAppNames = Get-DependentApps -ServerInstance $ServerInstance -TargetApps $targetApps -IncludeTargetApps $DependentAppNamesWithoutTarget = Get-DependentApps -ServerInstance $ServerInstance -TargetApps $targetApps $DependentAppNames echo "Uninstall dependencies of dependent apps" if ($uninstallApps) { foreach ($entry in $DependentAppNames) { $appName = $entry.Key $versions = $entry.Value if ([string]::IsNullOrEmpty($appName) -or [string]::IsNullOrEmpty($versions)) { Write-Output "The app is not published on the environment: $appName" } else { foreach ($version in $versions) { if ([string]::IsNullOrEmpty($version)) { Write-Output "The app version is not specified for $appName" } else { Uninstall-NAVApp -ServerInstance $ServerInstance -Name $appName -Version $version Write-Output "Successfully uninstalled $appName version $version" } } } } } echo "Install new apps" if ($installApps.Count -gt 0) { foreach ($appName in $installApps.Keys) { $appData = $installApps[$appName] $versionList = $appData["Version"] $Path = $appData["Path"] echo "versionList $versionList" echo "Path $Path" foreach ($version in $versionList) { if ($ScopeTenant) { Publish-BCApp -ServerInstance $ServerInstance -appPath $Path -appName $appName -appVersion $version -force:$force -PublishSync -InstallDataUpgrade -ScopeTenant } else { Publish-BCApp -ServerInstance $ServerInstance -appPath $Path -appName $appName -appVersion $version -force:$force -PublishSync -InstallDataUpgrade } } } } echo "Unpublish the old version of the applications that we have updated" foreach ($appName in $targetApps) { $matchingApp = $DependentAppNames | Where-Object { $_.Name -eq $appName } if ($matchingApp) { $UnpublishApps[$appName] = $matchingApp.Value } } $remainingApps = $UnpublishApps.Clone() echo "Unpublish apps:" $remainingApps while ($attempt -lt $maxAttempts -and $remainingApps.Count -gt 0) { $attempt++ Write-Output "Attempt $attempt of $maxAttempts" $failedApps = @{} foreach ($entry in $remainingApps.GetEnumerator()) { $appName = $entry.Key $versions = $entry.Value if ([string]::IsNullOrEmpty($appName) -or [string]::IsNullOrEmpty($versions)) { Write-Output "The app is not published on the environment: $appName" } else { foreach ($version in $versions) { if ([string]::IsNullOrEmpty($version)) { Write-Output "The app version is not specified for $appName" } else { if ($installApps.Contains($appName) -and $installApps[$appName].Version -eq $version) { Write-Output "Skipping unpublish for $appName version $version as it was just installed." } else { $UnpublishResult = $null & { Unpublish-NAVApp -ServerInstance $ServerInstance -Name $appName -Version $version } *>&1 | Tee-Object -Variable UnpublishResult if ([string]::IsNullOrEmpty($UnpublishResult)) { Write-Output "Successfully Unpublish $appName version $version" } else { Write-Output "Failed to Unpublish $appName version $version. Error: $UnpublishResult" if (-not $failedApps.ContainsKey($appName)) { $failedApps[$appName] = @() } $failedApps[$appName] += $version } } } } } } if ($failedApps.Count -gt 0) { Write-Output "Retrying failed apps..." $remainingApps = $failedApps.Clone() } else { Write-Output "All apps successfully unpublished." break } } $remainingDependentApps = $DependentAppNamesWithoutTarget | Where-Object { $installApps.keys -notcontains $_.Name } echo "Installing all dependencies" $remainingDependentApps if ($remainingDependentApps.Count -gt 0) { foreach ($app in $remainingDependentApps.GetEnumerator()) { $appName = "$($app.Key)" $version = "$($app.Value)" Write-Output "Installing $appName ...." Publish-BCApp -ServerInstance $ServerInstance -appName $appName -appVersion $version -InstallDataUpgrade Write-Output "Successfully Install $appName version $version" } } } function Publish-BCApp { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$ServerInstance, [string]$appPath, [Parameter(Mandatory = $true)] [string]$appName, [Parameter(Mandatory = $true)] [string]$appVersion, [switch]$force, [switch]$PublishSync, [switch]$InstallDataUpgrade, [switch]$ScopeTenant ) if ($PublishSync) { #try { Write-Host "Publishing $appName ... " if ($ScopeTenant) { Publish-NAVApp -ServerInstance $ServerInstance -Path $appPath -SkipVerification -Force -Scope Tenant } else { Publish-NAVApp -ServerInstance $ServerInstance -Path $appPath -SkipVerification -Force } #} #catch { # Write-Error "Failed to publish $appName version $appVersion to $ServerInstance : $_" # return #} #try { Write-Host "Syncing $appName ... " if ($force) { Sync-NAVApp -ServerInstance $ServerInstance -Name $appName -Version $appVersion -Mode ForceSync -ErrorAction Stop } else { Sync-NAVApp -ServerInstance $ServerInstance -Name $appName -Version $appVersion -ErrorAction Stop } #} #catch { # Write-Error "Failed to synchronize $appName version $appVersion on $ServerInstance : $_" # return #} } $navAppInfoFromDb = Get-NAVAppInfo -ServerInstance $ServerInstance -Name $appName -Version $appVersion -Tenant "default" -TenantSpecificProperties if ($null -eq $navAppInfoFromDb.ExtensionDataVersion -or $navAppInfoFromDb.ExtensionDataVersion -eq $navAppInfoFromDb.Version) { $install = $true } else { $upgrade = $true } if ($InstallDataUpgrade) { if ($install) { #try { Write-Host "Installing $appName ... " Install-NAVApp -ServerInstance $ServerInstance -Name $appName -Version $appVersion #} #catch { # Write-Error "Failed to install $appName version $appVersion on $ServerInstance : $_" # return #} } if ($upgrade) { #try { Write-Host "Upgrading $appName ... " Start-NAVAppDataUpgrade -ServerInstance $ServerInstance -Name $appName -Version $appVersion #} #catch { # Write-Error "Failed to start data upgrade for $appName version $appVersion on $ServerInstance : $_" # return #} } } Write-Output "Successfully published $appName version $appVersion to $ServerInstance`n" } function Get-BcManagementModule { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$ServerInstance ) $registryPath = Join-Path -Path "HKLM:\SYSTEM\ControlSet001\Services" -ChildPath "MicrosoftDynamicsNavServer`$$ServerInstance" try { if (-not (Test-Path -Path $registryPath)) { throw "Registry path not found: $registryPath" } $imagePath = (Get-ItemProperty -Path $registryPath -Name ImagePath).ImagePath if ($imagePath -notmatch '^(.*?)\\Microsoft\.Dynamics\.Nav\.Server\.exe') { throw "Executable path not found in ImagePath. Extracted string: $imagePath" } $serverPath = $matches[1] -replace '"', '' $managementModulePath = Join-Path -Path $serverPath -ChildPath "Microsoft.Dynamics.Nav.Management.psm1" if (-not (Test-Path -Path $managementModulePath)) { throw "Management module not found at path: $managementModulePath" } Import-Module "$managementModulePath" -ErrorAction Stop Write-Host "Import-Module successful" } catch { Write-Error "An error occurred: $_" } } function Download-And-ExtractArchive { param ( [Parameter(Mandatory = $true)] [string]$zipUrl, [Parameter(Mandatory = $true)] [string]$destination ) try { $tempDir = "$Env:BUILD_ARTIFACTSTAGINGDIRECTORY" $zipPath = Join-Path -Path $tempDir -ChildPath "apps.zip" Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath Expand-Archive -Path $zipPath -DestinationPath $destination -Force Write-Output "Archive downloaded and extracted to $destination" } catch { Write-Error "An error occurred: $_" } } function Get-DependentApps { param ( [string]$ServerInstance, [string[]]$TargetApps, [switch]$IncludeTargetApps ) $apps = Get-NAVAppInfo -ServerInstance $ServerInstance -Publisher "SMART business LLC" $dependenciesDict = @{} foreach ($app in $apps) { $appDependencies = (Get-NAVAppInfo -ServerInstance $ServerInstance -Name $app.Name).Dependencies $filteredDependencies = $appDependencies | Where-Object { $_.Publisher -ne "Microsoft" } $dependenciesDict[$app.Name] = $filteredDependencies | ForEach-Object { @{ Name = $_.Name Version = $_.Version } } } $dependentApps = @{} $dependenciesDict.GetEnumerator() | ForEach-Object { $appName = $_.Key $dependencies = $_.Value $isDependent = $dependencies | Where-Object { $targetApps -contains $_.Name } if ($isDependent) { $dependentApps[$appName] = ($apps | Where-Object { $_.Name -eq $appName }).Version } } if ($IncludeTargetApps) { $targetApps | ForEach-Object { $dependentApps[$_] = ($apps | Where-Object { $_.Name -eq $_ }).Version } } $keysToUpdate = $dependentApps.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key } foreach ($key in $keysToUpdate) { $dependentApps[$key] = ($apps | Where-Object { $_.Name -eq $key }).Version } return $dependentApps.GetEnumerator() | Sort-Object -Property Name -Unique } |