Getting Started with Wrap for Power Apps on Android

The recently introduced wrap for Power Apps enables you to create low-code, end-to-end branded, and natively deployed mobile applications for your organization. The official documentation does a great job of laying out the steps required to get your first wrap for Power Apps going, but in this guide I'll set out those steps in the most efficient and painless process to get you going even quicker. Then we'll take it a step further by automating a lot of the button clicks using PowerShell.

Overview

Completing your first wrap for Power Apps project can have multiple starting-off points, with many of the steps possible at the same time. However, after several iterations I've found the following to be one of the best ways of getting through the process:
  1. Install the required software
  2. Enable wrap for Power Apps in your environment
  3. Prepare your App Center environment
  4. Generate the private key/signature hash for your application
  5. Register your application in Azure
  6. Enable your application in Power Platform
  7. Configure and start your wrap for Power Apps build
  8. Download and sign the build output
  9. Distribute your application through standard channels or Mobile Device Management

Pre-Requisites

Note: The following pre-requisites, in general, need only be completed once per computer (Android Studio) or environment (Wrap for Power Apps Solution).

Android Studio

To get us started, we'll first want to install Android Studio. The main purpose for installing this is to get access to the included keytool.exe and appsigner.bat files, though you can also use it for emulating a device as well (among its other functionality).

You can get it directly from Google at the following link: https://developer.android.com/studio

By default, these resources get installed in the following directories:

Keytool
%USERPROFILE%\AppData\Local\Android\Sdk\build-tools\32.0.0
AppSigner
{Drive}:\Program Files\Android\Android Studio\jre\bin

Enable Wrap for Power Apps Solution

Next we'll want to install the wrap for Power Apps solution in our target environment. This will allow us to start using the Wrap feature later on. To do so, start by:

  • Signing into the Power Platform Admin center,
  • Selecting Resources > Dynamics 365 apps from the left-side menu,
  • Selecting Wrap for Power Apps from the list of enabled apps and clicking Install from the toolbar,
  • Choosing a target environment in the Select an environment dropdown,
  • Checking the box to agree to the terms of service,
  • Selecting Install to begin the installation process.

Preparing the App Center Environment

App Center will serve as the place where the wrap for Power Apps builds will ultimately be output into. Prior to starting our build, we will want to configure the requisite organization and app containers.

To get started, first sign into App Center (https://appcenter.ms) with your company account.

Creating a New Organization

If this is your first time accessing App Center, and you've not previously been given access to an existing organization, you'll need to set one up first. To do so, from the All apps screen in App Center:

  • Select Add new app > Add new organization from the button in the top-right corner,
  • Provide a unique Organization name,
  • Select Add new organization to create your organization.

Creating a New App Container

Once you've created a new organization, or accessed an existing organization, you'll need to create a new app container. From the Apps screen inside of your App Center organization:

  • Select Add new app (Note: If this organization has existing app containers, this is located in the top-right corner),
  • Provide a unique App name,
  • Select a Release Type (Note: Your choice here has no impact on the current build process.),
  • Select Android as the target operating system,
  • Select React Native as the target platform,
  • Select Add new app to create your container.

Creating an App Center API Token

Once you've created a new organization and app container, you'll need to create an API token to later allow the build process to write its output to the new container. To do so, while inside the App container you've created in App Center:

  • Select Settings from the left navigation,
  • Select App API tokens from the list of settings,
  • Select New API token from the top-right corner,
  • Provide a description for this new token (eg: 'Wrap for Power Apps'),
  • Select Full Access as the access type,
  • Select Add new API token to create your token,
  • Save the provided API token for use in later steps (Note: You will only be shown the value once, so be sure to copy it and keep it in a secure location.).

Getting the App Center URL

One last item to take note of before proceeding is the URL associated with the app container you've just created. You can copy this directly from your browser's URL field and it should look something similar to:

https://appcenter.ms/orgs/contoso-sales/apps/hello-wrap
Note: If your App Center URL contains users (eg: 'https://appcenter.ms/users/contoso/applications') in it, you may have overlooked the step Creating a New Organization above. Only app containers created in organizations will work with wrap for Power Apps at this time.

Generate the Private Key/Signature Hash and Registering Your Application

Important: This is the point in the process where we really begin to deviate from the outline provided by Microsoft in the official documentation. For your convenience, I'll call out where in the documentation you can find this guidance along with any pitfalls I encountered following it.
Note: The subsequent section titled Automating Key Generation and App Registration Using PowerShell will complete all of these tasks in an automated fashion. You're welcome to skip to that section if you're pressed for time.

Generate a Private Key

Instructions for generating a private a key using the standard procedure can be found at the following url: https://docs.microsoft.com/en-us/power-apps/maker/common/wrap/code-sign-android#generate-keys.

Generate a Signature Hash

Instructions for generating a signature hash using the standard procedure can be found at the following url: https://docs.microsoft.com/en-us/power-apps/maker/common/wrap/code-sign-android#generate-signature-hash.

Note: To run the command provided in the instructions, you'll also need to install OpenSSL, whose source can be found at: https://www.openssl.org

Registering Your Application

Instructions for registering your application in Azure using the standard procedure can be found at the following url: https://docs.microsoft.com/en-us/power-apps/maker/common/wrap/how-to#app-registration.

Note: Be sure to pay close attention to the redirect URI format expected, which can be found here: https://docs.microsoft.com/en-us/power-apps/maker/common/wrap/how-to#redirect-uri-format
Note: For the time being, wrap only supports apps registered with the Multitenant account type, be sure to select the proper value to avoid issues. Using the Multitenant account type will not prevent you from using the application as a single tenant deployment.

Enabling Your Application in the Power Platform

Instructions for enabling your registered application for use in the Power Platform using the standard procedure can be found at the following url: https://docs.microsoft.com/en-us/power-apps/maker/common/wrap/how-to#allow-registered-apps-in-your-environment.

Note: To run the command provided in the instructions, you'll also need to install the Power Apps admin module for PowerShell by using the instructions found here: https://docs.microsoft.com/en-us/power-platform/admin/powerapps-powershell#installation

Automating Key Generation and App Registration Using PowerShell

If after reading the steps outlined above you find yourself feeling a bit overwhelmed by the process, this section is perfect for you!

Microsoft provides plenty of tooling through their existing PowerShell modules to enable us to automate a great deal of those steps. Below you'll find a PowerShell script to handle all the steps from Generate a Private Key through Enabling Your Application in the Power Platform with just a few simple inputs.

Note: Click View PowerShell Script below to expand/collapse the full script.

View PowerShell Script ########################################################### ### install required modules ### ########################################################### #microsoft graph Install-Module Microsoft.Graph -Scope CurrentUser #microsoft power apps administration Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Scope CurrentUser ########################################################### ### generate key and retrieve signature hash ### ########################################################### Write-Host "Initializing variables..." -ForegroundColor "Cyan" #request input for key alias $keyAlias = Read-Host "Provide an alias for the key (eg: 'hellowrap')" #request input for keystore path $keystorePath = Read-Host "Provide a path/name for the keystore file (eg: 'c:\users\nflightless\documents\hellowrap.keystore')" #request input for key password $keyPassword = Read-Host "Provide a password for the key (eg: 'hellopassword')" #request inputs for key distinguished name $keyDistinguishedNameAttributes = [ordered]@{ cn= Read-Host "Key owner common name (eg: 'Nate Flightless') [Optional]" ou= Read-Host "Key organizational unit (eg: 'Operations') [Optional]" o= Read-Host "Key organization (eg: 'Contoso') [Optional]" l= Read-Host "Key locality (eg: 'Redmond') [Optional]" st= Read-Host "Key state code (eg: 'WA') [Optional]" c= Read-Host "Key country code (eg: 'US') [Optional]" } #for each non-blank distinguished name attribute ForEach ($keyDistinguishedNameAttribute in $keyDistinguishedNameAttributes.getEnumerator().where({$_.Value -ne ""})) { #append attribute to distinguished name string $keyDistinguishedName += "$($keyDistinguishedNameAttribute.Key)=$($keyDistinguishedNameAttribute.Value)," } #trim trailing comma on key distinguished name string $keyDistinguishedName = $keyDistinguishedName.Substring(0,$keyDistinguishedName.Length-1) Write-Host "Generating key..." -ForegroundColor "Yellow" #generate key/keystore file keytool -genkey -alias $keyAlias -keystore $keystorePath -keypass $keyPassword -storepass $keyPassword -keyalg RSA -keysize 2048 -validity 10000 -storetype PKCS12 -dname $keyDistinguishedName -noprompt 2>out-null Write-Host "Key generated successfully" -ForegroundColor "Green" Write-Host "Retrieving SHA-1 key fingerprint..." -ForegroundColor "Yellow" #retrieve generated key $signature = keytool -list -v -alias $keyAlias -keystore $keystorePath -storepass $keyPassword #extract SHA1 fingerprint $signatureSHA1 = ($signature | Where{$_ -match "SHA1: "}).Replace("SHA1: ", "") -replace "\W" Write-Host "Converting to base64..." -ForegroundColor "Yellow" #convert all hex-digit pairs in the signatureSHA1 fingerprint to an array of bytes $bytes = [byte[]] -split ($signatureSHA1 -replace '..', '0x$& ') #get the base64 encoding of the signatureSHA1 fingerprint $signatureHash = [System.Convert]::ToBase64String($bytes) Write-Host "Generated signature hash: $signatureHash" ########################################################### ### create app registration in azure ### ########################################################### #request signature hash if it is empty if ($signatureHash = "") { $signatureHash = Read-Host "Signature hash from the previously generated key" } $azureTenantId = Read-Host "Provide the tenant id to create the app registration in (eg: 'c6a1edf9-0185-4d26-a408-d3662176a19f')" $androidAppPackageName = Read-Host "Provide an app package name (eg: 'com.wrap.hello')" $androidAppDisplayName= Read-Host "Provide an app display name (eg: 'Hello Wrap')" #initialize redirect uri $redirectUri = @{ redirectUris = @( "msauth://$androidAppPackageName/$signatureHash" ) } #initialize required api permissions $requiredResourceAccess = @( @{ resourceAppId = "00000007-0000-0000-c000-000000000000" <# Dynamics CRM #> resourceAccess = @( @{ Id = "78ce3f0f-a1ce-49c2-8cde-64b5c0896db4" <# Dynamics CRM => user_impersonation #> Type = "Scope" }) } @{ resourceAppId = "fe053c5f-3692-4f14-aef2-ee34fc081cae" <# Azure API Connections #> resourceAccess = @( @{ Id = "6c3012bf-22c1-4bb5-959b-dff738314144" <# Azure API Connections => Runtime.All #> Type = "Scope" }) } @{ resourceAppId = "475226c6-020e-4fb2-8a90-7a972cbfc1d4" <# PowerAppsService #> resourceAccess = @( @{ Id = "0eb56b90-a7b5-43b5-9402-8137a8083e90" <# PowerAppsService => User #> Type = "Scope" }) } ) Write-Host "Requesting login to establish connection to Microsoft Graph..." -ForegroundColor "Cyan" #establish connection to microsoft graph Connect-MgGraph -TenantId $azureTenantId -Scopes "Application.ReadWrite.All" Write-Host "Registering new application..." -ForegroundColor Yellow #register the application in azure $appRegistration = New-MgApplication -DisplayName $androidAppDisplayName -SignInAudience "AzureADMultipleOrgs" -PublicClient $redirectUri -RequiredResourceAccess $requiredResourceAccess Write-Host ("New App Registration Id: " + $appRegistration.appId) Write-Host ("Display Name: " + $appRegistration.displayName) Write-Host ("Redirect Uri: " + $redirectUri) Write-Host ("Application Url: https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/appId/" + $appRegistration.appId) ########################################################### ### enable application in power platform ### ########################################################### Write-Host "Requesting login to establish connection to Power Platform Admin Center..." -ForegroundColor "Cyan" Write-Host "Enabling new application..." -ForegroundColor Yellow Add-AdminAllowedThirdPartyApps -ApplicationId $appRegistration.appId

If you haven't already done so, copy the full script above and paste it into your terminal application of choice. I'd recommend PowerShell ISE to allow you to fully inspect the code and also choose when to run it.

Assuming you did choose to paste it into PowerShell ISE, you can run the script by hitting the green Run icon in the toolbar, or by pressing F5 on your keyboard.

Note: I've made sure to comment the code throughout, which should help you sort out what it's doing.

If you'd like a deeper-dive into how the script is set up, keep an eye out for a subsequent article where I will do just that. For now, I'll try to explain it as concisely as possible.

To start, the script installs and later uses two modules (Microsoft Graph and Power Apps for Administrators), and also the previously installed resources from Android Studio.

After installing the necessary modules, it prompts the user (that's you!) to provide some of the required input parameters to generate a private key. Once the key is generated, it then does some magic to extract the properly encoded signature hash which it will need to create the redirect URI at the app registration stage. Speaking of app registration...

We make good use of the Microsoft Graph module to both create the registration (with the proper redirect URI) and also provision the required API permissions as well!

Finally, we take the id of the newly registered application and enable it using the cmdlet we got from the Power Apps for Administrators module.

Please make note of the values you provided the script (particularly the app package name and the keystore path), and the values the script wrote back to the console. You'll need these soon. The keystore file itself is created in the path you designated - once we are done with this whole process, you will want to store this file in a secure location as it will be needed for any subsequent builds you make for this application.

For the sake of progress, I'll assume you were able to either do all of the steps manually or better yet used the script so that we can move on to the next step in the process, which is...

Configuring Your Wrap for Power Apps Build

We've now made it into the homestretch of our wrap for Power Apps journey!. In this section we'll step back into the familiar world of the Power Platform to turn our Canvas App(s) into a proper native mobile application.

At this stage, we'll want to take stock to make sure all of the values we've retrieved or generated earlier are accounted for. For those following along, we should now have:

  • Android App Center URL (from Preparing the App Center Environment),
  • Android App Center API Token (from Preparing the App Center Environment),
  • App Package Name (aka: Bundle ID) (from Automating Key Generation and App Registration Using PowerShell ),
  • AAD Client ID (from Automating Key Generation and App Registration Using PowerShell ),
  • AAD Client Redirect URI (from Automating Key Generation and App Registration Using PowerShell ),

If you are missing any of these values, you'll want to retrace your steps as we'll need them in order to complete the wrap for Power Apps configuration.

Assuming you've got all of those values at the ready, we can proceed to configure our wrap build.

Note: If you haven't already done so, be sure to either create a new canvas app in a solution or bring an existing app(s) into a solution.

To begin, navigate to Power Apps (https://make.powerapps.com), then:

  • Select Solutions from the left navigation,
  • Select the solution that houses your canvas app(s),
  • Select the canvas application that you would like to designate as the Primary Application,
  • Select Wrap from the toolbar.

This will open the wrap for Power Apps build configuration pane, where you will:

  • Provide a display name for your application,
  • Select any secondary apps to include in the package,
  • Select Android as the target app platform,
  • Provide the Bundle ID (aka: App Package Name) from earlier steps,
  • Provide the AAD Client ID from earlier steps,
  • Upload Android app icons to be used throughout the mobile device,
  • Upload a Splash screen image to be used at app startup/sign-in,
  • Upload a Welcome image to be used at app startup/sign-in,
  • Provide the Background Fill Color and Button Fill Color to be used within the app,
  • Select a Status bar text theme,
  • Provide the App Center URL from earlier steps,
  • Provide the App Center API Token from earlier steps.
  • Select Save to save any changes to the configuration.

If all the required values have been provided (and pass initial validation checks), the Build button will become enabled for you to select. Select Build and confirm you'd like to begin the build process.

At this point, the backend wrap for Power Apps build process will initialize and begin to bundle up the primary canvas app (and any secondary apps). As of this writing, the current build process takes approximately 60 minutes to complete - though performace improvements are in the pipeline.

While the build process completes, feel free to navigate away from this page and come back later to check in on its status. In the following section we'll go over what to do once the process finalizes.

Signing Your Application Package

The fruit of our labor is just about ready for harvesting. Before we can do that, however, we'll need to run one last piece of code to sign the build's output.

Once your build process is marked as Succeeded in the build configuration pane, you'll want to head back over to App Center to retrieve the app package (an apk file) it created.

To do so, navigate to App Center and the app container you created from earlier steps, and then:

  • Select Distribute from the left navigation,
  • Select Releases from the left navigation (under Distribute),
  • Select the release number that correlates to your most recent build run,
  • Select Download in the top-right corner to download the file to your local hard drive.

The universal.apk file that is downloaded (which you may rename to something more identifiable), needs to be signed with the private key we generated earlier.

Here are the requisite lines of code to do this:
########################################################### ### sign the application file ### ########################################################### Write-Host "Initializing variables..." -ForegroundColor "Cyan" #request input for key alias $keyAlias = Read-Host "Provide the key alias (eg: 'hellowrap')" #request input for keystore path $keystorePath = Read-Host "Provide the path/name to the keystore (eg: 'c:\users\nflightless\documents\hellowrap.keystore')" #request input for key password $keyPassword = Read-Host "Provide the password for the key (eg: 'hellopassword')" #request path to unsigned apk file $apkPath = Read-Host "Provide the path for the unsigned apk file (eg: 'c:\users\nflightless\downloads\universal.apk')" #request path to write signed apk file $apkPath = Read-Host "Provide the path to write the signed apk file to (eg: 'c:\users\nflightless\downloads\universal_signed.apk')" apksigner.bat sign --ks $keystorePath --ks-key-alias $keyAlias --ks-pass pass:$keyPassword $apkPath Write-Host "App signing complete" -ForegroundColor "Green"

The script requests the values from earlier steps (relating to the key/keystore you generated), the path to the unsigned file you downloaded from App Center, and a path for the final signed file to generate.

As before, copy and paste the script to your terminal application of choice and run it. You'll find the signed apk file in the output path you provided.

You may wish to copy this file to your testing device to confirm all functionality is working as expected.

Distributing Your Application

Once you have successfully signed your application, you are now free to distribute it in a fashion to meet your users where they are. Options for distribution include:

Each of these options enable you to deploy your application at scale and to your users in a way that's familiar to them. The choice of which to choose is highly dependent on your organization's existing services and deployment practices.

Note: Deploying through the non-managed Google Play Store is subject to Google's developer guidelines and policies.

Wrapping Things Up

If you've made it this far, my hope is that you've been able to successfully get your first wrap project under your belt and have a solid understanding of the steps needed to do so.

The above guide is an initial step on my part towards fully automating the wrap for Power Apps process, which I expect to expand upon in short order as the service grows.

Be sure to check back periodically for updates as I come across areas of improvement.