AppHandling/Publish-PerTenantExtensionApps.ps1

<#
 .Synopsis
  Preview function for publishing PTE apps to an online tenant
 .Description
  Preview function for publishing PTE apps to an online tenant
#>

function Publish-PerTenantExtensionApps {
    Param(
        [Parameter(Mandatory=$true)]
        [string] $clientId,
        [Parameter(Mandatory=$true)]
        [string] $clientSecret,
        [Parameter(Mandatory=$true)]
        [string] $tenantId,
        [Parameter(Mandatory=$true)]
        [string] $environment,
        [Parameter(Mandatory=$false)]
        [string] $companyName,
        [Parameter(Mandatory=$true)]
        $appFiles,
        [switch] $useNewLine
    )

    $newLine = @{}
    if (!$useNewLine) {
        $newLine = @{ "NoNewLine" = $true }
    }

    $loginURL     = "https://login.microsoftonline.com"
    $scopes       = "https://api.businesscentral.dynamics.com/.default"
    $baseUrl      = "https://api.businesscentral.dynamics.com/v2.0/$environment/api/microsoft/automation/v1.0"
    
    Write-Host "Authenticating to $tenantId using $ClientId"
    $body = @{grant_type="client_credentials";scope=$scopes;client_id=$ClientID;client_secret=$ClientSecret}
    $oauth = Invoke-RestMethod -Method Post -Uri $("$loginURL/$tenantId/oauth2/v2.0/token") -Body $body
    $authHeaders = @{ "Authorization" = "Bearer $($oauth.access_token)" }
    Write-Host "Authenticated"
    
    $companies = Invoke-RestMethod -Headers $authHeaders -Method Get -Uri "$baseurl/companies"
    $company = $companies.value | Where-Object { ($companyName -eq "") -or ($_.name -eq $companyName) } | Select-Object -First 1
    if (!($company)) {
        throw "No company $companyName"
    }
    $companyId = $company.id
    Write-Host "Company $companyName has id $companyId"
    
    $getExtensions = Invoke-WebRequest -Headers $authHeaders -Method Get -Uri "$baseUrl/companies($companyId)/extensions"
    $extensions = (ConvertFrom-Json $getExtensions.Content).value | Sort-Object -Property DisplayName
    
    Write-Host "Extensions before:"
    $extensions | % { Write-Host " - $($_.DisplayName), Version $($_.versionMajor).$($_.versionMinor).$($_.versionBuild).$($_.versionRevision), Installed=$($_.isInstalled)" }
    Write-Host
    
    Sort-AppFilesByDependencies -appFiles $appFiles | ForEach-Object {
        $tempFolder = Join-Path $ENV:TEMP ([guid]::NewGuid().ToString())
    
        Extract-AppFileToFolder -appFilename $_ -appFolder c:\temp\appcontent -generateAppJson 6> $null
        $appJsonFile = "c:\temp\appcontent\app.json"
        $appJson = Get-Content $appJsonFile | ConvertFrom-Json
        Remove-Item -Path c:\temp\appcontent -Force -Recurse
    
        Write-Host @newLine "Publishing and Installing $([System.IO.Path]::GetFileName($_))"
        Invoke-WebRequest -Headers ($authHeaders+(@{"If-Match" = "*"})) `
            -Method Patch `
            -Uri "$baseUrl/companies($companyId)/extensionUpload(0)/content" `
            -ContentType "application/octet-stream" `
            -InFile $_ | Out-Null
        Write-Host @newLine "."    
        $completed = $false
        $errCount = 0
        while (!$completed)
        {
            Start-Sleep -Seconds 5
            try {
                $extensionDeploymentStatusResponse = Invoke-WebRequest -Headers $authHeaders -Method Get -Uri "$baseUrl/companies($companyId)/extensionDeploymentStatus"
                $extensionDeploymentStatuses = (ConvertFrom-Json $extensionDeploymentStatusResponse.Content).value
                $completed = $true
                $extensionDeploymentStatuses | Where-Object { $_.publisher -eq $appJson.publisher -and $_.name -eq $appJson.name -and $_.appVersion -eq $appJson.version } | % {
                    if ($_.status -eq "InProgress") {
                        Write-Host @newLine "."
                        $completed = $false
                    }
                    elseif ($_.Status -ne "Completed") {
                        $errCount = 5
                        throw "error"
                    }
                }
                $errCount = 0
            }
            catch {
                if ($errCount++ -gt 3) {
                    Write-Host "error"
                    throw "Unable to publish app"
                }
                $completed = $false
            }
        }
        if ($completed) {
            Write-Host "completed"
        }
    }
    
    $getExtensions = Invoke-WebRequest -Headers $authHeaders -Method Get -Uri "$baseUrl/companies($companyId)/extensions"
    $extensions = (ConvertFrom-Json $getExtensions.Content).value | Sort-Object -Property DisplayName
    
    Write-Host
    Write-Host "Extensions after:"
    $extensions | % { Write-Host " - $($_.DisplayName), Version $($_.versionMajor).$($_.versionMinor).$($_.versionBuild).$($_.versionRevision), Installed=$($_.isInstalled)" }
}
Export-ModuleMember -Function Publish-PerTenantExtensionApps