Using Conditional Expressions in Terraform - terraform

I heard that terraform doesn't support if else condition. I would like to know what is the alternative for if/else condition in that case when using terraform. I have the below piece of code which by default sets up the below 2 Rules on all the Network Security Group (i.e say around 20+ NSG Groups).
Now, for a specific Network Security Group (i.e nsg-restricted-jdmsg-chn), I wanted to adjust this default rule slightly.. I wanted to change the rdp port from 3389 to 33091 and for the second rule, I wanted to change the ssh port from 22 to 26.
How can we accomplish this condition rule in terraform?
resource "azurerm_network_security_rule" "testrules" {
for_each = local.nsgrules
resource_group_name = var.jdgrp.rg.name
location = var.jdgrp.rg.location
name = nsg-restricted-${var.cfg.name}"
security_rule {
name = rdp
direction = "Inbound"
access = "Allow"
priority = "100"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = 3389
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "192.168.2.0/24"
}
security_rule {
name = ssh
direction = Inbound
access = "Allow"
priority = "101"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "192.168.2.0/24"
}
}
Additional Info
The value for name = nsg-restricted-${var.cfg.name}" is getting it from the below code.
jd-tech.tf
module "jd-tech" {
for_each = {
"${local.jd-tech_rst.name" = local.jd-tech_rst
"${local.jd-tech_dct.name" = local.jd-tech_dct
"${local.jd-tech_rpr.name" = local.jd-tech_rpr
"${local.jd-tech_amb.name" = local.jd-tech_amb
...
...
}
source = "./jd-tech"
...
...
cfg = each.value
...
}

The easiest way to do this would be by using the conditional expresion (you probably know it as ternary operator from other programming languages) [1]. To achieve what you want, you just need to do this for RDP:
destination_port_range = local.jd-tech_chn.name == "nsg-restricted-jdmsg-chn" ? 33091 : 3389
And for SSH:
destination_port_range = local.jd-tech_chn.name == "nsg-restricted-jdmsg-chn" ? 26 : 22
You would have to define either a local variable or a regular one. In my answer it is a regular variable.
[1] https://www.terraform.io/language/expressions/conditionals#conditional-expressions

Related

Terraform azurerm provider count and csvdecode

I'm trying to populate NSG rules from a CSV file.
CSV file:
name,priority,direction,access,protocol,source_port_range,destination_port_range,destination_port_ranges,source_address_prefix,destination_address_prefix,resource_group_name,network_security_group_name
allowindatasubnet,600,inbound,allow,*,*,*,,192.168.3.0/24,*,resourcegroup1,networksecgroup1
allowinremote,700,inbound,allow,*,*,,"3389,22",192.168.1.128/27,*,resourcegroup1,networksecgroup1
denyinall,1000,inbound,deny,*,*,*,,*,*,resourcegroup1,networksecgroup1
tf file:
locals {
network_security_group_rules = csvdecode(file("/csvfile.csv"))
}
resource "azurerm_network_security_rule" "network_security_rule_WL1" {
count = length(local.network_security_group_rules)
name = local.network_security_group_rules[count.index].name
priority = local.network_security_group_rules[count.index].priority
direction = local.network_security_group_rules[count.index].direction
access = local.network_security_group_rules[count.index].access
protocol = local.network_security_group_rules[count.index].protocol
source_port_range = local.network_security_group_rules[count.index].source_port_range
destination_port_range = local.network_security_group_rules[count.index].destination_port_range
destination_port_ranges = [local.network_security_group_rules[count.index].destination_port_ranges]
source_address_prefix = local.network_security_group_rules[count.index].source_address_prefixyes
destination_address_prefix = local.network_security_group_rules[count.index].destination_address_prefix
resource_group_name = local.network_security_group_rules[count.index].resource_group_name
network_security_group_name = local.network_security_group_rules[count.index].network_security_group_name
}
This works fine without the destination_port_ranges attribute within the nsg rule resource block, but when I add it in I get an error:
Error: "destination_port_ranges": conflicts with destination_port_range
I understand that I need to use one argument or the other, but can anyone help me with the syntax or suggest changes that I can make allowing me to keep the same CSV format?
Also is my config correct for specifying a list of ports for the destination_port_ranges argument?
UPDATE:
I tried the following which was suggested by a friend but this threw up the same exception.
destination_port_range = local.network_security_group_rules[count.index].destination_port_range != "" ? local.network_security_group_rules[count.index].destination_port_range : null
destination_port_ranges = local.network_security_group_rules[count.index].destination_port_ranges != "" ? split(",", local.network_security_group_rules[count.index].destination_port_ranges) : null
Thanks!
As you said that you just need one argument, not both. As I see, all your destination port is a list or the character * and it means a range. Let's see the description of the argument destination_port_ranges and destination_port_range:
destination_port_range - (Optional) Destination Port or Range. Integer
or range between 0 and 65535 or * to match any. This is required if
destination_port_ranges is not specified.
destination_port_ranges - (Optional) List of destination ports or port
ranges. This is required if destination_port_range is not specified.
You use a list of destination ports or port ranges, so you just need to set the argument destination_port_ranges in the csv file for the network security rules.
Update:
You can use a module for the rules, the module is used to decide use which attribute for each rule:
./main.tf
locals {
network_security_group_rules = csvdecode(file("/csvfile.csv"))
}
module "rules" {
source = "./modules/rules"
count = length(local.network_security_group_rules)
rule = local.network_security_group_rules[count.index]
}
./modules/rules/main.tf
variable "rule" {}
resource "azurerm_network_security_rule" "network_security_rule_WL1" {
count = rule.destination_port_range == null ? 0 : 1
name = local.network_security_group_rules[count.index].name
priority = local.network_security_group_rules[count.index].priority
direction = local.network_security_group_rules[count.index].direction
access = local.network_security_group_rules[count.index].access
protocol = local.network_security_group_rules[count.index].protocol
source_port_range = local.network_security_group_rules[count.index].source_port_range
destination_port_range = local.network_security_group_rules[count.index].destination_port_range
source_address_prefix = local.network_security_group_rules[count.index].source_address_prefixyes
destination_address_prefix = local.network_security_group_rules[count.index].destination_address_prefix
resource_group_name = local.network_security_group_rules[count.index].resource_group_name
network_security_group_name = local.network_security_group_rules[count.index].network_security_group_name
}
resource "azurerm_network_security_rule" "network_security_rule_WL1" {
count = rule.destination_port_ranges == null ? 0 : 1
name = local.network_security_group_rules[count.index].name
priority = local.network_security_group_rules[count.index].priority
direction = local.network_security_group_rules[count.index].direction
access = local.network_security_group_rules[count.index].access
protocol = local.network_security_group_rules[count.index].protocol
source_port_range = local.network_security_group_rules[count.index].source_port_range
destination_port_ranges = [local.network_security_group_rules[count.index].destination_port_ranges]
source_address_prefix = local.network_security_group_rules[count.index].source_address_prefixyes
destination_address_prefix = local.network_security_group_rules[count.index].destination_address_prefix
resource_group_name = local.network_security_group_rules[count.index].resource_group_name
network_security_group_name = local.network_security_group_rules[count.index].network_security_group_name
}
In this way, you can't create a rule with both two attributes not null, I mean each rule only can have one of the two attributes.

What is the equivalent argument field for source, source service tag and destination field in NSG security rules azure using teraform deployment

In the Source and Destination columns, VirtualNetwork, AzureLoadBalancer, and Internet are service tags, rather than IP addresses. How do i create this using terraform?
I am trying to create NSG on azure using terraform. While creating nsg security rules fields like source, source service tags and destination field is mandatory for creating nsg. How do i create this field using terraform ?
You could see source_address_prefix or destination_address_prefix in the Argument Reference. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used.
For example, you just need to add the specific service tag in the source_address_prefix and destination_address_prefix field.
# VirtualNetwork
security_rule {
name = "RDP"
priority = 300
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "VirtualNetwork"
}
#AppService
security_rule {
name = "SQL"
priority = 310
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "AppService"
destination_address_prefix = "*"
}
result

azurerm_network_security_rule with variable source_address_prefix

I'm deploying some firewall rules on Azure with Terraform and would like to keep the "source_address_prefix" in a variable, given that the list contains more than 20 IPs and they can change. Since I have around 5 rules, it's not ideal to add the IPs in each block and would rather use a variable
Tried the following variations of variable:
source_address_prefix = ["${var.whitelist_ips}"]
source_address_prefix = "${var.whitelist_ips}"
variables.tf
variable "whitelist_ips" {
type = "list"
default = ["199.83.128.0/21","198.143.32.0/19", "149.126.72.0/21","103.28.248.0/22", "45.64.64.0/22", "185.11.124.0/22", "192.230.64.0/18", "107.154.0.0/16", "45.60.0.0/16", "45.223.0.0/16", "2a02:e980::/29"]
}
main.tf
resource "azurerm_network_security_rule" "https" {
name = "Whitelist-HTTPS"
priority = 101
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "443"
destination_port_range = "*"
source_address_prefix = ["${var.whitelist_ips}"]
destination_address_prefix = "${azurerm_public_ip.ingress.ip_address}"
resource_group_name = "test"
network_security_group_name = "test"
depends_on = [azurerm_resource_group.aks]
}
Getting the following errors:
Error: Incorrect attribute value type
on main.tf line 35, in resource "azurerm_network_security_rule" "http":
35: source_address_prefix = ["${var.whitelist_ips}"]
Inappropriate value for attribute "source_address_prefix": string required.
Begone with that weird 0.11 syntax, with explicit depends_on, also the source port seemed wrong:
resource azurerm_network_security_rule this {
name = "Whitelist-HTTPS"
resource_group_name = azurerm_resource_group.this.name
network_security_group_name = azurerm_network_security_group.this.name
priority = 101
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefixes = var.whitelist_ips
destination_address_prefix = azurerm_public_ip.ingress.ip_address
}
variable whitelist_ips {
description = "A list of IP CIDR ranges to allow as clients. Do not use Azure tags like `Internet`."
default = ["199.83.128.0/21", "198.143.32.0/19", "2a02:e980::/29"]
type = list(string)
}
Should have paid attention to the docs. The actual block is "source_address_prefixes" and not "source_address_prefix".

How to reference azure network security ID in a module in Terraform?

I'm not sure how to reference an azure network security group in a module. I created a module that I can reuse for any VM I create which works to an extent except I'm not sure how to assign the network security group ID to it. The below is an example (slightly amended, I don't have it on me) that is very close to what I have and is based on.
main.tf at root
module "vm1" {
source = "/modules/vm/"
NSG = ????
}
tfvars
nic_name = apache_vm_nic
location = West Europe
........
modules/vm/main.tf
.........
resource "azurerm_network_interface" "myterraformnic" {
name = "var.nic_name"
location = "var.location"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
network_security_group_id = { WHAT DO I PUT HERE? }
ip_configuration {
name = "myNicConfiguration"
subnet_id = "${azurerm_subnet.myterraformsubnet.id}"
private_ip_address_allocation = "dynamic"
public_ip_address_id = "${azurerm_public_ip.myterraformpublicip.id}"
}
}
resource "azurerm_network_security_group" "apache-nsg" {
name = "myNetworkSecurityGroup"
location = "eastus"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_network_security_group" "nginx-nsg" {
name = "myNetworkSecurityGroup"
location = "eastus"
resource_group_name = "${azurerm_resource_group.myterraformgroup.name}"
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
in the module/main.tf file under network_security_group_id, I can't exactly put ${azurerm_network_security_group.apache-nsg.id} or ${azurerm_network_security_group.nginx-nsg.id}. So what can I put so I can reuse this module for all VMs?
Thanks
Your question isn't quite clear to me but I am going to assume you want to create a generic network security group that you want to assign to multiple instances of your VM module.
If you want to pass the ID of a security group from main.tf at root, you'd do this:
Create a network security group resource outside your module, e.g. inside main.tf at root, just like you created a few inside your VM module (for Apache and Nginx), so that main.tf at root looks like this:
resource "azurerm_network_security_group" "some_generic_vm_nsg" {
....
}
module "vm1" {
source = "/modules/vm/"
NSG = "${azurerm_network_security_group.some_generic_vm_nsg.id}"
}
Note that we are now passing the ID of the nsg to your VM module instance.
However, your VM module has not declared the NSG variable yet. So create the file modules/vm/variables.tf and put this in it:
variable "NSG" {
type = "string"
}
And inside your module, network_security_group_id = { WHAT DO I PUT HERE? } becomes:
network_security_group_id = "${var.NSG}"
This way, you can assign the same network security group to multiple VM module instances.
You can study this documentation for more elaborate information.

Terraform Azure network security

I'm trying to configure a network security rule for a network security group in Azure via Terraform with multiple source addresses.
Based on the documentation
https://www.terraform.io/docs/providers/azurerm/r/network_security_rule.html
However, I'm not able to get this to work nor can I find any examples for it:
https://www.terraform.io/docs/providers/azurerm/r/network_security_rule.html#source_address_prefixes
I get the Error:
Error: azurerm_network_security_rule.test0: "source_address_prefix": required field is not set
Error: azurerm_network_security_rule.test0: : invalid or unknown key: source_address_prefixes
Here is my sample:
resource "azurerm_network_security_rule" "test0" {
name = "RDP"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefixes = "{200.160.200.30,200.160.200.60}"
destination_address_prefix = "VirtualNetwork"
network_security_group_name= "${azurerm_network_security_group.test.name}"
resource_group_name = "${azurerm_resource_group.test.name}"
}
Please let me know.
Thanks!
source_address_prefixes needs list of source address prefixes.
Modify it as below:
source_address_prefixes = ["200.160.200.30","200.160.200.60"]
There also a mistake in azurerm_network_security_group.test.name, the correct type is azurerm_network_security_group.test0.name. The following tf file works for me.
resource "azurerm_resource_group" "test0" {
name = "shuinsg"
location = "West US"
}
resource "azurerm_network_security_group" "test0" {
name = "shuinsgtest"
location = "${azurerm_resource_group.test0.location}"
resource_group_name = "${azurerm_resource_group.test0.name}"
}
resource "azurerm_network_security_rule" "test0" {
name = "RDP"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefixes = ["200.160.200.30","200.160.200.60"]
destination_address_prefix = "VirtualNetwork"
network_security_group_name= "${azurerm_network_security_group.test0.name}"
resource_group_name = "${azurerm_resource_group.test0.name}"
}
Here is my test result.
An "address_prefix" is a string values representing a CIDR e.g. 10.0.0.0/24. So in your case source_address_prefix = "200.160.200.30/32" and destination_address_prefix = "${azurerm_virtual_network.test.address_space.0}" depending on what you want to refer to.

Resources