BCDeploy.psm1



function GetToken {
    param (
        [string]$tenantId = "",
        [string]$clientId = "",
        [string]$clientSecret = ""
    )
    $body = @{grant_type="client_credentials"; scope="https://api.businesscentral.dynamics.com/.default"; client_id=$clientID; client_secret=$clientSecret; client_info=1}
    $token = (Invoke-RestMethod -Method Post -Uri $("https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token") -Body $body).access_token
    return $token;
}

function FindApp
{
    param(
        [string]$folder,
        [string]$app
    )
    $file = (Get-ChildItem -Path $folder -Filter "*_${app}_*.app")
    $filepath = $file.FullName;
    $info = $file.Name.TrimEnd(".app").Split('_');
    return @($info[0], $info[1], $info[2], $filepath);
}

function GetCompanyId {
    param (
        [string]$token = "",
        [string]$instance = ""
    )

    $baseuri = "https://api.businesscentral.dynamics.com/v2.0/${instance}/api/microsoft/automation/v2.0";
    $companies = Invoke-RestMethod -Method Get -Uri "${baseuri}/companies" -Headers @{Authorization = "Bearer ${token}" }
    $companyId = $Companies.value[0].id
    return $companyId
}

function GetStatus {
    param (
        [string]$token = "",
        [string]$instance = "",
        [string]$companyId = "",
        [string]$publisher = "",
        [string]$name = "",
        [string]$version = ""
    )
    $baseuri = "https://api.businesscentral.dynamics.com/v2.0/${instance}/api/microsoft/automation/v2.0";
    $response = Invoke-RestMethod -Method Get `
        -Uri "${baseuri}/companies($companyId)/extensionDeploymentStatus" `
        -Headers @{Authorization = "Bearer ${token}" }
    $match = $response.value `
        | Where-Object { $_.publisher -eq $publisher -and $_.name -eq $name -and $_.appVersion -eq $version }
    Write-Output "reposponse = "
    $response
    if(($match).count -gt 0)
    {
        return $match[0];
    }
    else {
        return "Unknown";
    }
}

function GetUpload {
    param (
        [string]$token = "",
        [string]$instance = "",
        [string]$companyId = ""
    )
    $baseuri = "https://api.businesscentral.dynamics.com/v2.0/${instance}/api/microsoft/automation/v2.0";
    $result = Invoke-RestMethod -Method Get `
        -Uri "${baseuri}/companies($companyId)/extensionUpload" `
        -Headers @{Authorization = "Bearer ${token}" }

    return $result.value[0]
}

function PostUpload {
    param (
        [string]$token = "",
        [string]$instance = "",
        [string]$companyId = ""
    )
    $baseuri = "https://api.businesscentral.dynamics.com/v2.0/${instance}/api/microsoft/automation/v2.0";
    $result = Invoke-RestMethod -Method Post `
        -Uri "${baseuri}/companies($companyId)/extensionUpload" `
        -Headers @{Authorization = "Bearer ${token}" } `
        -ContentType "application/json" `
        -Body ( @{
            "@odata.type"="#Microsoft.NAV.extensionUpload";
            "systemId"=[guid]::NewGuid();
            "schedule"="Current version";
        } | ConvertTo-Json )

    return $result.value
}

function PutUpload {
    param (
        [string]$token = "",
        [string]$instance = "",
        [string]$companyId = "",
        [string]$uploadId = "",
        [string]$path = ""
    )
    $baseuri = "https://api.businesscentral.dynamics.com/v2.0/${instance}/api/microsoft/automation/v2.0"
    Invoke-RestMethod `
    -Method Put `
    -Uri $("${baseuri}/companies(${companyId})/extensionUpload($uploadId)/extensionContent") `
    -Headers @{Authorization='Bearer ' + $token;'If-Match'='*'} `
    -ContentType "application/octet-stream" `
    -InFile $path
}

function InstallUpload {
    param (
        [string]$token = "",
        [string]$instance = "",
        [string]$companyId = "",
        [string]$uploadId = ""
    )
    $baseuri = "https://api.businesscentral.dynamics.com/v2.0/${instance}/api/microsoft/automation/v2.0";
    Invoke-RestMethod -Method Post `
        -Uri "${baseuri}/companies(${companyId})/extensionUpload($uploadId)/Microsoft.NAV.upload" `
        -Headers @{Authorization = "Bearer ${token}" }
}


function Publish-Extension {
    param (
        [string]$tenantId = "",
        [string]$clientId = "",
        [string]$clientSecret = "",
        [string]$instance = "",
        [string]$folder = "",
        [string]$app = ""
    )
    $appinfo = FindApp -folder $folder -app $app
    $publisher  = $appinfo[0];
    $name       = $appinfo[1];
    $version    = $appinfo[2];
    $path       = $appinfo[3];
    "Publishing $publisher - $name - $version ($path)"

    $token = GetToken -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret
    $companyId = GetCompanyId -token $token -instance $instance
    $upload = GetUpload -token $token -instance $instance -companyId $companyId
    if($upload)
    {
        "Using existing upload ($($upload.systemId))"
        $upload
    }
    else
    {
        PostUpload -token $token -instance $instance -companyId $companyId
        $upload = GetUpload -token $token -instance $instance -companyId $companyId
        "Created new upload ($($upload.systemId))"
        $upload
    }

    PutUpload -token $token -instance $instance -companyId $companyId -uploadId $upload.systemId -path $path
    "Uploaded app contents to $($upload.systemId)"
    $unknowncount = 0;
    do
    {
        $status = GetStatus -token $token -instance $instance -companyId $companyId -publisher $publisher -name $name -version $version
        if($($status.status) -eq "Unknown") { $unknowncount = $unknowncount + 1;}
        "$tenantId $instance $app - $($status.status)"
        Start-Sleep 5
    }
    while($status.status -ne "Completed" -or $unknowncount -gt 5)
    InstallUpload  -token $token -instance $instance -companyId $companyId -uploadId $upload.systemId
    "Installed app"
}