Terraform: Nested Conditional Example With Private Endpoint
Description:
Terraform doesn’t have a switch
statement like powershell but it does have conditionals. If you want, you can actually nest conditionals though it might look ugly. One place I did this the other day is for the three radio buttons for public access for a resource with private endpoints.
Anyone who deals with public access to resources with Private Endpoints in Azure is familiar with the Networking blade of resources that give you three radio buttons on the ‘Firewalls and Virtual Networks’ tab:
- “Allow public access from all networks” => Selecting this setting does not take away from private access if the resource has a private endpoint, it simply gives the
ability
of public to access the resource. - “Allow public access from specific virtual networks and IP addresses” => This blocks all public traffic unless you specify IP addresses.
- “Disable public access” => This blocks all network traffic that is not coming through the Private Endpoint, should be the default.
To Resolve:
-
So in our terraform code, let’s assume we have
var.enable_pep
as abool
andvar.ip_rules
as alist(string)
but optional, we could do the following to setup the three radio buttons:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
resource "azurerm_key_vault" "kv" { name = "keyvault1" location = var.region resource_group_name = var.resource_group enabled_for_disk_encryption = var.enabled_for_disk_encryption tenant_id = var.tenant_id # Keyvault => Networking => Firewalls and virtual networks Settings # if not enabling pep => "Allow public access from all networks" radio button selected # if enabling pep with ip rules => "Allow public access from specific virtual networks and IP addresses" radio button selected with ip rules defined. For this to work, public access must be true and the network_acls below will restrict. # if enabling pep with no ip rules => "Disable public access" radio button selected public_network_access_enabled = (var.enable_pep == true) && (length(var.ip_rules) > 0) ? true : ((var.enable_pep == true) && (length(var.ip_rules) == 0) ? false : true) # Only set Network ACLs if IP Rules are defined # NOTE 2022-10-21: I have noticed that even with this defined, sometimes it will choose the radio button "Disable public access". But.. if you move it up to "Allow public access from specific virtual networks and IP addresses" it will # have the IP addresses stored so it knows about them. # Also, if you add a single IP to the list and run another apply, it will move the radio button to the one you want. This has to be a glitch in the UI. dynamic "network_acls" { for_each = length(var.ip_rules) > 0 ? [1] : [] content { bypass = "AzureServices" default_action = "Deny" ip_rules = var.ip_rules } }
-
So the first thing to do when you see a long conditional if you want to understand it is to copy it to notepad++ and dissect it (my opinion of course, you do you) :
public_network_access_enabled = (var.enable_pep == true) && (length(var.ip_rules) > 0) ? true : ((var.enable_pep == true) && (length(var.ip_rules) == 0) ? false : true)
- First strip the final result, we know it’s just the attribute
public_network_access_enabled
- Next let’s add some line break to break it up:
1 2 3 4 5
(var.enable_pep == true) && (length(var.ip_rules) > 0) ? true : ((var.enable_pep == true) && (length(var.ip_rules) == 0) ? false : true)
1 2 3 4 5 6 7
condition1: (var.enable_pep == true) && (length(var.ip_rules) > 0) condition1_true_value: true condition1_false_value: ((var.enable_pep == true) && (length(var.ip_rules) == 0) condition2: (var.enable_pep == true) && (length(var.ip_rules) == 0) condition2_true_value: false condition2_false_value: true
-
So first, if enable_pep is
true
and theip_rules
have at least one value defined (they should default to an empty list[]
which gives lenght0
causing this to befalse
), then the true condition fires. It is set totrue
. According to my comments, this is because they have to be true for you to allow any public IPs and are dynamically listed below in thedynamic "network_acls"
section. -
OK, so what ifyou pass
enable_pep
is true but don’t even passip_rules
so they accept their default length of0
? Well that would trigger the false condition of the first conditional. Let’s look at it:((var.enable_pep == true) && (length(var.ip_rules) == 0) ? false : true)
. So what this saying is “ifenable_pep
is true andip_rules
has a lenght of0
(which is the case here), then trigger my true condition, else trigger my false”. -
So what is your true condition, well it is
false
. That means thatpublic_network_access_enabled
is set to false if we want to enable private endpoint but we don’t want any public IPs to access the resource, makes sense. -
So what is the false condition of the second conditional? Looks like it is set to
true
, when would that ever fire? Well, what if we passenable_pep
to false? The first condition says “ifenable_pep
true …” and the second condition says “ifenable_pep
true …”, so both of those would trigger false if you set it tofalse
, we need a “catch-all”. In this case, we wantpublic_network_access_enabled
to betrue
for those rare cases that we don’t have a private endpoint.
-
As mentioned in the comments, I have noticed a glitch in the Azure UI on resources with the middle radio button selected where if you set a list of public IP’s, it will default to the bottom radio button but if you select the middle one they “magically” show up like it knew about them the whole time. In addition, in terraform if you add an IP and run an apply, it will move to the middle radio button as expected. Not sure why this happens but take note. Thankfully, I don’t have to deal with the implications for this much because 99.99% of resources that have these options are set to the bottom radio button which disables all public network traffic.
Comments