<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Clemens Schotte</title>
    <description>The latest articles on DEV Community by Clemens Schotte (@cschotte).</description>
    <link>https://dev.to/cschotte</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F399435%2Fe27c927e-eb01-411a-9e7b-6e9731700bfb.jpeg</url>
      <title>DEV Community: Clemens Schotte</title>
      <link>https://dev.to/cschotte</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cschotte"/>
    <language>en</language>
    <item>
      <title>Help customers find your business with the Azure Maps Store Locator</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Mon, 03 Jun 2024 14:23:50 +0000</pubDate>
      <link>https://dev.to/cschotte/help-customers-find-your-business-with-the-azure-maps-store-locator-2f62</link>
      <guid>https://dev.to/cschotte/help-customers-find-your-business-with-the-azure-maps-store-locator-2f62</guid>
      <description>&lt;h2&gt;
  
  
  Maximize Visibility
&lt;/h2&gt;

&lt;p&gt;In today's digital age, the visibility of your business is paramount. Once you've captured customer interest online, the next crucial step is guiding them to your physical storefronts. &lt;a href="https://github.com/Azure-Samples/Azure-Maps-Locator" rel="noopener noreferrer"&gt;Azure Maps Store Locator&lt;/a&gt; streamlines this journey, offering an interactive and intuitive experience that leads customers right to your doorstep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplifying Store Discovery
&lt;/h2&gt;

&lt;p&gt;Creating a basic store locator using Azure Maps is already a straightforward task, involving the loading of store locations onto a map and potentially setting up a simple search functionality. However, for larger organizations managing thousands of locations and requiring advanced filtering options, a more sophisticated solution is essential. Fortunately, the Azure Maps Store Locator, combining the power of various Azure services, caters precisely to these needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-maps-store-locator%2Fstorelocator.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-maps-store-locator%2Fstorelocator.gif" alt="Azure Maps Store Locator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhance Your Locator Experience
&lt;/h2&gt;

&lt;p&gt;Imagine a tool that effortlessly connects potential customers to the nearest branch of your business, tailored to their specific needs. Whether they're searching for a particular service or other points of interest, &lt;a href="https://github.com/Azure-Samples/Azure-Maps-Locator" rel="noopener noreferrer"&gt;Azure Maps Store Locator&lt;/a&gt; is the user-friendly and adaptable tool you need. It's backed by a comprehensive management system, enabling you to create a rich locator experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature-Rich Platform
&lt;/h2&gt;

&lt;p&gt;Azure Maps Store Locator boasts a wide array of features to improve your location-based offerings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Store Locator Backend&lt;/strong&gt;: Integrates REST APIs and a Store Locator Web Control for seamless management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autocomplete Search&lt;/strong&gt;: Quickly find store names, addresses, POIs, or zip codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Manages over 10,000 locations without a hitch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proximity Insights&lt;/strong&gt;: View nearby stores and distance metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location-Based Search&lt;/strong&gt;: Searches can be performed based on the user's current location.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Travel Time Estimates&lt;/strong&gt;: Provides estimated travel times for various modes of transport.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Store Details&lt;/strong&gt;: Access store information, directions, and more through interactive popups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Filtering&lt;/strong&gt;: Users can filter stores based on specific features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Individual Store Pages&lt;/strong&gt;: Delve into what each store offers with detailed embedded maps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Employs Microsoft Entra ID for secure access to the location management system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich Data&lt;/strong&gt;: Store details include location, hours, photos, and the option to add custom features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: Features speech recognition and other accessibility enhancements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effortless Deployment&lt;/strong&gt;: Easily deploy within your Azure ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Setup Guide
&lt;/h2&gt;

&lt;p&gt;Deploying Azure Maps Store Locator is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Azure Subscription&lt;/strong&gt;: Confirm you have an Azure subscription. If not, obtain one for &lt;a href="https://azure.microsoft.com/free/" rel="noopener noreferrer"&gt;free&lt;/a&gt; at the official Azure website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure Shell Access&lt;/strong&gt;: Sign in to Azure Shell. &lt;a href="https://shell.azure.com/" rel="noopener noreferrer"&gt;https://shell.azure.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Run the provided PowerShell script to install the Azure Maps Store Locator.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://samples.azuremaps.com/storelocator/deploy.ps1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Content&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-maps-store-locator%2Farchitecture.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-maps-store-locator%2Farchitecture.jpg" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Integrating the store locator into your website requires some HTML and JavaScript to call the store locator backend REST APIs. Once implemented, the solution is yours to modify and tailor the source code in your Azure Maps Store Locator according to your specific needs.&lt;/p&gt;

&lt;p&gt;The Azure Maps Store Locator empowers you to create and maintain an intuitive location-based search experience to delight your customers. Enhance your online presence today with the power of Azure Maps!&lt;/p&gt;

&lt;p&gt;You find the Azure Maps Store Locator source code on &lt;a href="https://github.com/Azure-Samples/Azure-Maps-Locator" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>maps</category>
      <category>storelocator</category>
      <category>azuremaps</category>
    </item>
    <item>
      <title>Protecting and Hiding your Bing Maps Key</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Tue, 05 Apr 2022 12:48:20 +0000</pubDate>
      <link>https://dev.to/cschotte/protecting-and-hiding-your-bing-maps-key-2pn6</link>
      <guid>https://dev.to/cschotte/protecting-and-hiding-your-bing-maps-key-2pn6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When using &lt;a href="https://www.microsoft.com/maps"&gt;Bing Maps for Enterprise&lt;/a&gt; in your solution/application, you need a &lt;strong&gt;Basic Key&lt;/strong&gt; (limited free trial) or an &lt;strong&gt;Enterprise key&lt;/strong&gt; to use the services. For example, you would add a Bing Maps Key to the script URL loading the Bing Maps Web Control like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.bing.com/api/maps/mapcontrol?callback=GetMap&amp;amp;key={your bing maps key}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your key is open text on your site source code and people who look can find and use your key. Search engines will index your page and, as a result, will also store your key. Is this a problem? Not really.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting
&lt;/h2&gt;

&lt;p&gt;The Bing Maps key is mainly used to determine the usage and allow access to Bing Maps features. To protect your Bing Maps key, so it can't be misused on other websites, there is an option in the &lt;a href="https://www.bingmapsportal.com/"&gt;Bing Maps Dev Center&lt;/a&gt; to protect your key. This security option allows you to specify a list of referrers (website URLs) and IP numbers who can use your key. When at least one referrer rule is active, any requests that omit a referrer and any requests from non-approved referrers will be blocked, preventing others from using your key for requests. You can have up to 300 referrer and IP security rules per key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ba8u70Rb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/bing-maps-key/security-settings.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ba8u70Rb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/bing-maps-key/security-settings.jpg" alt="Bing Maps Key Security Settings" width="602" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your key is now protected but is still visible in your website code. So how do I hide my Bing Maps key?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A best practice is &lt;strong&gt;never to store any keys or certificates in source code&lt;/strong&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Hiding
&lt;/h2&gt;

&lt;p&gt;To hide the Bing Maps key, you create a simple API endpoint that will only return the Bing Maps key if the request comes from a trusted referral URL. The &lt;a href="https://samples.bingmapsportal.com/"&gt;Bing Maps Samples&lt;/a&gt; site is a good example that uses this approach.&lt;/p&gt;

&lt;p&gt;In this example we are using an Anonymous HttpTrigger &lt;a href="https://azure.microsoft.com/en-us/services/functions/"&gt;Azure Function&lt;/a&gt; written in C# that returns the Bing Maps key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetBingMapsKey&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;allowd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"https://samples.bingmapsportal.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                &lt;span class="s"&gt;"http://localhost"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetBingMapsKey"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;HttpTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthorizationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;referer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Referer"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;referer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnauthorizedResult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;referer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnauthorizedResult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Get your Bing Maps key from https://www.bingmapsportal.com/&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BING_MAPS_SUBSCRIPTION_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OkObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Bing Maps key is stored server-side in this Azure Function Application settings field. We are using the &lt;code&gt;GetEnvironmentVariable()&lt;/code&gt; to get the key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tX3XNeN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/bing-maps-key/app-settings.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tX3XNeN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/bing-maps-key/app-settings.png" alt="Azure Function Application settings" width="602" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to load the Bing Maps script and get the key from the API client-side. Finally, we use the following code snippet to load Bing Maps dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Dynamic load the Bing Maps Key and Script&lt;/span&gt;
    &lt;span class="c1"&gt;// Get your own Bing Maps key at https://www.microsoft.com/maps&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bingKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://samples.azuremaps.com/api/GetBingMapsKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`https://www.bing.com/api/maps/mapcontrol?callback=GetMap&amp;amp;key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bingKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser will run this code and create at runtime in the DOM the same line of &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag we have seen at the beginning of this blog post to load Bing Maps and the Key. An additional advantage is that the Bing Maps key is not stored in the source code anymore and that you can use &lt;a href="https://clemens.ms/bing-maps-key//infrastructure-as-code"&gt;IaC&lt;/a&gt; and build pipelines to deploy the solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Only hiding the Bing Maps key alone is not enough as a security measure. We recommend you still enable the security option in the Bing Maps Dev Center!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>bingmaps</category>
      <category>geospatial</category>
      <category>api</category>
      <category>key</category>
    </item>
    <item>
      <title>Use Azure Maps to calculate an isochrone to reach your customers</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Tue, 21 Sep 2021 14:28:31 +0000</pubDate>
      <link>https://dev.to/cschotte/calculate-an-isochrone-to-target-your-customers-1ld4</link>
      <guid>https://dev.to/cschotte/calculate-an-isochrone-to-target-your-customers-1ld4</guid>
      <description>&lt;p&gt;Imagine you are a store owner and would like to target customers that live within a 15-minute drive from your store with advertising for your weekly specials. You could draw a circle on a map, guessing that is about 15 minutes away, but it will not truly represent the time it will take for customers to get to your store. For example, a customer living near a major transit route can live further away from the store than a customer living in a less well-served part of the city. To meet this need, an &lt;strong&gt;isochrone&lt;/strong&gt; is a polygon (an area on a map) of expected travel time. It represents the locations that will take the specified time, or less, it will take to get to a specific point (your store, in this case). Estimating an isochrone correctly, including all the variables like traffic, road, and vehicle conditions, is very hard to do by yourself!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v8WX7SQo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/isochrone/london.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v8WX7SQo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/isochrone/london.jpg" alt="isochrone in London"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Maps Get Route Range
&lt;/h2&gt;

&lt;p&gt;Using the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/route/get-route-range"&gt;Get Route Range&lt;/a&gt; API from Azure Maps makes it very easy to calculate the isochrone. For our store example, we need to calculate a 15-minute isochrone for every store we have. We only need the coordinates (longitude and latitude) from our stores and the drivetime in seconds (15 x 60 = 900 sec).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://atlas.microsoft.com/route/range/json?subscription-key=[subscription-key]&amp;amp;api-version=1.0&amp;amp;query=47.65431,-122.1291891&amp;amp;timeBudgetInSec=900
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Search Customer Address
&lt;/h2&gt;

&lt;p&gt;To determine which customer is living inside the 15-minute drive isochrone, we first need the coordinates for every customer's address. Then, we simply loop through all our customers and use the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/search/get-search-address"&gt;Get Search Address&lt;/a&gt; API to get the coordinates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://atlas.microsoft.com/search/address/json?subscription-key=[subscription-key]&amp;amp;api-version=1.0&amp;amp;query=15127 NE 24th Street, Redmond, WA 98052
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Point In Polygon
&lt;/h2&gt;

&lt;p&gt;The last step is to check if a customer coordinate is inside one of our store isochrones. We use the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/spatial/post-point-in-polygon"&gt;Point In Polygon&lt;/a&gt; API to determine if this is true.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://atlas.microsoft.com/spatial/pointInPolygon/json?subscription-key=[subscription-key]&amp;amp;api-version=1.0&amp;amp;lat=33.5362475&amp;amp;lon=-111.9267386
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the request body (the store isochrone)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"geometryId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Polygon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;-111.9267386&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;33.5362475&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;-111.9627875&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;33.5104882&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;-111.9027061&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;33.5004686&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;-111.9267386&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="mf"&gt;33.5362475&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now can target those customers with store-specific offers and promotions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HXrh2aUn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/isochrone/chicago.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HXrh2aUn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/isochrone/chicago.jpg" alt="isochrone in Chicago"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;What schools are within a 20-minute walk from my home? What available jobs are within a 30-minute transit commute? Where you are located and what is near you brings context to the choice of where you want to live and Azure Maps Isochrone API can help you bring that location intelligence into your applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://azuremapscodesamples.azurewebsites.net/?search=Isochrone"&gt;Azure Maps Isochrone Samples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/how-to-use-best-practices-for-search"&gt;Best practices for Azure Maps Search Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/how-to-search-for-address"&gt;Search for a location using Azure Maps Search services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>maps</category>
      <category>isochrone</category>
      <category>azuremaps</category>
    </item>
    <item>
      <title>Azure DevOps Dashboard</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Wed, 16 Jun 2021 09:17:15 +0000</pubDate>
      <link>https://dev.to/cschotte/azure-devops-dashboard-2lfh</link>
      <guid>https://dev.to/cschotte/azure-devops-dashboard-2lfh</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;When you are managing Azure DevOps in a large enterprise organization, and you are still using only one Azure DevOps organization account, you are probably hitting some limits or have potential performance issues. Microsoft's recommendation is to have around 300 projects in a single Azure DevOps organization account. I have seen Azure DevOps organizations with more than 600 projects that still work.&lt;/p&gt;

&lt;p&gt;The solution is to set up a multi-organization structure. Move all the inactive projects to an archive or boneyard Azure DevOps organization account and add an extra Azure DevOps organization account per department.&lt;/p&gt;

&lt;p&gt;Next, you need some insights and automation on which projects have no activity anymore. The Azure DevOps Dashboard gives you the basic insights and an API to automate tasks like emailing the owners of inactive projects.&lt;/p&gt;

&lt;h1&gt;
  
  
  Azure DevOps Dashboard
&lt;/h1&gt;

&lt;p&gt;This dashboard solution generates a simple overview of all the &lt;a href="https://dev.azure.com/" rel="noopener noreferrer"&gt;Azure DevOps&lt;/a&gt; projects in your organization and calculates the last known activity in &lt;strong&gt;days&lt;/strong&gt; on commits, work items, and the project itself. You can connect this dashboard (using the included endpoint) to &lt;a href="https://flow.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft Power Automate&lt;/a&gt; or Excel to automate tasks on project level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-devops-dashboard%2Fdashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-devops-dashboard%2Fdashboard.png" alt="Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;The solution runs on as a single &lt;a href="https://azure.microsoft.com/en-us/services/app-service/web/" rel="noopener noreferrer"&gt;Azure Web App&lt;/a&gt;, it uses a background &lt;a href="https://docs.microsoft.com/en-us/azure/app-service/webjobs-create" rel="noopener noreferrer"&gt;WebJob&lt;/a&gt; to collect all the data needed to present in the web dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;An Azure account with an active subscription. &lt;a href="https://azure.microsoft.com/free/dotnet" rel="noopener noreferrer"&gt;Create an account for free&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows" rel="noopener noreferrer"&gt;Azure CLI on Windows&lt;/a&gt; to automate the following steps&lt;/li&gt;
&lt;li&gt;An Azure DevOps personal access token (PAT). See here &lt;a href="https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&amp;amp;tabs=preview-page" rel="noopener noreferrer"&gt;how to get a personal access token&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Download the Azure DevOps Dashboard &lt;a href="https://github.com/cschotte/Azure-DevOps-Dashboard/raw/main/Release.zip" rel="noopener noreferrer"&gt;Release.zip&lt;/a&gt; package.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Create an Azure Web App
&lt;/h3&gt;

&lt;p&gt;In the next steps, you will create a resource group, an app service plan (the webserver), and the Web App (the solution itself). We also add two application settings to store the Azure DevOps personal access token.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login into your Azure subscription
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;(Optional) Select the subscription where you like to deploy the dashboard.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az account set --subscription "&amp;lt;your subscription&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a resource group, change the name &lt;code&gt;rg-azdevops&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az group create -l westeurope -n rg-azdevops
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create an app service plan and webapp, change the names &lt;code&gt;plan-azdevops&lt;/code&gt; and &lt;code&gt;azdevops&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az appservice plan create -g rg-azdevops -n plan-azdevops -l westeurope

az webapp create -g rg-azdevops -p plan-azdevops -n azdevops -r "DOTNET|6.0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add your Azure DevOps URL and personal access token (PAT)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az webapp config appsettings set -g rg-azdevops -n azdevops --settings azDevOpsPat=&amp;lt;your token&amp;gt;
az webapp config appsettings set -g rg-azdevops -n azdevops --settings azDevOpsUri=https://dev.azure.com/&amp;lt;yourorgname&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Set the &lt;code&gt;always-on&lt;/code&gt; future we need for the WebJob
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az webapp config set -g rg-azdevops -n azdevops --always-on true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy the Azure DevOps Dashboard
&lt;/h3&gt;

&lt;p&gt;Did you download the Azure DevOps Dashboard &lt;a href="https://github.com/cschotte/Azure-DevOps-Dashboard/raw/main/Release.zip" rel="noopener noreferrer"&gt;Release.zip&lt;/a&gt; package? After the installation we also run the WebJob for the first time, this can take a while depending on how many projects you have in your Azure DevOps organization account.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Authentication&lt;/strong&gt; In the release package authentication is disabled! Please register your application first in your Azure Active Directory by following the steps described &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You only need to update the &lt;strong&gt;appsettings.json&lt;/strong&gt; inside the release package.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az webapp deployment source config-zip -g rg-azdevops -n azdevops --src Release.zip

az webapp webjob triggered run -n azdevops -g rg-azdevops --webjob-name Webjob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-devops-dashboard%2Farchitecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fazure-devops-dashboard%2Farchitecture.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also run the WebJob locally, set the following two environment variable first &lt;code&gt;azDevOpsUri&lt;/code&gt;&lt;br&gt;
and &lt;code&gt;azDevOpsPat&lt;/code&gt; that corresponds with your Azure DevOps organization account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SET azDevOpsPat=tjqp44k54nqfmppaqd7di27kpvh...........
SET azDevOpsUri=https://dev.azure.com/yourorgname.....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the API
&lt;/h2&gt;

&lt;p&gt;To automate tasks, you can use the API to connect to Excel, Microsoft Power Automate, or whatever you need. The &lt;code&gt;/api/data&lt;/code&gt; API will return a list of the following project properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://dev.azure.com/projectname"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"owners"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Contoso Admin name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"mailAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin@contoso.com"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"processTemplate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Scrum"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastProjectUpdateTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-03-22T11:40:32.09Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastCommitDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-04-23T18:00:27Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastWorkItemDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastKnownActivity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-03-22T11:40:32.09Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"projectAge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;83.92575148777316&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;Alle source code can be found on &lt;a href="https://github.com/cschotte/Azure-DevOps-Dashboard" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>dashboard</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Do I get wet feet? Draw a flood map using Azure Maps Elevation API</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Mon, 14 Jun 2021 21:12:51 +0000</pubDate>
      <link>https://dev.to/cschotte/do-i-get-wet-feeds-draw-a-flood-map-using-azure-maps-elevation-api-4h9k</link>
      <guid>https://dev.to/cschotte/do-i-get-wet-feeds-draw-a-flood-map-using-azure-maps-elevation-api-4h9k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When you are living in the Netherlands you are used to that, nearly 26% of its land falling below sea level, and about 50% is just only exceeding 1 m (3.3 ft) above. The Dutch people have lived many centuries battling the water, not only from the sea but also from her rivers. To protect the land the Dutch have built many sophisticated protecting- and management systems to handle the water, like the &lt;a href="https://www.bing.com/search?q=Dutch%20Delta%20Works"&gt;Delta Works&lt;/a&gt;. Building only a dike or dam is not enough. Today we have won, but we know that we cannot rust, climate change (heavy rain showers) and sea levels are rising globally. Do we (or you) get wet feet in the future?&lt;/p&gt;

&lt;p&gt;To start, we need to know which areas of land are almost below sea level so that we can plan and take additional actions. To make this visible we can use &lt;a href="https://azure.microsoft.com/en-us/services/azure-maps/"&gt;Azure Maps&lt;/a&gt; and use the &lt;a href="https://azure.microsoft.com/en-us/updates/azure-maps-elevation-service-is-now-generally-available/"&gt;Azure Maps Elevation Service&lt;/a&gt; to make a basic flood map. The Azure Maps Elevation Service provides pole-to-pole coverage with &amp;lt;4M absolute and &amp;lt;2m relative accuracy. The elevation data represents a digital terrain model (DTM), man-made entities (e.g., buildings) are artificially flattened, and elevation is measured to the ground surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do we need?
&lt;/h2&gt;

&lt;p&gt;To get started you need a free &lt;a href="https://azure.microsoft.com/en-us/free/"&gt;Azure subscription&lt;/a&gt; and an Azure Maps account. This provides us access to the Elevation API that we need to build the flood map. The Elevation API gives elevation back in meters for coordinates, with WGS84 longitude and latitude, on a map. We can use a Bounding Box, Points, and a Polyline as input. In the below picture you see an aerial photo of the harbor and city of Rotterdam. The flood map shows in blue all the land that is below sea level and is vulnerable, and in green land that is high enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N0dTiWnF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/azure-maps-elevation/floodmap.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0dTiWnF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/azure-maps-elevation/floodmap.jpg" alt="Floodmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Maps
&lt;/h2&gt;

&lt;p&gt;We only need a single HTML file, that references the Azure Maps &lt;em&gt;Maps Control&lt;/em&gt;, &lt;em&gt;Map Drawing Tools&lt;/em&gt; libraries, and some basic HTML and JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Add references to the Azure Maps Map control JavaScript and CSS files. --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Add references to the Azure Maps Map Drawing Tools JavaScript and CSS files. --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://atlas.microsoft.com/sdk/javascript/drawing/0/atlas-drawing.min.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://atlas.microsoft.com/sdk/javascript/drawing/0/atlas-drawing.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to initialize the map control and add additional layers to it to draw on, like the controls and the floodmap itself. First we initialize the map control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initialize a map instance.&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;atlas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myMap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;4.2432838&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;51.9022471&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// City of Rotterdam&lt;/span&gt;
    &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;satellite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Add authentication details for connecting to Azure Maps.&lt;/span&gt;
    &lt;span class="na"&gt;authOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;authType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subscriptionKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;subscriptionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your key&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Draw a flood map
&lt;/h2&gt;

&lt;p&gt;Now that we have the basics we need only add a &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/map-add-bubble-layer"&gt;BubbleLayer&lt;/a&gt; layer to the map, which we need to draw the floodmap with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a layer for rendering the elevation points.&lt;/span&gt;
&lt;span class="nx"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;atlas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BubbleLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;interpolate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;linear&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elevation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#006837&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#1a9850&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#66bd63&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;550&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#a6d96a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#d9ef8b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;650&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffffbf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fee08b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fdae61&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f46d43&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;850&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#d73027&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#a50026&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// Don't outline the bubbles.&lt;/span&gt;
    &lt;span class="c1"&gt;// This will make them blend together to create a heat map like visual.&lt;/span&gt;
    &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/javascript/api/azure-maps-drawing-tools/atlas.drawing.drawingmanager"&gt;DrawingManager&lt;/a&gt; we not only use to draw the Bounding Box, but it gives us also the coordinates back we need for the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/elevation"&gt;Elevation API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Get Data&lt;/em&gt; for Bounding Box API provides elevation data at equally spaced locations within a bounding box. A bounding box is defined by the coordinates for two corners (southwest, northeast) and then subsequently divided into rows and columns.&lt;/p&gt;

&lt;p&gt;Elevations are returned for the vertices of the grid created by the rows and columns. Up to &lt;em&gt;2,000&lt;/em&gt; elevations can be returned in a single request. The returned elevation values are ordered, starting at the southwest corner, and then proceeding west to east along the row. At the end of the row, it moves north to the next row, and repeats the process until it reaches the far northeast corner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;elevationBoundsUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://{azMapsDomain}/elevation/lattice/json?api-version=1.0&amp;amp;bounds={bounds}&amp;amp;rows={rows}&amp;amp;columns={columns}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;elevationBoundsUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{bounds}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{rows}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numRows&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{columns}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numColumns&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{azMapsDomain}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;atlas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDomain&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;processRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// Loop through the elevations, create point features with an elevation property.&lt;/span&gt;
&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;atlas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;atlas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;elevation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elevationInMeter&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we only need to overwrite the points to the data source, and we have a flood / elevation map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setShapes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example code
&lt;/h2&gt;

&lt;p&gt;This &lt;a href="https://azuremapscodesamples.azurewebsites.net/REST%20Services/Elevations%20by%20bounding%20box.html"&gt;Code Sample&lt;/a&gt; shows how to get elevations in a grid pattern with a bounding box. Try it out!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>maps</category>
      <category>elevation</category>
      <category>azuremaps</category>
    </item>
    <item>
      <title>Create your own indoor maps using Azure Maps Creator</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Tue, 08 Jun 2021 06:36:23 +0000</pubDate>
      <link>https://dev.to/cschotte/create-your-own-indoor-maps-using-azure-maps-creator-1745</link>
      <guid>https://dev.to/cschotte/create-your-own-indoor-maps-using-azure-maps-creator-1745</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When you are working inside a building, like an office, factory, shopping mall, or something like a museum. There are probably a lot of sensors that can tell you information about that building, like what is the temperature and air quality per room, is there any door open or is there some alarm happening. When you in a big building and you want to know the fastest route to a specific room/store/painting, you probably apricate some help in navigating. These are all smart buildings.&lt;/p&gt;

&lt;p&gt;Using indoor maps will not only help you visualize the building/floorplans and its IoT sensors, but also interact and navigate with it. For example, when you are for the first time in the &lt;a href="https://www.bing.com/search?q=paris+louvre+museum"&gt;Louvre Museum&lt;/a&gt; and want to know how to get to the famous &lt;a href="https://www.bing.com/search?q=Mona%20Lisa"&gt;Mona Lisa&lt;/a&gt; painting, you can use an interactive indoor map to find and navigate to what you were searching for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7eWx4SZT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/louvre-map.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7eWx4SZT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/louvre-map.jpg" alt="Map of the Louvre Museum"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using the same technology for mapping outdoors we can use Azure Maps also for indoor mapping solutions. &lt;a href="https://azure.microsoft.com/en-us/services/azure-maps/"&gt;Azure Maps&lt;/a&gt; is a collection of geospatial services APIs and SDKs that use fresh mapping data to provide geographic context to web and mobile applications. &lt;a href="https://azure.microsoft.com/en-us/updates/azure-maps-creator-is-now-generally-available/"&gt;Azure Maps Creator&lt;/a&gt; is the service that facilitates the creation of indoor maps using your own building drawings. Azure Maps has SDKs for Web, Android, and iOS (is coming), and is available in &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/power-bi-visual-getting-started"&gt;Power BI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c913zJWh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/azure-maps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c913zJWh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/azure-maps.png" alt="Azure Maps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Maps Creator
&lt;/h2&gt;

&lt;p&gt;To get started using the Azure Maps Creator services, you first need a free &lt;a href="https://azure.microsoft.com/en-us/free/"&gt;Azure subscription&lt;/a&gt; and an Azure Maps account. To use the Creator services, Azure Maps Creator must be created in an Azure Maps account. For information about how to create Azure Maps Creator in Azure Maps, see &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/how-to-manage-creator"&gt;Manage Azure Maps Creator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you have created an Azure Maps account you get two options to authenticate to the Azure Maps services; a subscription API key or by using Azure Active Directory (what is a more secure way). The API key you use in the API calls. Currently, there are two endpoint regions: Europe, and the United States.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_zCAFAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/postman.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_zCAFAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/postman.png" alt="POST Data Upload API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Second, you need to have or create your own CAD drawings from your building like floorplans in the &lt;code&gt;.DWG&lt;/code&gt; file format (AutoCAD DWG file format version AC1032). These drawings need then be packaged into a simple &lt;code&gt;.ZIP&lt;/code&gt; file including a &lt;code&gt;manifest.json&lt;/code&gt; file that describes the Drawing package. See for more information, the &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/drawing-package-guide"&gt;Conversion Drawing package guide&lt;/a&gt;. This step is very important, creating and preparing your floorplans including all the interactive elements that make your solution stand out or not.&lt;/p&gt;

&lt;p&gt;The process after the drawing package creation is very straightforward. You upload the drawing package using the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/data-v2"&gt;Data Upload API&lt;/a&gt;, convert it using the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/v2/conversion"&gt;Conversion API&lt;/a&gt;, and create a dataset and tileset using the &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/v2/dataset"&gt;Dataset API&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/rest/api/maps/v2/tileset"&gt;Tileset API&lt;/a&gt;. See this tutorial on how to do this step by step: &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/tutorial-creator-indoor-maps"&gt;Use Creator to create indoor maps&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dataset&lt;/strong&gt; is a collection of indoor map features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tileset is&lt;/strong&gt; a collection of vector data that represents a set of uniform grid tiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature statesets&lt;/strong&gt; are collections of dynamic properties (states) that are assigned to dataset features, such as rooms or equipment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZoXt56GL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/drawing-package.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZoXt56GL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/create-your-own-indoor-maps-using-azure-maps-creator/drawing-package.png" alt="Drawing package process"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering the indoor map
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/"&gt;Azure Maps Web SDK&lt;/a&gt; includes the Indoor Maps module. This module offers extended functionalities to the Azure Maps &lt;strong&gt;Map Control library&lt;/strong&gt;. The Indoor Maps module renders indoor maps created in Creator. It integrates widgets, such as floor picker, that help users visualize the different floors. The Indoor Maps module also supports dynamic map styling. For a step-by-step walkthrough to implement feature stateset dynamic styling in an application, see &lt;a href="https://docs.microsoft.com/en-us/azure/azure-maps/how-to-use-indoor-module"&gt;Use the Indoor Map module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like to see what more is possible with the Azure Maps Web Control, there is a sample gallery with a collection of 270 code samples. See the &lt;a href="https://azuremapscodesamples.azurewebsites.net/"&gt;Azure Maps Web SDK Samples&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>maps</category>
      <category>geographic</category>
      <category>location</category>
    </item>
    <item>
      <title>Protect your web applications using Azure Application Gateway</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Tue, 06 Apr 2021 15:14:16 +0000</pubDate>
      <link>https://dev.to/cschotte/protect-your-web-applications-using-azure-application-gateway-3i14</link>
      <guid>https://dev.to/cschotte/protect-your-web-applications-using-azure-application-gateway-3i14</guid>
      <description>&lt;h2&gt;
  
  
  What is Azure Application Gateway?
&lt;/h2&gt;

&lt;p&gt;Azure Application Gateway is a reverse proxy with optional WAF (Web Application Firewall) capability to allow incoming connections from external sources. The Gateway operates at Layer 3, 4, and 7 for IP-based, TCP/UDP-based, URL-based, and Host Header-based routing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fazure-application-gateway.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fazure-application-gateway.png" alt="Azure Application Gateway"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use the Application Gateway?
&lt;/h2&gt;

&lt;p&gt;Microsoft has multiple services to protect and accelerate your applications; they are used for different scenarios, depending on where your users are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/azure/traffic-manager/traffic-manager-overview" rel="noopener noreferrer"&gt;Azure Traffic Manager&lt;/a&gt; – DNS based load balancer across &lt;strong&gt;global regions&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://navatron.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://us.navatron.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://eu.navatron.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://docs.microsoft.com/en-us/azure/application-gateway/overview" rel="noopener noreferrer"&gt;Azure Application Gateway&lt;/a&gt; – &lt;strong&gt;Single region&lt;/strong&gt;, URL-based routing at the application

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://eu.navatron.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://eu.navatron.com/products&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://eu.navatron.com/support&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://eu.navatron.com/jobs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://docs.microsoft.com/en-us/azure/frontdoor/front-door-overview" rel="noopener noreferrer"&gt;Azure Front Door&lt;/a&gt; – &lt;strong&gt;Global&lt;/strong&gt; load balancing with SSL offloading 

&lt;ul&gt;
&lt;li&gt;IPv6&lt;/li&gt;
&lt;li&gt;Application acceleration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Global&lt;/strong&gt; Route clients to the closest available service region. Offload SSL and accelerate websites at the network edge.&lt;br&gt;
&lt;strong&gt;Regional / Internal&lt;/strong&gt; Route across zones and into your VNET. Private IP space routing and between your resources to build your regional application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Application Gateway Capabilities
&lt;/h2&gt;

&lt;p&gt;The v2 SKU has several enhancements which do not exist within the v1 SKU, allowing for additional capabilities.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;see also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant#feature-comparison-between-v1-sku-and-v2-sku" rel="noopener noreferrer"&gt;Feature comparison between v1 SKU and v2 SKU&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Application Gateway or WAF deployments under Standard v2 or WAF v2 SKU support autoscaling and can scale up or down based on changing traffic load patterns. Autoscaling (up to 125 instances) also removes the requirement to choose a deployment size or instance count during provisioning. Application Gateway can operate both in fixed capacity (autoscaling disabled) and in autoscaling enabled mode. The v2 SKU offers performance enhancements and adds support for critical new features like autoscaling, zone redundancy, and support for static VIPs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fautoscaling.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fautoscaling.png" alt="Autoscaling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Zone Redundancy
&lt;/h3&gt;

&lt;p&gt;An Application Gateway or WAF deployment can span multiple Availability Zones, removing the need to provision separate Application Gateway instances in each zone with a Traffic Manager. You can choose a single zone or multiple zones where Application Gateway instances are deployed, making it more resilient to zone failure. The back-end pool for applications can be similarly distributed across availability zones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static VIP
&lt;/h3&gt;

&lt;p&gt;The v1 SKU did not support a Static VIP and was only accessible via the configured URL. The v2 URL is only configurable with a Static VIP and will not change through reboots.&lt;/p&gt;

&lt;h3&gt;
  
  
  AKS Ingress Controller
&lt;/h3&gt;

&lt;p&gt;The Application Gateway Ingress Controller allows Azure Application Gateway to be used as the ingress for an Azure Kubernetes Service (AKS) cluster. The ingress controller runs as a pod within the AKS cluster. It consumes Kubernetes Ingress Resources and converts them to an Azure Application Gateway configuration, allowing the Gateway to load-balance traffic to Kubernetes pods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Faks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Faks.png" alt="Azure Kubernetes Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Vault Integration
&lt;/h3&gt;

&lt;p&gt;Application Gateway v2 supports integration with Key Vault for server certificates that are attached to HTTPS-enabled listeners. You can either attach the certificate directly to the Application Gateway or provide a reference to an existing Key Vault certificate or secret when you create an HTTPS-enabled listener.&lt;/p&gt;

&lt;p&gt;Integration includes many benefits such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More robust security because the application development team doesn't directly handle SSL certificates. This integration allows a separate security team to:

&lt;ul&gt;
&lt;li&gt;Set up Application Gateways.&lt;/li&gt;
&lt;li&gt;Control Application Gateway lifecycles.&lt;/li&gt;
&lt;li&gt;Grant permissions to selected Application Gateways to access certificates that are stored in your Key Vault.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Support for importing existing certificates into your key Vault. Or use Key Vault APIs to create and manage new certificates with any of the trusted Key Vault partners.&lt;/li&gt;

&lt;li&gt;Support for automatic renewal of certificates that are stored in your Key Vault.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Application Gateway currently supports software-validated certificates only. Hardware security module (HSM)-validated certificates are not supported. After Application Gateway is configured to use Key Vault certificates, its instances retrieve the certificate from Key Vault and install them locally for SSL termination. The instances also poll Key Vault at 24-hour intervals to retrieve a renewed version of the certificate if it exists. If an updated certificate is found, the SSL certificate currently associated with the HTTPS listener is automatically rotated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewriting HTTP/HTTPS Headers
&lt;/h3&gt;

&lt;p&gt;HTTP headers allow a client and server to pass additional information with a request or response. By rewriting these headers, you can accomplish important tasks, such as adding security-related header fields like HSTS/ X-XSS-Protection, removing response header fields that might reveal sensitive information, and removing port information from X-Forwarded-For headers.&lt;/p&gt;

&lt;p&gt;Application Gateway allows you to add, remove, or update HTTP request and response headers while the request and response packets move between the client and backend pools. And it allows you to add conditions to ensure that the specified headers are rewritten only when certain conditions are met.&lt;/p&gt;

&lt;p&gt;Application Gateway also supports several server variables that help you store additional information about requests and responses. This makes it easier for you to create powerful rewrite rules.&lt;/p&gt;

&lt;p&gt;All headers in requests and responses can be modified, except for the Host, Connection, and Upgrade headers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Socket Routing
&lt;/h3&gt;

&lt;p&gt;Web Application Gateway can make routing decisions based on the IP, and Port (Socket) requests. This allows for one Application Gateway to front-end multiple applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  URL Routing
&lt;/h3&gt;

&lt;p&gt;URL Path-Based Routing allows you to route traffic to backend server pools based on the request's URL Paths. One of the scenarios is to route requests for different content types to another pool.  For example, requests for &lt;code&gt;https://navatron.com/video/*&lt;/code&gt; are routed to &lt;em&gt;VideoServerPool&lt;/em&gt;, and &lt;code&gt;https://navatron.com/images/*&lt;/code&gt; are routed to &lt;em&gt;ImageServerPool&lt;/em&gt;. &lt;em&gt;DefaultServerPool&lt;/em&gt; is not sent any traffic in this example.&lt;/p&gt;

&lt;p&gt;Rules are processed in the order they are listed in the portal. It is highly recommended to configure multi-site listeners first prior to configuring a basic listener. This ensures that traffic gets routed to the proper back end. If a basic listener is listed first and matches an incoming request, it gets processed by that listener.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple-Site Hosting
&lt;/h3&gt;

&lt;p&gt;Multiple-site hosting enables you to configure more than one website on the same Application Gateway instance. This feature allows you to configure a more efficient topology for your deployments by adding up to 100 websites to one application gateway. Each website can be directed to its own pool. For example, Application Gateway can serve traffic for &lt;code&gt;navatron.com&lt;/code&gt; and &lt;code&gt;navatron.nl&lt;/code&gt; from two server pools called &lt;em&gt;MainServerPool&lt;/em&gt; and &lt;em&gt;DutchServerPool&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Similarly, two subdomains of the same parent domain can be hosted on the same application gateway deployment. Examples of using subdomains could include &lt;code&gt;https://blog.navatron.com&lt;/code&gt; and &lt;code&gt;https://api.navatron.com&lt;/code&gt; hosted on a single Application Gateway deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redirection
&lt;/h3&gt;

&lt;p&gt;Often, Web servers will contain pools specifically for web redirection from HTTP to HTTPS.  Application Gateway can handle this scenario natively, simplifying configs and freeing up resources on the web servers themselves.  This is a generic redirection mechanism to redirect from and to any port you define using rules. It also supports redirection to an external site as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional WAF
&lt;/h3&gt;

&lt;p&gt;Web application firewall (WAF) is a feature of Application Gateway that provides centralized protection of web applications from common exploits and vulnerabilities. WAF is based on rules from the OWASP (Open Web Application Security Project) core rule sets 3.0 or 2.2.9.&lt;/p&gt;

&lt;p&gt;Web applications are increasingly targeted for malicious attacks that exploit commonly known vulnerabilities. Preventing such attacks in application code can be challenging and may require rigorous maintenance, patching, and monitoring at many application topology layers. A centralized web application firewall helps make security management much more straightforward and reassures application administrators against threats or intrusions. A WAF solution can also react to a security threat faster by patching a known vulnerability at a central location versus securing each of individual web applications. Existing application gateways can be converted to a web application firewall-enabled application gateway easily.&lt;/p&gt;

&lt;p&gt;Application Gateway WAF protects against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL-injection&lt;/li&gt;
&lt;li&gt;Cross-site scripting&lt;/li&gt;
&lt;li&gt;Other common web attacks, such as command injection, HTTP request smuggling, HTTP response splitting, and remote file inclusion&lt;/li&gt;
&lt;li&gt;HTTP protocol violations&lt;/li&gt;
&lt;li&gt;HTTP protocol anomalies, such as missing host user-agent and accept headers&lt;/li&gt;
&lt;li&gt;Bots, crawlers, and scanners&lt;/li&gt;
&lt;li&gt;Common application misconfigurations (for example, Apache and IIS) through detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These WAF Features can be enabled in monitoring/learning mode or in enforcing mode. All the findings can be reviewed via various monitoring configurations like Azure Monitor, Azure Security Center, or Azure Diagnostics Logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-End SSL
&lt;/h3&gt;

&lt;p&gt;When SSL Encryption must be maintained, Application Gateway can be configured for End-to-End SSL. In this process, the Application Gateway will decrypt the traffic, review and evaluate based on configured policies, modify the headers as configured, and then re-encrypt traffic before sending it to the back-end servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fssl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fssl.png" alt="End-to-End SSL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;see also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/application-gateway/ssl-overview" rel="noopener noreferrer"&gt;Overview of TLS termination and end to end TLS with Application Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  SSL Termination
&lt;/h3&gt;

&lt;p&gt;Application gateway supports SSL/TLS termination at the Gateway, after which traffic typically flows unencrypted to the back-end servers. This feature allows web servers to be unburdened from costly encryption and decryption overhead.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;see also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/application-gateway/key-vault-certs" rel="noopener noreferrer"&gt;TLS termination with Key Vault certificates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Session Affinity
&lt;/h3&gt;

&lt;p&gt;The cookie-based session affinity feature is useful when you want to keep a user session on the same server. The Application Gateway can direct subsequent traffic from a user session to the same server for processing by using gateway-managed cookies. This is important in cases where the session state is saved locally on the server for a user session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Error Pages
&lt;/h3&gt;

&lt;p&gt;Application Gateway allows for the creation of custom error pages instead of displaying the default error pages. Custom branding and layout can be used within a custom error page.&lt;/p&gt;

&lt;p&gt;For example, a custom maintenance page can be displayed if the application isn't reachable or an unauthorized access page if a malicious request is sent to a web application. These custom error pages can replace HTTP 502 and 403 return codes.&lt;/p&gt;

&lt;p&gt;If an error originates from the back-end servers, then it's passed along unmodified back to the caller. A custom error page isn't displayed. Application Gateway can display a custom error page when a request can't reach the back-end.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSocket Support
&lt;/h3&gt;

&lt;p&gt;Application Gateway provides native support for the WebSocket protocol. There is no user-configurable setting to selectively enable or disable WebSocket support. The WebSocket protocol enables full-duplex communication between a server and a client over a long-running TCP connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP/2 Support
&lt;/h3&gt;

&lt;p&gt;Application Gateway provides native support for the HTTP/2 protocol. The HTTP/2 protocol enables full-duplex communication between a server and a client over a long-running TCP connection. This allows for a more interactive communication between the web server and the client, which can be bidirectional without the need for polling as required in HTTP-based implementations. These protocols have low overhead, unlike HTTP, and can reuse the same TCP connection for multiple requests/responses, resulting in a more efficient resource utilization. These protocols are designed to work over traditional HTTP ports of 80 and 443.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection Draining
&lt;/h3&gt;

&lt;p&gt;Connection draining helps you achieve graceful removal of back-end pool members during planned service updates. This setting is enabled via the back-end HTTP setting and can be applied to all back-end pool members during rule creation. Once enabled, Application Gateway ensures all de-registering instances of a back-end pool do not receive any new requests while allowing existing requests to complete within a configured time limit. This applies to both back-end instances that are explicitly removed from the back-end pool by an API call, and back-end instances reported as unhealthy as determined by the health probes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Health Probes
&lt;/h3&gt;

&lt;p&gt;Azure Application Gateway, by default, monitors the health of all resources in its back-end pool and automatically removes any resource considered unhealthy from the pool. Application Gateway continues to monitor the unhealthy instances and adds them back to the healthy back-end pool once they become available and respond to health probes. Application gateway sends the health probes with the same port that is defined in the back-end HTTP settings. This configuration ensures that the probe is testing the same port that customers would be using to connect to the back-end.  In addition to using default health probe monitoring, you can also customize the health probe to suit your application's requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fhealth-probes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fclemens.ms%2Fprotect-your-web-applications-using-azure-application-gateway%2Fhealth-probes.png" alt="Health Probes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;see also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-probe-overview" rel="noopener noreferrer"&gt;Application Gateway health monitoring overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Networking
&lt;/h3&gt;

&lt;p&gt;Azure Application Gateways are always deployed in a virtual network subnet. This subnet can only contain Application Gateways. Although this is an entirely managed service, behind the scenes, Azure is deploying one or more instances of load-balanced virtual appliances into your VNet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application Gateway can talk to instances outside of the virtual network as long as there is IP connectivity.&lt;/li&gt;
&lt;li&gt;Network Security Groups are supported on the Application Gateway subnet to provide segmentation/isolation.&lt;/li&gt;
&lt;li&gt;Front-End IPs on an Application Gateway can be either public or private depending on requirements.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>website</category>
      <category>gateway</category>
      <category>firewall</category>
    </item>
    <item>
      <title>Free and Built-In TLS/SSL certificates in Azure</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Sat, 03 Apr 2021 17:17:30 +0000</pubDate>
      <link>https://dev.to/cschotte/free-and-built-in-tls-ssl-certificates-in-azure-36cc</link>
      <guid>https://dev.to/cschotte/free-and-built-in-tls-ssl-certificates-in-azure-36cc</guid>
      <description>&lt;p&gt;Today, when a website does not have an &lt;strong&gt;SSL/TSL certificate&lt;/strong&gt;, web browsers give you a warning &lt;code&gt;not secure&lt;/code&gt;. This warning not only scares people but also gives you a disadvantage in search engine ranking. On Azure, web sites have a default https-enabled URL, like &lt;code&gt;https://sitename.azurewebsites.net/&lt;/code&gt;, but when you have a vanity domain configured, you are missing this secure connection. Luckily there are some free SSL/TLS certificate options to explore. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Encrypt
&lt;/h2&gt;

&lt;p&gt;Wait, there is &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;, its free! Why are you not using this excellent service? Yes, that is true, but there are some downsides to use Let's Encrypt (on Azure), like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need an Azure Web App Site Extension to enable and renew certificates, like this one from &lt;a href="https://github.com/sjkp/letsencrypt-siteextension/"&gt;Simon J.K. Pedersen&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Let's Encrypt does not have any official support or SLA model.&lt;/li&gt;
&lt;li&gt;It's complex to implement correctly for all the Azure services you need.&lt;/li&gt;
&lt;li&gt;Let's Encrypt only validates the domain name. There are &lt;strong&gt;no further validations&lt;/strong&gt; like other CAs, and certificates are misused for phishing attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Azure Built-In free certificates
&lt;/h2&gt;

&lt;p&gt;It should be easy and free to enable SSL/TSL certificates in Azure. This was the number one question on &lt;a href="https://feedback.azure.com/forums/170024-additional-services"&gt;UserVoice feedback&lt;/a&gt;. Microsoft implemented this Built-In free certificate option for some services, like Azure &lt;a href="https://azure.microsoft.com/en-us/services/cdn/"&gt;CDN&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/en-us/services/frontdoor/"&gt;Front Door&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/en-us/services/application-gateway/"&gt;Application Gateway&lt;/a&gt;, etc. When you put one of those services in front of your web site (like what I did with &lt;a href="https://clemens.ms/"&gt;this blog&lt;/a&gt;), you can enable an auto-renewable Built-In certificate for free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7VzMuDJq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/free-and-built-in-tls-ssl-certificates-in-azure/tls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7VzMuDJq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/free-and-built-in-tls-ssl-certificates-in-azure/tls.png" alt="enable TLS certificates"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;a href="https://docs.microsoft.com/en-us/azure/cdn/cdn-custom-ssl"&gt;Custom Domain HTTPS feature&lt;/a&gt; enables you to deliver content to your users securely over your own domain. This is done by encrypting the data between the CDN and your users' clients (typically web browsers) via TLS protocol (which is a successor of the SSL protocol) using a certificate. Using our "CDN managed certificate" capability, you can enable this feature with just a few clicks and have Azure CDN completely take care of certificate management tasks such as its renewal. You can also bring your own certificate (stored in Azure Key vault ) or even purchase a new certificate through Key vault and have Azure CDN use that certificate for securing the content delivery."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Use your own certificate
&lt;/h2&gt;

&lt;p&gt;There is also an option to use your own certificate, especially when you need a naked domain (without the "www"-prefix). This is not currently possible with the Build-In certificate option. See "&lt;a href="https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/"&gt;Hosting a Static Site on Azure using CDN and HTTPS&lt;/a&gt;" how to fix this.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>ssl</category>
      <category>tls</category>
      <category>https</category>
    </item>
    <item>
      <title>Infrastructure as Code</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Tue, 30 Mar 2021 19:16:36 +0000</pubDate>
      <link>https://dev.to/cschotte/infrastructure-as-code-1779</link>
      <guid>https://dev.to/cschotte/infrastructure-as-code-1779</guid>
      <description>&lt;h2&gt;
  
  
  Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Infrastructure as Code (IaC) is the process of managing and provisioning infrastructure and configuration dependencies for application stacks using machine-readable definition files rather than physical hardware configuration or interactive configuration tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Infrastructure as Code?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Source Controlled&lt;/li&gt;
&lt;li&gt;In code (Scripts &amp;amp; Templates)&lt;/li&gt;
&lt;li&gt;Automated &amp;amp; Continuous Deployment&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Feedback loop (Monitoring)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Categories of IaC tooling
&lt;/h3&gt;

&lt;p&gt;You can use many declarative &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/framework/devops/automation-infrastructure"&gt;infrastructure deployment technologies&lt;/a&gt; with Azure. These fall into two main categories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imperative IaC involves writing scripts in a language like Bash, PowerShell, C# script files, or Python. These programmatically execute a series of steps to create or modify your resources. When using imperative deployments, it is up to you to manage things like dependency sequencing, error control, and resource updates.&lt;/li&gt;
&lt;li&gt;Declarative IaC involves writing a definition of how you want your environment to look; the tooling then figures out how to make this happen by inspecting your current state, comparing it to the target state you've requested, and applying the differences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--14fjer2o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/infrastructure-as-code/tooling.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--14fjer2o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/infrastructure-as-code/tooling.png" alt="Tooling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/"&gt;Hashicorp Terraform&lt;/a&gt; is an open-source tool for provisioning and managing cloud infrastructure. It codifies infrastructure in configuration files that describe the topology of cloud resources. These resources include virtual machines, storage accounts, and networking interfaces. The Terraform CLI provides a simple mechanism to deploy and version the configuration files to Azure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/developer/terraform/overview"&gt;Terraform with Azure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://geekzter.medium.com/using-terraform-with-azure-azure-pipelines-github-actions-86e043bd0d9e"&gt;Using Terraform together with Azure, Azure Pipelines &amp;amp; GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Infrastructure as Code with Terraform has become pervasive for provisioning cloud infrastructure. Azure engineering has invested in Terraform AzureRM provider as a first-party control plane, of equal importance to Azure-Cli and PowerShell, for Azure. As companies mature in their use of Terraform, there is an increasing need to apply DevOps practices to their IaC development to increase velocity and reduce Mean-Time-To-Recover from failures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;See also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/solution-ideas/articles/immutable-infrastructure-cicd-using-jenkins-and-terraform-on-azure-virtual-architecture-overview"&gt;Immutable Infrastructure&lt;/a&gt; CI/CD using Jenkins and Terraform on Azure Virtual Architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;p&gt;Doing automation around terraform has several key challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How you deploy in production &lt;strong&gt;MUST&lt;/strong&gt; match how you deploy in development and testing?&lt;/li&gt;
&lt;li&gt;Testing requires that physical resources be deployed before the test.&lt;/li&gt;
&lt;li&gt;State management is challenging in multi-user/multi-team scenarios.&lt;/li&gt;
&lt;li&gt;Not a lot of best practices around how to segment/manage remote state &amp;amp; workspaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Terraform enables the definition, preview, and deployment of cloud infrastructure. Using Terraform, you create configuration files using HCL syntax. The HCL syntax allows you to specify the cloud provider - such as Azure - and the elements that make up your cloud infrastructure. After you create your configuration files, you create an execution plan that allows you to preview your infrastructure changes before they're deployed. Once you verify the changes, you apply the execution plan to deploy the infrastructure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;See also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best practices &lt;a href="https://docs.microsoft.com/en-us/azure/developer/terraform/best-practices-testing-overview"&gt;Terraform testing&lt;/a&gt; overview.&lt;/li&gt;
&lt;li&gt;Automated testing with &lt;a href="https://github.com/gruntwork-io/terratest"&gt;Terratest&lt;/a&gt;. Terratest is a critical component in the toolchain needed for effective terraform development.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project Lucidity
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/rguthrie-ghec/Terraform-Pipelines"&gt;Project Lucidity&lt;/a&gt; allows you to deploy infrastructure to Azure using Terraform easily. It addresses some of the challenges from above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An opinionated approach to terraform development on Azure using Azure DevOps&lt;/li&gt;
&lt;li&gt;Flexible installation script that creates a project with pipelines configured so you can focus on writing terraform and creating test&lt;/li&gt;
&lt;li&gt;Minimize fixed design choices while adhering to best practices

&lt;ul&gt;
&lt;li&gt;The directory structure for maintaining Terraform modules and deployments.&lt;/li&gt;
&lt;li&gt;Use environment variables stored in .env files instead of .tfvars&lt;/li&gt;
&lt;li&gt;Use Terratest to test your terraform code as an acceptance test&lt;/li&gt;
&lt;li&gt;Secrets should only ever live in KeyVault.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Azure Bicep
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/bicep-overview"&gt;Azure Bicep&lt;/a&gt; is an abstraction built on top of Azure ARM Templates and Azure Resource Manager that offers a cleaner code syntax with better support for modularity and code reuse. Azure Bicep moves away from the JSON syntax used by ARM Templates and is much easier to read and write Infrastructure as Code (IaC) in Azure.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Azure Bicep?
&lt;/h3&gt;

&lt;p&gt;Azure Bicep is a new declarative Domain Specific Language (DSL) for deploying Azure resources. This new language aims to make it easier to write Infrastructure as Code (IaC) targeting Azure Resource Manager (ARM) using a syntax that's more friendly than the JSON syntax of Azure ARM Templates.&lt;/p&gt;

&lt;p&gt;Azure Bicep works as an abstraction layer built on top of ARM Templates. Anything that can be done with Azure ARM Templates can be done with Azure Bicep as it provides a "transparent abstraction" over ARM (Azure Resource Manager). With this abstraction, all the types, API versions, and properties valid within ARM Templates are also valid with Azure Bicep.&lt;/p&gt;

&lt;p&gt;Azure Bicep is a compiled language. This means that the Azure Bicep code was converted into ARM Template code. Then, the resulting ARM Template code is used to deploy the Azure resources. This enables Azure Bicep to use its syntax and compiler for authoring Azure Bicep files that compile down to Azure Resource Manager (ARM) JSON as a sort of intermediate language (IL).&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Best Practices for IaC
&lt;/h2&gt;

&lt;p&gt;The security of IaC definitions, deployments, and operational workloads are critically important due to the high level of permissions required to implement an IaC solution effectively. This chapter attempts to outline key best practices that contribute to secure IaC strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use directory services for authentication
&lt;/h3&gt;

&lt;p&gt;Directory services enable you to safeguard user and system credentials by enforcing strong authentication and conditional access policies. These technologies allow you to manage your identities by ensuring that the right services have the right access to the right resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use directory services, like &lt;a href="https://azure.microsoft.com/en-us/services/active-directory/"&gt;Azure Active Directory&lt;/a&gt;, for authenticating users and services when deploying infrastructure so that Role-Based-Access-Control can be used to apply the principle of least privilege.&lt;/li&gt;
&lt;li&gt;When deploying infrastructure, use a dedicated identity so that the blast radius of a leaked credential is scoped as narrowly as possible.&lt;/li&gt;
&lt;li&gt;When deploying infrastructure, leverage platform authentication technologies that avoid needing to store a username and password to make it harder for credentials to leak. In practice, this means leveraging &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview"&gt;Managed Identities&lt;/a&gt; or &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure?view=azure-devops"&gt;Service Connections&lt;/a&gt; in Azure DevOps.&lt;/li&gt;
&lt;li&gt;When a username or password must be used based on technology limitations, all sensitive information should be stored in a secret store such as &lt;a href="https://clemens.ms/azure-key-vault/"&gt;Key Vault&lt;/a&gt; and masked in any logging output so that secrets are not leaked during deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mmsYgaZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/infrastructure-as-code/managed-identities.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mmsYgaZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/infrastructure-as-code/managed-identities.png" alt="Managed Identities"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rotate Deployment Credentials
&lt;/h3&gt;

&lt;p&gt;Credential rotation refers to the changing/resetting of a credential(s). Limiting a credential's lifespan reduces the risk from and effectiveness of credential-based attacks and exploits by condensing the window of time during which a stolen credential may be valid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All secrets required to authenticate to external services (i.e., Azure) should expire on a regular cadence so that any leaked credentials are not valid indefinitely.&lt;/li&gt;
&lt;li&gt;Design automated credential rotation from the beginning of a project so that the delivered solution can recover from a security breach quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Apply the principle of least privilege
&lt;/h3&gt;

&lt;p&gt;The principle of least privilege (PoLP), also known as the principle of minimal privilege or the principle of least authority, requires that in a particular abstraction layer of a computing environment, every module (such as a process, a user, or a program, depending on the subject) must be able to access only the information and resources that are necessary for its legitimate purpose. Limiting the access of a computing-environment reduces the risk from and effectiveness of credential-based attacks and exploits by minimizing the blast radius of a leaked credential.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identities used for infrastructure deployments should have a minimal set of privileges so that a leaked credential has the smallest blast radius possible.&lt;/li&gt;
&lt;li&gt;Use separate identities for deploying to each environment (i.e., dev, QA, prod) so that a leaked credential for one of those environments does not grant access to another environment. This also protects against a bug being tested in a dev IaC deployment from unintentionally impacting non-test environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Manage secrets securely
&lt;/h3&gt;

&lt;p&gt;Secure secrets management is essential to protect data in the cloud and can be used to store sensitive information like passwords and certificates securely. Managed secret stores often provide the ability to protect sensitive information using strong encryption while avoiding the need to keep secrets in a repository or as a plain text environment variable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secrets required for deployments, such as database credentials or private certificates, should be referenced from a secure vault to be stored at rest with encryption and are only accessible via authenticated requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deploy through automated pipelines
&lt;/h3&gt;

&lt;p&gt;CI/CD pipelines are at the core of daily operations for many businesses today. When set up correctly, these processes help keep the delivery process consistent by automating many manual tasks and providing visibility into how the software is being worked on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure deployments to non-development environments should only be done through repeatable processes such as a continuous deployment pipeline so that it is not possible for the deployed environment to drift from the environment as described through IaC templates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enforce branch policies and PRs
&lt;/h3&gt;

&lt;p&gt;Branch policies are an essential part of the Git workflow and enable you to: Isolate work in progress from the completed work in your master branch. Guarantee changes build before they get to Main. Limit who can contribute to specific branches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes to IaC should only be deployed to non-development environments after being reviewed by the team and merged into the appropriate branch so that malicious or unintended changes to infrastructure are not deployed into production environments. See Configure &lt;a href="https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies-overview?view=azure-devops"&gt;Branch Policies&lt;/a&gt; in Azure DevOps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use gated approvals in automated releases
&lt;/h3&gt;

&lt;p&gt;Approvals and gates give you additional control over the start and completion of the deployment pipeline. Each stage in a release pipeline can be configured with pre-deployment and post-deployment conditions, including waiting for users to manually approve or reject deployments and checking with other automated systems until specific conditions are verified.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure deployments should not be deployed into customer-facing environments without the explicit approval of an application operator so that they can monitor the application and its dependencies after the deployment completes. See &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/release/approvals/?view=azure-devops"&gt;Release Approvals and Gates&lt;/a&gt; Overview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nc0e1RPf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/infrastructure-as-code/approvals.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nc0e1RPf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/infrastructure-as-code/approvals.png" alt="Approvals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Blueprints
&lt;/h2&gt;

&lt;p&gt;Just as a blueprint allows an engineer or an architect to sketch a project's design parameters, &lt;a href="https://azure.microsoft.com/en-us/services/blueprints/"&gt;Azure Blueprints&lt;/a&gt; enables cloud architects and central information technology groups to define a repeatable set of Azure resources that implements and adheres to an organization's standards, patterns, and requirements. Azure Blueprints makes it possible for development teams to rapidly build and stand-up new environments with trust they're building within organizational compliance with a set of built-in components, such as networking, to speed up development and delivery.&lt;/p&gt;

&lt;p&gt;Blueprints are a declarative way to orchestrate the deployment of various resource templates and other artifacts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Role Assignments&lt;/li&gt;
&lt;li&gt;Policy Assignments&lt;/li&gt;
&lt;li&gt;Azure Resource Manager templates (ARM templates)&lt;/li&gt;
&lt;li&gt;Resource Groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Azure Blueprints service is backed by the globally distributed Azure Cosmos DB. Blueprint objects are replicated to multiple Azure regions. This replication provides low latency, high availability, and consistent access to your blueprint objects, regardless of which region Azure Blueprints deploys your resources to.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cQ9D-d6KkMY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  How it's different from ARM templates
&lt;/h3&gt;

&lt;p&gt;The service is designed to help with environment setup. This setup often consists of a set of resource groups, policies, role assignments, and ARM template deployments. A blueprint is a package to bring each of these artifact types together and allow you to compose and version that package, including through continuous integration and continuous delivery (CI/CD) pipeline. Ultimately, each is assigned to a subscription in a single operation that can be audited and tracked.&lt;/p&gt;

&lt;p&gt;Nearly everything that you want to include for deployment in Azure Blueprints can be accomplished with an ARM template. However, an ARM template is a document that doesn't exist natively in Azure – each is stored either locally or in source control. The template gets used for deployments of one or more Azure resources, but once those resources deploy there's no active connection or relationship to the template.&lt;/p&gt;

&lt;p&gt;With Azure Blueprints, the relationship between the blueprint definition (what should be deployed) and the blueprint assignment (what was deployed) is preserved. This connection supports improved tracking and auditing of deployments. Azure Blueprints can also upgrade several subscriptions at once that are governed by the same blueprint.&lt;br&gt;
There's no need to choose between an ARM template and a blueprint. Each blueprint can consist of zero or more ARM template artifacts. This support means that previous efforts to develop and maintain a library of ARM templates are reusable in Azure Blueprints.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it's different from Azure Policy
&lt;/h3&gt;

&lt;p&gt;A blueprint is a package or container for composing focus-specific sets of standards, patterns, and requirements related to the implementation of Azure cloud services, security, and design that can be reused to maintain consistency and compliance.&lt;/p&gt;

&lt;p&gt;A policy is a default allow and explicit deny system focused on resource properties during deployment and for already existing resources. It supports cloud governance by validating that resources within a subscription adhere to requirements and standards.&lt;/p&gt;

&lt;p&gt;Including a policy in a blueprint enables the creation of the right pattern or design during assignment of the blueprint. The policy inclusion makes sure that only approved or expected changes can be made to the environment to protect ongoing compliance to the intent of the blueprint.&lt;/p&gt;

&lt;p&gt;A policy can be included as one of many artifacts in a blueprint definition. Blueprints also support using parameters with policies and initiatives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blueprint definition locations
&lt;/h3&gt;

&lt;p&gt;When creating a blueprint definition, you'll define where the blueprint is saved. Blueprints can be saved to a management group or &lt;strong&gt;parent&lt;/strong&gt; subscription that you have Contributor access to. If the location is a management group, the blueprint is available to assign to any &lt;strong&gt;child&lt;/strong&gt; subscription of that management group.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Blueprints as Code
&lt;/h3&gt;

&lt;p&gt;Using the Blueprints in the Azure Portal is a great way to get started with Blueprints or to use Blueprints on a small-ish scale, but often you'll want to manage your &lt;a href="https://github.com/Azure/azure-blueprints"&gt;Blueprints-as-Code&lt;/a&gt; for a variety of reasons, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sharing blueprints&lt;/li&gt;
&lt;li&gt;Keeping blueprints in source control&lt;/li&gt;
&lt;li&gt;Putting blueprints in a CI/CD or release pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing strategies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://clemens.ms/compliance-as-code/"&gt;Compliance-as-Code&lt;/a&gt; can be summarized as the organizational capability to automate the implementation, verification, remediation, monitoring, and compliance status reporting. This automation comes in the form of code and is integrated into the code repositories used by Devs and Engineers. It becomes "just another piece of code." I have written an entire blog post about &lt;a href="https://clemens.ms/compliance-as-code/"&gt;Compliance-as-Code&lt;/a&gt; here.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>iac</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>CaveRace</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Sun, 28 Mar 2021 08:51:11 +0000</pubDate>
      <link>https://dev.to/cschotte/caverace-22ec</link>
      <guid>https://dev.to/cschotte/caverace-22ec</guid>
      <description>&lt;p&gt;&lt;strong&gt;CaveRace&lt;/strong&gt; is a classic maze-based video game I developed back in &lt;strong&gt;1997&lt;/strong&gt; with some other students. Inspired on a game, I played on my Commodore Amiga, called &lt;strong&gt;Dyna Blaster&lt;/strong&gt; &lt;a href="https://en.wikipedia.org/wiki/Bomberman_(1990_video_game)"&gt;(Bomberman)&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Forest level&lt;/th&gt;
&lt;th&gt;Winter level&lt;/th&gt;
&lt;th&gt;Winter level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--90UH6Cob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/demo1.png" alt="Forest level"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PPzL4lo3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/demo2.png" alt="Winter level"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AyWNWNBj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/demo3.png" alt="Lava level"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Download
&lt;/h2&gt;

&lt;p&gt;You can &lt;strong&gt;&lt;a href="https://clemens.ms/caverace/caverace-1.2-dos.zip"&gt;download&lt;/a&gt;&lt;/strong&gt; the 16-bit MS-DOS version of CaveRace (version 1.2) here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Story
&lt;/h2&gt;

&lt;p&gt;On the planed Eldora, there are miners collecting gold and diamonds. When the miners get unexpected visitors from outer space, the mining of precious minerals is in danger. The only defense the miners have is their explosions to make a way inside the mines and caves. CaveRace is a game where you collect as much as possible precious minerals and destroy all alien visitors.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Planed Eldora&lt;/th&gt;
&lt;th&gt;Collect treasure&lt;/th&gt;
&lt;th&gt;Alien visitors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jtdvxTFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/eldora.png" alt="Planed Eldora"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qAkAPVEy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/treasure.png" alt="Collect treasure"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Dik8a4m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/alien.png" alt="Alien"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Borland C
&lt;/h2&gt;

&lt;p&gt;The game is mainly written in the programming language &lt;a href="https://en.wikipedia.org/wiki/Borland_C%2B%2B"&gt;Borland C&lt;/a&gt; 3.1, and some graphic routines are in x68 assembly language. The minimal system requirements are an Intel 80386 IBM compatible PC running MS-DOS and VGA 320×200 pixels (Mode 13h) in 256-color mode video system.&lt;/p&gt;

&lt;p&gt;The game is using your mouse and keyboard for navigation and gameplay. The game loop is in sync with the refresh rate of the screen, not the best choice, but simple to understand. No sound is present in the MS-DOS version, later on, I ported CaveRace to Windows, this version has upgraded graphics and sounds (explosions).&lt;/p&gt;

&lt;p&gt;There is also a &lt;strong&gt;MapEditor&lt;/strong&gt; available to create levels yourselves. Let me know what you have created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphics
&lt;/h2&gt;

&lt;p&gt;The original artwork is created on an &lt;strong&gt;Amiga&lt;/strong&gt; using &lt;a href="https://en.wikipedia.org/wiki/Deluxe_Paint"&gt;Deluxe Paint&lt;/a&gt; in the file format IFF (Interchange File Format). The game CaveRace is using a binary RGB byte file format. Game tiles (16 x 16 pixels) and screens (320 x 200 pixels) are converted to this binary RGB byte file format.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Tiles&lt;/th&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BGS&lt;/td&gt;
&lt;td&gt;5 x 50(16x16)&lt;/td&gt;
&lt;td&gt;64000&lt;/td&gt;
&lt;td&gt;BackGrounds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOM&lt;/td&gt;
&lt;td&gt;17 x (16x16)&lt;/td&gt;
&lt;td&gt;4352&lt;/td&gt;
&lt;td&gt;Boms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAR&lt;/td&gt;
&lt;td&gt;320 x 200&lt;/td&gt;
&lt;td&gt;64000&lt;/td&gt;
&lt;td&gt;Carder (Picture)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ENM&lt;/td&gt;
&lt;td&gt;16 x (16x16)&lt;/td&gt;
&lt;td&gt;4096&lt;/td&gt;
&lt;td&gt;Enemys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FNT&lt;/td&gt;
&lt;td&gt;36 x (3x5)&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;td&gt;Font&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HIS&lt;/td&gt;
&lt;td&gt;320 x 200&lt;/td&gt;
&lt;td&gt;64000&lt;/td&gt;
&lt;td&gt;Hi-Scores (Picture)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ITM&lt;/td&gt;
&lt;td&gt;13 x (16x16)&lt;/td&gt;
&lt;td&gt;3328&lt;/td&gt;
&lt;td&gt;Items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAN&lt;/td&gt;
&lt;td&gt;18 x (16x16)&lt;/td&gt;
&lt;td&gt;4608&lt;/td&gt;
&lt;td&gt;Man&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MN1&lt;/td&gt;
&lt;td&gt;320 x 200&lt;/td&gt;
&lt;td&gt;64000&lt;/td&gt;
&lt;td&gt;Menu 1 (Picture)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MN2&lt;/td&gt;
&lt;td&gt;320 x 200&lt;/td&gt;
&lt;td&gt;64000&lt;/td&gt;
&lt;td&gt;Menu 2 (Picture)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PAL&lt;/td&gt;
&lt;td&gt;256 x (RGB)&lt;/td&gt;
&lt;td&gt;768&lt;/td&gt;
&lt;td&gt;Palette&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STS&lt;/td&gt;
&lt;td&gt;4 x (16x16)&lt;/td&gt;
&lt;td&gt;1024&lt;/td&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TRS&lt;/td&gt;
&lt;td&gt;6 x (16x16)&lt;/td&gt;
&lt;td&gt;1536&lt;/td&gt;
&lt;td&gt;Treacure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Game cheats
&lt;/h2&gt;

&lt;p&gt;Start the game using the switch &lt;strong&gt;-powerblast&lt;/strong&gt;, and now you can use the function keys for additional power.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;F1&lt;/td&gt;
&lt;td&gt;next level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F2&lt;/td&gt;
&lt;td&gt;max. health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F3&lt;/td&gt;
&lt;td&gt;max. bombs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F4&lt;/td&gt;
&lt;td&gt;more bomb power&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F5&lt;/td&gt;
&lt;td&gt;double points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;screen capture, output is saved to the file screen.raw&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;shows the rendering time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When running the game on old and slow systems, you can use the &lt;strong&gt;-slow&lt;/strong&gt; switch to speed up the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source
&lt;/h2&gt;

&lt;p&gt;The MS-DOS version of &lt;a href="https://github.com/cschotte/caverace"&gt;CaveRace is opensource&lt;/a&gt; under the Apache-2.0 license and can found op GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  CaveRace for Windows
&lt;/h2&gt;

&lt;p&gt;There is also a Microsoft &lt;strong&gt;Windows&lt;/strong&gt; and &lt;strong&gt;Windows Phone 7&lt;/strong&gt; version of CaveRace. Written in &lt;strong&gt;C#&lt;/strong&gt; and using &lt;strong&gt;DirectX&lt;/strong&gt; graphics. More information about how I ported the C version to C# in an upcoming blog post.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4rbRBwpPcKs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Desert &amp;amp; Lava levels&lt;/th&gt;
&lt;th&gt;Forest &amp;amp; Winter level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aGCACDPf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/desert.jpg" alt="Desert level CaveRace for Windows"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KoDH-qwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/forest.jpg" alt="Forest level CaveRace for Windows"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v13yQw07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/lava.jpg" alt="Lava level CaveRace for Windows"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W3cR-4o6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/caverace/winter.jpg" alt="Winter level CaveRace for Windows"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>gaming</category>
      <category>msdos</category>
      <category>c</category>
      <category>retro</category>
    </item>
    <item>
      <title>Generate PDF files with asp.net core on Azure</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Sun, 28 Mar 2021 08:42:21 +0000</pubDate>
      <link>https://dev.to/cschotte/generate-pdf-files-with-asp-net-core-on-azure-5853</link>
      <guid>https://dev.to/cschotte/generate-pdf-files-with-asp-net-core-on-azure-5853</guid>
      <description>&lt;p&gt;There are many libraries and services to generate PDF files for asp.net core web applications. There are excellent commercial solutions out there, but if you need a free solution, it gets harder. Some libraries are hard to use, or others are limited in functionality. I need a free, easy to use, and quick solution to generate PDF files on an &lt;a href="https://azure.microsoft.com/en-us/services/app-service/web/"&gt;Azure Web App&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can a View retrun a PDF?
&lt;/h2&gt;

&lt;p&gt;What I need is a View that returns a PDF and not HTML what it usually does. The beauty of using a standard View is that I can use my web and asp.net core knowledge to design the View. In this case, I need to generate invoices. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm using MVC as a pattern to structure my web project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I start by creating an invoice controller and a correlated view, where I can design an invoice in HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  html to pdf
&lt;/h2&gt;

&lt;p&gt;Next, we need a way to render the HTML (the View) as PDF. I use the opensource command-line tool &lt;a href="https://wkhtmltopdf.org/"&gt;wkhtmltopdf&lt;/a&gt;. It is easy to use and compatible to run on Azure Web Apps.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;wkhtmltopdf is an opensource (LGPLv3) command-line tool to render HTML into PDF using the Qt WebKit rendering engine. It runs entirely "headless."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wkhtmltopdf.org/downloads.html"&gt;Download&lt;/a&gt; wkhtmltopdf and add the files to your project. I created a folder &lt;code&gt;/wkhtmltopdf&lt;/code&gt; in my solution. When downloading, pick the correct version that is compatible with your Azure Web App (Windows or Linux).&lt;/p&gt;

&lt;p&gt;Add the files &lt;code&gt;wkhtmltoimage.exe&lt;/code&gt;, &lt;code&gt;wkhtmltopdf.exe&lt;/code&gt;, and &lt;code&gt;wkhtmltox.dll&lt;/code&gt; to your solution and change the 'Copy to Output Directory' to 'Copy always.'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1HWw7Rmg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/generate-pdf-files-with-asp.net-core-on-azure/solution_explorer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1HWw7Rmg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/generate-pdf-files-with-asp.net-core-on-azure/solution_explorer.png" alt="solution explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering the PDF
&lt;/h2&gt;

&lt;p&gt;An easy way to interact with the wkhtmltopdf command-line tool is to use the &lt;a href="https://github.com/webgio/Rotativa.AspNetCore"&gt;Rotativa&lt;/a&gt; library from NuGet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mGFxaBrD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/generate-pdf-files-with-asp.net-core-on-azure/rotativa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mGFxaBrD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/generate-pdf-files-with-asp.net-core-on-azure/rotativa.png" alt="Rotativa NuGet library"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we need to tell Rotativa where to find the wkhtmltopdf tool. Add the following line to &lt;code&gt;Startup.cs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;RotativaConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentRootPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"wkhtmltopdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we create a new action result in our InvoiceController that will return the PDF file. This new action result 'IndexAsPDF' will reuse the View we already had created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Pdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ViewAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;PageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MinimumFontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ContentDisposition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ContentDisposition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we go to the URL &lt;code&gt;https://localhost:44349/Invoice/Index&lt;/code&gt; it will show the HTML version of the Invoice, but when we go to &lt;code&gt;https://localhost:44349/Invoice/Pdf&lt;/code&gt; we get the PDF version.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>pdf</category>
      <category>aspnet</category>
      <category>webapp</category>
    </item>
    <item>
      <title>Hosting a Static Site on Azure using CDN and HTTPS</title>
      <dc:creator>Clemens Schotte</dc:creator>
      <pubDate>Sun, 28 Mar 2021 08:36:17 +0000</pubDate>
      <link>https://dev.to/cschotte/hosting-a-static-site-on-azure-using-cdn-and-https-3jlj</link>
      <guid>https://dev.to/cschotte/hosting-a-static-site-on-azure-using-cdn-and-https-3jlj</guid>
      <description>&lt;p&gt;Most websites don’t need a dynamically generated page for every visitor; it is slow, expensive, and requires continuous updates to be secure. A static site is fast and reliable. I hear you thinking; this is old school, most websites are interactive and are using a CMS in some kind to manage their content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless CMS
&lt;/h2&gt;

&lt;p&gt;The solution for a static website is to make use of a headless CMS, like &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; or &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;. Basascly, you generate a static site from content and a template, similar to what this blog is doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A fast and scalable website
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://azure.microsoft.com/"&gt;Microsoft Azure&lt;/a&gt; Cloud platform is the ideal candidate to host your website and web applications. Usually, when I create a website, I use &lt;strong&gt;App Service Web Apps&lt;/strong&gt;, which runs your solution in a scalable manner.&lt;/p&gt;

&lt;p&gt;But there is also another option, which is cheaper and is very fast. You can also host your static website from a &lt;a href="https://azure.microsoft.com/en-us/services/storage/blobs/"&gt;Storage Account&lt;/a&gt;, and when you combine this with Azure &lt;a href="https://azure.microsoft.com/en-us/services/cdn/"&gt;Content Delivery Network&lt;/a&gt; (CDN), your site is lightning fast and available worldwide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage Account
&lt;/h2&gt;

&lt;p&gt;We start by creating a general-purpose storage account in Azure. Use a unique name and choose a location close to yourself. I live in the Netherlands, so West Europe is, in my case, the best option. Replication I don’t need, so locally redundant, is the cheapest for me.&lt;/p&gt;

&lt;p&gt;Next, we need to &lt;strong&gt;enable the Static website option&lt;/strong&gt; in the storage account, and which also gives us the primary endpoint URL we need later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nte9kuvH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/storage_static_website.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nte9kuvH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/storage_static_website.png" alt="Storage Account Static Website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upload some test HTML content into the BLOB storage. I used the build-in Storage Explorer when uploading. Use the container &lt;strong&gt;$web&lt;/strong&gt; (this was created by enabling the Static website option).&lt;/p&gt;

&lt;p&gt;Let’s test if the files are visible in the browser. Use the primary endpoint URL from the enable the Static website option. It looks something like: &lt;code&gt;https://«your unique name».z6.web.core.windows.net/&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The z6 in the URL is the location of the datacenter and can be different in your situation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Content Delivery Network (CDN)
&lt;/h2&gt;

&lt;p&gt;To make your website fast and responsive, host it closeby your visitors. Visitors from the US and Australia are far from West Europe, so by using a CDN, the content is as closeby as possible. Azure CDN has &lt;a href="https://docs.microsoft.com/en-us/azure/cdn/cdn-pop-locations"&gt;130 point-of-presence (POP) locations&lt;/a&gt; worldwide.&lt;/p&gt;

&lt;p&gt;Creating an Azure CDN is easy, give it a unique name and point it to the primary endpoint URL from the Storage account we had created earlier.&lt;/p&gt;

&lt;p&gt;Test if your site is still working, and go to the CDN edge URL. It looks something like: &lt;code&gt;https://«your unique name».azureedge.net/&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain names and Azure DNS
&lt;/h2&gt;

&lt;p&gt;Our site is now working, served by a storage account, and cached by a CDN worldwide. Now we need a beautiful domain name and URL before we can communicate this to our visitors.&lt;/p&gt;

&lt;p&gt;I require a subdomain “www” and a naked/root domain without the “www” prefix pointing to the CDN edge URL. For the subdomain, this is easy, use a CNAME-record. But for a naked/root domain, it is a little bit more complicated. You can’t use a CNAME, but need an A- and AAAA-record pointing to an IP address. But how do I know the right IP address for the Azure CDN edge, and what happens if this IP address changes (and changes happen)? Luckily, when using Azure DNS, we can point to Azure resources what automatically takes the right IP address. We add the naked/root domain and subdomain to the CDN under Custom domains. And test if the website is working in our browsers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U0ZJUKGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/DNS_A_AzureResource.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U0ZJUKGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/DNS_A_AzureResource.png" alt="DNS Azure resources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Traffic encryption using SSL for HTTPS
&lt;/h2&gt;

&lt;p&gt;If your website is not using HTTPS, most browsers warn you nowadays. To make the traffic secure between your visitors and the site, we need encryption. We do this by enabling HTTPS at the CDN endpoint. By enabling HTTPS, you get a free certificate that automatically renews. There is a catch if you need HTTPS for a naked/root domain; you need to bring your certificate. Store your certificate in a Key Vault and give the CDN permissions to read this certificate. Then it is possible to enable HTTPS for a naked/root domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  URL rewire rules
&lt;/h2&gt;

&lt;p&gt;I don’t like the “www” prefix before my domain, but people still type this. To make it easy for people, I want to redirect traffic to the right URL.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trafic to “www” needs to go to my naked/root domain.&lt;/li&gt;
&lt;li&gt;Trafic that is not using HTTPS (encryption) needs to go to HTTPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Azure CDN Rules engine is more than capable of rewriting the URL, and I used HTTP 308 (permanent redirect) to redirect the traffic to the right place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DSNmSrVz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/CDN_rules_engine.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DSNmSrVz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clemens.ms/hosting-a-static-site-on-azure-using-cdn-and-https/CDN_rules_engine.png" alt="CDN Rules engine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Static Web Apps
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Azure &lt;a href="https://azure.microsoft.com/en-us/services/app-service/static/"&gt;Static Web Apps&lt;/a&gt; (currently in preview) gives you similar capabilities combined with a GitHub native workflow and serverless API’s.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>azure</category>
      <category>website</category>
      <category>cdn</category>
      <category>storageaccount</category>
    </item>
  </channel>
</rss>
