WebTest

Deploy Azure ARM using Octopus Deploy

At the moment I’m working on a project which uses Octopus Deploy for all deployments to Kubernetes clusters and Azure WebApps.

This project is used for powering 18 different frontends, so, the code is built once and deployed to 18 different sites. These sites are defined as a Tenant in Octopus, so, I only need to define the project once and connect the Tenant to the project. This way I can simply update 18 different applications using a single project with a single click!

Past week we’ve decided to create a ping test for all these applications using Azure Application Insights. Within the AI resource, there is an “Availability” blade:

Availability blade

Within this blade you can create a ping test which is just an ordinary web requests which validates the response code. It’s also possible to validate the returned body by checking the content.

Creating Tests

Creating a ping test isn’t really the challenge. However, creating ping tests for all 18 applications, including additional API endpoints for both the Development, Test, Staging and Production environments really is(around 50 endpoints must be validated per environment). And above all, automating stuff is just fun.

My first attempt was to create the tests using Powershell, based upon a JSON file containing all the endpoints:

Param (
	[Parameter(Mandatory=$True)] [string]$resourceGroup,
    [Parameter(Mandatory=$True)] [string]$resourceName = 10,
    [Parameter(Mandatory=$True)] [string][ValidateSet("tst","acc","prd")] $environment,
    [Parameter(Mandatory=$True)] [bool]$includeAlert
)

$json = Get-Content -Raw -Path "$PSScriptRoot\data\$environment.json" | ConvertFrom-Json
$endpoints = $json.endpoints
$subscriptionid = $json.settings.subscriptionID

Import-AzureRmContext -Path "D:\brand_$environment.json"
Set-AzureRmContext -SubscriptionId $subscriptionid

foreach($site in $endpoints){
    $testName = $site.brand + " " + $site.segment + " " + $site.datasource
    $url = $site.url
    $status = $site.enabled
    
    Write-Host "Creating Availability Test for: $testName"
    
    New-AzureRMResourceGroupDeployment -ResourceGroupName "$resourceGroup" -webTestName "$testName" -appName "$resourceName" -URL "$url" -Enabled $status -templatefile .\templates\install_webtest.json

    if($includeAlert){
        Start-Sleep -s 1
        New-AzureRMResourceGroupDeployment -ResourceGroupName "$resourceGroup" -webTestName "$testName" -appName "$resourceName" -URL "$url" -templatefile .\templates\install_alertrule.json
    }
}

This script is reading the endpoints from json and uses the:
“New-AzureRMResourceGroupDeployment” powershell command to create the Azure resource based upon a template file.

This is working, however, it uses data which is already defined in other systems, and I didn’t like the duplicate endpoint definitions. All these endpoints have already been defined in each Tenant in Octopus Deploy.

Automating stuff using Octopus

Looking at the available Octopus step templates, I noticed this step:

Deploy an Azure Resource Manager Template
Deploy an Azure Resource Manager Template

This step was exactly what I needed. This step allows you to deploy ARM templates to the defined deployment targets. Now, I can deploy all WebTests and alerts using existing tenants, combined with this step! Let’s check how to accomplish this!

Step details

This templates step has 2 options to use a template:

  1. Template from source code, this is directly inserted in the step itself
  2. Template from a package

The first option is actually quite nice, although not my preferred choice. Using this step allows you to insert the ARM template directly in the source editor. Octopus deploy then extracts the included parameters and lists these directly under the source itself, wow! this is nice. All Parameters can be inserted right away:

Auto generating parameters from ARM template

Almost ideal. Unfortunately, I don’t like putting code into a workflow which is not in version control.

I need to use ‘File inside a package’ option to retrieve the ARM template. But using this option, Octopus is not able to extract the parameters from the file, because at this point, Octopus doesn’t know the contents of the file itself. To make this work, we can use a parameter file which is a bridge between the ARM template and the ‘outside’ world.
More info about using this option can be found here:
https://octopus.com/docs/deployment-examples/azure-deployments/resource-groups

ARM templates

The current ARM template for a WebTest:

{
  "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "param_webTestName": { "type": "string" },
    "param_appName": { "type": "string" },
    "param_url": { "type": "string" },
    "param_enabled": { "type": "string" }
  },
  "variables": {
    "alertRuleName": "[concat(parameters('param_webTestName'), '-', toLower(parameters('param_appName')), '-', subscription().subscriptionId)]"
  },
  "resources": [
    {
      "id": "[resourceId('Microsoft.Insights/webtests', parameters('param_webTestName'))]",
      "name": "[parameters('param_webTestName')]",
      "apiVersion": "2015-05-01",
      "type": "Microsoft.Insights/webtests",
      "location": "westeurope",
      "tags": {
        "[concat('hidden-link:', resourceId('Microsoft.Insights/components', parameters('param_appName')))]": "Resource"
      },         
      "properties": {
        "SyntheticMonitorId": "[parameters('param_webTestName')]",
        "Name": "[parameters('param_webTestName')]",
        "Description": "",
        "Enabled": "[parameters('param_enabled')]",
        "Frequency": 300,
        "Timeout": 120,
        "Kind": "ping",
        "RetryEnabled": true,
        "Locations": [
          {
            "Id": "emea-nl-ams-azr"
          },
          {
            "Id": "emea-se-sto-edge"
          },
          {
            "Id": "emea-gb-db3-azr"
          },
          {
            "Id": "emea-fr-pra-edge"
          },
          {
            "Id": "us-va-ash-azr"
          }
        ],
        "Configuration": {
          "WebTest": "[concat('<WebTest Name=\"', parameters('param_webTestName') , '\" Id=\"cce28484-9b27-4cff-97d5-c84ae36d6f41\" Enabled=\"True\" CssProjectStructure=\"\" CssIteration=\"\" Timeout=\"120\" WorkItemIds=\"\" xmlns=\"http://microsoft.com/schemas/VisualStudio/TeamTest/2010\" Description=\"\" CredentialUserName=\"\" CredentialPassword=\"\" PreAuthenticate=\"True\" Proxy=\"default\" StopOnError=\"False\" RecordedResultFile=\"\" ResultsLocale=\"\"&gt; <Items&gt; <Request Method=\"GET\" Guid=\"a1638f22-2831-5fea-21c0-8a46498d711c\" Version=\"1.1\" Url=\"', parameters('param_url') , '\" ThinkTime=\"0\" Timeout=\"120\" ParseDependentRequests=\"True\" FollowRedirects=\"True\" RecordResult=\"True\" Cache=\"False\" ResponseTimeGoal=\"0\" Encoding=\"utf-8\" ExpectedHttpStatusCode=\"200\" ExpectedResponseUrl=\"\" ReportingName=\"\" IgnoreHttpStatusCode=\"False\" /&gt;</Items&gt; </WebTest&gt;')]"
        },
        "provisioningState": "Succeeded"
      }
    }
  ]
}

And the corresponding parameter file:

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "param_webTestName": { "value": "#{Azure.AI.Pingtest.Name}" },
      "param_appName": { "value": "#{Azure.Application.Insights}" },
      "param_url": { "value": "https://#{K8S.Service.HostName}#{Test.Path}" },
      "param_enabled": { "value": "#{Test.Enable}" }
    }
  }

Note the values for the parameters. Octopus is automatically replacing all the Octopus parameters included in the file. Now I need to make sure that each Tenant is able to return these parameters.

Last step is to connect the tenants to this project et voila! Each release is creating ping tests in Azure. This is all done within a few minutes now:

Azure webtest deployment using tenants

I can enable or disable each WebTest using the settings in the tenant itself. Every info I need can be managed within Octopus deploy.

All generated WebTests running!

And the final result:

Azure url ping test
Azure url ping test

Folkert

I'm a webdeveloper, looking for the best experience, working between development and design. Just a creative programmer. When I'm getting tired of programming C#, i'd love to create 3D images in 3D Studio Max, play the guitar, create an app for Android or crush some plastics on a climbing wall or try to stay alive when i´m descending some nice white powdered snowy mountains on my snowboard.

Leave a Reply

Your email address will not be published. Required fields are marked *

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