I need to setup multiple security rules for Azure resources.
On AWS, I could just do multiple ingress:
resource "aws_security_group" "mygroup" {
name = "mygroup"
ingress {
description = "allow all on ssh port"
from_port = var.ssh
to_port = var.ssh
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "public port"
from_port = var.public
to_port = var.public
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "restricted"
from_port = var.restricted
to_port = var.restricted
protocol = "tcp"
cidr_blocks = ["<restricted-ip>/32"]
}
But I do not know how to do this on Azure.
As far as I can see azurerm_network_security_group allows only one security_rule (is this correct?).
Maybe I would be able to create multiple azurerm_network_interface_security_group_association for the same network_interface_id but different network_security_group_id?
You use an azurerm_network_security_rule resource per rule you add as follows: (example quoted from azurerm_network_security_rule resource docs)
resource "azurerm_network_security_group" "example" {
name = "acceptanceTestSecurityGroup1"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_network_security_rule" "example" {
name = "test123"
priority = 100
direction = "Outbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.example.name
network_security_group_name = azurerm_network_security_group.example.name
}
Technically, you can define them inline, but you shouldn't as it makes it impossible for other modules to add security group rules if needed. This happens pretty often in practice in my experience, so please don't use inline rule, use separate resources. Your colleagues, including your future self, will thank you.
Related
Having an issue creating a conditional resource based on a variable that's evaluated and used to influence a count in the resource. The issue is that the conditionally created resource is then referred to in other places in the code. For example, this security group:
resource "aws_security_group" "mygroup" {
count = var.deploy_mgroup ? 1 : 0
name = "mygroup-sg"
vpc_id = aws_vpc.main.id
ingress {
description = "Allow something."
from_port = 8111
to_port = 8111
protocol = "tcp"
security_groups = [aws_security_group.anothergroup.id]
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Then this is referred to in another group:
resource "aws_security_group" "rds" {
name = "rds-sg"
vpc_id = aws_vpc.main.id
ingress {
description = "Allow PGSQL"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [var.ingress_src_ip]
security_groups = [aws_security_group.mygroup[0].id,aws_security_group.anothergroup.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
So in this case I recognise that the resource using count has to be referenced as a list, which works OK if the variable deploy_mgroup is set to true. If it's set to false, the resource that has the count is obviously never created, so the list that the second group refers to aws_security_group.mygroup[0].id is empty, which throws me an error.
I'm not sure what I need to do here, maybe this is just a bad approach and there's something better I should be using? I haven't used Terraform for quite a while and I've missed a few versions.
Any pointers would be appreciated!
Thanks
I hastly read your post, and I had no time to try the solution I am going to suggest. For that reason: sorry! :)
I suggest you to change:
security_groups = [aws_security_group.mygroup[0].id,aws_security_group.anothergroup.id]
to
security_groups = var.deploy_mgroup ? [aws_security_group.mygroup[0].id,aws_security_group.anothergroup.id] : null
Errata Corrige:
I suggest you to change:
security_groups = [aws_security_group.mygroup[0].id,aws_security_group.anothergroup.id]
to
security_groups =
var.deploy_mgroup
? [aws_security_group.mygroup[0].id, aws_security_group.anothergroup.id]
: [aws_security_group.anothergroup.id]
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".
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.
I've written a simple module to provision a variable AZ numbered AWS VPC. It creates the route tables, gateways, routes, etc., but I'm having trouble keeping the security groups part DRY, i.e. keeping the module re-usable when specifying security groups.
This is as close as I can get:
varibles.tf:
variable "staging_security_groups" {
type = "list"
default = [ {
"name" = "staging_ssh"
"from port" = "22"
"to port" = "22"
"protocol" = "tcp"
"cidrs" = "10.0.0.5/32,10.0.0.50/32,10.0.0.200/32"
"description" = "Port 22"
} ]
}
main.tf:
resource "aws_security_group" "this_security_group" {
count = "${length(var.security_groups)}"
name = "${lookup(var.security_groups[count.index], "name")}"
description = "${lookup(var.security_groups[count.index], "description")}"
vpc_id = "${aws_vpc.this_vpc.id}"
ingress {
from_port = "${lookup(var.security_groups[count.index], "from port")}"
to_port = "${lookup(var.security_groups[count.index], "to port")}"
protocol = "${lookup(var.security_groups[count.index], "protocol")}"
cidr_blocks = ["${split(",", lookup(var.security_groups[count.index], "cidrs"))}"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags {
Name = "${lookup(var.security_groups[count.index], "name")}"
environment = "${var.name}"
terraform = "true"
}
}
Now this is fine, as long as what you want is to create a security group per port :) What I really need, is some way to call ingress the number of times that there are values in the variable staging_security_groups[THE SECURITY GROUP].from_port (please excuse the made-up notation).
You could look at using aws_security_group_rule instead of having your rules inline. You can then create a module like this:
module/sg/sg.tf
resource "aws_security_group" "default" {
name = "${var.security_group_name}"
description = "${var.security_group_name} group managed by Terraform"
vpc_id = "${var.vpc_id}"
}
resource "aws_security_group_rule" "egress" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "All egress traffic"
security_group_id = "${aws_security_group.default.id}"
}
resource "aws_security_group_rule" "tcp" {
count = "${var.tcp_ports == "default_null" ? 0 : length(split(",", var.tcp_ports))}"
type = "ingress"
from_port = "${element(split(",", var.tcp_ports), count.index)}"
to_port = "${element(split(",", var.tcp_ports), count.index)}"
protocol = "tcp"
cidr_blocks = ["${var.cidrs}"]
description = ""
security_group_id = "${aws_security_group.default.id}"
}
resource "aws_security_group_rule" "udp" {
count = "${var.udp_ports == "default_null" ? 0 : length(split(",", var.udp_ports))}"
type = "ingress"
from_port = "${element(split(",", var.udp_ports), count.index)}"
to_port = "${element(split(",", var.udp_ports), count.index)}"
protocol = "udp"
cidr_blocks = ["${var.cidrs}"]
description = ""
security_group_id = "${aws_security_group.default.id}"
}
modules/sg/variables.tf
variable "tcp_ports" {
default = "default_null"
}
variable "udp_ports" {
default = "default_null"
}
variable "cidrs" {
type = "list"
}
variable "security_group_name" {}
variable "vpc_id" {}
Use the module in your main.tf
module "sg1" {
source = "modules/sg"
tcp_ports = "22,80,443"
cidrs = ["10.0.0.5/32", "10.0.0.50/32", "10.0.0.200/32"]
security_group_name = "SomeGroup"
vpc_id = "${aws_vpc.this_vpc.id}"
}
module "sg2" {
source = "modules/sg"
tcp_ports = "22,80,443"
cidrs = ["10.0.0.5/32", "10.0.0.50/32", "10.0.0.200/32"]
security_group_name = "SomeOtherGroup"
vpc_id = "${aws_vpc.this_vpc.id}"
}
References:
For why optionally excluding a resource with count looks like this (source):
count = "${var.udp_ports == "default_null" ? 0 : length(split(",", var.udp_ports))}"
And the variable is set to:
variable "udp_ports" {
default = "default_null"
}
I managed to create really simple yet dynamic security group module that you can use. Idea here is to have ability to add any port you desire, and add to that port any range of ips you like. You can even remove egress from module as it will be created by default, or follow idea i used in ingress so you have granular egress rules (if you wish so).
module/sg/sg.tf
data "aws_subnet_ids" "selected" {
vpc_id = "${var.data_vpc_id}"
}
resource "aws_security_group" "main" {
name = "${var.sg_name}-sg"
vpc_id = "${var.data_vpc_id}"
description = "Managed by Terraform"
ingress = ["${var.ingress}"]
lifecycle {
create_before_destroy = true
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
module/sg/vars.tf
variable "sg_name" {}
variable "data_vpc_id" {}
variable "ingress" {
type = "list"
default = []
}
ingress var needs to be type list. If you call vpc id manually you dont need data bit in module, im calling my vpc_id from terraform state that is stored in s3.
main.tf
module "aws_security_group" {
source = "module/sg/"
sg_name = "name_of_sg"
data_vpc_id = "${data.terraform_remote_state.vpc.vpc_id}"
ingress = [
{
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Managed by Terraform"
},
{
from_port = 0
to_port = 100
protocol = "tcp"
cidr_blocks = ["10.10.10.10/32"]
description = "Managed by Terraform"
},
{
from_port = 2222
to_port = 2222
protocol = "tcp"
cidr_blocks = ["100.100.100.0/24"]
description = "Managed by Terraform"
},
]
}
You can add as many ingress blocks you like, i have only 3 for test purposes. Hope this helps.
Note: You can follow this idea for many resources like RDS, where you need to specify parameters in parameter group or even tags. Cheers
Not sure if it was available at the time Brandon Miller's answer was written, but avoid count loops as they are ordered. So if you add or delete one port, it will cause all rules after it to be rebuilt as they rely on the count index, which changes. Far better to use a for_each loop. Make sure you use set not lists for this eg
variable "tcp_ports" {
default = [ ]
# or maybe default = [ "22", "443" ]
type = set(string)
}
resource "aws_security_group_rule" "tcp" {
for_each = var.tcp_ports
description = "Allow ${var.cdir} to connect to TCP port ${each.key}"
type = "ingress"
from_port = each.key
to_port = each.key
protocol = "tcp"
cidr_blocks = var.cdir
security_group_id = aws_security_group.default.id
}
Now you can add and delete ports without incurring unnecessary create and destroys
you you cant alter your data from lists to sets for any reason just wrap it eg
toset(var.tcp_ports)
or use a local to munge your data accordingly. You can also use maps as well
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.