Resetting PowerApps Environments Scripted

PowerApps and CDS without the bulk of the Dynamics 365 apps (Sales, Customer Service, etc.) are all the rage now and if you are a developer following a healthy ALM process then you are likely creating and destroying these CDS naked environments on a regular basis.

Developers love to work with script and code, and PowerShell modules provide a way of quickly executing repetitive tasks that might take multiple clicks and a bunch of typing within a UI. Microsoft provides 2 modules for PowerApps, for app creators, and for administrators (both in preview) that provide a series of cmdlets to help cut down the UI clicking.

The administration version is the module with commands for managing environments, but you’ll notice there is no easy reset command. There is a create, and delete environment but often the task that developers are doing is resetting an instance and this is a function of the old Dynamics 365 Administrator Center, where there is a Reset Instance button.

To help manage this common function I was performing myself a lot in developing ALM processes I created my own cmdlet that combines the listing of environments, delete, and create so I could quickly reset environments in a single command without going through the UI. The full script is available on one of my GitHub repos of ALM examples but I will break down how it works (link to script at the bottom of the post).

First things, first, you need the PowerApps administration module, so the script will see if you already have it installed and if not install it for you with PowerShell Gallery to resolve the dependencies required of the rest of the commands. This is good practice when writing scripts in general, its like adding Using statements and Nuget in C#.projects.

if (-not(Get-Module -Name Microsoft.PowerApps.Administration.PowerShell) -or -not(Get-Module -Name Microsoft.PowerApps.PowerShell)) {
    Write-Host "Importing PowerApp Admin Modules..."
    Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Force -AllowClobber
    Install-Module -Name Microsoft.PowerApps.PowerShell -Force -AllowClobber
}

All the rest of the functions in the script now work with the PowerApps admin cmdlets which require you to connect to a PowerApps Account. This is super quick and easy to do, just add Add-PowerAppsAccount and it will prompt for credentials and establish a context for the subsequent functions to use.

The next function in the script when you run it without a pre-defined environment as a parameter is for the rest of the script to function we need to know which environment you want to work with in this reset, so it will show you a list.

# get all environments and select item to remove
$cdsEnvironmentList = Get-PowerAppEnvironment

if ($EnvironmentName) {
    $cdsEnvironment = $cdsEnvironmentList | where EnvironmentName -EQ $EnvironmentName
} elseif ($EnvironmentDisplayName) {
    if ($UseDisplayNameMatch) {
        $cdsEnvironment = $cdsEnvironmentList | where DisplayName -Match $EnvironmentDisplayName
    } else {
        $cdsEnvironment = $cdsEnvironmentList | where DisplayName -EQ $EnvironmentDisplayName
    }
} else {
    Write-Output "No environment selected from parameters, listing environments..."

    Write-Output "0. [Create New Environment] `n"
    for ($a=0; $a -lt $cdsEnvironmentList.Length; $a++){
        Write-Output "$($a + 1): $($cdsEnvironmentList[$a].DisplayName) ($($cdsEnvironmentList[$a].EnvironmentName))"
    }

    do {
        try {
            $selectOk = $true
            [int]$value = Read-host "Please select an environment"
        }
        catch {
            $selectOk = $false
        }
    } until (($value -ge 0 -and $value -lt $cdsEnvironmentList.Length + 1) -and $selectOK)

    if ($value -ne 0) {
        $cdsEnvironment = $cdsEnvironmentList[$value - 1]
    }
}

At this point we should have an environment, either provide by a parameter passed in the initial call in the script or by selecting an environment from the listing functionality. Now we can go about deleting that environment.

The delete environment command is actually asynchronous as it can take some time to execute. The UI for removing in the PowerApps Admin actually mentions that environment deletes could take hours?! I have never actually seen an environment remove take more than a minute or 2 though. Firstly we want to make sure our delete command was accepted successfully (highlighted below), then we need to continue once it is done. The problem with the async command is that we submit it but don’t have a callback to know when it is completed and be able to start the create process. What I do here is again use the list environment command and see is that environment I submitted the delete for still showing up in the list of environments, if so sleep for some more time and then check again.

Write-Output "Initializing remove environment..."
    $removeEnvironmentResult = Remove-AdminPowerAppEnvironment -EnvironmentName $cdsEnvironment.EnvironmentName

    if ($removeEnvironmentResult.Code -eq 202 -and $removeEnvironmentResult.Description -eq "Accepted") {
        Write-Output "Remove environment submitted, sleeping waiting for delete..."
    } elseif ($removeEnvironmentResult.Errors) {
        Write-Warning "Environment removal error: $($removeEnvironmentResult.Internal.errors)"
        Return
    }

    # ensure the environment is removed before continuing
    do {
        Start-Sleep -Seconds $sleepSeconds
        $cdsEnvironmentList = Get-PowerAppEnvironment
        $removeEnvironment = $cdsEnvironmentList | where EnvironmentName -EQ $cdsEnvironment.EnvironmentName
    } While ($removeEnvironment)

    Write-Output "Environment Deleted."

Once I don’t see the deleted environment in the list, we can proceed to re-creating it. With the cmdlets for PowerApps this is a 2 step process of creating an environment and then creating a database for that environment.

try {
    # create new environment
    Write-Output "Creating new environment..."
    $newEnvironment = New-AdminPowerAppEnvironment -DisplayName $EnvironmentDisplayName -LocationName $LocationName -EnvironmentSku $EnvironmentSku

    # create database for new environment
    Write-Output "Creating database for $($newEnvironment.DisplayName)..."
    $newEnvironmentDb = New-AdminPowerAppCdsDatabase -EnvironmentName $newEnvironment.EnvironmentName -CurrencyName $CurrencyName -LanguageName $LanguageName
    Write-Output "Environment Reset:$($newEnvironment.DisplayName) - Completed"
}
catch {
    Write-Warning "Unabled to create new environment: $($_.Exception.Message)"
    Return
}

With that the script should complete and you have your new fresh PowerApps environment with a CDS database.

The script I have provided also has a bunch of parameters you can pass to it to pre-define some of the items that are required during the execution.

param (
    [string]
    $EnvironmentDisplayName,

    [string]
    $EnvironmentName,

    [switch]
    $UseDisplayNameMatch,

    [switch]
    $RemoveOnly,

    [switch]
    $BypassConfirm,

    [string]
    [ValidateSet("unitedstates","europe","asia","australia","india","japan","canada","unitedkingdom","unitedstatesfirstrelease","southamerica","france")]
    $LocationName = "canada",

    [ValidateSet("Trial","Production")]
    [string]
    $EnvironmentSku = "Trial",

    [string]
    $CurrencyName = "USD",

    [string]
    $LanguageName = "1033",

    [int]
    $SleepSeconds = 30
)

Some of the more interesting parameters you might want to take notice of, the EnvironmentName will allow the script to skip the environment listing step. The LocationName can define where the environment is provisioned, note it defaults to Canada (I am Canadian and that environment local gets the latest first!), and EnvironmentSku can control what SKU is used in the provisioning, it will default to Trial. The BypassConfirm will skip any confirmations you receive during the execution of the script.

Hopefully this helps developers that need to reset or even provision environments in a quick fashion and avoid the pain. If you have improvements to the script or enhancements please by all means make a pull request on the git repo!

GitHub Project: PowerApps CDS DevOps Template
PowerShell Script: ./scripts/PowerAppsEnvironmentReset.ps1