Auto-Update UWP Apps over Azure BLOB Storage Static Websites

UWP has a very nice app update mechanism outside of the store that allows you to publish new releases to a public-facing website – and UWP taking care of the rest. And while the docs recommend Azure Web Apps for this, you can get the same results for a lot cheaper using the Static Websites feature in Azure BLOB Storage.

Enabling MSIX ms-appinstaller protocol handler to support auto updates

Unfortuantely there’s one major drawback with this automatic update mechanism: it has been disabled by Microsoft by default on all Windows machines due to security concerns. That means it needs to be re-enabled manually on all devices you want to support.

  • Ensure you have App Installer Version 1.17.10941.0 or newer installed which you can get through the Windows Store
  • Donwload the Desktop App Installer policy and unzip it into the %systemroot%\PolicyDefinitions folder
  • Afterwards you can enable the policy through the Local Group Policy Editor by navigating to Computer Configuration > Administrative Templates > Windows Components > Desktop App Installer and selecting Enable App Installer ms-appinstaller protocol.

Configuring Update Location in the UWP App

The easiest way to set the URL from where to get the updates inside the UWP App is to set it in Visual Studio though the Project -> Publish -> Create App Packages dialog.

Create App Package with Automatic Updates enabled

This will update the AppInstallerUri Property inside your apps .csproj file. The URL should point to the root of the BLOB Storage’s Static Website inside the $web container and will look something like this: https://myappupdate.z1.web.core.windows.net

Configure Auto Update Location

Deploying App Package to Azure BLOB Storage using Azure DevOps Pipelines

The following YAML script shows how to build, sign and upload the UWP app to Azure BLOB Storage’s static website container. You do need to make sure that with each build the app’s version number increases in order for the automatic update to work. This can be done in the pipeline as well but is not within the scope of this blogpost.

trigger:
– main
pool:
vmImage: 'windows-latest'
variables:
– group: certificate
– name: solution
value: '**/*.sln'
– name: buildPlatform
value: 'x64'
– name: buildConfiguration
value: 'Release'
– name: appxPackageDir
value: '$(build.artifactStagingDirectory)\AppxPackages\\'
steps:
– task: NuGetToolInstaller@1
– task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
– task: DownloadSecureFile@1
name: mySecureFile
displayName: 'Get the pfx file certificat'
inputs:
secureFile: '$(signingCert.secureFilePath)'
– task: PowerShell@2
inputs:
targetType: 'inline'
script: |
Write-Host "Start adding the PFX file to the certificate store."
$pfxpath = '$(mySecureFile.secureFilePath)'
$password = '$(signingCert.password)'
Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet")
$store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "MY", CurrentUser
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadWrite")
$store.Add($cert)
$store.Close()
– task: VSBuild@1
inputs:
platform: 'x64'
solution: '$(solution)'
configuration: '$(buildConfiguration)'
msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)"
/p:AppxPackageDir="$(appxPackageDir)"
/p:AppxBundle=Always
/p:UapAppxPackageBuildMode=SideLoadOnly
/p:AppxPackageSigningEnabled=true
/p:PackageCertificateThumbprint="$(signingCert.thumbprint)"
/p:PackageCertificateKeyFile="$(mySecureFile.secureFilePath)"
/p:PackageCertificatePassword="$(signingCert.password)"'
– task: CopyFiles@2
displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
inputs:
SourceFolder: '$(system.defaultworkingdirectory)'
Contents: '**\bin\$(BuildConfiguration)\**'
TargetFolder: '$(build.artifactstagingdirectory)'
– task: AzureCLI@2
displayName: 'Clear Static website'
inputs:
azureSubscription: 'MySubscription'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: 'az storage blob delete-batch –account-name storageaccountname –source "\$web"'
– task: AzureCLI@2
displayName: 'Upload'
inputs:
azureSubscription: 'MySubscription'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: 'az storage blob upload-batch –account-name storageaccountname –destination "\$web" –source ''$(build.artifactstagingdirectory)/AppxPackages'' –overwrite'
– task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: drop'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.