<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Richard Spitz - Cloud Solution Architect at Microsoft</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/rspitz/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/rspitz/feed.xml</link>
    <description />
    <lastBuildDate>Thu, 24 Jan 2019 01:01:16 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Synchronously copy all managed disks of an Azure Virtual Machine to Azure Storage Accounts in multiple Azure regions</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/synchronously-copy-all-managed-disks-of-an-azure-virtual-machine-to-azure-storage-accounts-in-multiple-azure-regions</link>
      <pubDate>Thu, 24 Jan 2019 00:59:41 GMT</pubDate>
      <dc:creator><![CDATA[Richard Spitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/?p=605</guid>
      <description><![CDATA[There are many different ways to copy an Azure Virtual Machine's managed disks to different Azure...]]></description>
      <content:encoded><![CDATA[There are many different ways to copy an Azure Virtual Machine's managed disks to different Azure regions for the purpose of creating images local to that region. My experience with AzCopy --sync-copy using an Azure Virtual Machine spun up specifically for the purpose of doing the copy, has been the best so I wrapped the process in Azure Automation. With a 15-minute one time setup of the Automation assets (runbooks, variables), you're literally a command away from shooting your managed disks to any Azure region at an amazing speed of close to 1TB/hour.

Check out the project here: <a href="https://github.com/richardspitz/imagefactory">https://github.com/richardspitz/imagefactory</a>

Leave me a comment  either on here or on the repo and let me know what you think!

Richard Spitz
Cloud Solution Architect
Microsoft Silicon Valley]]></content:encoded>
    </item>
    <item>
      <title>How to study for the "70-535: Architecting Microsoft Azure Solutions" certification</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/how-to-study-for-the-70-535-architecting-microsoft-azure-solutions-certification</link>
      <pubDate>Wed, 23 May 2018 14:11:33 GMT</pubDate>
      <dc:creator><![CDATA[Richard Spitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/?p=555</guid>
      <description><![CDATA[Moving at a pace unthinkable a few years ago, Azure continues to grow leaps and bounds as the Cloud...]]></description>
      <content:encoded><![CDATA[Moving at a pace unthinkable a few years ago, Azure continues to grow leaps and bounds as the Cloud platform of choice for many. My own journey started four and a half years ago, transitioning from Windows Server Hyper-V to Azure IaaS. I had the good fortune of consulting on Azure for one of the largest IT companies in India when I made the switch and there's been no stopping ever since.

On to Microsoft certifications then. Even though I passed "70-533: Implementing Microsoft Azure Infrastructure Solutions" in 2015, my favorite exam till date has been "70-660: Windows Internals". Passing this exam marked the culmination of several years as a Microsoft Support Engineer for Windows Server and as a big fan of <a href="https://www.wired.com/2014/05/mark-russinovich/">Mark Russinovich</a> (I joined Microsoft the same year he did!) and his suite of Sysinternals tools, that are still saving many a sysadmin on a daily basis.

In much the same way, studying for and passing 70-535 gives me a sense of validation and satisfaction in the fact that several years ago when I first heard about the Cloud Solution Architect role at Microsoft, I knew that was the next role for me.

However, one of the key challenges to (officially) testing your knowledge of Azure is that same rapid transformation that makes it better each day.

How do you learn about a platform and the services it offers when what you learn today may be obsolete six months down the line?

Here are my recommendations for a 70-535 study plan that not just helps you pass the exam, but gives you a new appreciation for just how many services Microsoft Azure has to offer.

Microsoft certifications, like most professional IT certifications are meant to prove your skills on the platform in question and experience counts. If you've not worked on Azure before and would like to start preparing for the certification right away, my advice is to take a step back and either find Azure work inside your organization or set aside a few months to get some hands-on in your spare time.
<ol>
 	<li>If you don't already have an Azure subscription, check if your organization has <a href="https://www.visualstudio.com/subscriptions/">Visual Studio subscriptions</a> (formerly MSDN subscriptions) for employees, which give you access to USD150 of credit a month. If not, sign up for a <a href="https://azure.microsoft.com/en-us/free/">free trial</a> of Azure.</li>
 	<li>Book the exam as the first step, but not more than a month out.</li>
 	<li>Review the <a href="https://www.microsoft.com/en-us/learning/exam-70-535.aspx">70-535 Exam page</a> for the "Skills measured" section.</li>
 	<li>Check the page for the latest <a href="https://download.microsoft.com/download/6/A/5/6A5A0096-1E8A-425D-9C6A-CE28EE4708A5/535_OD_Changes.pdf">Exam 535 change document</a> (currently 22 March 2018) that is linked on the same page.</li>
 	<li>Download and print the change document. Use this to check services off the list, as you're done learning about them. In addition to being a checklist, it also calls out changes (in red) and new additions (blue) since the last update to the exam.</li>
 	<li>Buy the kindle version of <a href="https://www.amazon.com/Architecting-Microsoft-Azure-Solutions-complete-ebook/dp/B07BJKVM4G/ref=sr_1_2?ie=UTF8&amp;qid=1527079816&amp;sr=8-2">Sjoukje Zaal's exam guide</a> and use this as the starting point for each topic. This currently appears to be the only 70-535 guide out there at the moment.
(<a href="https://www.amazon.com/70-535-Architecting-Microsoft-Azure-Solutions/dp/1509304681/ref=sr_1_1?ie=UTF8&amp;qid=1527079816&amp;sr=8-1">Haishi Bai and co</a> have one coming out on 14 June 2018 as well which might be worth checking out. I have his Service Fabric book which is quite good)</li>
 	<li>Find an order you feel comfortable with and begin right away. I started with "Design Networking Implementation" since that's my primary area of expertise.</li>
 	<li>For each service that you read about in the book whether or not the book has a sample, navigate to the official Azure documentation and play around with the "Quickstart" samples. The official Azure documentation is without a doubt <em>the definitive source</em> for whichever service you're learning about. What's more, the documentation is now GitHub-based and therefore as nimble as can be.
I found that within days, I'd understood and knew how to configure services that I'd only previously heard about, whether it was setting up an IoT Hub Device Provisioning Service, deploying Service Fabric Linux container applications or building a data pipeline using Data Factory.
Here's how I recommend you navigate to these samples:
<ul>
 	<li>Open <a href="https://azure.microsoft.com">https://azure.microsoft.com </a></li>
</ul>
&nbsp;
<ul>
 	<li>Click "Products" and search for the service you'd like to learn about.
<a href="https://msdnshared.blob.core.windows.net/media/2018/05/Capture2.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/05/Capture2-1024x478.png" alt="" class="alignnone wp-image-565 size-large" width="1024" height="478" /></a></li>
</ul>
&nbsp;
<ul>
 	<li>Click "Documentation".
<a href="https://msdnshared.blob.core.windows.net/media/2018/05/Capture11.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/05/Capture11-1024x542.png" alt="" class="alignnone size-large wp-image-575" width="1024" height="542" /></a></li>
</ul>
&nbsp;
<ul>
 	<li>Pick a Quickstart!
<a href="https://msdnshared.blob.core.windows.net/media/2018/05/Capture21.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/05/Capture21-1024x509.png" alt="" class="alignnone size-large wp-image-585" width="1024" height="509" /></a></li>
</ul>
</li>
 	<li>Even though the documentation has the option to "Copy" commands (PowerShell, CLI), type these out manually to get a feel for them.</li>
 	<li>For Quickstarts with options to use the Azure Portal, PowerShell or CLI, be sure to use the portal first as the portal UI offers a more "rememberable" experience especially in terms of the options available. Once you've deployed a service using the portal, you could deploy once more using either PowerShell or the Azure CLI. Use a good mix of both tools.</li>
 	<li>Delete or stop services such as Virtual Machines to prevent them from using up all your free credit!</li>
 	<li>Don't shy away from deploying a Quickstart if the exam guide does not include one or the Azure documentation appears complex. Deploy as many services as possible in as many ways as possible and you'll know exactly what to answer come exam time.</li>
 	<li>The menu on the documentation page will almost always have a "Concepts" page. Be sure to peruse it to get a deeper understanding of the service.</li>
 	<li>For services that enable connectivity to on-premises resources such as Active Directory Domain Controllers (Azure AD Connect), On-Premises Data Gateway (Azure Analysis Services, Logic Apps), WCF or newer relay based applications (Service Bus Relay), deploy those resources inside Azure Virtual Machines. Using this approach, you'll be able to get hands-on with pretty much any Azure service other than ExpressRoute.</li>
 	<li>Be sure to check out and familiarize yourself with ARM templates. An excellent source is: <a href="https://github.com/Azure/azure-quickstart-templates">https://github.com/Azure/azure-quickstart-templates </a></li>
 	<li>Finally, refer back to the exam guide for topics such as best practices and architectural know-how, which are covered in the Azure documentation as well but the guide brings various sources together on one page.</li>
</ol>
That's it for now. I will add to this list if I recollect other tips and tricks that helped me pass the 70-535 exam.

Best of luck!

Richard Spitz
Cloud Solution Architect
Microsoft]]></content:encoded>
    </item>
    <item>
      <title>PowerShell script to determine if an IP Range is part of the Azure Datacenter IP Range</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/powershell-script-to-determine-if-an-ip-range-is-part-of-the-azure-datacenter-ip-range</link>
      <pubDate>Fri, 02 Mar 2018 13:10:35 GMT</pubDate>
      <dc:creator><![CDATA[Richard Spitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/?p=495</guid>
      <description><![CDATA[Microsoft publishes an XML document that contains the IP address ranges (including Compute, SQL and...]]></description>
      <content:encoded><![CDATA[Microsoft publishes <a href="https://www.microsoft.com/en-in/download/details.aspx?id=41653">an XML document</a> that contains the IP address ranges (including Compute, SQL and Storage ranges) used in the Microsoft Azure Datacenters. An updated file is posted weekly which reflects the currently deployed ranges and any upcoming changes to the IP ranges.

Working with <a href="https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-service-endpoints-overview">Virtual Network Service Endpoints</a>, I found that enabling the Microsoft.SQL endpoint (Azure SQL Database) <a href="https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-service-endpoints-configure#service-endpoint-configuration-overview">added a route</a> to my Azure Virtual Network's route table with a set of public IP ranges, all of which presumably are attached to the Azure SQL Database infrastructure.

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/03/Capture.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/Capture-1024x560.png" alt="" class="alignnone size-large wp-image-505" width="1024" height="560" /></a>

&nbsp;

However, "presumably" doesn't quite cut it when it comes to configuring firewalls.

Clicking <em>Download</em> will fetch you a CSV file with the last row giving you the Azure SQL Database Infrastructure IP Ranges.

A nifty bit of manual text editing later, this is what I had:

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/03/Capture11.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/Capture11-1024x626.png" alt="" class="alignnone size-large wp-image-525" width="1024" height="626" /></a>

&nbsp;

Notice I've added in a couple of dummy IP addresses, 1.1.1.1/32 and 2.2.2.2/32, to exhibit how the output is presented if IP Ranges don't belong to the Azure Datacenter IP Ranges.

If you've worked with networks and IP addresses, you'll know that these tend to be some of the most misunderstood concepts in IT. I found this out for myself when many years ago, I needed to understand how Hyper-V Network Virtualization worked. The point being, determining which IP range fits into which other IP Range is something best left to PowerShell!

Why PowerShell? Because it has been a while since I wrote my last script and did not want to pass on the chance to brush up my skills.

The most important part of the script is, of course, the logic that checks whether one IP Range is a part of another.

Before you set out to write any script, a best practice is to check if one exists on the Internet and play around with it. It didn't take to long to come across what, in my opinion, is a perfectly written piece of code that I could just plug my script into.

Shout-out to Luben Kirov from GI Architects, whose code is available here: <a href="https://www.gi-architects.co.uk/2016/02/powershell-check-if-ip-or-subnet-matchesfits/">https://www.gi-architects.co.uk/2016/02/powershell-check-if-ip-or-subnet-matchesfits/ </a>

I simply dropped all his code into a PSM1 file - a PowerShell Module - and import it in my script to use the checkSubnet function.

That done, all I had to do was build the plumbing around that function, using the Azure Datacenter IP Range XML, the input file I created above and a whole lot of <em>For Loops</em>.

Finally, it pays to always output an object at the end of the script. Why, you may ask. Because if you don't, <a href="https://www.oreilly.com/pub/au/4037">Ed "The Microsoft Scripting Guy" Wilson</a> will hunt you down and make you do it. Also, because there are so many other advantages - creating reports, passing your script on a pipeline, never again using Write-Host.

The end result:

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/03/Capture2.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/Capture2-1024x356.png" alt="" class="alignnone size-large wp-image-535" width="1024" height="356" /></a>

&nbsp;

Here's the script, quick and dirty - no error checking, just a tiny bit of optimization.

As always, let me know in the comments if this helps you!

Richard Spitz
Cloud Solution Architect
Microsoft

&nbsp;
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># ******************************************************************************************************************************</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Script to check a set of IP Address Ranges and determine if they belong to the Azure DataCenter IP Address Ranges</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Author: Richard Spitz, Cloud Solution Architect, Microsoft</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen">#</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Usage: Check-AzurePublicIP.ps1 -AzurePublicIPFilePath &lt;Path to Azure Public IP file&gt; -InputFilePath &lt;Path to Input IP Range&gt;</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen">#</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Example: Check-AzurePublicIP.ps1 -AzurePublicIPFilePath ".\PublicIPs_20180226.xml" -InputFilePath ".\AzSQLSvcEndpoints.txt" </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen">#</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Prerequisites: See ***DO THIS*** comments below</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen">#</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># ******************************************************************************************************************************</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># ***DO THIS***</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Download the latest Azure Datacenter Public IPs list from https://www.microsoft.com/en-us/download/details.aspx?id=41653</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># InputFile needs to be a text file with the Public IP ranges you want to check, with each value on a new line</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkblue">param</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> (</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: darkgray">[</span><span style="color: teal">string</span><span style="color: darkgray">]</span><span style="color: orangered">$AzurePublicIPFilePath</span> <span style="color: darkgray">=</span>  <span style="color: darkred">".\PublicIPs_20180226.xml"</span><span style="color: darkgray">,</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: darkgray">[</span><span style="color: teal">string</span><span style="color: darkgray">]</span><span style="color: orangered">$InputFilePath</span> <span style="color: darkgray">=</span> <span style="color: darkred">".\AzSQLSvcEndpoints.txt"</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">       )</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># ***DO THIS***</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Save the entire code from https://www.gi-architects.co.uk/2016/02/powershell-check-if-ip-or-subnet-matchesfits/ </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># to CheckSubnet.PSM1 in the same directory as this script file</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: blue">Import-Module</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: blueviolet">.\CheckSubnet.psm1</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">[</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: teal">xml</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">]</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$AzurePublicIPs</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkgray">=</span> <span style="color: blue">Get-Content</span> <span style="color: navy">-Path</span> <span style="color: orangered">$AzurePublicIPFilePath</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$inputFile</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkgray">=</span> <span style="color: blue">Get-Content</span> <span style="color: navy">-Path</span> <span style="color: orangered">$InputFilePath</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">[</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: teal">int</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">]</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$rangeFound</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkgray">=</span> <span style="color: purple">0</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">[</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: teal">string</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">]</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$range</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkgray">=</span> <span style="color: darkred">""</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">[</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: teal">string</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgray">]</span><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$region</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkgray">=</span> <span style="color: darkred">""</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$outputObjectArr</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkgray">=</span> @()</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Loop through the Input file, which contains IP ranges to check</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkblue">For</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'">(<span style="color: orangered">$l</span> <span style="color: darkgray">=</span> <span style="color: purple">0</span>; <span style="color: orangered">$l</span> <span style="color: darkgray">-lt</span> <span style="color: orangered">$inputFile</span><span style="color: darkgray">.</span>Length; <span style="color: orangered">$l</span><span style="color: darkgray">++</span>)</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">{</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkgreen"># Indicate progress</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: blue">Write-Host</span> <span style="color: darkred">"."</span> <span style="color: navy">-NoNewline</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkgreen"># Reset RangeFound Counter for every new IP Range from the InputFile</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: orangered">$rangeFound</span> <span style="color: darkgray">=</span> <span style="color: purple">0</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkgreen"># Loop through each Region node within the XML object</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkblue">For</span>(<span style="color: orangered">$i</span> <span style="color: darkgray">=</span> <span style="color: purple">0</span>; <span style="color: orangered">$i</span> <span style="color: darkgray">-lt</span> <span style="color: orangered">$AzurePublicIPs</span><span style="color: darkgray">.</span>AzurePublicIpAddresses<span style="color: darkgray">.</span>Region<span style="color: darkgray">.</span>Length;<span style="color: orangered">$i</span><span style="color: darkgray">++</span>)</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    {</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: darkgreen"># Loop through each IP Range within the XML object, to test the Subnet within it</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: darkblue">For</span>(<span style="color: orangered">$j</span> <span style="color: darkgray">=</span> <span style="color: purple">0</span>; <span style="color: orangered">$j</span> <span style="color: darkgray">-lt</span> <span style="color: orangered">$AzurePublicIPs</span><span style="color: darkgray">.</span>AzurePublicIpAddresses<span style="color: darkgray">.</span>Region<span style="color: darkgray">[</span><span style="color: orangered">$i</span><span style="color: darkgray">].</span>IpRange<span style="color: darkgray">.</span>Length; <span style="color: orangered">$j</span><span style="color: darkgray">++</span>)</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        {</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkgreen"># Store current IP Range Subnet as a string</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkgray">[</span><span style="color: teal">string</span><span style="color: darkgray">]</span><span style="color: orangered">$currentPublicIP</span> <span style="color: darkgray">=</span> <span style="color: orangered">$AzurePublicIPs</span><span style="color: darkgray">.</span>AzurePublicIpAddresses<span style="color: darkgray">.</span>Region<span style="color: darkgray">[</span><span style="color: orangered">$i</span><span style="color: darkgray">].</span>IpRange<span style="color: darkgray">[</span><span style="color: orangered">$j</span><span style="color: darkgray">].</span>Subnet</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkgreen"># Quick and dirty first octet check optimization that cuts down script execution time</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkblue">If</span>((<span style="color: orangered">$currentPublicIP</span><span style="color: darkgray">.</span>Split(<span style="color: darkred">"."</span>))<span style="color: darkgray">[</span><span style="color: purple">0</span><span style="color: darkgray">]</span> <span style="color: darkgray">-like</span> (<span style="color: orangered">$inputFile</span><span style="color: darkgray">[</span><span style="color: orangered">$l</span><span style="color: darkgray">].</span>Split(<span style="color: darkred">"."</span>))<span style="color: darkgray">[</span><span style="color: purple">0</span><span style="color: darkgray">]</span>)</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            {</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkgreen"># Test the "Condition" property of the returned object from the checkSubnet function (which is </span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkgreen"># defined within the CheckSubnet.psm1 module we imported). If true, increment a counter to </span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            <span style="color: darkgreen"># indicate that our IP Range was found </span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                <span style="color: darkblue">If</span>((<span style="color: blue">checkSubnet</span> <span style="color: orangered">$currentPublicIP</span> <span style="color: orangered">$inputFile</span><span style="color: darkgray">[</span><span style="color: orangered">$l</span><span style="color: darkgray">]</span>)<span style="color: darkgray">.</span>Condition)</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                {</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                    <span style="color: orangered">$rangeFound</span><span style="color: darkgray">++</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                    <span style="color: darkgreen"># Store values to report later</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                    <span style="color: orangered">$range</span> <span style="color: darkgray">=</span> <span style="color: orangered">$currentPublicIP</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                    <span style="color: orangered">$region</span> <span style="color: darkgray">=</span> <span style="color: orangered">$AzurePublicIPs</span><span style="color: darkgray">.</span>AzurePublicIpAddresses<span style="color: darkgray">.</span>Region<span style="color: darkgray">[</span><span style="color: orangered">$i</span><span style="color: darkgray">].</span>Name</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">                }</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">            } </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        }</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    }</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkgreen"># Create a new object with details and add it to list of output objects, using the "Exists" NoteProperty to</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkgreen"># indicate whether the Input IP Range exists within the Azure IP Range or not</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkblue">If</span>(<span style="color: orangered">$rangeFound</span>)</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    {</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">=</span> <span style="color: blue">New-Object</span> <span style="color: navy">-TypeName</span> <span style="color: blueviolet">PSObject</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">InputIPRange</span> <span style="color: navy">-Value</span> <span style="color: orangered">$inputFile</span><span style="color: darkgray">[</span><span style="color: orangered">$l</span><span style="color: darkgray">]</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">AzureIPRange</span> <span style="color: navy">-Value</span> <span style="color: orangered">$range</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">Region</span> <span style="color: navy">-Value</span> <span style="color: orangered">$region</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">Exists</span> <span style="color: navy">-Value</span> <span style="color: darkred">"Yes"</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$outputObjectArr</span> <span style="color: darkgray">+=</span> <span style="color: orangered">$tempObj</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: blue">Remove-Variable</span> <span style="color: blueviolet">tempObj</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    }</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    <span style="color: darkblue">else</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    {</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">=</span> <span style="color: blue">New-Object</span> <span style="color: navy">-TypeName</span> <span style="color: blueviolet">PSObject</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">InputIPRange</span> <span style="color: navy">-Value</span> <span style="color: orangered">$inputFile</span><span style="color: darkgray">[</span><span style="color: orangered">$l</span><span style="color: darkgray">]</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">AzureIPRange</span> <span style="color: navy">-Value</span> <span style="color: darkred">"N.A."</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">Region</span> <span style="color: navy">-Value</span> <span style="color: darkred">"N.A."</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$tempObj</span> <span style="color: darkgray">|</span> <span style="color: blue">Add-Member</span> <span style="color: navy">-MemberType</span> <span style="color: blueviolet">NoteProperty</span> <span style="color: navy">-Name</span> <span style="color: blueviolet">Exists</span> <span style="color: navy">-Value</span> <span style="color: darkred">"No"</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: orangered">$outputObjectArr</span> <span style="color: darkgray">+=</span> <span style="color: orangered">$tempObj</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        <span style="color: blue">Remove-Variable</span> <span style="color: blueviolet">tempObj</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">        </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">    }</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'">}</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: darkgreen"># Dispay Output</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: blue">Write-Host</span><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> <span style="color: darkred">" "</span></span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console';color: orangered">$outputObjectArr</span></p>
<p style="margin-bottom: .0001pt;line-height: normal;background: white"><span style="font-size: 9.0pt;font-family: 'Lucida Console'"> </span></p>
&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Just enough BGP to get your Azure dual-redundancy, active-active VPN gateways up and running</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/just-enough-bgp-to-get-your-azure-dual-redundancy-active-active-vpn-gateways-up-and-running</link>
      <pubDate>Mon, 19 Feb 2018 15:33:02 GMT</pubDate>
      <dc:creator><![CDATA[Richard Spitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/?p=325</guid>
      <description><![CDATA[I'd last dabbled in Azure Site to Site (S2S) VPN or Virtual Network Gateways when the configuration...]]></description>
      <content:encoded><![CDATA[I'd last dabbled in Azure Site to Site (S2S) VPN or Virtual Network Gateways when the configuration was straightforward, but the feature-set was lacking. Given an opportunity to get back into configuring VPN Gateways recently, I dove straight in. With new features like support for BGP and full-mesh high-availability connectivity, the humble Azure Virtual Network Gateway was finally cool.

Before we begin, this blog post is a walk-through to configure the <a href="https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#dual-redundancy-active-active-vpn-gateways-for-both-azure-and-on-premises-networks">dual-redundancy: active-active VPN gateways for both Azure and on-premises networks</a> option to connect your Azure Virtual Network to your on-premises network using the Azure Virtual Network Gateway. No Azure ExpressRoute here.

For the purpose of this walk-through, my entire setup is in Azure however you could replace the second Azure Virtual Network Gateway (in VNET2) with your on-premises VPN device. I am not using VNET Peering specifically to illustrate how Azure to on-premises connectivity can be configured.

This post also assumes some familiarity with basic networking concepts such as IP addresses, address spaces, subnets and Azure services such as Azure Virtual Networks and Virtual Network Gateways. If you're not familiar with the Azure Virtual Network Gateway, you can get started with the official <a href="https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways">Azure documentation for this service</a>.

We will use the Azure Portal, as I see cross-premises connectivity configuration as a one-time or one-off configuration and potentially not worth the effort required to understand Azure PowerShell, if you're not already familiar with it. That said, I'm a big fan of the Azure CLI, Azure PowerShell and ARM templates for any task that has even the slightest possibility of being repeatable. You could also argue that maintaining a record of the configuration is much easier to do with these technologies than with screenshots of the Azure Portal (which is bound to change over time), in which case, the article you should peruse is <a href="https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-activeactive-rm-powershell">this one</a>.

Before beginning the configuration, I needed to understand what BGP - Border Gateway Protocol and ASN - Autonomous System Number are, and what I had to enter into my Azure Virtual Network Gateway configuration for the BGP and ASN fields.

Without giving you the "Network Guru" version (I'm as far from one as can be), BGP is the protocol that network routers (on the internet and also on private networks) use to "exchange" routes and router "availability" information. As far as router availability goes, there's nothing for us to configure here and so we move on to the routes that are exchanged. It also helps to take note of the fact that you're almost certainly configuring this on a private network - your "private" Azure Virtual Network to your "private" on-premises network. This internet/private difference matters, as we'll see later on, with ASN.

The aforementioned routes that need to be exchanged are nothing but the address spaces/networks/subnets behind your Azure Virtual Network Gateway. Before BGP, these were the static "Address Prefixes" you'd include in your "Local Network Gateway" to let the router at the other end know that to reach these networks, they'd need to route the traffic via you. With BGP, there is no need to statically specify these address spaces/networks/subnets. BGP automatically exchanges this information with your router's "peers". So what you need to specify instead of a static address space, is actually the "BGP Peer IP address" of the router being represented by the "Local Network Gateway" in CIDR format (example: 192.168.0.228/32). For your on-premises VPN device (router), your network admin should give you this. This address is a private IP address and differs from the public IP addresses of the on-premises VPN devices and Azure Virtual Network Gateway that are used to set up the IPsec tunnel.

Coming to ASN (or AS Number), my "just enough" explanation is that this is a unique number assigned to your on-premises VPN device (router) as well as Azure VPN Gateway by your on-premises network admin/team. Why should it be issued by them? Because just like an IP address space/network/subnet, this needs to be unique across your entire private/corporate network. Why does it need to be unique? When BGP on a router learns about a route that has its own ASN in its path then it will be discarded.

On then, to the key difference between an Internet ASN and a private ASN. The former can only be issued by <a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">IANA</a>, while the latter is issued by your network team (or for testing, you could use a private ASN except <a href="https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-bgp-overview#are-there-asns-reserved-by-azure">these</a>. I generally use 65516 for one Azure VPN Gateway and 65521 for the other one that mimics my on-premises VPN device).

With that lengthy but necessary primer out of the way, it's time to move on to the walk-through.

This is our proposed network architecture:

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/networkdiagram.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/networkdiagram-1024x768.png" alt="" class="alignnone size-large wp-image-385" width="1024" height="768" /></a>

&nbsp;

&nbsp;

We start by creating our Virtual Networks, VNET1 in Southeast Asia and VNET 2 in US East 2.

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture-1024x838.png" alt="" class="alignnone wp-image-335 size-large" width="1024" height="838" /></a>

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture1.png"></a><a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture1.png" alt="" class="alignnone wp-image-345 size-full" width="540" height="915" />
</a>

In each Virtual Network, create the Gateway subnet.
<em><em>
Shown below is the Gateway Subnet creation for VNET1.</em></em><em>Perform the same set of steps for VNET2, but use 10.0.1.240/28 as the Address range.</em>

<em>Leave other settings at their default values.</em>

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture2.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture2-1024x594.png" alt="" class="alignnone wp-image-355 size-large" width="1024" height="594" /></a><a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture3.png">
</a><a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture3.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture3.png" alt="" class="alignnone wp-image-365 size-full" width="811" height="917" /></a>

For each of the Virtual Networks, create a Virtual Network Gateway.

<em><em><strong>Remember</strong> to use the VpnGw1 or higher SKU. The lower SKUs don't support this configuration.</em></em>

Shown below is the Virtual Network Gateway creation for VNET1.Perform the same set of steps for VNET2, but use 65521 as the ASN.

<em>For both Gateways, create new "Basic" Public IPs following the on-screen UI "blades", for First and Second IP configurations.</em><em>To locate the appropriate VNET, first set the "Location" property to Southeast Asia (VNET1)/East US 2 (VNET2).</em>

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture4.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture4.png" alt="" class="alignnone size-full wp-image-366" width="541" height="918" /></a>

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture5.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture5.png" alt="" class="alignnone wp-image-375 size-full" width="541" height="913" /></a>

&nbsp;

Next we create a "Local Network Gateway" per instance of the remote Azure Virtual Network Gateway. The Local Network Gateway is nothing but a means to save the configuration for the remote (or on-premises) VPN device. Referring to the network architecture diagram at the beginning of this post, each instance of the Azure Virtual Network Gateway on VNET2 is the remote (assume on-premises) VPN device for the Azure Virtual Network Gateway on VNET1.

<em>As the Azure Virtual Network Gateway on VNET2 is active-active, we need to configure two Local Network Gateways, each representing one instance. </em>

<em>The details we configure for our Local Network Gateway to be used by the Azure Virtual Network Gateway in VNET1 are those from the Azure Virtual Network Gateway in VNET2.
</em>
LocalNetworkGateway1-SEAsia:

IP Address: 52.232.247.238 (Public IP Address of first instance of remote Azure Virtual Network Gateway)
Address Space: 10.0.1.244/32 (First BGP Peer IP Address in CIDR format)
ASN: 65521 (ASN of Azure Virtual Network Gateway in VNET2)
BGP Peer IP Address: 10.0.1.244 (First BGP Peer IP Address)
Resource Group: VNET1RG (Resource Group of VNET1)
Location: Southeast Asia (location of VNET1)

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture6.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture6-1024x309.png" alt="" class="alignnone size-large wp-image-395" width="1024" height="309" /></a>

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture7.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture7.png" alt="" class="alignnone size-full wp-image-405" width="890" height="495" /></a>

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture8.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture8.png" alt="" class="alignnone size-full wp-image-415" width="539" height="913" /></a>

Next, using the second set of IP addresses, we configure the Local Network Gateway for the second instance of the Azure Virtual Network Gateway in VNET2:

LocalNetworkGateway2-SEAsia:
<div class="fxc-copyablelabel-temp-element">IP Address: 52.232.247.4 (Public IP Address of second instance of remote Azure Virtual Network Gateway)
Address Space: 10.0.1.245/32 (Second BGP Peer IP Address in CIDR format)
ASN: 65521 (ASN of Azure Virtual Network Gateway in VNET2)
BGP Peer IP Address: 10.0.1.245 (Second BGP Peer IP Address)
Resource Group: VNET1RG (Resource Group of VNET1)
Location: Southeast Asia (location of VNET1)</div>
<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture9.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture9-1024x333.png" alt="" class="alignnone size-large wp-image-425" width="1024" height="333" /></a>

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture10.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture10.png" alt="" class="alignnone size-full wp-image-435" width="800" height="470" /></a>

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture11.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture11.png" alt="" class="alignnone size-full wp-image-445" width="540" height="920" /></a>

&nbsp;

Now, we create the Local Network Gateways for the Azure Virtual Network Gateway in VNET2, treating the Azure Virtual Network Gateway in VNET1 as the remote VPN device.

<em><em>Using the guidance above, collate the values from the Azure Portal required for the configuration using the Azure Virtual Network Gateway in VNET1.</em></em>

LocalNetworkGateway1-EastUS2:
<div class="fxc-copyablelabel-temp-element">IP Address: 52.187.135.207 (Public IP Address of first instance of remote Azure Virtual Network Gateway)
Address Space: 192.168.1.244/32 (First BGP Peer IP Address in CIDR format)
ASN: 65516 (ASN of Azure Virtual Network Gateway in VNET2)
BGP Peer IP Address: 192.168.1.244 (First BGP Peer IP Address)
Resource Group: VNET2RG (Resource Group of VNET2)
Location: East US 2 (location of VNET2)<em> </em></div>
<div></div>
LocalNetworkGateway2-EastUS2:

IP Address: 52.163.240.185 (Public IP Address of second instance of remote Azure Virtual Network Gateway)
Address Space: 192.168.1.245/32 (Second BGP Peer IP Address in CIDR format)
ASN: 65516 (ASN of Azure Virtual Network Gateway in VNET2)
BGP Peer IP Address: 192.168.1.245 (Second BGP Peer IP Address)
Resource Group: VNET2RG (Resource Group of VNET2)
Location: East US 2 (location of VNET2)

&nbsp;

Finally, from a configuration standpoint, we need to create a "Connection" from each Azure Virtual Network Gateway to a Local Network Gateway, which represents each instance of the remote VPN device.

&nbsp;

<em><em><em>Though we only need to create one "Connection" from the Azure Virtual Network Gateway to one instance of the remote VPN device, internally, the Azure Virtual Network Gateway sets up two IPsec tunnels to the instance of the remote VPN device - one from each instance of the Azure Virtual Network Gateway. This means at the end of the configuration, we have full-mesh connectivity.</em></em></em>

&nbsp;

First Connection:

Connection type: Site-to-site (IPsec) (Type of tunnel for on-premises VPN device connectivity)
Resource Group: VNET1RG (Resource Group of VNET1)
Location: Southeast Asia (Location of VNET1)
Virtual Network Gateway: VNET1-GW (Azure Virtual Network Gateway in VNET1)
Local Network Gateway: LocalNetworkGateway1-SEAsia (Local Network Gateway representing first instance of Azure Virtual Network Gateway in VNET2)
Connection Name: AUTO POPULATED
Shared key (PSK): USE YOUR OWN STRONG KEY (use SAME value for all subsequent Connections)
Enable BGP: Checked

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture12.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture12.png" alt="" class="alignnone size-full wp-image-446" width="855" height="921" /></a>

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture13.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture13.png" alt="" class="alignnone size-full wp-image-455" width="855" height="921" /></a>

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture14.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture14-1024x800.png" alt="" class="alignnone size-large wp-image-465" width="1024" height="800" /></a>

&nbsp;

Similarly, create another connection.

Second Connection:<em></em>

Connection type: Site-to-site (IPsec) (Type of tunnel for on-premises VPN device connectivity)
Resource Group: VNET1RG (Resource Group of VNET1)
Location: Southeast Asia (Location of VNET1)
Virtual Network Gateway: VNET1-GW (Azure Virtual Network Gateway in VNET1)
Local Network Gateway: <strong>LocalNetworkGateway2-SEAsia</strong> (Local Network Gateway representing the <strong>second</strong> instance of Azure Virtual Network Gateway in VNET2)
Connection Name: AUTO POPULATED
Shared key (PSK): USE YOUR OWN STRONG KEY (use SAME value as previous connection)
Enable BGP: Checked

Now, switching over to the Azure Virtual Network Gateway in VNET2, we create two more Connections.

First Connection:<em></em>

Connection type: Site-to-site (IPsec) (Type of tunnel for on-premises VPN device connectivity)
Resource Group: VNET2RG (Resource Group of VNET2)
Location: East US 2 (Location of VNET2)
Virtual Network Gateway: VNET2-GW (Azure Virtual Network Gateway in VNET2)
Local Network Gateway: LocalNetworkGateway1-EastUS2 (Local Network Gateway representing first instance of Azure Virtual Network Gateway in VNET1)
Connection Name: AUTO POPULATED
Shared key (PSK): USE YOUR OWN STRONG KEY (use SAME value for all subsequent Connections)
Enable BGP: Checked

Second Connection:<em></em>

Connection type: Site-to-site (IPsec) (Type of tunnel for on-premises VPN device connectivity)
Resource Group: VNET2RG (Resource Group of VNET2)
Location: East US 2 (Location of VNET2)
Virtual Network Gateway: VNET2-GW (Azure Virtual Network Gateway in VNET1)
Local Network Gateway: <strong>LocalNetworkGateway2-EastUS2</strong> (Local Network Gateway representing the <strong>second</strong> instance of Azure Virtual Network Gateway in VNET1)
Connection Name: AUTO POPULATED
Shared key (PSK): USE YOUR OWN STRONG KEY (use SAME value as previous connection)
Enable BGP: Checked<em></em><em></em>

&nbsp;

That's it! You should now have dual-redundancy, active-active VPN gateways configured and running.

But wait, you'd like to verify all your hard work, after all. Take a look at both your Azure Virtual Network Gateways. Under connections, this is what you should see:

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture15.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture15-1024x285.png" alt="" class="alignnone size-large wp-image-475" width="1024" height="285" /></a>

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture16.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture16-1024x287.png" alt="" class="alignnone size-large wp-image-476" width="1024" height="287" /></a>

&nbsp;

...and BGP and all that talk about exchanging routes and so on?

Install <a href="https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-5.3.0">Azure PowerShell</a> and log into Azure:

<em>Add-AzureRmAccount</em>

Then run:

<em>Get-AzureRmVirtualNetworkGatewayBGPPeerStatus -ResourceGroupName VNET1RG -VirtualNetworkGatewayName VNET1-GW</em>

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2018/02/Capture17.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/02/Capture17-1024x563.png" alt="" class="alignnone size-large wp-image-485" width="1024" height="563" /></a>

&nbsp;

<strong>Bonus Tip:
</strong>
Check out this Azure PowerShell cmdlet. It generates a VPN device specific configuration script for your Azure Virtual Network Gateway configuration that you could share with your network team to configure the on-premises VPN device.

<em>Get-AzureRmVirtualNetworkGatewayConnectionVpnDeviceConfigScript -DeviceFamily "IOS (ISR, ASR)" -DeviceVendor Cisco -FirmwareVersion "15.x (IKEv2)" -Name VNET1-GW-LocalNetworkGateway1-SEAsia -ResourceGroupName VNET1RG</em>

&nbsp;

If you're wondering what <em>VNET1-GW-LocalNetworkGateway1-SEAsia</em> in the command is, it's the auto-generated name for one of the connections we configured. Navigate to Azure Virtual Network Gateway --&gt; Connections to get this value.

To get the supported VPN device details to plug into the command above, run this Azure PowerShell cmdlet:

<em>Get-AzureRmVirtualNetworkGatewaySupportedVpnDevice -ResourceGroupName VNET1RG -Name VNET1-GW</em>

&nbsp;

If this article helped you, let me know in the comments section.

Richard Spitz
Cloud Solution Architect
Microsoft]]></content:encoded>
    </item>
    <item>
      <title>How to hot-add a Shared VHDX</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/how-to-hot-add-a-shared-vhdx</link>
      <pubDate>Fri, 24 Jan 2014 06:55:00 GMT</pubDate>
      <dc:creator><![CDATA[RichardSpitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/2014/01/24/how-to-hot-add-a-shared-vhdx/</guid>
      <description><![CDATA[Shared VHDX, a new feature in Windows Server 2012 R2, is a boon for administrators wanting to create...]]></description>
      <content:encoded><![CDATA[<p>Shared VHDX, a new feature in Windows Server 2012 R2, is a boon for administrators wanting to create a Failover Cluster running in Hyper-V virtual machines. Create a VHDX, attach it to&nbsp;the SCSI Controller of a&nbsp;virtual machine, tick &quot;Enable virtual hard disk sharing&quot; under Advanced Features of the Hard Drive (Click the little &#39;+&#39; to access Advanced Features). Repeat for as many virtual machines you want, power them up&nbsp;and you&#39;re good to go!</p>
<p>Now you&#39;ve set up your Failover Cluster&nbsp;and everything&#39;s just jolly. Then you get a request to add more storage capacity to the Failover Cluster. At this point, you&#39;re thinking - No Sweat! You fire up Hyper-V Manager, create a new VHDX, then go to Failover Cluster Manager to attach it to the&nbsp;first virtual machine as shared, go to the second virtual machine,&nbsp;do the same thing.</p>
<p>BOOM!</p>
<p></p>
<p><em><strong>Error applying Hard Drive changes</strong></em></p>
<p><em>&#39;VM2&#39; failed to add device &#39;Virtual hard Disk&#39;. (Virtual machine ID XYZ)</em></p>
<p><em>Failed to open attachment &#39;C:\ClusterStorage\Volume1\SharedDisk.vhdx&#39;. Error: &#39;The requested operation cannot be performed on the virtual disk as it is currently used in shared mode.&#39;.&nbsp;</em></p>
<p></p>
<p>Wow - so much for &#39;No Sweat&#39;! What&#39;s going on here?!?</p>
<p>What&#39;s going on is that you&#39;ve very likely not checked the &quot;Enable virtual hard disk sharing&quot; checkbox under Advanced Features <em>before</em> hitting the &quot;Apply&quot; button. Ensure that you&#39;ve done that and you&#39;re good to go.</p>
<p>If you can&#39;t be bothered with&nbsp;the GUI, there&#39;s always PowerShell!</p>
<p>For every virtual machine that you want to add the Shared VHDX to, run this command (Import the Hyper-V module if you haven&#39;t already done so - &#39;Import-Module Hyper-V&#39;):</p>
<p><span style="font-size:x-small;background-color:#0000ff;"><span style="color:lightcyan;font-family:&#39;Lucida Console&#39;;">Add-VMHardDiskDrive</span><span style="color:whitesmoke;font-family:&#39;Lucida Console&#39;;"> </span><span style="color:moccasin;font-family:&#39;Lucida Console&#39;;">-VMName</span><span style="color:whitesmoke;font-family:&#39;Lucida Console&#39;;"> </span><span style="color:violet;font-family:&#39;Lucida Console&#39;;">VM2</span><span style="color:whitesmoke;font-family:&#39;Lucida Console&#39;;"> </span><span style="color:moccasin;font-family:&#39;Lucida Console&#39;;">-Path</span><span style="color:whitesmoke;font-family:&#39;Lucida Console&#39;;"> </span><span style="color:violet;font-family:&#39;Lucida Console&#39;;">C:\ClusterStorage\Volume1\SharedDisk.vhdx</span><span style="color:whitesmoke;font-family:&#39;Lucida Console&#39;;"> </span><span style="color:moccasin;font-family:&#39;Lucida Console&#39;;">-ShareVirtualDisk</span></span></p>
<p>If you have Clustered Hyper-V, remember to run the command on the node where the virtual machine is hosted.</p>
<p>That&#39;s all, Folks!</p>
<p>Thanks to my colleague and buddy Surinder Singh&nbsp;for pointing this out to me. You really do learn something new everyday.</p>]]></content:encoded>
    </item>
    <item>
      <title>My First (Useful) PowerShell Script!</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/my-first-useful-powershell-script</link>
      <pubDate>Fri, 26 Jul 2013 00:19:00 GMT</pubDate>
      <dc:creator><![CDATA[RichardSpitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/2013/07/26/my-first-useful-powershell-script/</guid>
      <description><![CDATA[Heard of PowerShell? No? Just Kidding! Even if you run your IT department under a rock or on Mars,...]]></description>
      <content:encoded><![CDATA[<p>Heard of PowerShell? No? Just Kidding! Even if you run your IT department under a rock or on Mars, you've heard of PowerShell - It's that ubiquitous!</p>
<p>I've always been interested in picking it up as a skill, it is an extremely useful skill to have, however, I've never had the time and when I've had the time, I've been lazy.&nbsp;The question always was when will I find&nbsp;the time?&nbsp;Well, as the old adage goes, there's no time like the present.</p>
<p>I was in the process of configuring my new work laptop, running Windows 8, with <a title="https://www.microsoft.com/en-us/download/details.aspx?id=36188" href="https://blogs.technet.com/controlpanel/blogs/posteditor.aspx/Client Hyper-V" target="_blank">Client Hyper-V</a>. I had all my Sysprep'ed VHD/VHDX files in a folder and was about to follow this process to create new virtual machines:</p>
<ol>
<li>Create a new differencing disk, with the parent disk being one of the sysprep'ed VHD/VHDX files (I have one for every version of Windows)</li>
<li>Create a new virtual machine and point it to the VHD/VHDX create in the previous step.</li>
</ol>
<p>These steps are&nbsp;all well and good if you only want to set up a couple of virtual machines. However, if you find yourself performing these steps over and over again, you'll come to hate that little 'Next' button on the New Hard Disk or New Virtual Machine wizards. Enter PowerShell.</p>
<p>I fired up the PowerShell ISE (Enable Admin Tools on the Start Screen - Settings - Tiles), built the commands using the Command Add-On (View Menu&nbsp;- Show Command Add-on) and voila! My&nbsp;first&nbsp;PowerShell Script!</p>
<p><span style="font-family: courier new,courier;">$VMOS = $args[0]</span><br /><span style="font-family: courier new,courier;">$VMName = $args[1]</span><br /><span style="font-family: courier new,courier;">$DiskExtn = ".vhd"</span></p>
<p><span style="font-family: courier new,courier;">if ($VMOS -eq "WIN2012") {$DiskExtn = ".vhdx"} </span><br /><span style="font-family: courier new,courier;">elseif($VMOS -eq "WIN8") {$DiskExtn = ".vhdx"}</span></p>
<p><span style="font-family: courier new,courier;">$VHDParentPath = "C:\HyperV\BaseVHDs\" + $VMOS + $DiskExtn</span><br /><span style="font-family: courier new,courier;">$VHDPath = "C:\HyperV\VHDs\" + $VMName + $DiskExtn</span><br /><span style="font-family: courier new,courier;">$VMPath = "C:\HyperV\VMs\" + $VMName</span></p>
<p><span style="font-family: courier new,courier;">New-VHD -ParentPath $VHDParentPath -Path $VHDPath</span><br /><span style="font-family: courier new,courier;">New-VM -VHDPath $VHDPath -MemoryStartupBytes 1073741824 -Name `<br /></span><span style="font-family: courier new,courier;">$VMName -Path $VMPath -SwitchName VM2VM</span></p>
<p>That's it. 15 minutes, a bit of Binging for PowerShell Syntax and I can now create a new virtual machine with any version of Windows,&nbsp;in a few keystrokes and 10 seconds.</p>]]></content:encoded>
    </item>
    <item>
      <title>Field, Here I Come!</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/field-here-i-come</link>
      <pubDate>Wed, 24 Jul 2013 10:01:00 GMT</pubDate>
      <dc:creator><![CDATA[RichardSpitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/2013/07/24/field-here-i-come/</guid>
      <description><![CDATA[Now and again, a colleague or customer points to my blog, having run into one of the issues I've...]]></description>
      <content:encoded><![CDATA[<p>Now and again, a colleague or customer points to my blog, having run into one of the issues I've described in a post. It's at these times that&nbsp;I remember I have a blog! My last post was over two years ago, but I won't make any excuses for not posting and will get right back into it.</p>
<p>I have moved into a new role at Microsoft, as a Premier Field Engineer and will be responsible for delivering workshops, health checks, deployments&nbsp;and onsite support to Microsoft's Premier Customers in India. This role&nbsp;takes me away from the world of troubleshooting I've known for the last 8 years and, clich&eacute;d as it may sound, shows me the bigger picture from conceptualization&nbsp;to design and then implementation and finally maintenance.&nbsp;</p>
<p>The theme of my blog so far has been troubleshooting issues and while I will post a weird and undocumented-anywhere-else issue that I've resolved every once in a while, the majority will deal with new features in Windows, configuring those features, tuning them if required and the like.</p>
<p>My areas of expertise are Failover Clustering and Virtualization using Hyper-V, however, I look forward to dabbling in capacity planning, performance tuning and deployment.</p>
<p>With that said, I'm going back to hit the (e)books and will have something up here soon. I promise!</p>]]></content:encoded>
    </item>
    <item>
      <title>Cluster service fails to start on Windows Server 2008 SP2, cluster.log contains “Too many failures while attempting to start RHS process.”</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/cluster-service-fails-to-start-on-windows-server-2008-sp2-cluster-log-contains-too-many-failures-while-attempting-to-start-rhs-process</link>
      <pubDate>Thu, 24 Nov 2011 02:48:00 GMT</pubDate>
      <dc:creator><![CDATA[RichardSpitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/2011/11/24/cluster-service-fails-to-start-on-windows-server-2008-sp2-cluster-log-contains-too-many-failures-while-attempting-to-start-rhs-process/</guid>
      <description><![CDATA[It&rsquo;s been a while since my last post &ndash; not due to the dearth of interesting issues to...]]></description>
      <content:encoded><![CDATA[<p>It&rsquo;s been a while since my last post &ndash; not due to the dearth of interesting issues to blog about, rather, there have been TOO many issues, interesting and otherwise. Too many issues == too much work!</p>
<p>Nonetheless, this particular issue was interesting enough to make me take some time out, despite my busy schedule, and write about it.</p>
<p>On one node of a Windows Server 2008 SP2 Failover Cluster, the Cluster service would start, then terminate within a few seconds. If you run a <strong>cluster log /g</strong> and review C:\Windows\Cluster\Reports\cluster.log, you&rsquo;ll see this:</p>
<p>&nbsp;</p>
<p>00001928.00000f2c::2011/11/23-07:05:30.498 INFO&nbsp; [RCM] Created monitor process 5828 / 0x16c4 <br />00001d10.00001228::2011/11/23-07:05:31.496 INFO&nbsp; [RHS] Initializing. <br />00001928.00000f2c::2011/11/23-07:05:31.590 INFO&nbsp; [RCM] Created 32-bit monitor process 3800 / 0xed8 <br />00001928.00000f2c::2011/11/23-07:05:35.068 WARN&nbsp; [RCM] rcm::RcmMonitor::StartMonitor: Retrying... <br />00001928.00000f2c::2011/11/23-07:05:35.068 INFO&nbsp; [RCM] Created 32-bit monitor process 5340 / 0x14dc <br />00001928.00000f2c::2011/11/23-07:05:38.656 WARN&nbsp; [RCM] rcm::RcmMonitor::StartMonitor: Retrying... <br />00001928.00000f2c::2011/11/23-07:05:38.656 INFO&nbsp; [RCM] Created 32-bit monitor process 9564 / 0x255c <br />00001928.00000f2c::2011/11/23-07:05:42.337 WARN&nbsp; [RCM] rcm::RcmMonitor::StartMonitor: Retrying... <br />00001928.00000f2c::2011/11/23-07:05:42.337 INFO&nbsp; [RCM] Created 32-bit monitor process 10092 / 0x276c <br />00001928.00000f2c::2011/11/23-07:05:45.956 WARN&nbsp; [RCM] rcm::RcmMonitor::StartMonitor: Retrying... <br />00001928.00000f2c::2011/11/23-07:05:45.956 INFO&nbsp; [RCM] Created 32-bit monitor process 7376 / 0x1cd0 <br />00001928.00000f2c::2011/11/23-07:05:48.608 ERR&nbsp;&nbsp; [CORE] Node 1: exception caught ERROR_SUCCESS(0)' because of 'Too many failures while attempting to start RHS process.' <br />00001928.00000f2c::2011/11/23-07:05:48.624 ERR&nbsp;&nbsp; Exception in the InstallState is fatal (status = 0) <br />00001928.00000f2c::2011/11/23-07:05:48.624 ERR&nbsp;&nbsp; FatalError is Calling Exit Process. <br />00001928.000027d4::2011/11/23-07:05:48.624 INFO&nbsp; [CS] About to exit process... <br />00001d10.00001228::2011/11/23-07:05:48.640 WARN&nbsp; [RHS] Cluster service has terminated. <br />00001d10.00001228::2011/11/23-07:05:48.671 INFO&nbsp; [RHS] Exiting.</p>
<p>&nbsp;</p>
<p>From the log snippet, we can see that the 64 bit RHS.exe was launched with Process ID 5828, but after multiple attempts, the Cluster service is not able to launch the 32 bit RHS.exe process.</p>
<p>The RHS.exe process is critical to the functioning of the cluster and if it cannot launch, the Cluster service cannot run. As this is a 64 bit version of Windows, we have a 64 bit version of RHS.exe &ndash; for 64 bit Resource DLLs and a 32 bit version &ndash; for 32 bit Resource DLLs. You can think of Resource DLLs as an interface between the Cluster service and the applications(SQL, Exchange, SAP) or components(IP Address, Network Name, Physical Disks) which have Cluster resources. These DLLs provide the Cluster service with &ldquo;entry points&rdquo; to control the applications and components through their Cluster resources. Examples of these entry points are Open, Close, Online, Offline.</p>
<p>Now we know that the Cluster service fails to start on this node because it cannot launch the 32 bit RHS.exe process. Our attention therefore turns to why the 32 bit RHS.exe fails to launch.</p>
<p>If the Cluster service fails to launch, would we be able to launch it manually? Let&rsquo;s give it a shot.</p>
<p>We&rsquo;ve got to remember that this is the 32 bit RHS.exe, which is in C:\Windows\SysWOW64 &ndash; not C:\Windows\Cluster, which contains the 64 bit RHS.exe</p>
<p>Open an elevated (Administrator) command prompt, change directory to C:\Windows\SysWOW64 and run RHS.exe. Immediately, we get this error:</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/84/77/metablogapi/3007.dbghelp_3F7DB70C.png" original-url="https://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-84-77-metablogapi/3007.dbghelp_5F00_3F7DB70C.png"><img style="border: 0px currentcolor; display: inline; background-image: none;" title="dbghelp" border="0" alt="dbghelp" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/84/77/metablogapi/4186.dbghelp_thumb_0AFCE4C4.png" original-url="https://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-84-77-metablogapi/4186.dbghelp_5F00_thumb_5F00_0AFCE4C4.png" width="784" height="395" /></a></p>
<p>&nbsp;</p>
<p>There we go! The Cluster service is not able to launch it and neither are we able to do so, manually.</p>
<p>Why am I excited? Because the pop-up message mentions a certain &ldquo;dbghelp.dll&rdquo;. The first place to look for this file would be the C:\Windows\SysWOW64 &ndash; because that&rsquo;s where RHS.exe is as well.</p>
<p>When I browsed through the folder to look for dbghelp.dll, we found that the file was present but there was also a dbghelp.dll.old file that caught my attention.</p>
<p>Dbghelp.dll was about 1300kb and dbghelp.dll.old was 780kb. That&rsquo;s interesting. Why? Because my lab Windows Server 2008 SP2 system has the 780kb dbghelp.dll, but not the&nbsp;1300kb file.</p>
<p>Turns out that the Application team had a specific requirement to use the 1300kb dbghelp.dll while upgrading their clustered application, so they switched out the original file, put this one in and forgot about it. Their application may have been compatible with this new dbghelp.dll, however, RHS.exe was not, and that&rsquo;s why it failed to start.</p>
<p>Resolving the issue now that we know the root cause is a matter of renaming the 1300kb dbghelp.dll to something else, taking ownership of the 780kb dbghelp.dll.old file, giving Administrators FULL CONTROL, renaming this file to dbghelp.dll, changing the ownership back to NT SERVICE\TrustedInstaller and changing Administrators permissions back to only Read and Read &amp; Execute. PHEW! Issue resolved!</p>]]></content:encoded>
    </item>
    <item>
      <title>Certain icons do not appear the first time you open Control Panel on Windows 7</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/certain-icons-do-not-appear-the-first-time-you-open-control-panel-on-windows-7</link>
      <pubDate>Thu, 16 Jun 2011 01:43:00 GMT</pubDate>
      <dc:creator><![CDATA[RichardSpitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/2011/06/16/certain-icons-do-not-appear-the-first-time-you-open-control-panel-on-windows-7/</guid>
      <description><![CDATA[I was over at the NTDebugging blog the other day and while browsing through the posts, I came across...]]></description>
      <content:encoded><![CDATA[<p>I was over at the <a target="_blank" href="https://blogs.msdn.com/b/ntdebugging/">NTDebugging blog</a> the other day and while browsing through the posts, I came across this one, written quite brilliantly by Matt Burrough</p>
<p><strong>Control Panel Policy and the Missing Icon </strong><br /><a href="https://blogs.msdn.com/b/ntdebugging/archive/2011/04/22/control-panel-policy-and-the-missing-icon.aspx">https://blogs.msdn.com/b/ntdebugging/archive/2011/04/22/control-panel-policy-and-the-missing-icon.aspx</a></p>
<p>After reading a few lines, I realized why this post had piqued my interest. A colleague of mine was working on a similar issue &ndash; where System Center Configuration Manager (SCCM) control panel icons weren&rsquo;t visible the first time you opened the control panel after a reboot. He was discussing this case with another colleague and I happened to overhear a bit of the conversation. </p>
<p>If you want to understand the cause of the issue and how he debugged it, I highly recommend that you head on over to Matt&rsquo;s post. If you&rsquo;d rather just fix this issue and get it over with, here&rsquo;s the lowdown.</p>
<p>This issue only occurs when you have the <a target="_blank" href="https://technet.microsoft.com/en-us/library/cc940208.aspx">&ldquo;Hide specified control panel applets&rdquo;</a> or <a target="_blank" href="https://technet.microsoft.com/en-us/library/cc940227.aspx">&ldquo;Show only specified control panel applets&rdquo;</a> settings enabled via a group policy. In our case, the &ldquo;Hide specified control panel applets&rdquo; setting was enabled. This policy results in Windows attempting to read the <em>System.ApplicationName</em> property of every control panel icon, to determine if they should be hidden or visible. Problem is, if some of the icons do not have this property set in the registry, these icons are not displayed the first time around. Close and open control panel and they&rsquo;re there. A bit odd. This is the result of an issue in the way Windows handles the missing <em>System.ApplicationName</em> property. As Matt mentions, the Windows development team have been made aware of this. </p>
<p>How do we fix this so that the missing icons appear every time the control panel is opened?</p>
<p>You need to create the <em>System.ApplicationName</em> registry key under </p>
<p><em>HKLM\Software\Microsoft\Windows\CurrentVersion\Control Panel\Extended Properties</em></p>
<p>Under this key, create a new string value with the name set to the path to the CPL file and value set to the <em>System.ApplicationName</em> property of the CPL item. </p>
<p>Matt&rsquo;s post mentions that since the Mail icon is a inbuilt item, it's System.ApplicationName is known. </p>
<p>However, in our case, all missing control panel icons belong to SCCM. A search of the registry failed to locate any information about whether the SCCM CPL items even had a System.ApplicationName.</p>
<p>After a few moments wondering how we&rsquo;re going to get past this hurdle, it came to me. Windows doesn&rsquo;t display the icons the first time around <em>only</em> when it cannot find their <em>System.ApplicationName</em> property. It doesn&rsquo;t matter <em>what</em> the <em>System.ApplicationName </em>property is set to, just as long as it is not in the list of hidden icons, set via group policy.</p>
<p>We set the <em>System.ApplicationName</em> property for the SCCM control panel icons in this way:</p>
<ul>
<li>Configuration Manager &ndash; SMSCFGRC</li>
<li>Program Download Monitor &ndash; SMSPDM</li>
<li>Run Advertised Programs &ndash; SMSRAP</li>
<li>Remote Control &ndash; SMSRCCPL</li>
</ul>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/84/77/5076.ControlPanel.PNG" original-url="https://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-84-77/5076.ControlPanel.PNG"><img src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/84/77/5076.ControlPanel.PNG" original-url="https://blogs.technet.com/resized-image.ashx/__size/550x234/__key/communityserver-blogs-components-weblogfiles/00-00-00-84-77/5076.ControlPanel.PNG" border="0" /></a><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/84/77/8117.ControlPanel.PNG" original-url="https://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-84-77/8117.ControlPanel.PNG"></a></p>
<p>We rebooted the system and opened the control panel. All icons are visible the first time itself. Issue worked around!</p>]]></content:encoded>
    </item>
    <item>
      <title>How to run a script when a Resource Group fails over to the passive node of a Windows Server 2003 based MSCS Cluster</title>
      <link>https://docs.microsoft.com/archive/blogs/rspitz/how-to-run-a-script-when-a-resource-group-fails-over-to-the-passive-node-of-a-windows-server-2003-based-mscs-cluster</link>
      <pubDate>Mon, 30 May 2011 08:19:00 GMT</pubDate>
      <dc:creator><![CDATA[RichardSpitz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/rspitz/2011/05/30/how-to-run-a-script-when-a-resource-group-fails-over-to-the-passive-node-of-a-windows-server-2003-based-mscs-cluster/</guid>
      <description><![CDATA[Having recently scripted the Cluster WMI Namespace (for fun) and blogged about it with my Cluster...]]></description>
      <content:encoded><![CDATA[<p>Having recently scripted the Cluster WMI Namespace (for fun) and blogged about it with my <a target="_blank" href="https://blogs.technet.com/b/rspitz/archive/2011/05/11/playing-around-with-the-root-mscluster-wmi-namespace-and-wmi-notifications.aspx">Cluster WMI Notifications</a> post, I saw an opportunity to put that experience into practice here. We had a 2 node Active-Passive MSCS Cluster and the requirement was to run a custom script that did some work when a Resource Group failed over to the passive node. My plan was to kick off the custom script when we received a notification that the Resource Group had come online on the passive node. </p>
<p>A quick assessment of the proposed solution exposed its main flaw &ndash; that is &ndash; the script would be external to the cluster and would need to be managed separately. Any number of things could go wrong and prevent the custom script from running, which is a big no-no in a production environment.</p>
<p>So while I did learn a lot from testing WMI Notifications for the Cluster Namespace, it wasn&rsquo;t suited to this scenario.</p>
<p>On then, to the next out of the box idea. Or not so out of the box. Remember a resource type called &ldquo;Generic Script&rdquo;? Sounds exactly like what is required here.</p>
<p>However, you can&rsquo;t just write a script, create a &ldquo;Generic Script&rdquo; resource and point it to the script. If you try &ndash; as I did, while doing my research, you&rsquo;ll find that the entire Cluster Administrator hangs just as you hit the Finish button on the &ldquo;New Resource&rdquo; wizard. This is because &ndash; as you&rsquo;ll soon find out &ndash; the &ldquo;Generic Script&rdquo; resource DLL is looking for the Open entry point.</p>
<p>Scouring the internet got me this MSDN link: </p>
<p><strong>Using the Generic Script Resource Type</strong> <br /><a href="https://msdn.microsoft.com/en-us/library/aa373089(v=VS.85).aspx">https://msdn.microsoft.com/en-us/library/aa373089(v=VS.85).aspx</a></p>
<p>Any script that is to be configured as a &ldquo;Generic Script&rdquo; resource will need to contain functions named Open, Online, LooksAlive, IsAlive, Offline, Close and Terminate. These are the functions that the Cluster Resource Monitor will call to perform the relevant operations. In this way, it is different from a typical script in the same way an executable is different from a DLL. They both contain runnable code, but one can run by itself, whereas the other can&rsquo;t.</p>
<p>For our purpose, we&rsquo;ve got to call a custom script when the resource comes online on the passive node &ndash; which happens when the active node fails. This means that you put the custom script code directly into the Online function or call the custom script from a line of script in the Online function. Once the &ldquo;Generic Script&rdquo; resource comes online, it says online even after the script has run. This is good because you wouldn&rsquo;t want the resource to go offline after it has finished its work. If it did, it would raise all kinds of&nbsp; alarms in monitoring software, such as &ldquo;Resource Group ABC is partially offline&rdquo; and so on.</p>
<p>Another interesting discovery for me on this case was the <em>Resource</em> object and its <em>LogInformation</em> method. You can use this to log entries to the Cluster log, from the script, so you can see which entry points are called and when. This will give you an even clearer idea of where to place your custom script, if you need to call it more often (to check for any relevant condition).</p>
<p>Also, the precondition to running the custom script is that we&rsquo;re on the passive node. This is a basic task for VBScript. What you want to do is get the computer name from WMI, compare it to the passive node name and run the custom script if we&rsquo;re on the passive node or do nothing if we&rsquo;re on the active node.</p>
<p>Here&rsquo;s the script. After it, you&rsquo;ll find some more information about how to configure it and some pitfalls to avoid.&nbsp;</p>
<p><span style="font-family: verdana,geneva; font-size: small;">'====================================================== <br />' File name: GenericScriptRes.vbs <br />'&nbsp; <br />'Run custom script only on a particular node when group <br />'comes online. <br />'====================================================== </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function Open( ) <br />Open = 0 <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function Online( ) <br />Resource.LogInformation "Entering Script Online" <br />On Error Resume Next </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">strComputer = "." <br />Set objWMIService = GetObject("winmgmts:\\" &amp; strComputer &amp; "\root\cimv2") <br />Set nodeInfo = objWMIService.ExecQuery("Select Name from Win32_ComputerSystem") </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">For Each objNode in nodeInfo </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If objNode.Name = "WIN2K3NODE2" Then <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource.LogInformation "Custom Script will run here. " </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' PLACE CUSTOM SCRIPT HERE </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource.LogInformation "Custom Script will not run here. " <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Next </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Online = 0 <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function Offline( ) <br />Resource.LogInformation "Entering Script Offline" <br />Offline = 0 <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function Close() <br />Close = 0 <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function Terminate( ) <br />Terminate = 0 <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function LooksAlive( ) <br />'Resource.LogInformation "Entering Script LooksAlive" <br />LooksAlive = true <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">Function IsAlive( ) <br />'Resource.LogInformation "Entering Script IsAlive" <br />IsAlive = true <br />End Function </span></p>
<p><span style="font-family: verdana,geneva; font-size: small;">'======================================================</span></p>
<p>I&rsquo;ve commented out the LooksAlive() and IsAlive() Resource.LogInformation calls because these functions are called every 5 seconds and 1 minute respectively. They should only be uncommented when testing, or else your Cluster.log file will be flooded with these entries. Also, these functions return &ldquo;true&rdquo; rather than &ldquo;0&rdquo; like the others, as recommended by the the MSDN article that details the <a target="_blank" href="https://msdn.microsoft.com/en-us/library/aa372846(v=VS.85).aspx">Scripting Entry Points</a>. </p>
<p>This article also mentions that the scripts should be stored locally rather than on a shared disk. Since the resource should come online on both nodes (but only run on the passive node), you can drop the script into &ldquo;C:\Windows\Cluster&rdquo; or another location on both nodes so long as the path to the script file remains the same.</p>
<p>Another point worth noting is that the resource can go through an offline-online cycle on the passive node (most likely through manual intervention) which means that the custom script may run more than once. This is not a problem if the custom script actions perform a static activity such as updating the node name in a file referenced by another application. However, if it performs an activity that needs to occur only once after the initial failover, additional checks need to be put in place to prevent it from running again on the same node. One example of a check can be to create a tag or placeholder file on the shared storage before the Online function exits and then put a check at the beginning of the same function that checks for the file and doesn&rsquo;t run the script if it is present. To clear this file when the Resource Group fails over to the active node, you could put in a snippet of code &ndash; again in the Online function &ndash; that checks if the node is the Active node and then deletes the file. </p>
<p>That&rsquo;s it then. The MSDN articles I&rsquo;ve linked to will give you all the information you need to have about the &ldquo;Generic Script&rdquo; resource.</p>]]></content:encoded>
    </item>
  </channel>
</rss>