Publish/Publish-BcAppToDevEndpoint.ps1
<#
.Synopsis Publish App to a NAV/BC Instance via Dev Endpoint .Parameter appFile Path of the app you want to publish .Parameter syncMode Specify Add, Clean or Development based on how you want to synchronize the database schema. Default is Add .Parameter credential Specify the credentials for the admin user if you use DevEndpoint and authentication is set to UserPassword .Example Publish-BcAppDevEndpoint -devServerUrl "https://navdev.smartcloud.com.ua:7149/localizationapps" -devAuthType "Windows" -appFile "D:\temp-so\SMART business_SMART AL Dev Tools_1.0.0.0.app" #> function Publish-BcAppToDevEndpoint { Param ( [string] $devServerUrl = "", [switch] $sslVerificationDisabled, [string] $devAuthType = "UserPassword", [Parameter(Mandatory=$true)] $appFile, [Parameter(Mandatory=$false)] [ValidateSet('Add','Clean','Development','ForceSync')] [string] $syncMode, [Parameter(Mandatory=$false)] [ValidateSet('Default','Ignore','Strict')] [string] $dependencyPublishingOption, [int] $timeoutMinutes = 7, [Parameter(Mandatory=$false)] [string] $tenant = "default", [Hashtable] $bcAuthContext, [string] $environment, [pscredential] $credential ) Add-Type -AssemblyName System.Net.Http $appFolder = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid().ToString()) $appFiles = CopyAppFilesToFolder -appFiles $appFile -folder $appFolder $force = $true $successCounter = 0 $failCounter = 0 try { if ($appFolder) { $appFiles = @(Sort-AppFilesByDependencies -appFiles $appFiles -WarningAction SilentlyContinue) } $appFiles | Where-Object { $_ } | ForEach-Object { try { $appFile = $_ $sslVerificationDisabled = $false if ($bcAuthContext) { $bcAuthContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext } if ($environment) { $devServerUrl = "$($bcContainerHelperConfig.apiBaseUrl.TrimEnd('/'))/v2.0/$environment" $tenant = "" } $handler = New-Object System.Net.Http.HttpClientHandler if ($bcAuthContext) { $HttpClient = [System.Net.Http.HttpClient]::new($handler) $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $bcAuthContext.AccessToken) } else { if ($devAuthType -eq "Windows") { $handler.UseDefaultCredentials = $true } $HttpClient = [System.Net.Http.HttpClient]::new($handler) if ($devAuthType -eq "UserPassword") { if (!($credential)) { throw "You need to specify credentials when you are not using Windows Authentication" } $pair = ("$($Credential.UserName):"+[System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password))) $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) $base64 = [System.Convert]::ToBase64String($bytes) $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Basic", $base64); } } if ($timeoutMinutes -gt 0) { $HttpClient.Timeout = [System.TimeSpan]::FromMinutes($timeoutMinutes); } else { $HttpClient.Timeout = [System.Threading.Timeout]::InfiniteTimeSpan } $HttpClient.DefaultRequestHeaders.ExpectContinue = $false if ($sslVerificationDisabled) { if (-not ([System.Management.Automation.PSTypeName]"SslVerification").Type) { Add-Type -TypeDefinition " using System.Net.Security; using System.Security.Cryptography.X509Certificates; public static class SslVerification { private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } public static void Disable() { System.Net.ServicePointManager.ServerCertificateValidationCallback = ValidationCallback; } public static void Enable() { System.Net.ServicePointManager.ServerCertificateValidationCallback = null; } }" } Write-Host "Disabling SSL Verification" [SslVerification]::Disable() } $schemaUpdateMode = "synchronize" if ($syncMode -eq "Clean") { $schemaUpdateMode = "recreate"; } elseif ($syncMode -eq "ForceSync") { $schemaUpdateMode = "forcesync" } $url = "$devServerUrl/dev/apps?SchemaUpdateMode=$schemaUpdateMode" if ($tenant) { $url += "&tenant=$tenant" } if ($dependencyPublishingOption -eq "Ignore" -or $dependencyPublishingOption -eq "Strict") { $url += "&dependencyPublishingOption=$dependencyPublishingOption" } $appName = [System.IO.Path]::GetFileName($appFile) $multipartContent = [System.Net.Http.MultipartFormDataContent]::new() $FileStream = [System.IO.FileStream]::new($appFile, [System.IO.FileMode]::Open) try { $fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data") $fileHeader.Name = "$AppName" $fileHeader.FileName = "$appName" $fileHeader.FileNameStar = "$appName" $fileContent = [System.Net.Http.StreamContent]::new($FileStream) $fileContent.Headers.ContentDisposition = $fileHeader $multipartContent.Add($fileContent) Write-Host "Publishing $appName to $url" $result = $HttpClient.PostAsync($url, $multipartContent).GetAwaiter().GetResult() if (!$result.IsSuccessStatusCode) { $message = "Status Code $($result.StatusCode) : $($result.ReasonPhrase)" try { $resultMsg = $result.Content.ReadAsStringAsync().Result try { $json = $resultMsg | ConvertFrom-Json $message += "`n$($json.Message)" } catch { $message += "`n$resultMsg" } } catch {} Write-Host -ForegroundColor Red $message $failCounter += 1 } else { $successCounter += 1 Write-Host -ForegroundColor Green "App $([System.IO.Path]::GetFileName($appFile)) successfully published" } } catch { Write-Host "Error Message: " $_.Exception.Message $failCounter += 1 } finally { $FileStream.Close() } if ($sslverificationdisabled) { Write-Host "Re-enabling SSL Verification" [SslVerification]::Enable() } } catch { $failCounter += 1 Write-Host "App $([System.IO.Path]::GetFileName($appFile)) not published: $_" } } } finally { Remove-Item $appFolder -Recurse -Force } Write-Host "$successCounter apps published successfully" if ($failCounter -gt 0) { Write-Warning "$failCounter apps not published" -ErrorAction Stop } } Write-Verbose "Function imported: Publish-BcAppToDevEndpoint" |