How to create Business Central Online Sandbox with API. Wave 2 with F8.

This blog I wanted to write for a while. But as NDA under Business Central Wave 2 is lifted my hands are untied 

Today I want to talk about how to create a script that will spin up Business Central Wave 2 online sandbox for you.

Why?

I can imagine different scenarios. Maybe you want to test your extensions against Wave 2 without docker. Or you want to create a custom Wave 2 sandbox with your data, your extensions and give it to the potential customer. May be online sandboxes are a part of your CI/CD process, or maybe you want to run the script, because it’s so cool to sit and see how the process goes 

AAD Authentication

First thing you need to cover – to configure AAD Authentication.

I described that in my previous blog

So the output from the previous blog, we use here is the authentication key

image

Create Business Central Cloud SandBox with Admin API

Here is the description of API. But without proper examples, it’s difficult to create the script quickly.

So here we go. 

Available Versions

You have a choice: do you want to create a sandbox with the current version, or with a next (preview) version.

To get the list of available versions I created this code. Oh, yes. This is all in a Powershell 🙂

				
					$url ='https://api.businesscentral.dynamics.com'
$authHeaderDA = GetAuthHeader -DAemail $DAemail -DApassword $DApassword -tenantdomain $tenantdomain -appId $appId

Get-BCCloudAvailableVersions -url $url -authHeaderDA $authHeaderDA

function Get-BCCloudAvailableVersions{
    param (
        [Parameter(Mandatory=$true)]
        [string] $url,
        [Parameter(Mandatory=$true)]
        $authHeaderDA
    )

    $req = $url +  '/v1.2/admin/applications/BusinessCentral/rings/'
    $result = Invoke-WebRequest `
                                -Uri   $req `
                                -Headers @{Authorization=$authHeaderDA} `
                                -Method Get 

    $result.Content 
}
				
			

The result will be 

image

If you notice, the output gives us 2 available versions of Business Central that are available for the sandbox creation: 14.5.35970 (current) and 15.0.36135.0 (next). 

Caution!

When preparing this blog I tried to create current sandbox (with API) I got this error.

{“code”:”ResourceDoesNotExist”,”message”:”The resource does not exist”,”target”:””}

image

2 days of investigations and discussing with support team (big thanks to them, btw) resulted in this answer. Hope I can publish that and it’s not a secret 

				
					You are calling an API that tries to create a sandbox environment. Since an application version is not provided, we try to create the sandbox environment on the same Application Version as your production tenant (in this case 14.4.35602.0). However, we cannot create a sandbox tenant on a version that is not currently enabled for new signups. Late last week, we enabled signups on 14.5, which means that new signups are no longer allowed on 14.4. This API likely stopped working for you as soon as we disabled signups on 14.4 last week. In my opinion, this is a bug. I’ll discuss that with my team. In the meantime, you have two possible mitigation:

Use the API that allows ApplicationVersion and RingName to be specified:
>The application version that is currently available is 14.5.35970.0
>The ring name should be PROD
If you really need your environment to be on 14.4, create your environment as a copy of Production. This will ignore the check for whether signups are enabled.
				
			

And really I had this situation.

image

But let’s return to the Wave 2

Get the next available version number

If you want to create a repetitive script, you need to “know” the next version number, cos you should pass it to the create-new-bc-sandbox URL. Otherwise, you can just hardcode it, and the script will work for a while… not long.

 
				
					$url ='https://api.businesscentral.dynamics.com'
$authHeaderDA = GetAuthHeader -DAemail $DAemail -DApassword $DApassword -tenantdomain $tenantdomain -appId $appId

Get-BCCloudAvailablePreviewVersion -url $url -authHeaderDA $authHeaderDA

function Get-BCCloudAvailablePreviewVersion{
    param (
        [Parameter(Mandatory=$true)]
        [string] $url,
        [Parameter(Mandatory=$true)]
        $authHeaderDA
    )

    $supportedVersions = Get-BCCloudAvailableVersions -url $url -authHeaderDA $authHeaderDA | ConvertFrom-Json
    $appVersion = $supportedVersions.value | where { $_.ringFriendlyName -eq "Preview" } | Select -ExpandProperty "applicationVersion"
    $major = $appVersion.major
    $minor = $appVersion.minor
    $build = $appVersion.build
    $revision = $appVersion.revision
     
    return ("$major.$minor.$build.$revision")
}
				
			

The result will be 

image

Caution! 

Seems that while preparing this blog, I discovered all possible bugs 🙂 

This time applicationVersion was returned in flat format, meaning that it was a text and not a JSON. I reported to support and seems it was resolved. But if you will have that in the future here is the parsing code.

				
					    #There where problems in API when appVersion returned in flat format 
    $major = $appVersion |
    Select-String '(?<=major=)\d+' |
    Select-Object -Expand Matches |
    Select-Object -Expand Value

    $minor =$appVersion |
    Select-String '(?<=minor=)\d+' |
    Select-Object -Expand Matches |
    Select-Object -Expand Value

    $build = $appVersion |
    Select-String '(?<=build=)\d+' |
    Select-Object -Expand Matches |
    Select-Object -Expand Value

    $revision = $appVersion |
    Select-String '(?<=revision=)\d+' |
    Select-Object -Expand Matches |
    Select-Object -Expand Value

    return ("$major.$minor.$build.$revision") 
				
			

Hope you won’t need it.

Create new Wave 2 Sandbox

And now we are at the point when we are ready to create new Wave 2 Sandbox

				
					Clear-Host
$url ='https://api.businesscentral.dynamics.com'
$authHeaderDA = GetAuthHeader -DAemail $DAemail -DApassword $DApassword -tenantdomain $tenantdomain -appId $appId
$sandboxName = 'My-Wave2-Sandbox'

New-BCCloudSandBox -url $url -authHeaderDA $authHeaderDA -sandboxName $sandboxName -nextVersion:$true

function New-BCCloudSandBox {
    param (
        [Parameter(Mandatory=$true)]
        [string] $url,
        [Parameter(Mandatory=$true)]
        $authHeaderDA,
        [Parameter(Mandatory=$true)]
        [string] $sandboxName,
        [switch] $nextVersion
    )

    if (! $nextVersion) {
        $req = $url +  "/v1.2/admin/applications/BusinessCentral/environments/$sandboxName"
    } else {
        $appVersion = Get-BCCloudAvailablePreviewVersion -url $url -authHeaderDA $authHeaderDA
        $req = $url +  "/v1.2/admin/applications/BusinessCentral/environments/$sandboxName/$appVersion/PREVIEW"
    }

    Write-Host "Creating new Sandbox..."          

    $JSON = @'
    {
        "type": "Sandbox"
    }
'@

     try {
       $result = Invoke-WebRequest `
            -Uri   $req `
            -Headers @{Authorization=$authHeaderDA} `
            -Method PUT `
            -Body $JSON -ContentType "application/json"
    } catch {
        $status = $_.Exception.Response.StatusCode.value__
    }

}
				
			

The result will be

image

and in the Admin portal

image

 

We could stop here, but we could make the script a bit better, right?

Get the list of installed environments in the tenant

During the spin-up process, or afterwards, we can get the list of installed environments in your tenant with the environment status.

				
					$url ='https://api.businesscentral.dynamics.com'
$authHeaderDA = GetAuthHeader -DAemail $DAemail -DApassword $DApassword -tenantdomain $tenantdomain -appId $appId
$sandboxName = 'My-Wave2-Sandbox'

Get-BCCloudEnvironments -url $url -authHeaderDA $authHeaderDA -envName $sandboxName | ConvertFrom-Json | ConvertTo-Json

function Get-BCCloudEnvironments {
    param (
        [Parameter(Mandatory=$true)]
        [string] $url,
        [Parameter(Mandatory=$true)]
        $authHeaderDA,
        $envName
    )

    $req = $url +  '/v1.2/admin/applications/BusinessCentral/environments/' + $envName
    $result = Invoke-WebRequest `
                                -Uri   $req `
                                -Headers @{Authorization=$authHeaderDA} `
                                -Method Get 

    $result.Content   
}
				
			

with the output

image

If you notice, there is a Status parameter that we will use.

Check the status of the Sandbox Environment

Now we are ready to get the status of the environment

				
					Clear-Host
$url ='https://api.businesscentral.dynamics.com'
$authHeaderDA = GetAuthHeader -DAemail $DAemail -DApassword $DApassword -tenantdomain $tenantdomain -appId $appId
$sandboxName = 'My-Wave2-Sandbox'

Get-BCCloudSandBoxStatus -url $url -authHeaderDA $authHeaderDA -sandboxName $sandboxName 

function Get-BCCloudSandBoxStatus {
    param (
        [Parameter(Mandatory=$true)]
        [string] $url,
        [Parameter(Mandatory=$true)]
        $authHeaderDA,
        [Parameter(Mandatory=$true)]
        [string] $sandboxName
    )
    
    try{
        $result = Get-BCCloudEnvironments -url $url -authHeaderDA $authHeaderDA -envName $sandboxName
        if ($result) {$currStatus = $result  | ConvertFrom-Json |select status}
        return ($currStatus.status)
    } catch {
        $currStatus = $_.Exception.Response.StatusCode
        return ($currStatus)
    }    
}
				
			

and the output

image

Wait until sandbox will be created

If this script should be embedded in the more general script, where you first create a sandbox and then upload extensions, you need to wait until the sandbox will be created., before doing the rest.

We have status now, so let’s check it periodically during the creation process.

				
					$url ='https://api.businesscentral.dynamics.com'
$authHeaderDA = GetAuthHeader -DAemail $DAemail -DApassword $DApassword -tenantdomain $tenantdomain -appId $appId
$sandboxName = 'My-Wave2-Sandbox'

Get-BCCloudSandBoxStatus -url $url -authHeaderDA $authHeaderDA -sandboxName $sandboxName 

function Wait-BCCloudSandBoxReady {
    param (
        [Parameter(Mandatory=$true)]
        [string] $url,
        [Parameter(Mandatory=$true)]
        $authHeaderDA,
        [Parameter(Mandatory=$true)]
        [string] $sandboxName
    )

    $status = Get-BCCloudSandBoxStatus -url $url -authHeaderDA $authHeaderDA -sandboxName $sandboxName
    if ($status -eq "NotFound")
    {
        Write-Host -ForegroundColor Red "Sandbox is not found"
        return
    } 
    if ($status -eq "Removing")
    {
        Write-Host -ForegroundColor Yellow "Sandbox is removing"
        return
    }
    if ($status -eq "Unauthorized")
    {
        return
    }
    while (($status -ne "Active") -and ($status -ne "NotFound"))
    {
        $wait = 30
        $status = Get-BCCloudSandBoxStatus -url $url -authHeaderDA $authHeaderDA -sandboxName $sandboxName
        if (($status -ne "Active") -and ($status -ne "NotFound"))
        {
            Write-Host -ForegroundColor Yellow "Sandbox is cooking. Current status is" $status ". Next try in $wait sec"
            Start-Sleep -Seconds $wait    
        }
    }
    
    Write-Host -ForegroundColor Green "Sandbox is " $status            
    
}
				
			

Join all together

The final script could be found here.

image

Enjoy!

Update

My MVP friend Stefano Demiliani Technical Blog added a valuable comment: “Be careful that sandboxes created with a preview version can be updated or deleted (yes, deleted!) without notice by Microsoft.”

Share Post:

Leave a Reply

About Me

DMITRY KATSON

A Microsoft MVP, Business Central architect and a project manager, blogger and a speaker, husband and a twice a father. With more than 15 years in business, I went from developer to company owner. Having a great team, still love to put my hands on code and create AI powered Business Central Apps that just works.

Follow Me

Recent Posts

Tags