After writing my previous blog about the implementation of conditionals in Terraform, I would like to spend this article discussing some additional quirks I found with Azure and Terraform. Terraform is still very much a language under development, so this blog will focus on Terraform v0.12.4 and AzureRm v1.31. For those unfamiliar with Terraform conditionals, conditionals are solely done through ternary operators. If we do not want a resource to be created under certain conditions, we would use the ternaries to deploy a resource with a count of 0- essentially deploying a resource 0 times. For the rest of this blog, I would like to talk about more the strange things I found working with Terraform and Azure for the first time.
Reserved IP Addresses
If you normally work with dynamically assigned IP address, you will likely not realize that Azure subnets reserved five IP addresses. By the conventions of imperative programming, one might be inclined to start from the beginning of the subnet, or cidrhost(prefix, 0). However, Azure will throw an error about the IP being reserved. This is not entirely unexpected: by convention the first address, 0, is used to identify a /24 network. Similarly, the last addresses of a /24 network is used to broadcast to all devices on a network. Indeed, within Azure, both of these addresses are reserved beforehand- even for subnets with different masks than /24. What is more unexpected is that the next few addresses will result in the same error- the subnet addresses are reserved. According to an Microsoft virtual network faq, "x.x.x.1-x.x.x.3 is reserved in each subnet for Azure services." All in all, there are 5 total IP address reserved within each Azure subnet. x.x.x.0 - x.x.x.3 and the last address of the subnet. Thus, any static IP allocations using cidrhost must start from at least 4.
Network routes getting destroyed and recreate each "apply"
This is due to a weird transition stage to version 2.0 of AzureRm. Terraform documentation informs readers how to avoid this behavior- just not the symptoms if you forget to follow their advice. I got this error when I only created a route table association, but did not include the route table ID within the subnet. When the template is redeployed, the subnet does not expect a route table and destroys the one linked to it through the association.
According to the Terraform documentation on subnets, "At this time Subnet
<-> Route Table associations need to be configured both using this field (which is now Deprecated) and/or using the
azurerm_subnet_route_table_association resource." Various Terraform resources take advantage of this association resource, but some resources seem work without an explicit Id declaration. For now, it might be good practice to include these field declarations to avoid strange behaviors with redeployment.
"Missing Required Argument" error for list of objects
Some Terraform resources will require a "list of objects" as an argument. Naturally, the list of required elements will depend on the resource. For this section, we will discuss my experience working with the Azure route table resource within Terraform. You can specify routes directly in this resource as a list of route objects. If you read the documentation for the route object, it says that the next_hop_in_ip_address parameter is optional. However, if you have more than one route configured, Terraform will throw an error during planning about the next_hop_in_ip_address argument being missing.
It turns out that Terraform requires you to declare the argument null . Before I encountered this error, I did not even know that Terraform support null data types. My guess for the cause of this error- I have not found any explanation for it- is that Terraform is directly constructing Route resources based on this list. It seems that by explicitly creating a resource within a resource block, optional parameters are automatically declared as null. This does not seem to be the case when creating a list of objects.
Generally, Terraform will allow you create these resources within these list of object as their own resources. In the situation discussed in this section, individual azurerm_route resources can be created. This leads to more maintainable code. In general, Terraform can be picky about these in-line resource declaration. Another author has reported the mixed in-line and individual routes will lead to conflicts in Terraform.
Deploying Azure Resources with the same name
Copy and paste can be a programmer's best friend, but sometime we slip up and forgot to change something after pasting. While Terraform complains about its own resources sharing the same name, it has no qualms about resources having the same name in Azure.The name of a resource in terraform does not necessarily have to match its name in the cloud provider: sometimes this is necessary due to naming limitation or situations such as loops. Strangely, it seems that Azure does not always throw errors when tasked with creating two or more resources with the same name.
In my case, I accidentally created two network security group with the same name in Azure. This yielded no errors during the planning phase- in fact, no errors were thrown at all. The only symptom is that Azure get stuck deploying. After force restarting Powershell, Terraform is actually able to complete deployment without any warnings. Only after checking the Azure Portal, was I able to realize what went wrong. It seemed that the network security group that was deployed last overwrote the previous one.
Hopefully, this will be fixed. But for now, if your Terraform Azure deployment freezes, know that redundant naming might be a cause.