Deploying your code with ARM templates in Azure Managed Apps

I'm currently trying to publish an Azure Managed Application in the Azure Marketplace , and one of the issues I've encountered is deploying the code for my Azure Functions and Web Apps.

First, let me show you a simplified structure of the zip file that I submitted for certification:

- createUiDefinition.json
- mainTemplate.json
- nestedtemplates (folder)
   - webapi.json

Here's how it works:

  • the "createUIDefinition.json" file contains the UI that you see in Azure, it's the place where you configure your resources(storage type, name of the app, app service plan, etc.) You can try the Create UI definition sandbox website and see how it works.
  • the "mainTemplate.json" is where you specify what resources you want to create (web apps, databases, storage, virtual machines, etc.)
  • nested templates are a way to separate the creation of your resources so you don't put everything in the "mainTemplate.json" file. In my example, I have just an webapi that I want to deploy so I'll just show this file.

Using these json files you can have the resources created in a matter of minutes, which is pretty cool! However, the Web API needs the code to be deployed, otherwise it will be just an empty Azure Web App, so how do we do that?

Well, we could use a site extension which does the deploy, like this:

{
    "type": "Extensions",
    "apiVersion": "2021-01-01",
    "name": "MSDeploy",
    "dependsOn": [
        "[resourceId('Microsoft.Web/Sites/', variables('appServiceName'))]",
        "[resourceId('Microsoft.Web/Sites/config', variables('appServiceName'), 'connectionstrings')]"
    ],
    "properties": {
        "packageUri": "https://www.mystorage.com\webapi.zip"
    }
}

This site extension will download the zip file from the address I specified in the packageUri property and deploy it inside my Azure Web App. Pretty simple, right?

Well, not exactly...it seems that in order to pass certification, you can't specify external resources, so your code has to be inside the same zip file that you submit for certification. Thankfully, the reviewers pointed me to the right documentation and I was able to figure out what to do, so I'll describe the steps in here as well.

It seems that you are allowed to also deploy scripts or artifacts, not only json files, but you have to follow some rules.

1. Create a zip file of your published app

In my case, I just had to download the artifact generated by my Azure DevOps pipeline, but you can also do it manually if you use the command "dotnet publish" and zip the contents of the "publish" folder.

2. Add the published app inside the zip file

In the root I only have the "createUIDefinition.json" and "mainTemplate.json" files, this is the recommended approach. That's why I created an "artifacts" folder where I placed my published app, so now the structure looked like this:

- createUiDefinition.json
- mainTemplate.json
- nestedtemplates (folder)
   - webapi.json
- artifacts (folder)
   - webapi.zip - this is a zip file with the contents of the "bin\Release\net5.0\publish" folder

3. Point the packageURI property to the location of the zip file

In order to do this we have to allow 2 parameters in the mainTemplate.json file, they are called "_artifactsLocation" and "_artifactsLocationSasToken". DO NOT change their name, because they are automatically initialized during deployment and if you change the name like I did initially then it won't work.

This is how you add them in the parameters of the mainTemplate:

"parameters": {
        "_artifactsLocation": {
            "type": "string",
            "metadata": {
                "description": "The base URI where artifacts required by this template are located including a trailing '/'"
            },
            "defaultValue": "[deployment().properties.templateLink.uri]"
        },
        "_artifactsLocationSasToken": {
            "type": "securestring",
            "metadata": {
                "description": "The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured."
            },
            "defaultValue": ""
        }
}

4. Build the URI of the zip file

With these two parameters you can then create a variable that contains the location of the zip file:

"zipUrl": "[uri(parameters('_artifactsLocation'), concat('artifacts/webapi.zip', parameters('_artifactsLocationSasToken')))]"

As you can see, I use the _artifactsLocation as the base URI, then I concatenate the location of the zip file and the SAS token.

5. Use the zipUrl in the deployment of your web api

{
    "type": "Extensions",
    "apiVersion": "2021-01-01",
    "name": "MSDeploy",
    "dependsOn": [
        "[resourceId('Microsoft.Web/Sites/', variables('appServiceName'))]",
        "[resourceId('Microsoft.Web/Sites/config', variables('appServiceName'), 'connectionstrings')]"
    ],
    "properties": {
        "packageUri": "[variables('zipUrl')]"
    },
    "condition": "[not(equals(variables('zipUrl'), ''))]"
}

Now your code will be deployed in the app service that you created.

Did you find this article valuable?

Support Bogdan Bujdea by becoming a sponsor. Any amount is appreciated!