Terraform Map of List input variables-For_each - foreach

I am trying to implement for_each for map of list input variables.
What the output should be is a list of subnets should be created for AZ us-east-1a(3 subnets) and us-east-1b (2 subnets).
Need your help for the below:
MAIN.TF
resource "aws_vpc" "name" {
cidr_block = "192.168.0.0/16"
}
resource "aws_subnet" "name" {
for_each = var.subnetcidr
vpc_id = aws_vpc.name.id
availability_zone = each.key
cidr_block = toset(each.value) //need help here
}
TFVARS FILE
subnetcidr = {
"us-east-1a" = ["192.168.1.0/24","192.168.2.0/24","192.168.0.0/24"]
"us-east-1b" = ["192.168.3.0/24","192.168.4.0/24"]
}
Error:
Error: Incorrect attribute value type
on main.tf line 28, in resource "aws_subnet" "name":
28: cidr_block = toset(each.value)
|----------------
| each.value is tuple with 2 elements
Inappropriate value for attribute "cidr_block": string required.
Any help would be appreciated. TIA!

The error seems clear:
Inappropriate value for attribute "cidr_block": string required
Not sure why you are passing a list...
If you need to keep it as a list, an option could be getting the first value:
resource "aws_vpc" "name" {
cidr_block = "192.168.0.0/16"
}
resource "aws_subnet" "name" {
for_each = var.subnetcidr
vpc_id = aws_vpc.name.id
availability_zone = each.key
cidr_block = element(each.value, 0)
}
If you can change the subnetcidr variable, go with something like:
subnetcidr = {
"192.168.0.0/24" = "us-east-1a"
"192.168.1.0/24" = "us-east-1a"
"192.168.2.0/24" = "us-east-1a"
"192.168.3.0/24" = "us-east-1b"
"192.168.4.0/24" = "us-east-1b"
}
As you can see now the unique cidr blocks are the keys and the zones are the values.
Then in your code we do like this:
resource "aws_vpc" "name" {
cidr_block = "192.168.0.0/16"
}
resource "aws_subnet" "name" {
for_each = var.subnetcidr
vpc_id = aws_vpc.name.id
availability_zone = each.value
cidr_block = each.key
}

I was finally able to solve this. Below is how.
resource "aws_vpc" "name" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "name" {
vpc_id = aws_vpc.name.id
for_each = transpose(var.subnet)
availability_zone = each.value[0]
cidr_block = each.key
}
variable subnet {}
subnet = {
"us-east-1a" = ["10.0.0.0/20","10.0.16.0/20","10.0.32.0/20"]
"us-east-1b" =
["10.0.64.0/20","10.0.80.0/20"]
"us-east-1d" = ["10.0.112.0/20","10.0.128.0/20","10.0.144.0/20","10.0.96.0/20"]
}

Related

How to create multiple vm nics in a loop using terraform with static ipaddress assigned

I am trying to create 2 VM's in Azure using Terraform and each VM requires 4 nics, one in each subnet of the vnet and these nics must be configured with static IP addresses. Is there any way to accomplish this using loops ideally with for_each?
I can do this with 'dynamic' ip address assignment for the ip_configuration block without-issues but need it working with 'static' IP assignment and don't know how to pass the ip address inside the ip_configuration block of the network interface resource block.
I don't know if dynamic block is the right model for this here but I tried to use nested loop with dynamic ip_configuration block without much luck :-(
Here is what I have so far (Snippet for nics for subnet1) and would love to get some help;
variable "subnet1IpAddresses" {
type = map(string)
description = "The subnet1 IP address to assign to vm nodes."
default = {
"1" = "10.10.1.4"
"2" = "10.10.1.5"
}
}
locals {
vmName = [format("%s%s%s%s%s", var.envPrefix, "-", var.dptPrefix, var.vmPrefix, "01"), format("%s%s%s%s%s", var.envPrefix, "-", var.dptPrefix, var.vmPrefix, "02")]
subnet1Ip = [var.subnet1IpAddresses[1], var.subnet1IpAddresses[2]]
}
#create NIC for each NVA and Assign Static IP addresses.
resource "azurerm_network_interface" "subnet1Nics" {
for_each = toset(local.vmName)
name = "${each.value}subnet1-nic"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
enable_ip_forwarding = "true"
enable_accelerated_networking = "true"
dynamic ip_configuration {
for_each = local.subnet1Ip
content {
name = "ipconfig1"
subnet_id = azurerm_subnet.snet1.id
private_ip_address = ip_configuration.value
private_ip_address_allocation = "static"
primary = "true"
}
}
}
The problem with the above code 'as-is' is that it try to create two ip_configurations for the same VM so VM1 will have nic1 with two IP configurations i.e. (10.10.1.4 and 10.10.1.5) but that's not what I need.
Simply put, below is the outcome I desire for this specific example and hoping someone can help with it.
VM1's nic1 with IP (10.10.1.4)
VM2's nic1 with IP (10.10.1.5)
Thanks.
Here is an example for your purpose: 2 VMs, each VM has 4 NICs, 4 subnets in the VNet, each NIC of the VM in a subnet.
main.tf:
provider "azurerm" {
features {}
}
variable "nics" {}
variable "vnet_prefix" {}
variable "subnet_prefixes" {}
resource "azurerm_resource_group" "group" {
name = "charlesVM"
location = "East Asia"
}
resource "azurerm_virtual_network" "vnet" {
name = "charles-vnet"
resource_group_name = azurerm_resource_group.group.name
location = azurerm_resource_group.group.location
address_space = var.vnet_prefix
}
resource "azurerm_subnet" "subnets" {
count = length(var.subnet_prefixes)
name = "subnet-${count.index}"
resource_group_name = azurerm_resource_group.group.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefix = element(var.subnet_prefixes, count.index)
}
resource "azurerm_network_interface" "nics" {
count = length(var.nics)
name = "nic-${count.index}"
location = azurerm_resource_group.group.location
resource_group_name = azurerm_resource_group.group.name
ip_configuration {
name = "config-${count.index}"
subnet_id = element(azurerm_subnet.subnets[*].id, count.index % 4)
private_ip_address_allocation = "Static"
private_ip_address = element(var.nics, count.index)
}
}
locals {
vm_nics = chunklist(azurerm_network_interface.nics[*].id, 4)
}
resource "azurerm_linux_virtual_machine" "vm" {
count = 2
name = "azurevm-${count.index}"
resource_group_name = azurerm_resource_group.group.name
location = azurerm_resource_group.group.location
size = "Standard_DS3_v2"
admin_username = "adminuser"
network_interface_ids = element(local.vm_nics, count.index)
admin_ssh_key {
username = "adminuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
}
terraform.tfvar:
nics = [
"10.0.0.4",
"10.0.1.4",
"10.0.2.4",
"10.0.3.4",
"10.0.0.5",
"10.0.1.5",
"10.0.2.5",
"10.0.3.5"
]
vnet_prefix = ["10.0.0.0/16"]
subnet_prefixes = [
"10.0.0.0/24",
"10.0.1.0/24",
"10.0.2.0/24",
"10.0.3.0/24"
]
Note that you want to use the static IP address, then you need to calculate them manually to avoid the conflict, and make sure each IP address of the NIC is right in the subnet. And you also need to choose the right VM size that can support 4 NICs for one VM.

terraform azurerm_scheduled_query_rules_log

Hi I'm running terraform
Terraform v0.13.4
provider registry.terraform.io/hashicorp/azurerm v2.41.0
I'm trying to set up azure metric monitoring for vm
resource "azurerm_scheduled_query_rules_log" "scheduled_rules" {
for_each = local.alert_rules
name = "${var.client_initial}-${each.key}"
location = var.resource_group_name.location
resource_group_name = var.resource_group_name
criteria {
metric_name = each.value.metric_name
dimension {
name = "Computer"
operator = "Include"
values = var.virtual_machines
}
}
data_source_id = var.log_analytics_workspace_ID
description = each.value.description
enabled = true
}
However when i run plan, it tells me
53: resource "azurerm_scheduled_query_rules_log" "scheduled_rules" {
The provider provider.azurerm does not support resource type
"azurerm_scheduled_query_rules_log".
I see this new resource is introduced in azurerm 2.1, not sure why it's not available on 2.41.0?
I also face the same error. It should be the resource azurerm_monitor_scheduled_query_rules_log instead of azurerm_scheduled_query_rules_log. There might be some mistakes or do not update in the terraform Example Usage.
Here is a working example with Terraform v0.14.3 + azurerm v2.41.0
# Example: LogToMetric Action for the named Computer
resource "azurerm_monitor_scheduled_query_rules_log" "example" {
name = format("%s-queryrule", "some")
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
criteria {
metric_name = "Average_% Idle Time"
dimension {
name = "Computer"
operator = "Include"
values = ["targetVM"]
}
}
data_source_id = azurerm_log_analytics_workspace.example.id
description = "Scheduled query rule LogToMetric example"
enabled = true
}

Metric math alarms: How can I use a for_each expression to loop over metrics within a dynamic block?

I am trying to create dynamic metric math alarms, that are configurable with a JSON.
I am struggling with looping over the metric alarm with a for_each expression as this is a loop within a loop.
Here is an example of what I am trying to do:
resource "aws_cloudwatch_metric_alarm" "Percentage_Alert" {
for_each = var.percentage_error_details
locals { alarm_details = each.value }
alarm_name = "${terraform.workspace}-${each.key}"
comparison_operator = local.alarm_details["Comparison_Operator"]
evaluation_periods = "1"
threshold = local.alarm_details["Threshold"]
metric_query {
id = "e1"
expression = local.alarm_details["Expression"]
label = local.alarm_details["Label"]
return_data = "true"
}
dynamic "metric_query" {
for metric in each.value["Metrics"]{
id = metric.key
metric_name = metric.value
period = local.alarm_details["Period"]
stat = local.alarm_details["Statistic"]
namespace = local.full_namespace
unit = "Count"
}
}
}
And this is the sample JSON
{
"locals": {
"Name": {
"Name": "metric_math",
"Metrics": {
"m1": "Sucess",
"m2": "Failure"
},
"Expression": "100*(m2/(m1+m2))",
"Threshold" : 1,
"Period": 25,
"Priority": "critical",
"Statistic": "Sum",
"Label": "label",
"Comparison_Operator": "GreaterThanOrEqualToThreshold"
}
}
}
And this is the error message i'm getting:
Error: Invalid block definition
On ../modules/cloudwatch/metriclogfilter/main.tf line 89: Either a quoted
string block label or an opening brace ("{") is expected here.
Any help would be much appreciated.

Issues in passing key vault secrets to the Data.tf and referencing in Main.tf

I have created multiple key vault secrets in the Azure portal and trying pass them in terraform Data.tf. But i am not sure how to pass multiple secrets and refer in the main.tf. Can someone help on this.
My requirement is to pass multiple values in the Name attribute and refer in Main.tf
data.tf
data "azurerm_key_vault" "key_vault" {
name = "test-key-vault-cts"
resource_group_name = "gcdmvrlyprd03-30cf06a8"
}
data "azurerm_key_vault_secret" "admin_password" {
name = "admin-password"
key_vault_id = data.azurerm_key_vault.key_vault.id
}
Main.tf
module "location_us-west" {
source = "./Modules"
web_server_location = "westus2"
web_server_rg = "${var.web_server_rg}-us-west"
resource_prefix = "${var.resource_prefix}-us-west"
web_server_address_space = "10.0.0.0/22"
#web_server_address_prefix = "10.0.1.0/24"
web_server_name = var.web_server_name
environment = var.environment
size = var.vm_size
admin_user = var.user
admin_password = data.azurerm_key_vault_secret.admin_password.value
web_server_count = var.web_server_count
web_server_subnets = {
"web-server" = "10.0.1.0/24"
"AzureBastionSubnet" = "10.0.2.0/24"
}
}
If you want to reference a second secret via data, create a second block of code.
data "azurerm_key_vault_secret" "admin_password2" {
name = "admin-password2"
key_vault_id = data.azurerm_key_vault.key_vault.id
}
In my opinion, you can put the data sources together with the module location_us-west in the main.tf file. So that you can quote the data.azurerm_key_vault_secret.admin_password.value directly. I don't recommend you desperate the data sources from the main.tf file if you just want to quote the secrets without any other actions.
And for the secrets, you need to put the names for multiple secrets to get them from the Key vault. So if you want to get multiple secrets only one time, you can change the code like this example:
variable "secret_names" {
type = list
default = [
"test1",
"test2"
]
}
data "azurerm_key_vault" "example" {
name = "key_vault_name"
resource_group_name = "group_name"
}
data "azurerm_key_vault_secret" "example" {
count = length(var.secret_names)
name = element(var.secret_names, count.index)
key_vault_id = data.azurerm_key_vault.example.id
}
Then the output data.azurerm_key_vault_secret.example.*.value will contain all the secret values in a list.

Insert Additional data into Lua table at index

I have a table called "inventory", initialized like so:
inventory = {}
inventory[1] = { qty = 0 }
I want to add more data to this table, at the index 1, eg:
val = { id = "example" }
inventory[1] = inventory[1], val
Is there a way I can do this while preserving the data that is already in this table at this index?
The final result should be something like:
inventory[1] = { qty = 0, id = "example" }
But if I try to print the id after trying this code I get:
print(inventory[1].id) == Nil
inventory[1].id = "example"
or
inventory[1]["id"] = "example"
or
this other SO answer with first_table being inventory[1] and second_table being val.
FWIW, you'd need 2 variables on the left side of the expression for inventory[1] = inventory[1], val to work: a, b = x, y.
You need to take the first key in the table and use it:
local inventory = {}
inventory[1] = { qty = 0 }
local val = { id = "example" }
--
local KeyName = next(val)
inventory[1][KeyName] = val[KeyName]
print(inventory[1][KeyName])
-- or
print(inventory[1].id)

Resources