"DuplicateResourceName" creating azurerm_network_security_group dynamic rules - azure

As a terraform user I'm interested on allowing ICMP&TCP protocols between 2 virtual machines. In order to achieve that I created adynamic network_security_group but terraform is throwing below error:
│ Error: creating/updating Network Security Group: (Name "***01-tf-SG***" / Resource Group "RG_Terraform"): network.SecurityGroupsClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: Code="InvalidRequestFormat" Message="Cannot parse the request." Details=[{"code":"DuplicateResourceName","message":"Resource /subscriptions//resourceGroups//providers/Microsoft.Network/networkSecurityGroups/ has two child resources with the same name (01-tf-SG)."}]
│
│ with azurerm_network_security_group.linux_vm_nsg,
│ on main.tf line 291, in resource "azurerm_network_security_group" "linux_vm_nsg":
│ 291: resource "azurerm_network_security_group" "linux_vm_nsg" {
│
╵
It seems the problem is related the name 01-tf-SG but name field is mandatory and even using different names for resouce_name and content_name the issue still happens.
See the Terraform code from mian.tf file:
resource "azurerm_network_security_group" "linux_vm_nsg" {
depends_on = [azurerm_resource_group.main]
name = "01-tf-SG"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
dynamic "security_rule" {
for_each = toset(["Icmp", "Tcp"])
content {
name = "01-tf-SG"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = security_rule.value
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "172.16.25.10/32"
destination_address_prefix = "10.0.1.10/32"
}
}
}

You can generate different name for the security_rule. For example as follows:
dynamic "security_rule" {
for_each = {for idx, val in ["Icmp", "Tcp"]: idx => val}
content {
name = "01-tf-SG-${each.security_rule.key}"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = security_rule.value
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "172.16.25.10/32"
destination_address_prefix = "10.0.1.10/32"
}
}

Related

Need to pass values for nsg rules in terraform resource block from a map type variable

I am trying a create a resource block for azurerm_network_security_rule such that it can be mapped with subnet variable being used in resource block as well so that subnet association with nsg would be easy.
for eg. variable is like
variable "subnets" {
description = "A list of subnets inside the VPC"
type = map(any)
default = {
subnet = {
name = "test"
address_prefix = ["x.x.x.x/x"]
attached_nat = "false"
inbound_rules = [
# [name, priority, direction, access, protocol, destination_port_range, source_address_prefix, destination_address_prefix]
["http", "100", "Inbound", "Allow", "Tcp", "80", "*", "0.0.0.0/0"],
]
outbound_rules = [
# [name, priority, direction, access, protocol, destination_port_range, source_address_prefix, destination_address_prefix]
["ntp_out", "103", "Outbound", "Allow", "Udp", "123", "", "0.0.0.0/0"],
]
}
}
}
Getting error
**Error: Invalid for_each argument
│
│ on main.tf line 120, in resource "azurerm_network_security_rule" "subnet":
│ 120: for_each = concat([for k, v in var.subnets: {"inbound_rules"=lookup(v,"inbound_rules",[])}], [for k, v in var.subnets: {"outbound_rules"=lookup(v,"outbound_rules",[])}])
│ ├────────────────
│ │ var.subnets is map of object with 1 element
│
│ The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type │ tuple.╵**
my resource block is like -
resource "azurerm_network_security_group" "subnet" {
for_each = var.subnets
name = format("%s-${each.value.name}")
location = var.location
resource_group_name = var.resource_group_name
}
resource "azurerm_network_security_rule" "subnet" {
for_each = concat([for k, v in var.subnets: {"inbound_rules"=lookup(v,"inbound_rules",[])}], [for k, v in var.subnets: {"outbound_rules"=lookup(v,"outbound_rules",[])}])
name = each.value[0]
priority = each.value[1]
direction = each.value[2]
access = each.value[3]
protocol = each.value[4]
source_port_range = "*"
destination_port_range = each.value[5]
source_address_prefix = each.value[6]
destination_address_prefix = each.value[7]
description = "${each.value[2]}_Port_${each.value[5]}"
resource_group_name = var.resource_group_name
network_security_group_name = azurerm_network_security_group.subnet[each.key].name
}
If someone can help figuring out the loop to put in for_each to achieve it.

How to get round Plugin Errors when deploying Azure resources using Terraform?

I'm getting the following error
│ Error: Plugin error
│
│ with provider["registry.terraform.io/hashicorp/azurerm"],
│ on nsg.tf line 13, in provider "azurerm":
│ 13: provider "azurerm" {
│
│ The plugin returned an unexpected error from plugin.(*GRPCProvider).ConfigureProvider: rpc error: code = Internal desc = grpc: error while marshaling: string field
│ contains invalid UTF-8
when I try to do Terraform plan on the following code
# Configure the Microsoft Azure Provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.74.0"
}
}
}
# Configure the Microsoft Azure Provider
provider "azurerm" {
features {}
}
resource "azurerm_network_security_group" "nsg" {
name = "TestNSG"
location = "East US"
resource_group_name = "TFResourcegroup"
}
resource "azurerm_network_security_rule" "example1" {
name = "Web80"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = "TFResourcegroup"
network_security_group_name = azurerm_network_security_group.nsg.name
}
resource "azurerm_network_security_rule" "example2" {
name = "Web8080"
priority = 1000
direction = "Inbound"
access = "Deny"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8080"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = "TFResourcegroup"
network_security_group_name = azurerm_network_security_group.nsg.name
}
resource "azurerm_network_security_rule" "example3" {
name = "WebOUT"
priority = 1000
direction = "Outbound"
access = "Deny"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = "TFResourcegroup"
network_security_group_name = azurerm_network_security_group.nsg.name
}
these are my terraform, provider and Azure versions
PS C:\russ\nsg> terraform --version
Terraform v1.0.4
on windows_amd64
+ provider registry.terraform.io/hashicorp/azurerm v2.74.0
PS C:\russ\nsg> az --version
azure-cli 2.27.2
what I've tried.....
Researching on the internet,.it says that this only happens in Azure CLI in the Azure Portal..I've tried it on Visual Studio Code and Powershell on my own machine..but I get the same results..I even tried putting https://registry.terraform.io/providers/hashicorp/azurerm/latest as the source instead of the usual "hashicorp/azurerm" but that threw errros as well
I'm really stuck...any guidance would be gratefully received ..I've been happily deploying Azure resources all day using the same method...but for some reason..it just doesn't like this one...
This is likely due to an issue with your Az CLI session and you probably need to login again using az login.
You can confirm if this is the issue by running an Az CLI command that needs to interact with resources (not just az account show). This is how I identified the issue:
$ az vm list -g my-resource-group
AADSTS700082: The refresh token has expired due to inactivity. The token was issued on 2021-06-27T00:13:23.1948087Z and was inactive for 90.00:00:00.

Passing the outputs from a module when the module is created using a for_each

This links to my the previous question, click the link for the rest of my code and how it all fits together: Use output value from module that has a for_each set
Whilst the answer was helpful in solving the issue and allowing me to run the pipeline, I think there is an error because of the way the VM is generated using the for_each on the module. This results in the incorrect value being passed to the network_security_rule. Below is an example of the error:
Error: Error Creating/Updating Network Security Rule "nsr-sbox-http80" (NSG "module.fico_app_vm.linux_vm_nsg" / Resource Group "rg-sbox-app"): network.SecurityRulesClient#CreateOrUpdate: Failure sending request: StatusCode=404 -- Original Error: Code="ResourceNotFound" Message="The Resource 'Microsoft.Network/networkSecurityGroups/module.fico_app_vm.linux_vm_nsg' under resource group 'rg-sbox-app' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix"
on main.tf line 58, in resource "azurerm_network_security_rule" "fico-app-sr-80":
58: resource "azurerm_network_security_rule" "fico-app-sr-80" {
outputs.tf
output "linux_vm_ips" {
value = azurerm_network_interface.dwp_network_interface.private_ip_address
}
output "linux_vm_nsg" {
value = azurerm_network_security_group.dwp_network_security_group.name
}
At first I thought it was because the NSG isn't being created, but I checked the console and it does create it. The issue is the NSG is created in the module for each VM. The VM's are created by looping over the variable in tfvars file. How do I pass the NSG name created in the module to the security rule which is outside of the module?:
resource "azurerm_network_security_rule" "fico-app-sr-80" {
name = "nsr-${var.environment}-${var.directorate}-${var.business_unit}-${var.vm_identifier}${var.instance_number}-http80"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "80"
source_address_prefixes = ["module.fico_web_vm.linux_vm_ips"]
destination_address_prefix = "VirtualNetwork"
resource_group_name = azurerm_resource_group.rg_fico_app.name
network_security_group_name = "module.fico_app_vm.linux_vm_nsg"
}
# Network Security Group
resource "azurerm_network_security_group" "network_security_group" {
name = "nsg-${var.environment}-${var.directorate}-${var.business_unit}-${var.vm_identifier}-${var.vm_name}"
resource_group_name = var.resource_group
location = var.location
}
Something to note as well, is that the var.vm_name iterates through the key of each map and this makes up part of the name of the NSG.
module in main.tf:
module "fico_app_vm" {
for_each = var.app_servers
source = "../modules/compute/linux_vm"
source_image_id = var.app_image_id
location = var.location
vm_name = each.key
vm_identifier = "${var.vm_identifier}${var.instance_number}"
vm = each.value
disks = each.value["disks"]
resource_group = azurerm_resource_group.rg_fico_app.name
directorate = var.directorate
business_unit = var.business_unit
environment = var.environment
network_rg_identifier = var.network_rg_identifier
subnet_name = "sub-${var.environment}-${var.directorate}-${var.business_unit}-be01"
diag_storage_account_name = var.diag_storage_account_name
ansible_storage_account_name = var.ansible_storage_account_name
ansible_storage_account_key = var.ansible_storage_account_key
log_analytics_workspace_name = var.log_analytics_workspace_name
backup_policy_name = var.backup_policy_name
enable_management_locks = true
}
tfvars:
app_servers ={
app-1 = {
size = "Standard_E2s_v3"
admin_username = "xxx"
public_key = "xxx"
disks = [32, 32]
zone_vm = "1"
zone_disk = ["1"]
}
}
I think it's the problem that you read the previous answer not carefully. The answer shows you need to change the security group name like this:
network_security_group_name = module.fico_app_vm.linux_vm_nsg
There are no double-quotes. Double-quotes means it's a string. But you need to use the module attribute, not a string with a value "module.fico_app_vm.linux_vm_nsg". The error also shows it directly.
I realised what my error was. I needed to change this:
network_security_group_name = "module.fico_app_vm.linux_vm_nsg"
to this
network_security_group_name = module.fico_app_vm[each.key].linux_vm_nsg
But I also needed to reference the value in the source address prefixes. So to reference the key I created another variable and changed this:
source_address_prefixes = ["module.fico_web_vm.linux_vm_ips"]
to this:
source_address_prefix = module.fico_web_vm[each.value.web_server].linux_vm_ip
I also had to change to source_address_prefix and thats why the type error was occurring!
The security rule now looks like this:
resource "azurerm_network_security_rule" "fico-app-sr-80" {
for_each = var.app_servers
name = "nsr-${var.environment}-${var.directorate}-${var.business_unit}-${var.vm_identifier}${var.instance_number}-http80"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = module.fico_web_vm[each.value.web_server].linux_vm_ip
destination_address_prefix = "VirtualNetwork"
resource_group_name = azurerm_resource_group.rg_dwp_fico_app.name
network_security_group_name = module.fico_app_vm[each.key].linux_vm_nsg
And variable looks like this:
app_servers = {
app-1 = {
size = "Standard_E2s_v3"
admin_username = "azureuser"
public_key = xxxx
disks = [32, 32]
zone_vm = "1"
zone_disk = ["1"]
web_server = "web-1"
}
}

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".

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