{"id":2150,"date":"2015-12-04T03:04:14","date_gmt":"2015-12-04T03:04:14","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/index.php\/2015\/12\/04\/dynamic-web-test-generation-using-app-insights\/"},"modified":"2020-03-18T15:07:29","modified_gmt":"2020-03-18T22:07:29","slug":"dynamic-web-test-generation-using-app-insights","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/dynamic-web-test-generation-using-app-insights\/","title":{"rendered":"Dynamic Web Test Generation Using App Insights"},"content":{"rendered":"<p><a href=\"http:\/\/www.sitecore.net\">SiteCore<\/a> is a business intelligence company based out of Copenhagen Denmark. They provide a solution that allows developers to understand deep business analytics in their own services. We had the opportunity to work with SiteCore\u2019s development team in Kuala Lumpur for a one-week hackfest in order to explore how they could use <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/application-insights\/\">Application Insights<\/a>, an Azure application telemetry service, to understand the health and availability of their services.<\/p>\n<h1 id=\"the-problem\">The Problem<\/h2>\n<p>One of the things that SiteCore does for each deployment is to run a series of recurring web tests. These are tests that ping an endpoint and expect a particular response. So for example, a test may be pseudo-coded as follows:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code>GET http:\/\/deploymenthost.com\/tests\/databaseconnection\r\n\r\nEXPECTED RESPONSE: 200\r\n<\/code><\/pre>\n<\/div>\n<p>The test will periodically call the specified endpoint with the specified method and headers and returns a response. When the endpoint is called by the web test the actual test logic is performed server-side. Sometimes this is ensuring a database is connected, other times it may just be checking to see if the service itself is still running. If the test fails, then an alert needs to be sent to the appropriate maintainer.<\/p>\n<p>SiteCore\u2019s unique problem is that they deploy a large number of services and the endpoints often change depending on new tests, host names or a combination of both. They needed a way to deploy their tests based on information known only at deploy-time.<\/p>\n<h1 id=\"the-solution\">The Solution<\/h2>\n<p><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/application-insights\/\">Application Insights<\/a> offers automated web tests which can be ran from any Azure data center in the world to ensure availability for a variety of markets. You can easily create a web test from the portal:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-12-04-Dynamic-Web-Test-Generation-Using-App-Insights-ss1.png\" alt=\"Web Tests Blade\" \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-12-04-Dynamic-Web-Test-Generation-Using-App-Insights-ss2.png\" alt=\"Create Test Blade\" \/><\/p>\n<p>This works great for simple scenarios, however in the case of SiteCore, the number of tests and the properties of a test are very fluid and can change per deployment. Because of this we needed to be able to describe web tests programmatically.<\/p>\n<p>Applications Insights does this, but its a bit tricky at first as documentation around this is quite scarce \u2013 which hopefully this post sheds some light on.<\/p>\n<h2 id=\"describing-a-web-test-with-xml\">Describing a Web Test with XML<\/h2>\n<p>So it turns out that Web Tests are actually described by an XML schema, which coincidentally is used by Visual Studio Web Tests.<\/p>\n<p>We spared you the trouble of creating a webtest in Visual Studio and reverse-engineering the schema. Here\u2019s what a typical web ping test XML looks like:<\/p>\n<div class=\"language-xml highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"nt\">&lt;WebTest<\/span> <span class=\"na\">Name=<\/span><span class=\"s\">\"WebTest1\"<\/span> <span class=\"na\">Id=<\/span><span class=\"s\">\"0000-0000-0000-0000\"<\/span> <span class=\"na\">Enabled=<\/span><span class=\"s\">\"True\"<\/span> <span class=\"na\">CssProjectStructure=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">CssIteration=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">Timeout=<\/span><span class=\"s\">\"0\"<\/span> <span class=\"na\">WorkItemIds=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">xmlns=<\/span><span class=\"s\">\"http:\/\/microsoft.com\/schemas\/VisualStudio\/TeamTest\/2010\"<\/span> <span class=\"na\">Description=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">CredentialUserName=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">CredentialPassword=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">PreAuthenticate=<\/span><span class=\"s\">\"True\"<\/span> <span class=\"na\">Proxy=<\/span><span class=\"s\">\"default\"<\/span> <span class=\"na\">StopOnError=<\/span><span class=\"s\">\"False\"<\/span> <span class=\"na\">RecordedResultFile=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">ResultsLocale=<\/span><span class=\"s\">\"\"<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;Items&gt;<\/span>\r\n    <span class=\"nt\">&lt;Request<\/span> <span class=\"na\">Method=<\/span><span class=\"s\">\"GET\"<\/span> <span class=\"na\">Guid=<\/span><span class=\"s\">\"a5f10126-e4cd-570d-961c-cea43999a200\"<\/span> <span class=\"na\">Version=<\/span><span class=\"s\">\"1.1\"<\/span> <span class=\"na\">Url=<\/span><span class=\"s\">\"http:\/\/microsoft.com\"<\/span> <span class=\"na\">ThinkTime=<\/span><span class=\"s\">\"0\"<\/span> <span class=\"na\">Timeout=<\/span><span class=\"s\">\"300\"<\/span> <span class=\"na\">ParseDependentRequests=<\/span><span class=\"s\">\"True\"<\/span> <span class=\"na\">FollowRedirects=<\/span><span class=\"s\">\"True\"<\/span> <span class=\"na\">RecordResult=<\/span><span class=\"s\">\"True\"<\/span> <span class=\"na\">Cache=<\/span><span class=\"s\">\"False\"<\/span> <span class=\"na\">ResponseTimeGoal=<\/span><span class=\"s\">\"0\"<\/span> <span class=\"na\">Encoding=<\/span><span class=\"s\">\"utf-8\"<\/span> <span class=\"na\">ExpectedHttpStatusCode=<\/span><span class=\"s\">\"200\"<\/span> <span class=\"na\">ExpectedResponseUrl=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">ReportingName=<\/span><span class=\"s\">\"\"<\/span> <span class=\"na\">IgnoreHttpStatusCode=<\/span><span class=\"s\">\"False\"<\/span> <span class=\"nt\">\/&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/Items&gt;<\/span>\r\n<span class=\"nt\">&lt;\/WebTest&gt;<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>It turns out that in Azure Resource Manager we can deploy App Insights web test resources which are of the resource type <code class=\"highlighter-rouge\">Microsoft.Insights\/webtests<\/code>.<\/p>\n<p>A <code class=\"highlighter-rouge\">Microsoft.Insights\/webtests<\/code> resource may look like this (a referenced <code class=\"highlighter-rouge\">Microsoft.Insights\/component<\/code> is omitted for brevity):<\/p>\n<div class=\"language-json highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"p\">{<\/span>\r\n  <span class=\"nt\">\"name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Test1\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"apiVersion\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"2015-05-01\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"type\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"microsoft.insights\/webtests\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"location\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Central US\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"tags\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nt\">\"[concat('hidden-link:', resourceId('microsoft.insights\/components\/appinsightsresource')))]\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Resource\"<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"nt\">\"dependsOn\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n    <span class=\"s2\">\"[concat('microsoft.insights\/components\/', parameters('appName'))]\"<\/span>\r\n  <span class=\"p\">],<\/span>\r\n  <span class=\"nt\">\"properties\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nt\">\"Name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Test1\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Description\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"A healthy descriptive piece of text\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Enabled\"<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Frequency\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">300<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Timeout\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">30<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Kind\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"ping\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Locations\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[{<\/span> <span class=\"nt\">\"Id\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"us-il-ch1-azr\"<\/span> <span class=\"p\">}],<\/span>\r\n    <span class=\"nt\">\"Configuration\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nt\">\"WebTest\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"&lt;WebTest Name=\"<\/span><span class=\"err\">WebTest<\/span><span class=\"mi\">1<\/span><span class=\"s2\">\" Id=\"<\/span><span class=\"mi\">0000-0000-0000-0000<\/span><span class=\"s2\">\" Enabled=\"<\/span><span class=\"err\">True<\/span><span class=\"s2\">\" CssProjectStructure=\"\" CssIteration=\"\" Timeout=\"<\/span><span class=\"mi\">0<\/span><span class=\"s2\">\" WorkItemIds=\"\" xmlns=\"<\/span><span class=\"err\">http:\/\/microsoft.com\/schemas\/VisualStudio\/TeamTest\/<\/span><span class=\"mi\">2010<\/span><span class=\"s2\">\" Description=\"\" CredentialUserName=\"\" CredentialPassword=\"\" PreAuthenticate=\"<\/span><span class=\"err\">True<\/span><span class=\"s2\">\" Proxy=\"<\/span><span class=\"err\">default<\/span><span class=\"s2\">\" StopOnError=\"<\/span><span class=\"err\">False<\/span><span class=\"s2\">\" RecordedResultFile=\"\" ResultsLocale=\"\"&gt;&lt;Items&gt;&lt;Request Method=\"<\/span><span class=\"err\">GET<\/span><span class=\"s2\">\" Guid=\"<\/span><span class=\"err\">a<\/span><span class=\"mi\">5<\/span><span class=\"err\">f<\/span><span class=\"mi\">10126<\/span><span class=\"err\">-e<\/span><span class=\"mi\">4<\/span><span class=\"err\">cd<\/span><span class=\"mi\">-570<\/span><span class=\"err\">d<\/span><span class=\"mi\">-961<\/span><span class=\"err\">c-cea<\/span><span class=\"mi\">43999<\/span><span class=\"err\">a<\/span><span class=\"mi\">200<\/span><span class=\"s2\">\" Version=\"<\/span><span class=\"mf\">1.1<\/span><span class=\"s2\">\" Url=\"<\/span><span class=\"err\">http:\/\/microsoft.com<\/span><span class=\"s2\">\" ThinkTime=\"<\/span><span class=\"mi\">0<\/span><span class=\"s2\">\" Timeout=\"<\/span><span class=\"mi\">300<\/span><span class=\"s2\">\" ParseDependentRequests=\"<\/span><span class=\"err\">True<\/span><span class=\"s2\">\" FollowRedirects=\"<\/span><span class=\"err\">True<\/span><span class=\"s2\">\" RecordResult=\"<\/span><span class=\"err\">True<\/span><span class=\"s2\">\" Cache=\"<\/span><span class=\"err\">False<\/span><span class=\"s2\">\" ResponseTimeGoal=\"<\/span><span class=\"mi\">0<\/span><span class=\"s2\">\" Encoding=\"<\/span><span class=\"err\">utf<\/span><span class=\"mi\">-8<\/span><span class=\"s2\">\" ExpectedHttpStatusCode=\"<\/span><span class=\"mi\">200<\/span><span class=\"s2\">\" ExpectedResponseUrl=\"\" ReportingName=\"\" IgnoreHttpStatusCode=\"<\/span><span class=\"err\">False<\/span><span class=\"s2\">\" \/&gt;&lt;\/Items&gt;&lt;\/WebTest&gt;\"<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"nt\">\"SyntheticMonitorId\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Test1\"<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>After discovering this, you can tell that it would be easy to programmatically create web tests by using a template XML file and rendering that XML file with test-specific parameters such as the expected HTTP status code or the URL for the test to use.<\/p>\n<p>SiteCore originally wanted to have this logic within a Powershell script they currently use to create their deployments. They would have to use the Azure Resource Manager APIs to create the resources individually and manage the deployment of each. This works, but brings additional complexity and management to the deployment logic.<\/p>\n<p>It turns out there is an easier way to do this. Using Azure Resource Manager, we can leverage desired-state deployments with a bit of clever template writing.<\/p>\n<h2 id=\"writing-advanced-azure-resource-management-templates\">Writing Advanced Azure Resource Management Templates<\/h2>\n<p>You may have heard of Azure Resource Management templates which allow you to describe infrastructure by extending JSON syntax. What you may have not known is that <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/resource-group-template-functions\/\">Azure Resource Manager Template Language<\/a> comes with a variety of features to bring some basic logic such as iteration and arithmetic into a template allowing for more complex deployments with much less code.<\/p>\n<p>Resource Manager templates have the ability to deploy resources iteratively using the <code class=\"highlighter-rouge\">copy<\/code> resource management property. For a given resource, using the <code class=\"highlighter-rouge\">copy<\/code> property you can specify the name of the copy operation as well the number of times it should iterate:<\/p>\n<div class=\"language-json highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"s2\">\"copy\"<\/span><span class=\"err\">:<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nt\">\"name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"createTests\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"count\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">10<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>The next question you may ask is, \u2018How does the template know how many and what kind of tests to deploy\u2019. Using a template parameters file, we can actually pass more complex template parameters than what you may see on a <a href=\"https:\/\/github.com\/Azure\/azure-quickstart-templates\/blob\/master\/201-site-to-site-vpn\/azuredeploy.parameters.json\">typical Azure Resource Manager template parameters file<\/a>.<\/p>\n<p>Using a parameters file which passes an array of objects, we can use each object to describe all the necessary information for each test:<\/p>\n<div class=\"language-json highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"p\">{<\/span>\r\n  <span class=\"nt\">\"$schema\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"http:\/\/schema.management.azure.com\/schemas\/2015-01-01\/deploymentParameters.json#\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"contentVersion\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"1.0.0.0\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"parameters\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nt\">\"appName\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nt\">\"value\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"someapp\"<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"nt\">\"emails\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nt\">\"value\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n        <span class=\"s2\">\"youremail@yourdomain.com\"<\/span>\r\n      <span class=\"p\">]<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"nt\">\"tests\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nt\">\"value\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"nt\">\"name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"test1\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"url\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"http:\/\/www.microsoft.com\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"expected\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">200<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"frequency_secs\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">300<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"timeout_secs\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">30<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"failedLocationCount\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"description\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"a description for test1\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"guid\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"cc1c4b95-0a39-48ce-9c7b-fa41f0fc0bee\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"locations\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[{<\/span>\r\n            <span class=\"nt\">\"Id\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"us-il-ch1-azr\"<\/span>\r\n          <span class=\"p\">}]<\/span>\r\n        <span class=\"p\">},<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"nt\">\"name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"test2\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"url\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"http:\/\/www.microsoft.com\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"expected\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">404<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"frequency_secs\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">300<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"timeout_secs\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">30<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"failedLocationCount\"<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"description\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"a description for test3\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"guid\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"cc1c4b95-0a39-48ce-9c7b-fa41f0fc0bef\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"nt\">\"locations\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[{<\/span>\r\n            <span class=\"nt\">\"Id\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"us-il-ch1-azr\"<\/span>\r\n          <span class=\"p\">}]<\/span>\r\n        <span class=\"p\">}<\/span>\r\n      <span class=\"p\">]<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>Obviously, you can have any number of tests, we\u2019re showing only 2 for brevity.<\/p>\n<h3 id=\"rendering-a-template-test-resource\">Rendering a Template Test Resource<\/h3>\n<p>This might sound confusing but we can render the parameter variables within a <em>general<\/em> <code class=\"highlighter-rouge\">microsoft.insights\/webtest<\/code> model resource rather than literally writing them like above. It\u2019s the same concept of creating a <code class=\"highlighter-rouge\">for<\/code> loop with generalized integration code, just in an interesting JSON-like programming way.<\/p>\n<p>In the parameters file above we have a <code class=\"highlighter-rouge\">tests<\/code> array which contains each of our test descriptor objecs. A <em>generalized<\/em> version of the <code class=\"highlighter-rouge\">microsoft.insights\/webtest<\/code> resource we saw earlier would look like this:<\/p>\n<div class=\"language-json highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"p\">{<\/span>\r\n  <span class=\"nt\">\"name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex()].name]\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"apiVersion\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"2015-05-01\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"type\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"microsoft.insights\/webtests\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"location\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Central US\"<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"nt\">\"tags\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nt\">\"[concat('hidden-link:', resourceId('microsoft.insights\/components\/', parameters('appName')))]\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Resource\"<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"nt\">\"dependsOn\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n    <span class=\"s2\">\"[concat('microsoft.insights\/components\/', parameters('appName'))]\"<\/span>\r\n  <span class=\"p\">],<\/span>\r\n  <span class=\"nt\">\"properties\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nt\">\"Name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex()].name]\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Description\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex()].description]\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Enabled\"<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Frequency\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex()].frequency_secs]\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Timeout\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex()].timeout_secs]\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Kind\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"ping\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Locations\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex(1)].locations]\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"Configuration\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nt\">\"WebTest\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[concat('&lt;WebTest Name=\"', parameters('tests')[copyIndex(1)].name, '\"',  ' Id=\"', parameters('tests')[copyIndex(1)].guid ,'\"    Enabled=\"True\" CssProjectStructure=\"\" CssIteration=\"\" Timeout=\"0\" WorkItemIds=\"\" xmlns=\"http:\/\/microsoft.com\/schemas\/VisualStudio\/TeamTest\/2010\" Description=\"\" CredentialUserName=\"\" CredentialPassword=\"\" PreAuthenticate=\"True\" Proxy=\"default\" StopOnError=\"False\" RecordedResultFile=\"\" ResultsLocale=\"\"&gt;        &lt;Items&gt;        &lt;Request Method=\"GET\" Guid=\"a5f10126-e4cd-570d-961c-cea43999a200\" Version=\"1.1\" Url=\"', parameters('tests')[copyIndex()].url ,'\" ThinkTime=\"0\" Timeout=\"300\" ParseDependentRequests=\"True\" FollowRedirects=\"True\" RecordResult=\"True\" Cache=\"False\" ResponseTimeGoal=\"0\" Encoding=\"utf-8\" ExpectedHttpStatusCode=\"', parameters('tests')[copyIndex()].expected ,'\" ExpectedResponseUrl=\"\" ReportingName=\"\" IgnoreHttpStatusCode=\"False\" \/&gt;&lt;\/Items&gt;&lt;\/WebTest&gt;')]\"<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"nt\">\"SyntheticMonitorId\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[parameters('tests')[copyIndex()].name]\"<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"nt\">\"copy\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nt\">\"name\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"createTests\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nt\">\"count\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"[length(parameters('tests'))]\"<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>Using the <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/resource-group-template-functions\/#parameters\"><code class=\"highlighter-rouge\">parameters<\/code> template function<\/a> we can grab the <code class=\"highlighter-rouge\">tests<\/code> array and use the <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/resource-group-template-functions\/#copyindex\"><code class=\"highlighter-rouge\">copyIndex()<\/code> function<\/a> which returns the current (zero-based) index of iteration. Using the <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/resource-group-template-functions\/#length\">length function<\/a> we set the <code class=\"highlighter-rouge\">createTests<\/code> iterator to the length of the array, allowing the iteration to cycle through all the elements in the <code class=\"highlighter-rouge\">tests<\/code> array. Using the standard javascript array indexer syntax we can access each test object by doing <code class=\"highlighter-rouge\">parameters('tests')[copyIndex()]<\/code> and appending the property such as <code class=\"highlighter-rouge\">.name<\/code> or <code class=\"highlighter-rouge\">.description<\/code> to access those particular fields.<\/p>\n<p>Finally, using the <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/resource-group-template-functions\/#concat\"><code class=\"highlighter-rouge\">concat<\/code> function<\/a>, we can render the parameterized XML by simply concatenating the replaced parameter with the necessary quotation marks and remaining xml.<\/p>\n<h3 id=\"one-small-caveat\">One Small Caveat<\/h3>\n<p>As of writing this there\u2019s a tiny bug in App Insights provisioning that doesn\u2019t allow the modification, addition or removal of App Insights resources in parallel. Because of this we have to use the <code class=\"highlighter-rouge\">dependsOn<\/code> property to force resources to provision serially. Checkout the <a href=\"https:\/\/github.com\/Azure\/azure-quickstart-templates\/blob\/master\/201-dynamic-web-tests\/azuredeploy.json\">actual template<\/a> for the clever hacking to the template that needed to be done. This bug will be fixed in the coming months which will allow parallelism with App Insights.<\/p>\n<h3 id=\"updating-adding-and-removing-tests\">Updating, Adding and Removing Tests<\/h3>\n<p>Another amazing thing about using a template like this one is that using <strong>create<\/strong> mode when deploying the template will update your web tests for the desired state specified by the template. So the only logic required is that which generates the template parameters file. This saves a lot of work in your deployment scripts for handling existing tests.<\/p>\n<p>What this means is that existing web test resources with the same test name will just have its settings updated, without erasing the test history. When you add or remove tests from your parameters file, those tests will either be added or removed. As the template deploys, tests start running immediately after they provision, even if the entire deployment isn\u2019t complete. This allows for maintainers to quickly know a deployment is running since tests start when they are provisioned, rather than when the entire deployment completes.<\/p>\n<h1 id=\"how-to-dynamically-create-your-own-web-tests\">How to Dynamically Create Your Own Web Tests<\/h2>\n<p>Now that we\u2019ve published a <a href=\"https:\/\/github.com\/Azure\/azure-quickstart-templates\/pull\/916\">reusable template<\/a> for dynamically generating web tests, you can head over to the <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/templates\/201-dynamic-web-tests\/\">template deployment page<\/a> where you can provision web tests by generating your own parameters JSON and either using the <a href=\"http:\/\/npmjs.org\/azure-cli\">CLI<\/a> or the <strong>deploy to azure<\/strong> button to create the tests.<\/p>\n<p>As your deployment changes, you can update existing web tests by re-deploying the template. You can also add and remove tests by simply removing or adding tests from your template parameters (just be sure to use <strong>create<\/strong> mode deployments).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how leverage complex Azure Resource Manager Template Language to dynamically create and update Web availability Tests.<\/p>\n","protected":false},"author":21345,"featured_media":11151,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[10],"tags":[60,64,328,349],"class_list":["post-2150","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-app-services","tag-azure","tag-azure-application-insights","tag-sitecore","tag-telemetry"],"acf":[],"blog_post_summary":"<p>Learn how leverage complex Azure Resource Manager Template Language to dynamically create and update Web availability Tests.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2150","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21345"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=2150"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2150\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/11151"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=2150"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=2150"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=2150"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}