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
Submission: On May 24 via manual from IT — Scanned from IT
Form analysis
7 forms found in the DOMGET 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&d=identicon&forcedefault=y&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&d=identicon&forcedefault=y&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"> ( <a href="javascript:HighlanderComments.doExternalLogout( 'wordpress' );">Log Out</a> /
<a href="#" onclick="javascript:HighlanderComments.switchAccount();return false;">Change</a> ) </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&d=identicon&forcedefault=y&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"> ( <a href="javascript:HighlanderComments.doExternalLogout( 'twitter' );">Log Out</a> /
<a href="#" onclick="javascript:HighlanderComments.switchAccount();return false;">Change</a> ) </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"> ( <a href="javascript:HighlanderComments.doExternalLogout( 'facebook' );">Log Out</a> /
<a href="#" onclick="javascript:HighlanderComments.switchAccount();return false;">Change</a> ) </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.