standard attributes
how to add
given name
family name
schema {
attribute_data_type = "Sring"
developer_only_attribute = false
mutable = false
name = "famil name"
required = false
string_attribute_constraints {
min_length = 7
max_length = 150
}
string vals=var.vals // which will contain string in the format below
//"hr:recruitment,department:human,tool:sap,fruit:apple"
labels={
hostname=var.hostname
monitored=var.monitored
customer=var.cust1
machine-type=var.machinetype
}
I need to set labels(key,value) pairs for google_compute_instance by combining the above 2 properties to form a map.
So i converted vals to list by this code split(",",var.vals)
This gives me a list
tolist([
"hr:recruitment",
"department:human",
"tool:sap",
"fruit:apple",
])
Expected Output consisting of only map(string) combining labels and vals
labels:
{
hostname:var.hostname
monitored:var.monitored
customer:var.cust1
machine-type:var.machinetype
hr:"recruitment",
department:"human",
tool:"sap",
fruit:"apple"
}
How to convert this list to a map and combine it with labels?
I tried below & it seems to be working but it heavily relies on data quality of vals string (shouldn't contain duplicate keys). At it's core it relies on zipmap to build the map from keys & values lists.
// Having vals as a string variable
variable "vals" {
description = "The name of availability set"
type = string
default = "hr:recruitment,department:human,tool:sap,fruit:apple"
}
// hostname, monitored, cust1 & machinetype vars
variable "hostname" {
type = string
default = "dummy.net"
}
variable "monitored" {
type = bool
default = true
}
variable "cust1" {
type = string
default = "xyz"
}
variable "machinetype" {
type = string
default = "linux"
}
locals {
// parsing locally to split the vals string to fetch the keys
keys = [
for s in split(",", var.vals) : split(":", s)[0]
]
// parsing locally to split the vals string to fetch the values
values = [
for s in split(",", var.vals) : split(":", s)[1]
]
//I used labels also as vars
labels = {
hostname = var.hostname
monitored = var.monitored
customer = var.cust1
machine-type = var.machinetype
}
}
// Finally, zipmap to construct map from keys & values & merge with local.labels
output "final_map" {
value = merge(local.labels, zipmap(local.keys, local.values))
}
Finally, the output looks like below ::
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
final_map = {
"customer" = "xyz"
"department" = "human"
"fruit" = "apple"
"hostname" = "dummy.net"
"hr" = "recruitment"
"machine-type" = "linux"
"monitored" = true
"tool" = "sap"
}
I want to reuse the string from addresses in dns_aaaa_record_set in dns_ptr_record, how?
resource "dns_aaaa_record_set" "app-stage-dns" {
count = length(var.datacenter)
zone = format("%s.", var.dns_zone)
name = "app-stage-${var.datacenter[count.index]}.mydomain"
addresses = [replace(openstack_compute_instance_v2.app-stage[count.index].access_ip_v6, "/\\[|\\]/", "")]
ttl = 300
}
resource "dns_ptr_record" "app-stage-dns-ptr" {
count = length(var.datacenter)
zone = format("%s.", var.dns_ptr_zone)
ptr = "app-stage-${var.datacenter[count.index]}.mydomain"
name = <dns_aaaa_record_set[addresses]>
ttl = 300
}
Generally you could do the following:
resource "dns_ptr_record" "app-stage-dns-ptr" {
count = length(var.datacenter)
zone = format("%s.", var.dns_ptr_zone)
ptr = "app-stage-${var.datacenter[count.index]}.mydomain"
name = dns_aaaa_record_set.app-stage-dns[count.index].addresses[0]
ttl = 300
}
You have to be careful about name. addresses in your dns_aaaa_record_set is a list, and name must be only a string, not list.
Is there a way to loop through a range or list for a variable in terraform.
Below is what I would like to accomplish but I am not sure how to do it.
module "vlan_list" {
depends_on = [module.vlan_pools]
source = "../modules/add_vlans"
vlan_list = {
for i in range(1,100): {
"access" = {
vlan_pool = module.vlan_pools.vlan_pool["access"]
from = i
to = i
}
}
}
}
Let me add more information to it because unfortunately that didn't work. I get:
Error: Invalid value for module argument
on pools_vlan.tf line 34, in module "vlan_list":
34: vlan_list = {
35: for i in range(1, 100):
36: "access" => {
37: vlan_pool = module.vlan_pools.vlan_pool["access"]
38: from = i
39: to = i
40: }...
41: }
The given value is not suitable for child module variable "vlan_list" defined
at ../modules/add_vlans/variables.tf:5,1-21: element "access": object
required.
So I have created a module with the following
resource "aci_ranges" "add_vlan" {
for_each = local.vlan_list
alloc_mode = each.value["alloc_mode"]
annotation = each.value["annotation"]
name_alias = each.value["name_alias"]
vlan_pool_dn = each.value["vlan_pool"]
role = each.value["role"]
from = "vlan-${each.value["from"]}"
to = "vlan-${each.value["to"]}"
}
From Here I have defined a variables file to make it so users don't have to enter every variable... they can accept defaults
terraform {
experiments = [module_variable_optional_attrs]
}
variable "vlan_list" {
description = "Add VLANs to VLAN Pools"
type = map(object({
alloc_mode = optional(string)
annotation = optional(string)
from = optional(number)
name_alias = optional(string)
role = optional(string)
to = optional(number)
vlan_pool = optional(string)
}))
}
locals {
vlan_list = {
for k, v in var.vlan_list : k => {
alloc_mode = coalesce(v.alloc_mode, "static")
annotation = (v.annotation != null ? v.annotation : "")
from = (v.from != null ? v.from : 1)
name_alias = (v.name_alias != null ? v.name_alias : "")
role = coalesce(v.role, "external")
to = coalesce(v.to, 1)
vlan_pool = (v.vlan_pool != null ? v.vlan_pool : "")
}
}
}
So what I shared above is what someone would enter to consume the module... Here is a more complete example that I would like to do:
module "vlan_list" {
depends_on = [module.vlan_pools]
source = "../modules/add_vlans"
vlan_list = {
for i in range(1, 100):
"access" => {
vlan_pool = module.vlan_pools.vlan_pool["access"]
from = i
to = i
}...
for i in ranges([1000-1200], [1300-1400]):
"vmm_dynamic" => {
alloc_mode = "dynamic"
vlan_pool = module.vlan_pools.vlan_pool["vmm_dynamic"]
from = i
to = i
}...
for i in list[4, 100, 101]:
"l3out" => {
vlan_pool = module.vlan_pools.vlan_pool["l3out"]
from = i
to = i
}...
}
}
I know the second range on the vmm_dynamic key is not correct at all... I am just trying to show what I would like to be able to do if possible.
When the resource creates the entries, from the API, if I do it in a range as shown below; if someone needed to change the range for the first pool (in example) to 1-50,52-99, it would delete the entry and re-create it. whereas if they are creating the entry with each entry being created individually it wouldn't delete all of the individual entries, optimally.
I can do the following and it works fine... but being able to add the VLANs individually from a loop would be preferable.
module "vlan_list" {
depends_on = [module.vlan_pools]
source = "../modules/add_vlans"
vlan_list = {
"access" = {
vlan_pool = module.vlan_pools.vlan_pool["access"]
from = 1
to = 99
},
"vmm_dynamic" = {
alloc_mode = "dynamic"
vlan_pool = module.vlan_pools.vlan_pool["vmm_dynamic"]
from = 1000
to = 1199
},
"l3out_1" = {
vlan_pool = module.vlan_pools.vlan_pool["l3out"]
from = 4
to = 4
},
"l3out_2" = {
vlan_pool = module.vlan_pools.vlan_pool["l3out"]
from = 100
to = 101
},
}
}
Thanks in advance for help on this.
Just as one more point of reference... This is how I had previously accomplished this with Python, but I am trying to move this to native Terraform
def vlan_list_full(vlan_list):
full_vlan_list = []
if re.search(r',', str(vlan_list)):
vlist = vlan_list.split(',')
for v in vlist:
if re.fullmatch('^\\d{1,4}\\-\\d{1,4}$', v):
a,b = v.split('-')
a = int(a)
b = int(b)
vrange = range(a,b+1)
for vl in vrange:
full_vlan_list.append(vl)
elif re.fullmatch('^\\d{1,4}$', v):
full_vlan_list.append(v)
elif re.search('\\-', str(vlan_list)):
a,b = vlan_list.split('-')
a = int(a)
b = int(b)
vrange = range(a,b+1)
for v in vrange:
full_vlan_list.append(v)
else:
full_vlan_list.append(vlan_list)
return full_vlan_list
def vlan_pool
for z in range(1, 3):
vgroup = 'VLAN_Grp%s' % (z)
vgrp = 'VGRP%s_Allocation' % (z)
templateVars['Allocation_Mode'] = templateVars[vgrp]
if re.search(r'\d+', str(templateVars[vgroup])):
vlan_list = vlan_list_full(templateVars[vgroup])
for v in vlan_list:
vlan = str(v)
if re.fullmatch(r'\d+', vlan):
templateVars['VLAN_ID'] = int(vlan)
# Add VLAN to VLAN Pool File
create_tf_file('a+', dest_dir, dest_file, template, **templateVars)
I can't seem to find any examples of how to do this without Python.
If you want to create a map with a single key of access and a list of values, then you can use ellipsis operator (...). Also your syntax is incorrect. Thus, the following should be used in this case:
vlan_list = {
for i in range(1, 100):
"access" => {
vlan_pool = module.vlan_pools.vlan_pool["access"]
from = i
to = i
}...
}
Here is how I recently accomplished what I asked above. It was about a year to figure it out but this is the way I could accomplish it.
# Map of Object input Variables is as shown below
variable "vlan_pools" {
default = {
"default" = {
alias = ""
allocation_mode = "dynamic"
description = ""
encap_blocks = {
"default" = {
allocation_mode = "inherit"
description = ""
role = "external"
vlan_range = "**REQUIRED**"
}
}
}
}
description = <<-EOT
key - name of the VLAN Pool
* alias: A changeable name for a given object. While the name of an object, once created, cannot be changed, the alias is a field that can be changed.
* allocation_mode: The allocation mode. The values can be:
- dynamic (default): Managed internally by the APIC to allocate VLANs for endpoint groups (EPGs). A vCenter Domain can associate only to a dynamic pool.
- static: One or more EPGs are associated with a domain, and that domain is associated with a static range of VLANs. You must configure statically deployed EPGs within that range of VLANs.
When you create VLAN ranges, you can also assign the allocation mode to be inherited from the parent.
* description: Description to add to the Object. The description can be up to 128 alphanumeric characters.
* encap_blocks:
- allocation_mode: The allocation mode. The values can be:
* dynamic: Managed internally by the APIC to allocate VLANs for endpoint groups (EPGs). A vCenter Domain can associate only to a dynamic pool.
* inherit (default): The inherited mode from the parent device.
* static: One or more EPGs are associated with a domain, and that domain is associated with a static range of VLANs. You must configure statically deployed EPGs within that range of VLANs.
- description: Description to add to the Object. The description can be up to 128 alphanumeric characters.
- role: Role of the VLAN range. The options are:
* external (Default): Used for allocating VLANs for each EPG assigned to the domain. The VLANs are used when packets are sent to or from leafs.
* Internal: Used for private VLAN allocations in the internal vSwitch by the Cisco ACI Virtual Edge (AVE). The VLANs are not seen outside the ESX host or on the wire.
- vlan_range: single vlan; i.e. 1. range of vlans; i.e. 1-5. Or List of Vlans; i.e. 1-5,10-15
EOT
type = map(object(
{
alias = optional(string)
allocation_mode = optional(string)
description = optional(string)
encap_blocks = map(object(
{
allocation_mode = optional(string)
description = optional(string)
role = optional(string)
vlan_range = string
}
))
}
))
}
# Which I then run some conditional modifications in locals to format the variables.
locals {
#__________________________________________________________
#
# VLAN Pools Variables
#__________________________________________________________
# This first loop is to handle optional attributes and return
# default values if the user doesn't enter a value.
vlan_pools = {
for k, v in var.vlan_pools : k => {
alias = v.alias != null ? v.alias : ""
allocation_mode = v.allocation_mode != null ? v.allocation_mode : "dynamic"
description = v.description != null ? v.description : ""
encap_blocks = v.encap_blocks != null ? v.encap_blocks : {}
}
}
# Loop 1 is to determine if the encap_blocks are:
# A Single number 1
# A Range of numbers 1-5
# A List of numbers 1-5,10-15
# And then to return these values as a list
vlan_ranges_loop_1 = flatten([
for key, value in local.vlan_pools : [
for k, v in value.encap_blocks : {
allocation_mode = v.allocation_mode != null ? v.allocation_mode : "inherit"
description = v.description != null ? v.description : ""
key1 = key
key2 = k
role = v.role != null ? v.role : "external"
vlan_split = length(regexall("-", v.vlan_range)) > 0 ? tolist(split(",", v.vlan_range)) : length(
regexall(",", v.vlan_range)) > 0 ? tolist(split(",", v.vlan_range)
) : [v.vlan_range]
vlan_range = v.vlan_range
}
]
])
# Loop 2 takes a list that contains a "-" or a "," and expands those values
# into a full list. So [1-5] becomes [1, 2, 3, 4, 5]
vlan_ranges_loop_2 = {
for k, v in local.vlan_ranges_loop_1 : "${v.key1}_${v.key2}" => {
allocation_mode = v.allocation_mode
description = v.description
key1 = v.key1
key2 = v.key2
role = v.role
vlan_list = length(regexall("(,|-)", jsonencode(v.vlan_range))) > 0 ? flatten([
for s in v.vlan_split : length(regexall("-", s)) > 0 ? [for v in range(tonumber(
element(split("-", s), 0)), (tonumber(element(split("-", s), 1)) + 1)
) : tonumber(v)] : [s]
]) : v.vlan_split
}
}
# Loop 3 will take the vlan_list created in Loop 2 and expand this
# out to a map of objects per vlan.
vlan_ranges_loop_3 = flatten([
for k, v in local.vlan_ranges_loop_2 : [
for s in v.vlan_list : {
allocation_mode = v.allocation_mode
description = v.description
key1 = v.key1
role = v.role
vlan = s
}
]
])
# And lastly loop3's list is converted back to a map of objects
vlan_ranges = { for k, v in local.vlan_ranges_loop_3 : "${v.key1}_${v.vlan}" => v }
# End of Local Loops
}
# And lastly these values are consumed by the resources with for_each loops.
resource "aci_vlan_pool" "vlan_pools" {
for_each = local.vlan_pools
alloc_mode = each.value.allocation_mode
description = each.value.description
name = each.key
name_alias = each.value.alias
}
resource "aci_ranges" "vlans" {
depends_on = [
aci_vlan_pool.vlan_pools
]
for_each = local.vlan_ranges
description = each.value.description
alloc_mode = each.value.allocation_mode
from = "vlan-${each.value.vlan}"
to = "vlan-${each.value.vlan}"
role = each.value.role
vlan_pool_dn = aci_vlan_pool.vlan_pools[each.value.key1].id
}
I am sure someone with more experience might be able to accomplish this with fewer steps in locals but this worked for me at least.
I have a strange situation as shown below
variable is declared as :
variable "subnet_info" {
type = map(object({vpc_name=string, subnet_name=string, subnet_ip_range=string, region=string}))
default = {
abc = {
vpc_name = "abc"
subnet_name = "abc"
subnet_ip_range = ""
region = "europe-west3"
}
bcd = {
vpc_name = "bcd"
subnet_name = "bcd"
subnet_ip_range = ""
region = "europe-west3"
}
}
}
I have declared a modules as follow
module "abc" {
source = "./modular_approach/modules/general_network/"
for_each = var.subnet_info
individual_vpc_name = each.value
individual_subnet_name = each.value
}
I want to pass the value of vpc and subnet from the variable subnet_info. The problem is that there will be many values in that variable. If I run the for_each I want to condition in the module that if the key is "abc" then access the internal (key,value) other wise skip. Is this possible in terraform?
Thanks in advance!
If you really wish to filter like that, the following construct should do :
for_each = {for k,v in var.subnet_info: k=>v if k == "abc"}