thomasthornton.cloud Open in urlscan Pro
192.0.78.24  Public Scan

URL: https://thomasthornton.cloud/2022/01/03/integrating-azure-application-gateway-with-multiple-custom-domains-with-azure-apim/
Submission: On May 24 via manual from IT — Scanned from IT

Form analysis 7 forms found in the DOM

GET https://thomasthornton.cloud/

<form id="header-searchform" class="header-searchform has-border-radius th-bg-white" method="get" action="https://thomasthornton.cloud/" role="search">
  <label for="header-search-input" class="th-w-full th-mb-0">
    <span class="screen-reader-text">Search for:</span>
    <input type="text" id="header-search-input" class="field th-text-secondary-base th-w-full" placeholder="Search..." name="s" value="">
  </label>
  <button type="submit" class="submit has-border-radius clean-button th-flex th-ml-2xs">
    <svg class="svg-icon th-fill-current" width="24" height="24" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
      stroke-linejoin="round">
      <circle cx="11" cy="11" r="8"></circle>
      <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
    </svg> <span class="screen-reader-text">Search</span>
  </button>
</form>

POST https://thomasthornton.cloud/wp-comments-post.php

<form action="https://thomasthornton.cloud/wp-comments-post.php" method="post" id="commentform" class="comment-form" novalidate=""><input type="hidden" id="highlander_comment_nonce" name="highlander_comment_nonce" value="79144757a8"><input
    type="hidden" name="_wp_http_referer" value="/2022/01/03/integrating-azure-application-gateway-with-multiple-custom-domains-with-azure-apim/">
  <input type="hidden" name="hc_post_as" id="hc_post_as" value="guest">
  <div class="comment-form-field comment-textarea">
    <div id="comment-form-comment"><textarea id="comment" name="comment" title="Enter your comment here..." placeholder="Enter your comment here..." style="overflow: hidden; overflow-wrap: break-word; resize: none; height: 38px;"></textarea></div>
  </div>
  <div id="comment-form-identity" style="display: none;">
    <div id="comment-form-nascar">
      <p>Fill in your details below or click an icon to log in:</p>
      <ul>
        <li class="selected" style="display:none;">
          <a href="#comment-form-guest" id="postas-guest" class="nascar-signin-link" title="Login via Guest">
									</a>
        </li>
        <li>
          <a href="#comment-form-load-service:WordPress.com" id="postas-wordpress" class="nascar-signin-link" title="Login via WordPress.com">
					<svg xmlns="http://www.w3.org/2000/svg" role="presentation" viewBox="0 0 24 24"><rect x="0" fill="none" width="24" height="24"></rect><g><path fill="#0087be" d="M12.158 12.786l-2.698 7.84c.806.236 1.657.365 2.54.365 1.047 0 2.05-.18 2.986-.51-.024-.037-.046-.078-.065-.123l-2.762-7.57zM3.008 12c0 3.56 2.07 6.634 5.068 8.092L3.788 8.342c-.5 1.117-.78 2.354-.78 3.658zm15.06-.454c0-1.112-.398-1.88-.74-2.48-.456-.74-.883-1.368-.883-2.11 0-.825.627-1.595 1.51-1.595.04 0 .078.006.116.008-1.598-1.464-3.73-2.36-6.07-2.36-3.14 0-5.904 1.613-7.512 4.053.21.008.41.012.58.012.94 0 2.395-.114 2.395-.114.484-.028.54.684.057.74 0 0-.487.058-1.03.086l3.275 9.74 1.968-5.902-1.4-3.838c-.485-.028-.944-.085-.944-.085-.486-.03-.43-.77.056-.742 0 0 1.484.114 2.368.114.94 0 2.397-.114 2.397-.114.486-.028.543.684.058.74 0 0-.488.058-1.03.086l3.25 9.665.897-2.997c.456-1.17.684-2.137.684-2.907zm1.82-3.86c.04.286.06.593.06.924 0 .912-.17 1.938-.683 3.22l-2.746 7.94c2.672-1.558 4.47-4.454 4.47-7.77 0-1.564-.4-3.033-1.1-4.314zM12 22C6.486 22 2 17.514 2 12S6.486 2 12 2s10 4.486 10 10-4.486 10-10 10z"></path></g></svg>				</a>
        </li>
        <li>
          <a href="#comment-form-load-service:Twitter" id="postas-twitter" class="nascar-signin-link" title="Login via Twitter">
					<svg xmlns="http://www.w3.org/2000/svg" role="presentation" viewBox="0 0 24 24"><rect x="0" fill="none" width="24" height="24"></rect><g><path fill="#1DA1F2" d="M22.23 5.924c-.736.326-1.527.547-2.357.646.847-.508 1.498-1.312 1.804-2.27-.793.47-1.67.812-2.606.996C18.325 4.498 17.258 4 16.078 4c-2.266 0-4.103 1.837-4.103 4.103 0 .322.036.635.106.935-3.41-.17-6.433-1.804-8.457-4.287-.353.607-.556 1.312-.556 2.064 0 1.424.724 2.68 1.825 3.415-.673-.022-1.305-.207-1.86-.514v.052c0 1.988 1.415 3.647 3.293 4.023-.344.095-.707.145-1.08.145-.265 0-.522-.026-.773-.074.522 1.63 2.038 2.817 3.833 2.85-1.404 1.1-3.174 1.757-5.096 1.757-.332 0-.66-.02-.98-.057 1.816 1.164 3.973 1.843 6.29 1.843 7.547 0 11.675-6.252 11.675-11.675 0-.178-.004-.355-.012-.53.802-.578 1.497-1.3 2.047-2.124z"></path></g></svg>				</a>
        </li>
        <li>
          <a href="#comment-form-load-service:Facebook" id="postas-facebook" class="nascar-signin-link" title="Login via Facebook">
					<svg xmlns="http://www.w3.org/2000/svg" role="presentation" viewBox="0 0 24 24"><rect x="0" fill="none" width="24" height="24"></rect><g><path fill="#3B5998" d="M20.007 3H3.993C3.445 3 3 3.445 3 3.993v16.013c0 .55.445.994.993.994h8.62v-6.97H10.27V11.31h2.346V9.31c0-2.325 1.42-3.59 3.494-3.59.993 0 1.847.073 2.096.106v2.43h-1.438c-1.128 0-1.346.537-1.346 1.324v1.734h2.69l-.35 2.717h-2.34V21h4.587c.548 0 .993-.445.993-.993V3.993c0-.548-.445-.993-.993-.993z"></path></g></svg>				</a>
        </li>
      </ul>
    </div>
    <div id="comment-form-guest" class="comment-form-service selected">
      <div class="comment-form-padder">
        <div class="comment-form-avatar">
          <a href="https://gravatar.com/site/signup/" target="_blank">				<img src="https://1.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?s=25&amp;d=identicon&amp;forcedefault=y&amp;r=G" alt="Gravatar" width="25" class="no-grav grav-hashed grav-hijack" id="grav-ad516503a11cd5ca435acc9bb6523536-0">
</a>
        </div>
        <div class="comment-form-fields">
          <div class="comment-form-field comment-form-email">
            <label for="email">Email <span class="required">(required)</span> <span class="nopublish">(Address never made public)</span></label>
            <div class="comment-form-input"><input id="email" name="email" type="email" value=""></div>
          </div>
          <div class="comment-form-field comment-form-author">
            <label for="author">Name <span class="required">(required)</span></label>
            <div class="comment-form-input"><input id="author" name="author" type="text" value=""></div>
          </div>
          <div class="comment-form-field comment-form-url">
            <label for="url">Website</label>
            <div class="comment-form-input"><input id="url" name="url" type="url" value=""></div>
          </div>
        </div>
      </div>
    </div>
    <div id="comment-form-wordpress" class="comment-form-service">
      <div class="comment-form-padder">
        <div class="comment-form-avatar">
          <img src="https://1.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?s=25&amp;d=identicon&amp;forcedefault=y&amp;r=G" alt="WordPress.com Logo" width="25" class="no-grav grav-hashed grav-hijack"
            id="grav-ad516503a11cd5ca435acc9bb6523536-1">
        </div>
        <div class="comment-form-fields">
          <input type="hidden" name="wp_avatar" id="wordpress-avatar" class="comment-meta-wordpress" value="">
          <input type="hidden" name="wp_user_id" id="wordpress-user_id" class="comment-meta-wordpress" value="">
          <input type="hidden" name="wp_access_token" id="wordpress-access_token" class="comment-meta-wordpress" value="">
          <p class="comment-form-posting-as pa-wordpress">
            <strong></strong> You are commenting using your WordPress.com account. <span class="comment-form-log-out"> (&nbsp;<a href="javascript:HighlanderComments.doExternalLogout( 'wordpress' );">Log&nbsp;Out</a>&nbsp;/&nbsp;
              <a href="#" onclick="javascript:HighlanderComments.switchAccount();return false;">Change</a>&nbsp;) </span>
            <span class="pa-icon"><svg xmlns="http://www.w3.org/2000/svg" role="presentation" viewBox="0 0 24 24">
                <rect x="0" fill="none" width="24" height="24"></rect>
                <g>
                  <path fill="#0087be"
                    d="M12.158 12.786l-2.698 7.84c.806.236 1.657.365 2.54.365 1.047 0 2.05-.18 2.986-.51-.024-.037-.046-.078-.065-.123l-2.762-7.57zM3.008 12c0 3.56 2.07 6.634 5.068 8.092L3.788 8.342c-.5 1.117-.78 2.354-.78 3.658zm15.06-.454c0-1.112-.398-1.88-.74-2.48-.456-.74-.883-1.368-.883-2.11 0-.825.627-1.595 1.51-1.595.04 0 .078.006.116.008-1.598-1.464-3.73-2.36-6.07-2.36-3.14 0-5.904 1.613-7.512 4.053.21.008.41.012.58.012.94 0 2.395-.114 2.395-.114.484-.028.54.684.057.74 0 0-.487.058-1.03.086l3.275 9.74 1.968-5.902-1.4-3.838c-.485-.028-.944-.085-.944-.085-.486-.03-.43-.77.056-.742 0 0 1.484.114 2.368.114.94 0 2.397-.114 2.397-.114.486-.028.543.684.058.74 0 0-.488.058-1.03.086l3.25 9.665.897-2.997c.456-1.17.684-2.137.684-2.907zm1.82-3.86c.04.286.06.593.06.924 0 .912-.17 1.938-.683 3.22l-2.746 7.94c2.672-1.558 4.47-4.454 4.47-7.77 0-1.564-.4-3.033-1.1-4.314zM12 22C6.486 22 2 17.514 2 12S6.486 2 12 2s10 4.486 10 10-4.486 10-10 10z">
                  </path>
                </g>
              </svg></span>
          </p>
        </div>
      </div>
    </div>
    <div id="comment-form-twitter" class="comment-form-service">
      <div class="comment-form-padder">
        <div class="comment-form-avatar">
          <img src="https://1.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?s=25&amp;d=identicon&amp;forcedefault=y&amp;r=G" alt="Twitter picture" width="25" class="no-grav grav-hashed grav-hijack" id="grav-ad516503a11cd5ca435acc9bb6523536-2">
        </div>
        <div class="comment-form-fields">
          <input type="hidden" name="twitter_avatar" id="twitter-avatar" class="comment-meta-twitter" value="">
          <input type="hidden" name="twitter_user_id" id="twitter-user_id" class="comment-meta-twitter" value="">
          <input type="hidden" name="twitter_access_token" id="twitter-access_token" class="comment-meta-twitter" value="">
          <p class="comment-form-posting-as pa-twitter">
            <strong></strong> You are commenting using your Twitter account. <span class="comment-form-log-out"> (&nbsp;<a href="javascript:HighlanderComments.doExternalLogout( 'twitter' );">Log&nbsp;Out</a>&nbsp;/&nbsp;
              <a href="#" onclick="javascript:HighlanderComments.switchAccount();return false;">Change</a>&nbsp;) </span>
            <span class="pa-icon"><svg xmlns="http://www.w3.org/2000/svg" role="presentation" viewBox="0 0 24 24">
                <rect x="0" fill="none" width="24" height="24"></rect>
                <g>
                  <path fill="#1DA1F2"
                    d="M22.23 5.924c-.736.326-1.527.547-2.357.646.847-.508 1.498-1.312 1.804-2.27-.793.47-1.67.812-2.606.996C18.325 4.498 17.258 4 16.078 4c-2.266 0-4.103 1.837-4.103 4.103 0 .322.036.635.106.935-3.41-.17-6.433-1.804-8.457-4.287-.353.607-.556 1.312-.556 2.064 0 1.424.724 2.68 1.825 3.415-.673-.022-1.305-.207-1.86-.514v.052c0 1.988 1.415 3.647 3.293 4.023-.344.095-.707.145-1.08.145-.265 0-.522-.026-.773-.074.522 1.63 2.038 2.817 3.833 2.85-1.404 1.1-3.174 1.757-5.096 1.757-.332 0-.66-.02-.98-.057 1.816 1.164 3.973 1.843 6.29 1.843 7.547 0 11.675-6.252 11.675-11.675 0-.178-.004-.355-.012-.53.802-.578 1.497-1.3 2.047-2.124z">
                  </path>
                </g>
              </svg></span>
          </p>
        </div>
      </div>
    </div>
    <div id="comment-form-facebook" class="comment-form-service">
      <div class="comment-form-padder">
        <div class="comment-form-avatar">
          <img src="" alt="Facebook photo" width="25" class="no-grav">
        </div>
        <div class="comment-form-fields">
          <input type="hidden" name="fb_avatar" id="facebook-avatar" class="comment-meta-facebook" value="">
          <input type="hidden" name="fb_user_id" id="facebook-user_id" class="comment-meta-facebook" value="">
          <input type="hidden" name="fb_access_token" id="facebook-access_token" class="comment-meta-facebook" value="">
          <p class="comment-form-posting-as pa-facebook">
            <strong></strong> You are commenting using your Facebook account. <span class="comment-form-log-out"> (&nbsp;<a href="javascript:HighlanderComments.doExternalLogout( 'facebook' );">Log&nbsp;Out</a>&nbsp;/&nbsp;
              <a href="#" onclick="javascript:HighlanderComments.switchAccount();return false;">Change</a>&nbsp;) </span>
            <span class="pa-icon"><svg xmlns="http://www.w3.org/2000/svg" role="presentation" viewBox="0 0 24 24">
                <rect x="0" fill="none" width="24" height="24"></rect>
                <g>
                  <path fill="#3B5998"
                    d="M20.007 3H3.993C3.445 3 3 3.445 3 3.993v16.013c0 .55.445.994.993.994h8.62v-6.97H10.27V11.31h2.346V9.31c0-2.325 1.42-3.59 3.494-3.59.993 0 1.847.073 2.096.106v2.43h-1.438c-1.128 0-1.346.537-1.346 1.324v1.734h2.69l-.35 2.717h-2.34V21h4.587c.548 0 .993-.445.993-.993V3.993c0-.548-.445-.993-.993-.993z">
                  </path>
                </g>
              </svg></span>
          </p>
        </div>
      </div>
    </div>
    <div id="comment-form-load-service" class="comment-form-service">
      <div class="comment-form-posting-as-cancel"><a href="javascript:HighlanderComments.cancelExternalWindow();">Cancel</a></div>
      <p>Connecting to %s</p>
    </div>
  </div>
  <script type="text/javascript">
    var highlander_expando_javascript = function() {
      function hide(sel) {
        var el = document.querySelector(sel);
        if (el) {
          el.style.setProperty('display', 'none');
        }
      }

      function show(sel) {
        var el = document.querySelector(sel);
        if (el) {
          el.style.removeProperty('display');
        }
      }
      var input = document.createElement('input');
      var comment = document.querySelector('#comment');
      if (input && comment && 'placeholder' in input) {
        var label = document.querySelector('.comment-textarea label');
        if (label) {
          var text = label.textContent;
          label.parentNode.removeChild(label);
          comment.setAttribute('placeholder', text);
        }
      }
      // Expando Mode: start small, then auto-resize on first click + text length
      hide('#comment-form-identity');
      hide('#comment-form-subscribe');
      hide('#commentform .form-submit');
      if (comment) {
        comment.style.height = '10px';
        var handler = function() {
          comment.style.height = HighlanderComments.initialHeight + 'px';
          show('#comment-form-identity');
          show('#comment-form-subscribe');
          show('#commentform .form-submit');
          HighlanderComments.resizeCallback();
          comment.removeEventListener('focus', handler);
        };
        comment.addEventListener('focus', handler);
      }
    }
    if (document.readyState !== 'loading') {
      highlander_expando_javascript();
    } else {
      document.addEventListener('DOMContentLoaded', highlander_expando_javascript);
    }
  </script>
  <div id="comment-form-subscribe" style="display: none;">
    <p class="comment-subscription-form"><input type="checkbox" name="subscribe" id="subscribe" value="subscribe" style="width: auto;"> <label class="subscribe-label" id="subscribe-label" for="subscribe" style="display: inline;">Notify me of new
        comments via email.</label></p>
    <p class="post-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto;"> <label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog"
        style="display: inline;">Notify me of new posts via email.</label></p>
  </div>
  <p class="form-submit" style="display: none;"><input name="submit" type="submit" id="comment-submit" class="submit" value="Post Comment"> <input type="hidden" name="comment_post_ID" value="3616" id="comment_post_ID">
    <input type="hidden" name="comment_parent" id="comment_parent" value="0">
  </p>
  <p style="display: none;"><input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="b45d51e04c"></p>
  <input type="hidden" name="genseq" value="1653368371">
  <p style="display: none !important;"><label>Δ<textarea name="ak_hp_textarea" cols="45" rows="8" maxlength="100"></textarea></label><input type="hidden" id="ak_js_1" name="ak_js" value="1653368371851">
    <script>
      document.getElementById("ak_js_1").setAttribute("value", (new Date()).getTime());
    </script>
  </p>
</form>

POST https://subscribe.wordpress.com

<form action="https://subscribe.wordpress.com" method="post" accept-charset="utf-8" id="subscribe-blog">
  <p>Enter your email address to follow this blog and receive notifications of new posts by email.</p>
  <div class="jetpack-subscribe-count">
    <p> Join 263 other followers </p>
  </div>
  <p id="subscribe-email">
    <label id="subscribe-field-label" for="subscribe-field" class="screen-reader-text"> Email Address: </label>
    <input type="email" name="email" style="width: 95%; padding: 1px 10px" placeholder="Enter your email address" value="" id="subscribe-field">
  </p>
  <p id="subscribe-submit">
    <input type="hidden" name="action" value="subscribe">
    <input type="hidden" name="blog_id" value="150990645">
    <input type="hidden" name="source" value="https://thomasthornton.cloud/2022/01/03/integrating-azure-application-gateway-with-multiple-custom-domains-with-azure-apim/">
    <input type="hidden" name="sub-type" value="widget">
    <input type="hidden" name="redirect_fragment" value="subscribe-blog">
    <input type="hidden" id="_wpnonce" name="_wpnonce" value="6a62dda56d"> <button type="submit" class="wp-block-button__link"> Follow </button>
  </p>
</form>

GET https://thomasthornton.cloud/

<form role="search" method="get" class="search-form" action="https://thomasthornton.cloud/">
  <label>
    <span class="screen-reader-text">Search for:</span>
    <input type="search" class="search-field" placeholder="Search …" value="" name="s">
  </label>
  <input type="submit" class="search-submit" value="Search">
</form>

POST https://subscribe.wordpress.com

<form method="post" action="https://subscribe.wordpress.com" accept-charset="utf-8" style="display: none;">
  <div class="actnbr-follow-count">Join 263 other followers</div>
  <div>
    <input type="email" name="email" placeholder="Enter your email address" class="actnbr-email-field" aria-label="Enter your email address">
  </div>
  <input type="hidden" name="action" value="subscribe">
  <input type="hidden" name="blog_id" value="150990645">
  <input type="hidden" name="source" value="https://thomasthornton.cloud/2022/01/03/integrating-azure-application-gateway-with-multiple-custom-domains-with-azure-apim/">
  <input type="hidden" name="sub-type" value="actionbar-follow">
  <input type="hidden" id="_wpnonce" name="_wpnonce" value="6a62dda56d">
  <div class="actnbr-button-wrap">
    <button type="submit" value="Sign me up"> Sign me up </button>
  </div>
</form>

<form id="jp-carousel-comment-form">
  <label for="jp-carousel-comment-form-comment-field" class="screen-reader-text">Write a Comment...</label>
  <textarea name="comment" class="jp-carousel-comment-form-field jp-carousel-comment-form-textarea" id="jp-carousel-comment-form-comment-field" placeholder="Write a Comment..."></textarea>
  <div id="jp-carousel-comment-form-submit-and-info-wrapper">
    <div id="jp-carousel-comment-form-commenting-as">
      <fieldset>
        <label for="jp-carousel-comment-form-email-field">Email (Required)</label>
        <input type="text" name="email" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-email-field">
      </fieldset>
      <fieldset>
        <label for="jp-carousel-comment-form-author-field">Name (Required)</label>
        <input type="text" name="author" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-author-field">
      </fieldset>
      <fieldset>
        <label for="jp-carousel-comment-form-url-field">Website</label>
        <input type="text" name="url" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-url-field">
      </fieldset>
    </div>
    <input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="Post Comment">
  </div>
</form>

POST /2022/01/03/integrating-azure-application-gateway-with-multiple-custom-domains-with-azure-apim/

<form action="/2022/01/03/integrating-azure-application-gateway-with-multiple-custom-domains-with-azure-apim/" method="post">
  <label for="target_email">Send to Email Address</label>
  <input type="email" name="target_email" id="target_email" value="">
  <label for="source_name">Your Name</label>
  <input type="text" name="source_name" id="source_name" value="">
  <label for="source_email">Your Email Address</label>
  <input type="email" name="source_email" id="source_email" value="">
  <input type="text" id="jetpack-source_f_name" name="source_f_name" class="input" value="" size="25" autocomplete="off" title="This field is for validation and should not be changed">
  <div class="g-recaptcha" data-sitekey="6LcmyE0UAAAAALID28yVNg7pFCodGaArJzHitez_" data-theme="light" data-type="image" data-tabindex="0" data-lazy="true" data-url="https://www.google.com/recaptcha/api.js?hl=en-gb"></div>
  <img style="float: right; display: none" class="loading" src="
			https://s0.wp.com/wp-content/mu-plugins/post-flair/sharing/images/loading.gif			" alt="loading" width="16" height="16">
  <input type="submit" value="Send Email" class="sharing_send">
  <a rel="nofollow" href="#cancel" class="sharing_cancel" role="button">Cancel</a>
  <div class="errors errors-1" style="display: none;"> Post was not sent - check your email addresses! </div>
  <div class="errors errors-2" style="display: none;"> Email check failed, please try again </div>
  <div class="errors errors-3" style="display: none;"> Sorry, your blog cannot share posts by email. </div>
</form>

Text Content

Skip to content

Thomas Thornton

Microsoft Azure Blog – Microsoft MVP

 * Home
 * Curriculum Vitae (CV)
 * Blog Features
 * Contact
 * Speaking and Events

Menu
Open a search box Close a search box
Search for: Search
 * Home
 * Curriculum Vitae (CV)
 * Blog Features
 * Contact
 * Speaking and Events


INTEGRATING AZURE APPLICATION GATEWAY WITH MULTIPLE CUSTOM DOMAINS WITH
AZURE APIM

on 3rd Jan 20223rd Jan 2022by Thomas Thornton4 Comments

Want to publish various APIs with multiple domain names while using the same
Azure APIM instance? In this blog I am going to show how you can achieve this
with Integrating an Azure Application Gateway with multiple custom domains with
a single Azure APIM instance


CONTEXT

You may have an APIM instance deployed with an internal setup – in theory, the
APIM instance is only accessible within your Virtual Network (vNET). You may
want to access some APIs publicly and even access them using multiple custom
domains.

Rather than deploying multiple APIM instances to cater for each domain, we can
achieve this using an Azure Application Gateway to publicly expose the APIM
instance on various domains or even subdomains. Other benefits of using an Azure
Application Gateway with APIM is websocket support & the benefits of Application
Gateway such as WAF protection.


HIGH-LEVEL ARCHITECTURE

In the below diagram, i’ve shown at high-level of what will be deployed within
this blog.

 * Virtual network with an Internal APIM instance, Virtual Machine (will be used
   to test internal APIM access)
 * Private DNS zone: thomasthornton.cloud
 * Application Gateway
 * Users that can access the APIM instance via three URLs:
   * api.thomasthornton.cloud
   * api.tamops.cloud
   * api.tt.cloud

As mentioned in the above, this setup and blog caters also for sub-domains, in
the below diagram

 * Users can access the APIM instance via sub-domains of *.thomasthornton.cloud


DEPLOYMENT & SETUP

In this deployment, I will deploy the initial setup with Azure CLI & additional
will be done via portal to show the various setup options required.

The below will create:

 * Resource Group
 * Virtual Network
 * Virtual Network with the 3 relevant subnets as per the above diagrams
 * Network Security Group & associated rules for apim & Virtual Machine access
 * Assigns Network Security Group to both apim & vm subnets
 * Virtual Machine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# Variables
 
#Resource Group
LOCATION="UKSOUTH"
RG_NAME="tamops-appgw-apim"
 
#Virtual Network
VNET_NAME="tamops-apim-vnet"
VNET_ADDRESS_PREFIX="10.0.0.0/16"
SUBNET_APPGW_NAME="appgw-subnet"
SUBNET_APPGW_PREFIX="10.0.0.0/24"
SUBNET_APIM_NAME="apim-subnet"
SUBNET_APIM_PREFIX="10.0.1.0/24"
SUBNET_VM_NAME="vm-subnet"
SUBNET_VM_PREFIX="10.0.2.0/24"
 
#Network Security Group
NSG_NAME="tamops-apim-nsg"
LOCATION_IP="1.2.3.4"
 
#Virtual Machine
VM_NAME="tamops-apim-vm"
VM_PIP_NAME="tamops-apim-vm-pip"
 
 
# Create Resource Group
az group create -l $LOCATION -n $RG_NAME
 
# Create Virtual Network
az network vnet create \
  --name $VNET_NAME \
  --resource-group $RG_NAME \
  --location $LOCATION \
  --address-prefix $VNET_ADDRESS_PREFIX \
  --subnet-name $SUBNET_APPGW_NAME \
  --subnet-prefix $SUBNET_APPGW_PREFIX
 
# Create Additional Subnets
az network vnet subnet create \
  --address-prefix $SUBNET_APIM_PREFIX \
  --name $SUBNET_APIM_NAME \
  --resource-group $RG_NAME \
  --vnet-name $VNET_NAME
 
az network vnet subnet create \
  --address-prefix $SUBNET_VM_PREFIX \
  --name $SUBNET_VM_NAME \
  --resource-group $RG_NAME \
  --vnet-name $VNET_NAME
 
# Create NSG
az network nsg create --resource-group $RG_NAME --name $NSG_NAME --location
$LOCATION
 
# Create NSG rules
az network nsg rule create \
  --resource-group $RG_NAME \
  --nsg-name $NSG_NAME \
  --name RDP \
  --access Allow \
  --protocol Tcp \
  --direction Inbound \
  --priority 100 \
  --source-address-prefixes $LOCATION_IP \
  --source-port-range "*" \
  --destination-address-prefix "*" \
  --destination-port-range 3389
 
az network nsg rule create \
  --resource-group $RG_NAME \
  --nsg-name $NSG_NAME \
  --name apimanagement \
  --access Allow \
  --protocol Tcp \
  --direction Inbound \
  --priority 101 \
  --source-address-prefix ApiManagement \
  --source-port-range "*" \
  --destination-address-prefix "*" \
  --destination-port-range 3443
 
# Assign NSGs to subnets
az network vnet subnet update \
  --vnet-name $VNET_NAME \
  --name $SUBNET_APIM_NAME \
  --resource-group $RG_NAME \
  --network-security-group $NSG_NAME
 
az network vnet subnet update \
  --vnet-name $VNET_NAME \
  --name $SUBNET_VM_NAME \
  --resource-group $RG_NAME \
  --network-security-group $NSG_NAME
 
# Create VM
az network public-ip create \
  --resource-group $RG_NAME \
  --name $VM_PIP_NAME
 
az network nic create \
  --resource-group $RG_NAME \
  --name $VM_NAME \
  --vnet-name $VNET_NAME \
  --subnet $SUBNET_VM_NAME \
  --network-security-group $NSG_NAME \
  --public-ip-address $VM_PIP_NAME
 
az vm create \
  --resource-group $RG_NAME \
  --name $VM_NAME \
  --nics $VM_NAME \
  --image Win2019Datacenter \
  --admin-username tamops

CREATE APIM

With the initial deployment now complete, lets create APIM instance via Azure
Portal

Create as below (only screenshots i’ve included are what I’ve added or changed –
the rest of configuration has been kept default)

Note:- Pricing tier needs to be either Developer or Premium (to allow for
internal vNET setup)

Allowing this to create, can take 30+ minutes

GENERATING SELF-SIGNED ROOT CA & SELF-SIGNED CERTIFICATE FOR
API.THOMASTHORNTON.CLOUD

Time to generate a self-signed root CA & self-signed certificate for
api.thomasthornton.cloud. I used this tutorial as part of this

Root CA creation

1
2
3
4
#Create Root CA
openssl ecparam -out rootthomasthorntoncloud.key -name prime256v1 -genkey
openssl req -new -sha256 -key rootthomasthorntoncloud.key -out
rootthomasthorntoncloud.csr
openssl x509 -req -sha256 -days 365 -in rootthomasthorntoncloud.csr -signkey
rootthomasthorntoncloud.key -out rootthomasthorntoncloud.crt

Successful output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\thomast\Downloads>openssl ecparam -out rootthomasthorntoncloud.key
-name prime256v1 -genkey
 
C:\Users\thomast\Downloads>openssl req -new -sha256 -key
rootthomasthorntoncloud.key -out rootthomasthorntoncloud.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:root.thomasthornton.cloud
Email Address []:
 
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Create self-signed certificate for api.thomasthornton.cloud

1
2
3
4
# API.thomasthornton.cloud
openssl ecparam -out apithomasthorntoncloud.key -name prime256v1 -genkey
openssl req -new -sha256 -key apithomasthorntoncloud.key -out
apithomasthorntoncloud.csr
openssl x509 -req -in apithomasthorntoncloud.csr -CA 
rootthomasthorntoncloud.crt -CAkey rootthomasthorntoncloud.key -CAcreateserial
-out apithomasthorntoncloud.crt -days 365 -sha256

Successful output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\thomast\Downloads>openssl ecparam -out apithomasthorntoncloud.key -name
prime256v1 -genkey
 
C:\Users\thomast\Downloads>openssl req -new -sha256 -key
apithomasthorntoncloud.key -out apithomasthorntoncloud.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:api.thomasthornton.cloud
Email Address []:
 
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Create .pfx with this .crt & .key (create password as part of this)

1
2
3
C:\Users\thomast\Downloads>openssl pkcs12 -export -in apithomasthorntoncloud.crt
-inkey apithomasthorntoncloud.key -out apithomasthorntoncloud.pfx
Enter Export Password:
Verifying - Enter Export Password:

Now update the APIM with new custom domain


CREATE PRIVATE DNS ZONE

As shown in my initial diagram, a private dns zone is required as part of this
implementation.

The below will:

 * Create DNS Zone: thomasthornton.cloud
 * Assign DNS zone
 * Add APIM private IP address to the DNS zone and a record:
   api.thomasthornton.cloud

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RG_NAME="tamops-appgw-apim"
DNS_ZONE_NAME="thomasthornton.cloud"
VNET_ID="/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/tamops-appgw-apim/providers/Microsoft.Network/virtualNetworks/tamops-apim-vnet"
API_DNS="api"
API_IP_ADDRESS="10.0.1.5"
 
# Private DNS zone
 
az network private-dns zone create -g $RG_NAME -n $DNS_ZONE_NAME
 
az network private-dns link vnet create -g $RG_NAME -n linkzone -z
$DNS_ZONE_NAME \
    -v $VNET_ID -e False
 
az network private-dns record-set a add-record -g $RG_NAME -z $DNS_ZONE_NAME \
    -n $API_DNS -a $API_IP_ADDRESS

CREATE MOCK API TO TEST APIM CONNECTIVITY

In APIM select APIs -> Add API & Http(manually define) – enter as below

For now, both URL schemes allowed (http & https)

Select the new API & remove subscription required inside settings option

Add a new mock policy using the below config -> select Frontend -> options ->
Open API editor(json) & paste in below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
{
    "openapi": "3.0.1",
    "info": {
        "title": "Health Check",
        "description": "",
        "version": "1.0"
    },
    "servers": [{
        "url": "http://api.thomasthornton.cloud/testapi"
    }, {
        "url": "https://api.thomasthornton.cloud/testapi"
    }],
    "paths": {
        "/liveness": {
            "get": {
                "summary": "health",
                "description": "health",
                "operationId": "health",
                "responses": {
                    "200": {
                        "description": "",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "Up"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/clone-61d2d/liveness": {
            "get": {
                "summary": "health (https)",
                "description": "health (https)",
                "operationId": "61d2d7378b2d0d754278af5b",
                "responses": {
                    "200": {
                        "description": "",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "tamops"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "apiKeyHeader": {
                "type": "apiKey",
                "name": "Ocp-Apim-Subscription-Key",
                "in": "header"
            },
            "apiKeyQuery": {
                "type": "apiKey",
                "name": "subscription-key",
                "in": "query"
            }
        }
    },
    "security": [{
        "apiKeyHeader": []
    }, {
        "apiKeyQuery": []
    }]
}

Log into Virtual Machine & confirm APIM mock API is accessible as below:


SETUP APPLICATION GATEWAY

As there has been a private dns zone configured, I will be assigning backend
pool with FQDN (api.thomasthornton.cloud) of the APIM that was registered
previously within the private DNS zone.

Now add a routing rule, with listener setup

Ensure you upload the certificate created previously that was uploaded to the
apim instance.

Once complete ,add a new http setting (for now it will be http only)

Now deploy! Once complete, we now need to update the Health probes with path

/status-0123456789abcdef

This path can be used to determine if the APIM is healthy to receive traffic or
not.

Once this has been completed, we’re ready to test connectivity from the Public
Internet to Application Gateway -> APIM. For this, I have just updated my
localhost file with the dns entry as below (take public IP from Application
Gateway)

1
20.90.157.188 api.thomasthornton.cloud

Test connectivity from your web browser

It works traffic flow has successfully been tested Application Gateway -> APIM

Though, the traffic flow is not full end to end ssl as from Application Gateway
-> APIM is http, I will show can end to end SSL can be achieved later.


ADD ADDITIONAL DOMAINS TO APPLICATION GATEWAY

As mentioned at the start of this blog post, you may want to publish various
APIs with multiple domain names while using the same Azure APIM instance? The
initial setup has been completed, lets add additional domains to the application
gateway; while still only accessing the same backend APIM instance.

To start, create additional self-signed certificates for the following domains:

 * api.tamops.cloud
 * api.tt.cloud

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# API.tamops.cloud
openssl ecparam -out apitamopscloud.key -name prime256v1 -genkey
openssl req -new -sha256 -key apitamopscloud.key -out apitamopscloud.csr
openssl x509 -req -in apitamopscloud.csr -CA  rootthomasthorntoncloud.crt -CAkey
rootthomasthorntoncloud.key -CAcreateserial -out apitamopscloud.crt -days 365
-sha256
 
# generate PFX
openssl pkcs12 -export -in apitamopscloud.crt -inkey apitamopscloud.key -out
apitamopscloud.pfx
 
 
# API.tt.cloud
openssl ecparam -out apittcloud.key -name prime256v1 -genkey
openssl req -new -sha256 -key apittcloud.key -out apittcloud.csr
openssl x509 -req -in apittcloud.csr -CA  rootthomasthorntoncloud.crt -CAkey
rootthomasthorntoncloud.key -CAcreateserial -out apittcloud.crt -days 365
-sha256
 
# generate PFX
openssl pkcs12 -export -in apittcloud.crt -inkey apittcloud.key -out
apittcloud.pfx

Once created, the only additional change required is to add additional HTTPs
listeners to the Application Gateway (for each, you will keep the same
settings/configurations as per the original Application Gateway setup)

Follow the same process for each, upload the certificate each time!

Apply new routing rules for each of the new Listeners, (below shows only for
api.tamops.cloud, perform same process for each additional domain)

Update localdns file with additional entries and test connectivity

1
2
3
20.90.157.188 api.thomasthornton.cloud
20.90.157.188 api.tamops.cloud
20.90.157.188 api.tt.cloud

Awesome! Now successfully accessed the same backend APIM instance with multiple
different public domains!


SETUP END-TO-END SSL

As mentioned, the current setup is not configured for end-to-end SSL, this is
the final step of the setup!

To enable full end-to-end SSL encryption, we need to

 * Configure an additional HTTPS setting & upload the root Cert to the
   Application Gateway
 * Create an https health probe on Application Gateway
 * Update each rule to use https setting rather than Http

Setup an additional HTTP setting (same as the HTTP setting setup but this time,
include the root certificate that was created previously)

Create an additional Health probe as below for https

Update each rule with the new http setting: https

Once this has been setup, you can test each URL – also can review Backend health
within Application Gateway settings


COMPLETE!

Awesome, a length blog post but the setup has now been completed! What was
covered:

 * Setup and configure APIM with custom DNS
 * Relevant network security configured with Network Security Group
 * Azure Private DNS setup and registration
 * Setup and configure Application Gateway to work with APIM
 * Configure multiple domains to be used to access to same backend APIM instance
 * End to end SSL termination setup

Some additional reading:

 * https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-integrate-internal-vnet-appgateway
 * https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-integrate-internal-vnet-appgateway

Repo containing code & diagrams


SHARE THIS:

 * Click to share on Twitter (Opens in new window)
 * Click to share on LinkedIn (Opens in new window)
 * Click to share on Facebook (Opens in new window)
 * Click to share on Reddit (Opens in new window)
 * Click to share on WhatsApp (Opens in new window)
 * Click to email this to a friend (Opens in new window)
 * 

Using Azure Container Apps and KEDA to create self-hosted scalable Azure DevOps
Agents deployed using Azure DevOps and Azure CLI17th Feb 2022In "Azure"

Deploy to Azure Container App from Azure Container Registry using a CI/CD Azure
DevOps Pipeline and Azure CLI11th Feb 2022In "Azure"

Deleted APIM Instance and get error ServiceAlreadyExistsInSoftDeletedState
when redeploying16th Feb 2022In "Azure"

Posted in: Azure Networking
Tagged: Application Gateway Azure Automation Microsoft Azure Networking


POST NAVIGATION

Previous Entry: A DevOps journey using Azure DevOps
Next Entry: Running builds and releases in Azure DevOps using an Elgato
Stream Deck



4 COMMENTS

 1. Pingback: Top Stories from the Microsoft DevOps Community - 2022.01.07 -
    Azure DevOps Blog
    
 2. Pingback: Top Stories from the Microsoft DevOps Community – 2022.01.07 - IT
    Skills You Need
    
 3. Bharathram says:
    3rd Mar 2022 at 12:13 am
    
    excellent article! thanks for sharing your knowledge!
    
    Reply
    1. Thomas Thornton says:
       3rd Mar 2022 at 3:59 am
       
       Thank you, glad you enjoyed it!
       
       Reply
       
    


LEAVE A REPLY CANCEL REPLY

Fill in your details below or click an icon to log in:

 * 
 * 
 * 
 * 

Email (required) (Address never made public)

Name (required)

Website


You are commenting using your WordPress.com account. ( Log Out /  Change )

You are commenting using your Twitter account. ( Log Out /  Change )

You are commenting using your Facebook account. ( Log Out /  Change )

Cancel

Connecting to %s

Notify me of new comments via email.

Notify me of new posts via email.





Δ


 * Email
 * Twitter
 * LinkedIn
 * GitHub






BLOG STATS

 * 447,393 Views




FOLLOW BLOG VIA EMAIL

Enter your email address to follow this blog and receive notifications of new
posts by email.

Join 263 other followers

Email Address:

Follow


SEARCH

Search for:


TOP POSTS

 * Conditional Variables in Azure DevOps Pipelines
 * Build and push Docker Images to Azure Container Registry using Azure DevOps
   Pipelines
 * Terraforming from zero to pipelines as code with Azure DevOps
 * Creating dynamic variables during a pipeline run in Azure DevOps
 * Deploy Terraform using GitHub Actions to Azure


FOLLOW ME ON TWITTER

Go to the top
Blog at WordPress.com.

 * Follow Following
    * Thomas Thornton
      Join 263 other followers
      
      Sign me up
    * Already have a WordPress.com account? Log in now.

 *  * Thomas Thornton
    * Customise
    * Follow Following
    * Sign up
    * Log in
    * Copy shortlink
    * Report this content
    * View post in Reader
    * Manage subscriptions
    * Collapse this bar

 

Loading Comments...

 

Write a Comment...
Email (Required) Name (Required) Website

Send to Email Address Your Name Your Email Address

Cancel
Post was not sent - check your email addresses!
Email check failed, please try again
Sorry, your blog cannot share posts by email.