Create Bot for Microsoft Graph with DevOps 6: Continuous Deployment – Release Definition

This time, I create a Release Definition to complete the CI/CD pipeline. While build definition defines how to build the source, release definition defines how to release the compiled bits. I include following three tasks.

  • Infrastructure as a Code (ARM template for Azure) to automate infrastructure.
  • Release
  • Function Test

You can find the detail of ARM Template here.

ARM (Azure Resource Manager) Template

I already created App Service via Visual Studio, but I will automate it by using ARM Template.

Get Template

It’s tedious to write a template from scratch, and you can get it from Azure Portal.

1. Login to https://portal.azure.com. To start from scratch, let’s delete the resource group provisioned previously.
* Before delete the environment, please take a note for each application settings as you need them later.

image

2. Next, create a Web App from new.

image

3. Specify the settings and click [Automation options], which generates templates for you.

image

4. Click [Download]

image

5. Extract the template.zip file. You find template.json, which contains service definitions and parameters.json, which contains values for each parameters.

image

6. As this template only contains single WebApp service definition, you can add more as you need. For now, replace the template.json as follows.

{
  "parameters": {
    "webName": {
      "type": "string"
    },
    "webNameTest": {
      "type": "string"
    },
    "hostingPlanName": {
      "type": "string"
    },
    "hostingEnvironment": {
      "type": "string"
    },
    "location": {
      "type": "string"
    },
    "sku": {
      "type": "string"
    },
    "skuCode": {
      "type": "string"
    },
    "workerSize": {
      "type": "string"
    },
    "serverFarmResourceGroup": {
      "type": "string"
    },
    "subscriptionId": {
      "type": "string"
    },
    "botId": {
      "type": "string"
    },
    "microsoftAppId": {
      "type": "string"
    },
    "microsoftAppPassword": {
      "type": "string"
    },
    "activeDirectory.RedirectUrl": {
      "type": "string"
    },
    "botIdTest": {
      "type": "string"
    },
    "microsoftAppIdTest": {
      "type": "string"
    },
    "microsoftAppPasswordTest": {
      "type": "string"
    },
    "activeDirectory.RedirectUrlTest": {
      "type": "string"
    }
  },
  "resources": [
    {
      "apiVersion": "2016-03-01",
      "name": "[parameters('webName')]",
      "type": "Microsoft.Web/sites",
      "properties": {
        "name": "[parameters('webName')]",
        "serverFarmId": "[concat('/subscriptions/', parameters('subscriptionId'),'/resourcegroups/', parameters('serverFarmResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
        "hostingEnvironment": "[parameters('hostingEnvironment')]"
      },
      "location": "[parameters('location')]",
      "tags": {
        "[concat('hidden-related:', '/subscriptions/', parameters('subscriptionId'),'/resourcegroups/', parameters('serverFarmResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "empty"
      },
      "dependsOn": [
        "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
      ],
      "resources": [
        {
          "apiVersion": "2015-08-01",
          "name": "appsettings",
          "type": "config",
          "tags": {
            "displayName": "WebAppSettings"
          },
          "properties": {
            "BotId": "[parameters('botId')]",
            "MicrosoftAppId": "[parameters('microsoftAppId')]",
            "MicrosoftAppPassword": "[parameters('microsoftAppPassword')]",
            "ActiveDirectory.RedirectUrl": "[parameters('activeDirectory.RedirectUrl')]"
          },
          "dependsOn": [
            "[concat('Microsoft.Web/sites/', parameters('webName'))]"
          ]
        }
      ]
    },
    {
      "apiVersion": "2016-03-01",
      "name": "[parameters('webNameTest')]",
      "type": "Microsoft.Web/sites",
      "properties": {
        "name": "[parameters('webNameTest')]",
        "serverFarmId": "[concat('/subscriptions/', parameters('subscriptionId'),'/resourcegroups/', parameters('serverFarmResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
        "hostingEnvironment": "[parameters('hostingEnvironment')]"
      },
      "location": "[parameters('location')]",
      "tags": {
        "[concat('hidden-related:', '/subscriptions/', parameters('subscriptionId'),'/resourcegroups/', parameters('serverFarmResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "empty"
      },
      "dependsOn": [
        "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
      ],
      "resources": [
        {
          "apiVersion": "2015-08-01",
          "name": "appsettings",
          "type": "config",
          "tags": {
            "displayName": "WebAppSettings"
          },
          "properties": {
            "BotId": "[parameters('botIdTest')]",
            "MicrosoftAppId": "[parameters('microsoftAppIdTest')]",
            "MicrosoftAppPassword": "[parameters('microsoftAppPasswordTest')]",
            "ActiveDirectory.RedirectUrl": "[parameters('activeDirectory.RedirectUrlTest')]"
          },
          "dependsOn": [
            "[concat('Microsoft.Web/sites/', parameters('webNameTest'))]"
          ]
        }
      ]      
    },
    {
      "apiVersion": "2016-09-01",
      "name": "[parameters('hostingPlanName')]",
      "type": "Microsoft.Web/serverfarms",
      "location": "[parameters('location')]",
      "properties": {
        "name": "[parameters('hostingPlanName')]",
        "workerSizeId": "[parameters('workerSize')]",
        "numberOfWorkers": "1",
        "hostingEnvironment": "[parameters('hostingEnvironment')]"
      },
      "sku": {
        "Tier": "[parameters('sku')]",
        "Name": "[parameters('skuCode')]"
      }
    }
  ],
  "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0"
}

7. Replace the code in paramters.json. Change the value to fit your environment.

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "webName": {
      "value": "o365botprod"
    },
    "webNameTest": {
      "value": "o365bottest"
    },
    "hostingPlanName": {
      "value": "O365BotPlan"
    },
    "hostingEnvironment": {
      "value": ""
    },
    "location": {
      "value": "South Central US"
    },
    "sku": {
      "value": "Standard"
    },
    "workerSize": {
      "value": "0"
    },
    "serverFarmResourceGroup": {
      "value": "O365BotRG"
    },
    "skuCode": {
      "value": "S1"
    },
    "subscriptionId": {
      "value": "__YourAsureSubscriptionId__"
    },
    "botId": {
      "value": "__YourBotId__"
    },
    "microsoftAppId": {
      "value": "__YourMicrosoftAppId__"
    },
    "microsoftAppPassword": {
      "value": "__YourMicrosoftAppPassword__"
    },
    "activeDirectory.RedirectUrl": {
      "value": "__YourSite__/api/OAuthCallback"
    },
    "botIdTest": {
      "value": "__YourTestBotId__"
    },
    "microsoftAppIdTest": {
      "value": "__YourTestMicrosoftAppId__"
    },
    "microsoftAppPasswordTest": {
      "value": "__YourTestMicrosoftAppPassword__"
    },
    "activeDirectory.RedirectUrlTest": {
      "value": "__YourTestSite__/api/OAuthCallback"
    }
  }
}

Create repository for ARM template

In VSTS, create new repository for ARM template.

1. Login to your VSTS and go to the project.

2. Create the repository dropdown and click [New repository].

image

3. Enter any name such as ARM.

image

4. Click [Initialize].

image

5. Once the repository created, copy template.json and parameters.json.

image

Release Definition

Now I am ready to create the Release Definition.

Create Release Definitoin

1. Select Build & Release and go to Releases, click [New definition].

image

2. Select Empty as template and click [Next].

image

3. Select your build for this release and enable [Continuous deployment]. As you can see, Jenkins is also supported Smile.

image

Now blank definition is created.

Add Artifacts

If you need files in addition to Build output, you can link them as Artifact.

1. Select Artifacts tab and click [Link an artifact source]

image

2. Select ARM repository by using Git type.

image

3. Do the same for BotWithDevOps Repository.

image

Add ARM task

1. Go back to Environment tab, and rename [Environment 1] to [ARM]

image

2. Click [Add tasks].

image

3. From Deploy category, add [Azure Resource Group Deployment].

image

4. Change the template version down to [1.*]. v2 didn’t work well in my lab.

image

5.Select the Azure subscription, and click [Authorize].  Select [Create or update resource group] for Action which create the environment if not exist, otherwise update the settings to match with templates.

image

6. Click […] menu next to Template, and select template.json from ARM artifact.

image

7. Do the same for parameters.json.

image

Add Release Definition

1. Click [Add environment] and select [Create new environment].

image

2. Select [Azure App Service Deployment with Test] template.

image

3. Select [Automatically approve] and create.

image

4. Change the environment name to Test.

image

5. Specify Azure Subscription and App Service Name.

image

6. Select RunTests task, and update Test assemblies to *functiontests*.dll.

7. Click […] for Settings File

image

8. Select Test.runsettings from BotWithDevOps git.

image

9. I also enabled [Code coverage enabled]. Just select any option as you want.

image

10. Click [Run on agent] and select [Hosted VS2017].

image

11. Let’s add prod environment, too. You can clone the environment this time.

image

12. Change the environment name to [Prod]

image

13. Change App Service name.

image

14. Update Run Tests Settings file, too.

image

15. Then name the release definition and click [Save]

image

Test the Release Definition

Let’s test the definition.

1. Click [Create Release] from Release.

image

2. Select the latest check-in and create.

image

3. Select the definition on the left pane, and you see release is queued. Click […] to open it.

image

4. Click Log tab to see details.

image

5. Confirm the result. If something went wrong, fix the issue.

Summery

Okay, most of the DevOps part has been done! I will start introducing BotBuilder feature from next time.

Ken