<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-02-21T10:11:26-06:00</updated><id>/feed.xml</id><title type="html">Sean’s Blog</title><subtitle>President of SkyForge Consulting, Microsoft MVP in Azure and Dev Tech, Public Speaker, and user group leader.</subtitle><author><name>Sean Whitesell</name></author><entry><title type="html">How to Use Azure Network Security Groups</title><link href="/2025/02/15/How-to-Use-Azure-Network-Security-Groups" rel="alternate" type="text/html" title="How to Use Azure Network Security Groups" /><published>2025-02-15T22:38:11-06:00</published><updated>2025-02-17T12:17:00-06:00</updated><id>/2025/02/15/How-to-Use-Azure-Network-Security-Groups</id><content type="html" xml:base="/2025/02/15/How-to-Use-Azure-Network-Security-Groups"><![CDATA[<p>There has been a lot of confusion around Azure’s <a href="https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview?wt.mc_id=DT-MVP-5003848" target="_blank">Network Security Groups</a> “NSG”. This post aims to explain details and hopefully alleviate misunderstandings as well as suggest some good ways to use NSGs. A bit of warning, this is not meant to be an introduction to NSGs so much as it is to explain some details and best uses.</p>

<h2 id="what-is-an-azure-network-security-group">What is an Azure Network Security Group?</h2>

<p>First off, let’s cover what an NSG really is. They are a type of firewall for layer 3 and 4 traffic. The <em>layer 3 and 4</em> is referring to the <a href="https://en.wikipedia.org/wiki/OSI_model" target="_blank">Open Systems Interconnection</a> model. Layer 3, the Network Layer, is for Internet Protocol “IP” addresses such as 192.168.0.1. Layer 4, the Transport Layer, is for protocols like TCP, UDP, ICMP, and others.</p>

<p>An NSG provides firewall functionality on these two layers. Each rule can work with a single IP address or a range of IP addresses. You can also specify ports and even <a href="https://learn.microsoft.com/en-us/azure/virtual-network/service-tags-overview?wt.mc_id=DT-MVP-5003848" target="_blank">Azure Service Tags</a>. Each rule has a priority and is processed in order. If one rule allows some traffic but another rule of higher priority denies the traffic then traffic is denied because of that priority. There are cases where one rule can allow a range while another rule denies a specific IP address. The traffic allowed or denied is still based on the priority and not specificity.</p>

<!-- Don't forget to mention the VirtualNetowrk service tag -->

<h2 id="how-does-an-nsg-compare-to-azure-firewall">How does an NSG compare to Azure Firewall?</h2>

<p>The NSG does process rules like the Azure Firewall, however there are three drastic differences. First, the Azure Firewall requires a Public IP address or a Public IP Prefix (IP range). The public IP endpoint(s) are used for the incoming and outgoing traffic. The NSG does <em>not</em> have the same kind of entrypoints. More on this <a href="#nsg-and-subnet-association">later</a> in this post. Second, Azure Firewall has collections and groups of rules each with their own associated priorities and capabilities such as Destination Network Address Translation “DNAT”. The NSG only has a single list of rules. Lastly, the Azure Firewall helps route traffic to its destination. The NSG does nothing in regards to routing.</p>

<!-- Add image of AZ FW with public IP -->

<h2 id="nsg-and-subnet-association">NSG and Subnet Association</h2>

<p>The NSG can have several rules but none of them are in effect until the NSG is associated to either a subnet of a Virtual Network “VNet” <em>and/or</em> directly to a Network Interface Card “NIC”. This association can be confusing so I want to spell this out as clearly as I can. Let’s start with a scenario of having a single Virtual Machine “VM”. You notice that in order to create the VM a NIC must be created and in the process the NIC must be associated with a subnet in a VNet. This association to the subnet it what gives the NIC its IP address.</p>

<p>When an NSG is associated with the subnet, the rules then apply to all traffic in that subnet. Yes, you can associate an NSG to some or all of the subnets within an VNet and also to include other subnets of other VNets. For now, I’m keeping it simple for this explanation.</p>

<p>In an NSG, rules are either inbound or outbound and have a Source and a Destination. If a rule is inbound and has a destination IP of that of an associated subnet, then the inbound network traffic is subjected to this rule. Consider this rule in our example.</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">Priority</span> <span class="p">=</span> <span class="m">1001</span>
<span class="n">Direction</span> <span class="p">=</span> <span class="n">Inbound</span>
<span class="n">Source</span> <span class="p">=</span> <span class="n">Any</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="p">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="p">=</span> <span class="m">192.168</span><span class="p">.</span><span class="m">0.5</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="p">=</span> <span class="m">22</span>
<span class="n">Action</span> <span class="p">=</span> <span class="n">Allow</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This rule is stating that traffic coming from <em>Any</em> source that is destined to <em>192.168.0.5</em> on port <em>22</em> will be allowed. Now let’s add another rule. This time the rule is to deny all traffic bound to the same destination regardless of destination port.</p>

<div class="language-verilog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">Priority</span> <span class="o">=</span> <span class="mi">1000</span>
<span class="n">Direction</span> <span class="o">=</span> <span class="n">Inbound</span>
<span class="n">Source</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="o">=</span> <span class="mf">192.168.0.5</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Action</span> <span class="o">=</span> <span class="n">Deny</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The problem with this is that no traffic is getting to any port on that IP address. This is because the priority is set to a higher priority than the other rule allowing traffic to port 22. Priority 1000 is higher than 1001.</p>

<blockquote>
  <p><strong>Note:</strong> NSG rules have a priority from 100 to 4096. The lower the number, the higher the priority!</p>
</blockquote>

<p>So, for traffic to successfully reach <em>192.168.0.5</em> on port <em>22</em> we need to swap the priority.</p>

<div class="language-verilog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="n">Priority</span> <span class="o">=</span> <span class="mi">1000</span>
<span class="n">Direction</span> <span class="o">=</span> <span class="n">Inbound</span>
<span class="n">Source</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="o">=</span> <span class="mf">192.168.0.5</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="o">=</span> <span class="mi">22</span>
<span class="n">Action</span> <span class="o">=</span> <span class="n">Allow</span>

<span class="n">Priority</span> <span class="o">=</span> <span class="mi">1001</span>
<span class="n">Direction</span> <span class="o">=</span> <span class="n">Inbound</span>
<span class="n">Source</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="o">=</span> <span class="mf">192.168.0.5</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Action</span> <span class="o">=</span> <span class="n">Deny</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Remember, the rules regarding the destination address of <em>192.168.0.5</em> are only applicable if the NSG is associated to the subnet range that includes that address, OR is directly associated to the NIC that has that address.</p>

<!-- Add image of NSG associated with a subnet -->
<!-- Add image of NSG associated with a NIC -->

<h2 id="direction-of-rules">Direction of Rules</h2>

<p>When creating a rule we need to understand where the traffic is originating and where it is destined. For example, if traffic is coming from the internet to a public IP address then the rule will be Inbound likely with a Service Tag of “Internet”. This Inbound rule might have a source of a collection of public IP addresses that are from business partners or specific people. Perhaps the traffic originated with a private IP address.</p>

<p>Let’s go through a quick example of a VM with a public IP address. You associate the NSG directly to the NIC of the VM. A quick note; this <a href="#associating-an-nsg-to-nics">not advised</a> and I explain further down. The expectation is to allow yourself direct access from your home. Since the traffic will originate from your home we need to create an Inbound rule. For this example, let’s assume that your home IP address is 1.2.3.4 and the IP address of the VMs public IP is 5.6.7.8. Create the rule as shown below and test.</p>

<div class="language-verilog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">Priority</span> <span class="o">=</span> <span class="mi">2000</span>
<span class="n">Direction</span> <span class="o">=</span> <span class="n">Inbound</span>
<span class="n">Source</span> <span class="o">=</span> <span class="mf">1.2.3.4</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="o">=</span> <span class="mf">5.6.7.8</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="o">=</span> <span class="mi">22</span>
<span class="n">Action</span> <span class="o">=</span> <span class="n">Allow</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Did you notice it didn’t work? Most of the details are correct except for one. This is a big item to remember. Public IP address on a NIC are first converted to their private IP address before an NSG rule is applied. So, we need to change the rule to have the destination of the private IP address.</p>

<div class="language-verilog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">Priority</span> <span class="o">=</span> <span class="mi">2000</span>
<span class="n">Direction</span> <span class="o">=</span> <span class="n">Inbound</span>
<span class="n">Source</span> <span class="o">=</span> <span class="mf">1.2.3.4</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="o">=</span> <span class="mf">192.168.0.5</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="o">=</span> <span class="mi">22</span>
<span class="n">Action</span> <span class="o">=</span> <span class="n">Allow</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>What if the traffic is originating from code running on the VM and needs to get to a public endpoint in the internet? For this, we’ll create an Outbound rule. Why outbound? Because the traffic originates from inside the VNet it’s direction is outbound. It doesn’t make sense to have an Outbound rule with a Source of Internet so that rule will not be effective.</p>

<p>An Outbound rule is created with a source IP of the NIC. The destination is to some random public IP for this example. Notice the destination port is 443 but the source port is <em>Any</em>. When web-based traffic occurs, it first starts with an agreement to further the conversation on an open source port. It’s rare that you will set a source port. However, there are those special cases when there is tight control over that traffic. Web-based traffic is of the few types of conversations that leverage other open ports.</p>

<div class="language-verilog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">Priority</span> <span class="o">=</span> <span class="mi">2000</span>
<span class="n">Direction</span> <span class="o">=</span> <span class="n">Outbound</span>
<span class="n">Source</span> <span class="o">=</span> <span class="mf">192.168.0.5</span>
<span class="n">Source</span> <span class="n">Port</span> <span class="o">=</span> <span class="n">Any</span>
<span class="n">Destination</span> <span class="o">=</span> <span class="mf">15.16.17.18</span>
<span class="n">Destination</span> <span class="n">Port</span> <span class="o">=</span> <span class="mi">443</span>
<span class="n">Action</span> <span class="o">=</span> <span class="n">Allow</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Wait! Don’t we already have a rule with that priority? Yes, we do. However, the list of Inbound and Outbound rules are independent of each other. So, it’s ok to have matching rule priority numbers as the are not conflicting.</p>

<blockquote>
  <p><strong>Tip:</strong> You can get your public IP address by executing:</p>

  <p><code class="language-plaintext highlighter-rouge">curl ifconfig.me</code></p>
</blockquote>

<h2 id="associating-an-nsg-to-nics">Associating an NSG to NICs</h2>

<p>While you technically can associate an NSG to a NIC it’s not advised. If you have a rule that is applicable to an associated subnet and another to an associated NIC, you do <em>not</em> get more security. The reason it’s advised to not associate an NSG to a NIC is simply to due to management of the rules. In a larger environment, you’re more likely to want to apply rules that affect multiple NICs on the same subnet. This is much easier than creating multiple rules that seem like duplicates each to a single NIC.</p>

<p class="notice"><strong>Note:</strong> Have you noticed that I refer to NICs and not VMs? This is because the NSG rules apply to all NICs even those of Private Endpoints.</p>

<h2 id="reply-traffic">Reply Traffic</h2>

<p>The NSG is stateful meaning that it will allow (until traffic is idle long enough) a reply to a request without a rule for the opposite direction. For example, calls from a VM out to some location on the internet will have replies allowed in.</p>

<h2 id="a-dirty-little-secret">A Dirty Little Secret</h2>

<p>When I was first learning about Azure networking I thought an NSG provided something like a perimeter around my VMs. Having a rule to deny traffic to a subnet meant I was scrutinizing the traffic to those VMs and that those VMs would be allowed to openly talk to each other. This is not the case at all!</p>

<p>The truth is, there is no virtual network! Every bit of information about VNet, subnet, routing, DNS, and NSG rules are explicitly applied to all of the NICs of the associated subnet(s). So when an NSG rule is for a subnet, it’s really being applied to each NIC on that subnet! Blocking SSH in a rule to a subnet means each NIC in that subnet has that block.</p>

<p>In reality, the NICs are simply programs running on an <a href="https://en.wikipedia.org/wiki/Field-programmable_gate_array" target="_blank">FPGA</a> chip. Even the server that is “virtual” is made to believe, at the OS level, that the NIC is a real piece of hardware. It’s just code.</p>

<p>This also explains why you cannot simply move a VM to another VNet. You can change association to another subnet but to move to another VNet requires several underlying things to be recreated. At this time, it’s just not possible.</p>

<!-- ## NSG Design - Building Tight Network Security -->

<!-- Scenarios

talk about 5 tuple source, source port, destination, destination port, and protocol

NSG to one Subnet same Vnet
NSG to 1+ Subnet same Vnet
NSG to 1+ Subnet 1+ Vnets
NSG 1 - Subnet 1 - NSG 2
NSG 1 - NIC 1 - NSG 2
NSG 1 - NIC 1 - NSG 2 

Outbound port being same as inbound port

VM public IP to private IP then to NSG
-->]]></content><author><name>Sean Whitesell</name></author><category term="Articles" /><category term="Network Security Group" /><category term="Azure" /><summary type="html"><![CDATA[There has been a lot of confusion around Azure’s Network Security Groups “NSG”. This post aims to explain details and hopefully alleviate misunderstandings as well as suggest some good ways to use NSGs. A bit of warning, this is not meant to be an introduction to NSGs so much as it is to explain some details and best uses.]]></summary></entry><entry><title type="html">Removing an Azure Application Gateway</title><link href="/2021/02/23/Removing-an-Azure-Application-Gateway" rel="alternate" type="text/html" title="Removing an Azure Application Gateway" /><published>2021-02-23T15:09:00-06:00</published><updated>2025-02-17T00:00:00-06:00</updated><id>/2021/02/23/Removing-an-Azure-Application-Gateway</id><content type="html" xml:base="/2021/02/23/Removing-an-Azure-Application-Gateway"><![CDATA[<p>While working with Terraform scripts I created many Azure Application Gateways. Sometime after they were created I would delete them as they were only needed to prove my scripts were working with Azure DevOps. I was using Terraform functions and special <em>magic</em> to get things just right. Then I noticed one of my App Gateways refused to delete.</p>

<p>I was using the Azure Portal as I have done many times. Simply selected the resources, then Delete, applied ‘yes’ when prompted. After a few minutes they were all gone as expected. One day, one of the App Gateways with the required resources like Public Ip Address, Virtual Network, etc, were still there after an attempt to delete.</p>

<p>Selecting the App Gateway showed the details that included the IP address, version, etc. But, it also showed in a large bar: <code class="language-plaintext highlighter-rouge">Failed</code>. I have seen it show <code class="language-plaintext highlighter-rouge">Deleting</code> before but never failed. I selected the Delete option again and after many minutes nothing changed. So, I tried to delete it using PowerShell.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$gtw</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-AzApplicationGateway</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"dev-example-appgateway"</span><span class="w">
</span><span class="nv">$gtw</span></code></pre></figure>

<p>Executing the two lines above showed the App Gateway’s Provisioning State as <code class="language-plaintext highlighter-rouge">Failed</code> and Operation State as <code class="language-plaintext highlighter-rouge">Stopping</code>.</p>

<p>I did some research and tried several things:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Start-AzApplicationGateway</span><span class="w"> </span><span class="nt">-ApplicationGateway</span><span class="w"> </span><span class="nv">$gtw</span><span class="w">
</span><span class="n">Stop-AzApplicationGateway</span><span class="w"> </span><span class="nt">-ApplicationGateway</span><span class="w"> </span><span class="nv">$gtw</span><span class="w">
</span><span class="n">Set-AzApplicationGateway</span><span class="w"> </span><span class="nt">-ApplicationGateway</span><span class="w"> </span><span class="nv">$gtw</span></code></pre></figure>

<p>Each took a few minutes and either did nothing or gave an error message. One message caught my eye.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup1/providers/Microsoft.Network/publicIPAddresses/dev-example-public-ip used by resource

/subscriptions/ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /resourceGroups/ myResourceGroup1/providers/Microsoft.Network/applicationGateways/dev-example-appgateway is not in Succeeded state. Resource is in Failed state.
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Looking closely I noticed the issue may not be the App Gateway after all, but a dependent resource; Public Ip Address.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$pip</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-AzPublicIpAddress</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">dev-example-public-ip</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="nx">myResourceGroup1</span><span class="w">
</span><span class="nv">$pip</span></code></pre></figure>

<p>Showing the details of the PIP also showed it was in a failed state.</p>

<p>All this time I thought it was the Application Gateway. So, with this extra knowledge, I tried a different approach. Some of the research suggested executing the Set command with no changes.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Set-AzPublicIpAddress</span><span class="w"> </span><span class="nt">-PublicIpAddress</span><span class="w"> </span><span class="nv">$pip</span><span class="w">
</span><span class="nv">$pip</span></code></pre></figure>

<p>It worked! Well, at least for that resource. I tried the Set again for the App Gateway and it didn’t show a change. Instead, it gave the same error. Ok, let’s try the delete again just to see. Mind you, I have tried this command before including the -Force switch.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Remove-AzApplicationGateway</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">dev-example-appgateway</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="nx">myResourceGroup1</span></code></pre></figure>

<p>After a few minutes, it simply returned to a prompt. No error message this time. So, I went back to the Azure Portal and refreshed. It worked! Problem solved.</p>

<p>The significance here is that one resource can show a failed state when it’s really a dependent resource that is in trouble. I hope this helps someone else and you don’t spend the research time like I did.</p>]]></content><author><name>Sean Whitesell</name></author><category term="Articles" /><category term="PowerShell" /><category term="Azure" /><category term="Application Gateway" /><summary type="html"><![CDATA[While working with Terraform scripts I created many Azure Application Gateways. Sometime after they were created I would delete them as they were only needed to prove my scripts were working with Azure DevOps. I was using Terraform functions and special magic to get things just right. Then I noticed one of my App Gateways refused to delete.]]></summary></entry><entry><title type="html">Terraform + Azure Availability Zones</title><link href="/2021/02/23/Terraform-and-Azure-Availability-Zones" rel="alternate" type="text/html" title="Terraform + Azure Availability Zones" /><published>2021-02-23T15:09:00-06:00</published><updated>2021-02-23T15:09:00-06:00</updated><id>/2021/02/23/Terraform-and-Azure-Availability-Zones</id><content type="html" xml:base="/2021/02/23/Terraform-and-Azure-Availability-Zones"><![CDATA[<p>While learning Terraform some time back, I wanted to leverage Availability Zones in Azure. I was specifically looking at Virtual Machine Scale Sets. <a href="https://www.terraform.io/docs/providers/azurerm/r/virtual_machine_scale_set.html">Terraform Azure Virtual Machine Scale Sets</a> Looking at the documentation Terraform has, I noticed there is no good example on using zones. So, I tried a few things to see what was really needed for that field. While doing some research, I noticed there are many people in the same situation. No good examples. I figured I’d create this post to help anyone else. And, of course, it’s a good reminder for me too if I forget the syntax on how I did this.</p>

<h2 id="body">Body</h2>

<p>While learning Terraform some time back, I wanted to leverage Availability Zones in Azure. I was specifically looking at Virtual Machine Scale Sets. <a href="https://www.terraform.io/docs/providers/azurerm/r/virtual_machine_scale_set.html">Terraform Azure Virtual Machine Scale Sets</a> Looking at the documentation Terraform has, I noticed there is no good example on using zones. So, I tried a few things to see what was really needed for that field. While doing some research, I noticed there are many people in the same situation. No good examples. I figured I’d create this post to help anyone else. And, of course, it’s a good reminder for me too if I forget the syntax on how I did this.</p>

<p>Here’s a very simple Terraform file. I just created a new folder then a new file called zones.tf. Here’s the contents:</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="k">variable</span> <span class="s2">"location"</span> <span class="p">{</span>
    <span class="nx">description</span> <span class="p">=</span> <span class="s2">"The location where resources will be created"</span>
    <span class="nx">default</span> <span class="p">=</span> <span class="s2">"centralus"</span>
    <span class="nx">type</span> <span class="p">=</span> <span class="nx">string</span>
<span class="p">}</span>

<span class="nx">locals</span> <span class="p">{</span>
    <span class="nx">regions_with_availability_zones</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"centralus"</span><span class="p">,</span><span class="s2">"eastus2"</span><span class="p">,</span><span class="s2">"eastus"</span><span class="p">,</span><span class="s2">"westus"</span><span class="p">]</span>
    <span class="nx">zones</span> <span class="p">=</span> <span class="nx">contains</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">regions_with_availability_zones</span><span class="p">,</span> <span class="kd">var</span><span class="p">.</span><span class="nx">location</span><span class="p">)</span> <span class="err">?</span> <span class="nx">list</span><span class="p">(</span><span class="s2">"1"</span><span class="p">,</span><span class="s2">"2"</span><span class="p">,</span><span class="s2">"3"</span><span class="p">)</span> <span class="err">:</span> <span class="kc">null</span>
<span class="p">}</span>

<span class="k">output</span> <span class="s2">"zones"</span> <span class="p">{</span>
    <span class="nx">value</span> <span class="p">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">zones</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The variable <code class="language-plaintext highlighter-rouge">location</code> is allowed to be changed from outside the script. But, I used <code class="language-plaintext highlighter-rouge">locals</code> for variables I didn’t want to be changed from outside. I hard coded a list of Azure regions that have availability zones. Right now it’s just a list of regions in the United States. Of course, this is easily modifiable to add other regions.</p>

<p>The <code class="language-plaintext highlighter-rouge">zones</code> local variable uses the contains function to see if the specified region is in that array. If so, then the value is a list of strings. Else it’s null. This is important. The zones field in Azure resources required either a list of strings or null. An empty list didn’t work for me.</p>

<p>As it is right now, you can run the Terraform Apply command and you should see some output. Changing the value of the location variable to something not in the list and you may not see output at all simply because the value is null.</p>

<p>Now, looking at a partial example from the Terraform documentation:</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">resource</span> <span class="s2">"azurerm_virtual_machine_scale_set"</span> <span class="s2">"example"</span> <span class="p">{</span> 
    <span class="nx">name</span> <span class="p">=</span> <span class="s2">"mytestscaleset-1"</span> 
    <span class="nx">location</span> <span class="p">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">location</span>
    <span class="nx">resource_group_name</span> <span class="p">=</span> <span class="s2">"</span><span class="k">${</span><span class="nx">azurerm_resource_group</span><span class="p">.</span><span class="nx">example</span><span class="p">.</span><span class="nx">name</span><span class="k">}</span><span class="s2">"</span> 
    <span class="nx">upgrade_policy_mode</span> <span class="p">=</span> <span class="s2">"Manual"</span> 
    <span class="nx">zones</span> <span class="p">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">zones</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now the zones field can be used safely when the value is either a list of strings or null. After I ran the complete Terraform script for VM Scale Set, I went to the Azure Portal to verify it worked.</p>

<p>This proved to me that I can use a region with and without availability zones in the same Terraform script.</p>

<p>For a list of Azure regions with Availability Zones, see: <a href="https://docs.microsoft.com/en-us/azure/availability-zones/az-overview" target="_blank">Azure Availability Zones</a></p>]]></content><author><name>Sean Whitesell</name></author><category term="Articles" /><category term="Terraform" /><category term="Azure" /><category term="Availability Zones" /><summary type="html"><![CDATA[While learning Terraform some time back, I wanted to leverage Availability Zones in Azure. I was specifically looking at Virtual Machine Scale Sets. Terraform Azure Virtual Machine Scale Sets Looking at the documentation Terraform has, I noticed there is no good example on using zones. So, I tried a few things to see what was really needed for that field. While doing some research, I noticed there are many people in the same situation. No good examples. I figured I’d create this post to help anyone else. And, of course, it’s a good reminder for me too if I forget the syntax on how I did this.]]></summary></entry><entry><title type="html">Azure VMs Marked for Deletion</title><link href="/2020/07/23/Azure-VM-marked-for-deletion" rel="alternate" type="text/html" title="Azure VMs Marked for Deletion" /><published>2020-07-23T16:09:00-05:00</published><updated>2020-07-23T16:09:00-05:00</updated><id>/2020/07/23/Azure-VM-marked-for-deletion</id><content type="html" xml:base="/2020/07/23/Azure-VM-marked-for-deletion"><![CDATA[<h2 id="are-you-serious">Are You Serious?!?</h2>

<p>The other day I was building out two Virtual Machines using an ARM template. I had been building many other VMs with no issues. Now, I have a failure. And two other VMs had issues being created. All four VMs had a Provision Status of <code class="language-plaintext highlighter-rouge">Failed</code>.</p>

<p>I checked the quota and determined that was not the issue. Looking at the failure message of each VM only said a failure occurred and provided no real details.</p>

<h2 id="why-are-you-here">Why are you here?</h2>

<p>This was ultimately fixed. This post is about a few things that occurred that allowed me to get these VMs out of a failed state.</p>

<p>The two VMs built using an ARM template worked successfully in another region. That other region had Availability Zones where the region with the failure did not. I did modify the ARM template to use Availability Sets instead.</p>

<p>The other two VMs were created by hand by a coworker. The steps used worked on other VMs in the same region. So, why the failures at all? Unfortunately, I don’t know. Fluke in the “matrix” for all I know. In those cases, were still stuck with determining the next step.</p>

<h2 id="the-right-sku">The Right SKU</h2>

<p>One suggestion worked on the two VMs not created by the ARM template. I changed the size of the VM. They went from “Standard_F4s_v2” to “Standard_F4s”. After the size change, I was able to start the VMs. A coworker then confirmed he was able to install needed software.</p>

<p>I tried to change the size on the VMs created by the ARM template. But it failed to work. Why? Because I tried to delete the VMs so I could start over. That actually hurt me. I didn’t yet know about attempting to change the VM size. So, what finally fixed it for me?</p>

<h2 id="finally-my-answer">Finally, My Answer</h2>

<p>This info was what I got from a Microsoft ticket. The first round of discussions with them resolved nothing. It wasn’t until they realize another piece of my puzzle that they gave a different suggestion.</p>

<p>Our VMs were in Availability Sets. That alone is not an issue. The trick was to stop each VM in the Availability Sets. Once each VM was stopped I retried the delete of the problematic VMs. <strong>IT WORKED!!</strong> I was finally able to remove those VMs and recreate them.</p>

<p>Once recreated I then went to each of the other VMs and started them. They all come back online. <strong>Eureka!!!</strong></p>

<p>Hopefully, this extra tidbit of detail will help you. If a VM fails on creation, don’t delete. Instead, try to change the VM size. If that fails, look at what else help dictate how a VM is allocated. For me, the Availability Set was the extra detail. Stopping all the VMs in an Availability Set allowed me to delete the problematic VMs and recreate them.</p>]]></content><author><name>Sean Whitesell</name></author><category term="Articles" /><category term="Terraform" /><category term="Azure" /><category term="Virtual Machines" /><summary type="html"><![CDATA[Are You Serious?!?]]></summary></entry></feed>