How to Leverage ARM Templates in Azure for Intelligent Deployment

May 31 2018
 

Learn about Azure Resource Manager (ARM) templates and how to leverage them for intelligent deployment of the AppDynamics Site Extension.


We previously showed in a blog post, The AppD Approach: Deployment Options for .NET Microservices Agent, how the advanced portability and refinement of AppDynamics’ new .NET Microservices Agent delivers great value to our customers who monitor .NET Core applications. Let’s dig further into how to deploy this agent following Infrastructure as Code (IaC) practices.

Azure Resource Manager (ARM) templates are an exciting framework for IaC. They allow you to create an entire environment from a simple template, with all the resources needed for your applications.

The ability to include the AppDynamics Site Extension as part of your ARM template deployment, however, introduces a few operational challenges, including:

  • How do you ensure your monitoring solution is installed only on specific environments?

  • Should you avoid creating a separate ARM template for each environment?

We can solve these challenges by putting the “Code” portion of IaC to work.

Condition is the Key

When working with ARM templates, It doesn’t take long for you to ask the question:

  • How do you deploy a resource based on a condition?

The Azure Resource Manager team answered with the condition attribute, which controls whether a resource will be deployed or not. If the condition check evaluates to “true,” the resource will be deployed. If not, the resource is skipped.

To enable the ARM template to be dynamic enough to deploy the AppDynamics Site Extension only when a condition evaluates to true, you must provide a parameter to contain your condition test, and add the condition attribute to your Site Extension resource.

Test Condition Parameter

"shouldInstallAppDynamicsExtension": {
  "type": "bool",
  "defaultValue": false,
  "metadata": {
    "description": "Conditional parameter to determine if AppDynamics Site Extension should be installed."
  }
}

Site Extension Resource with “condition” Attribute

{
    "apiVersion": "2015-08-01",
    "condition": "[parameters('shouldInstallAppDynamicsExtension')]",
    "name": "[parameters('appDynamicsSiteExtension')]",
    "type": "siteextensions",
    "dependsOn": [
    "[resourceId('Microsoft.Web/Sites/', variables('webSiteName'))]"
    ]
}

With the condition attribute added to your Site Extension resource, and a parameter waiting for you to give it a true or false value, you can easily determine—with a single template—when to deploy the AppDynamics Site Extension.

The next question becomes:

  • How can we configure the AppDynamics Site Extension automatically?

Object Parameters for the Win

One of the great options in ARM templates is the ability to create parameters of object type. This allows you to use a JSON object as a parameter value. Mix that capability with other ARM template magic, and you can easily configure the AppDynamics Site Extension without hard-coding any of template’s application settings.

AppDynamics Settings Object Type Parameter

"appDynamicsSettings": {
    "type": "object",
    "defaultValue": {
    "appdynamics.controller.hostName": "",
    "appdynamics.controller.port": "",
    "appdynamics.agent.accountName": "",
    "appdynamics.agent.accountAccessKey": "",
    "appdynamics.agent.applicationName": "",
    "appdynamics.controller.ssl.enabled": "",
    "appdynamics.agent.tierName": ""
    },
    "metadata": {
    "description": "Settings needed to get AppDynamics Site Extension configured."
    }
}

By setting the Application Settings Configuration Resource’s properties attribute value—the object type parameter for the AppDynamics Site Extension configuration—you can dynamically set the list of configuration settings without creating individual parameters for each setting. Then, as those settings change over time, your template doesn’t need to.

Application Settings Configuration Resource with Dynamic Properties

{
    "name": "appsettings",
    "type": "config",
    "apiVersion": "2015-08-01",
    "dependsOn": [
    "[resourceId('Microsoft.Web/sites', variables('webSiteName'))]"
    ],
    "tags": {
    "displayName": "appsettings"
    },
    "properties": "[parameters('appDynamicsSettings')]"
}

An important note about the Application Settings resource in an ARM template: it completely removes and replaces all the application settings in the App Service. If any application settings were manually added or changed after a deployment, they will be deleted.

The next challenge is determining how to configure your AppDynamics Site Extension configuration settings to deploy only when you deploy the Site Extension. Let’s explore how Template Functions can help with this.

To Union or Not to Union

One of the issues with the previous example—limiting the App Services application settings to only the AppDynamics parameter object—is that it doesn’t provides a place for adding additional settings. You can solve this issue by creating an object variable to contain the base application settings, and then figure out how to conditionally combine both groups of settings.

Object Variable to Define App Settings

variables": {
   "websiteName":"some-website-name"
   "appSettings": {
   "SomeRandomAppSetting": "Some Random Value"
   "AnotherRandomAppSetting": "Another Random Value"
   } 
}

ARM templates have a useful collection of functions to help make then more dynamic. To add the logic check for whether you should deploy the AppDynamics Site Extension configuration settings, you can use the if logical function and the union object function.

To begin, dynamically set the properties attribute by passing the previous condition test parameter—“shouldInstallAppDynamicsExtension”— to the if function. Based on that conditional check, you can then return a combination of the base “appSettings” variable and the “appDynamicsSettings” parameter with the union function, or just return the “appSettings” variable.

Conditionally Setting the Application Settings Properties Attribute

{
    "name": "appsettings",
    "type": "config",
    "apiVersion": "2015-08-01",
    "dependsOn": [
    "[resourceId('Microsoft.Web/sites', variables('webSiteName'))]"
    ],
    "tags": {
    "displayName": "appsettings"
    },
    "properties": "[if(parameters('shouldInstallAppDynamicsExtension'), union(variables('baseAppSettings'), parameters('appDynamicsSettings')), variables('baseAppSettings'))]"
}

Putting It All Together

You now have all the parts to dynamically deploy the AppDynamics Site Extension to an Azure App Service, based on a single boolean parameter. Let’s take a look at the ARM template as a whole.

The example below is a complete template for deploying a Hosting Plan (Service Plan), App Service (Web App), Application Settings for the App Service, and the Site Extension for the App Service. Included are the additional parameters and variables to enable the deployment of the AppDynamics Site Extension:

Complete ARM Template Example

{
 "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
 "contentVersion": "1.0.0.0",
 "parameters": {
"hostingPlanName": {
  "type": "string",
  "minLength": 1
},
"skuName": {
  "type": "string",
  "defaultValue": "F1",
  "allowedValues": [
    "F1",
    "D1",
    "B1",
    "B2",
    "B3",
    "S1",
    "S2",
    "S3",
    "P1",
    "P2",
    "P3",
    "P4"
  ],
  "metadata": {
    "description": "Describes plan's pricing tier and capacity. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
  }
},
"skuCapacity": {
  "type": "int",
  "defaultValue": 1,
  "minValue": 1,
  "metadata": {
    "description": "Describes plan's instance count"
  }
},
"appDynamicsSiteExtension": {
  "type": "string",
  "defaultValue": "AppDynamics.WindowsAzure.SiteExtension.4.4.Release",
  "allowedValues": [
    "AppDynamics.WindowsAzure.SiteExtension.4.4.Release",
    "AppDynamics.WindowsAzure.SiteExtension.4.3.Release",
    "AppDynamics.Java.Agent.4.4.Release"
  ],
  "metadata": {
    "description": "Full path name of the AppDynamics Site Extension to install."
  }
},
"shouldInstallAppDynamicsExtension": {
  "type": "bool",
  "defaultValue": false,
  "metadata": {
    "description": "Conditional parameter to determine if AppDynamics Site Extension should be installed."
  }
},
"appDynamicsSettings": {
  "type": "object",
  "defaultValue": {
    "appdynamics.controller.hostName": "",
    "appdynamics.controller.port": "",
    "appdynamics.agent.accountName": "",
    "appdynamics.agent.accountAccessKey": "",
    "appdynamics.agent.applicationName": "",
    "appdynamics.controller.ssl.enabled": "",
    "appdynamics.agent.tierName": ""
  },
  "metadata": {
    "description": "Settings needed to get AppDynamics Site Extension configured."
  }
}
 },
 "variables": {
"webSiteName": "[concat('some-webapp-', uniqueString(resourceGroup().id))]",
"appSettings": {
  "SomeRandomAppSetting": "Some Random Value",
  "AnotherRandomAppSetting": "Another Random Value"
},
 },
 "resources": [
{
  "apiVersion": "2015-08-01",
  "name": "[parameters('hostingPlanName')]",
  "type": "Microsoft.Web/serverfarms",
  "location": "[resourceGroup().location]",
  "tags": {
    "displayName": "HostingPlan"
  },
  "sku": {
    "name": "[parameters('skuName')]",
    "capacity": "[parameters('skuCapacity')]"
  },
  "properties": {
    "name": "[parameters('hostingPlanName')]"
  }
},
{
  "apiVersion": "2015-08-01",
  "name": "[variables('webSiteName')]",
  "type": "Microsoft.Web/sites",
  "location": "[resourceGroup().location]",
  "tags": {
    "[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
    "displayName": "Website"
  },
  "dependsOn": [
    "[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
  ],
  "properties": {
    "name": "[variables('webSiteName')]",
    "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
  },
  "resources": [
    {
      "name": "appsettings",
      "type": "config",
      "apiVersion": "2015-08-01",
      "dependsOn": [
        "[resourceId('Microsoft.Web/sites', variables('webSiteName'))]"
      ],
      "tags": {
        "displayName": "appsettings"
      },
      "properties": "[if(parameters('shouldInstallAppDynamicsExtension'), union(variables('appSettings'), parameters('appDynamicsSettings')), variables('appSettings'))]"
    },
    {
      "apiVersion": "2015-08-01",
      "condition": "[parameters('shouldInstallAppDynamicsExtension')]",
      "name": "[parameters('appDynamicsSiteExtension')]",
      "type": "siteextensions",
      "dependsOn": [
        "[resourceId('Microsoft.Web/Sites/', variables('webSiteName'))]"
      ]
      }
  ]
}
 ]
}

Stayed Tuned for More on ARM Templates

I hope these steps have given you an overview of Azure Resource Manager (ARM) templates, and how to leverage them for intelligent deployment of the AppDynamics Site Extension. In my next blog, I’ll show how to use the ARM template in a release plan on Visual Studio Team Services (VSTS) to configure and deploy a sample Azure App Service with the AppDynamics Site Extension.


Learn more to understand the big picture of AppDynamics’ seamless integration with Microsoft Azure.

Josh Lyons
Josh is a Senior Software Engineer at AppDynamics, focused on .NET and Azure development. As a developer with over 18 years in industry Josh loves working with teams to solve challenging problems, and is constantly putting himself in position to learn new techniques and technologies.

Thank you! Your submission has been received!

Oops! Something went wrong while submitting the form