Terraform: Merging a list of subnets and nat gateways using a conditional - terraform

I'm trying to create a merged list containing the output of an aws_subnets maps with appropriate nat gateway ids. The value of a tag is used to determine whether the nat gw id should be merged with the subnet data. I need to do something like the following but the conditional isn't valid. How can I test the values properly?
locals {
nat_gw_subnet_map = {
for nat_gw in aws_nat_gateway.private : [
for subnet in aws_subnet.private : {
if subnet.tags["env"] == nat_gw.tags["env"] :
merge(subnet, {nat_gateway_id = nat_gw.id})
}
]
}
}

Related

dynamic block to skip vpc config to lambda

I would like to skip adding a vpc to lambda in certain env. The current terraform code to update vpc is like below
data "aws_subnet" "lambda-private-subnet_1" {
availability_zone = var.environment_type_tag != "prd" ? "us-east-1a" : null
dynamic "filter" {
for_each = var.environment_type_tag == "prd" ? [] : [1]
content {
name = "tag:Name"
values = [var.subnet_value]
}
}
}
resource "aws_lambda_function" "tests" {
dynamic "vpc_config" {
for_each = var.environment_type_tag == "prd" ? [] : [1]
content {
subnet_ids = [data.aws_subnet.lambda-private-subnet_1.id]
security_group_ids = [var.security_group]
}
}
}
During 'terraform plan', the output is like below
##[error][1m[31mError: [0m[0m[1mmultiple EC2 Subnets matched; use additional constraints to reduce matches to a single EC2 Subnet[0m
I would like to skip the 'data "aws_subnet"' block if its 'prd' environment type.
So there are four different questions in this question. We can attempt to answer each one:
dynamic block to skip vpc config to lambda
This is already occurring with the given code. The dynamic blocks are "skipped" in prd with the current code.
I would like to skip adding a vpc to lambda in certain env.
If you mean "subnet" instead of "vpc", then this is also already occurring with the given code. Otherwise, please update with the vpc config.
##[error][1m[31mError: [0m[0m[1mmultiple EC2 Subnets matched; use additional constraints to reduce matches to a single EC2 Subnet[0m
The error message is due to the fact that your filters match multiple subnets outside of prd, and therefore you need to constrain the filter conditions.
I would like to skip the 'data "aws_subnet"' block if its 'prd' environment type.
You just need to extend your current code to make the data optional:
data "aws_subnet" "lambda-private-subnet_1" {
for_each = var.environment_type_tag == "prd" ? [] : toset(["this"])
...
}
You can then remove the for_each from the dynamic block in the resource as it is redundant, and update the attribute references with elements accordingly:
subnet_ids = [data.aws_subnet.lambda-private-subnet_1["this"].id]

Terraform Openstack: deploy new instance with same security groups as existing one

Assume I already have an existing host with security groups assigned to it. I can retrieve the state of the host the following way:
data "openstack_compute_instance_v2" "host_data_01" {
# ID for host A
id = "88c96f9d-7951-4ab5-9e37-f1c5b33e8889"
}
In the statefile I can see what security groups are assigned so I can just go ahead and write them down. But is there a way to assign all security groups of that host to my new instance I'm trying to deploy?
An example of assigning the first security group entry of that set.
security_groups = [
element(sort(data.openstack_compute_instance_v2.host_data_01.security_groups), 0)
]
In pseudo-code what I want is:
security_groups = [
for entry in data.module.module_name.security_groups
add entry
]
Kind regards
Roman
After a few days of taking a break from it, I found the incredibly simple solution.
The output of a security_groups element is structured the same way as an input should be, so you can very simply assign a security_group output as a variable to a new host:
resource "openstack_compute_instance_v2" "host_01" {
...
security_groups = data.openstack_compute_instance_v2.test_host.security_groups
...
}
data "openstack_compute_instance_v2 "test_host" {
id = "12345"
}

How are azure terraform variables applied for multiple tf files processed together?

For Azure Terraform:
If a variable is declared in a tf file will this value be applied to same variable in other tf files processed together? Why is there a default value associated with a variable statement?
If I made a tfvars file:
cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ]
Can cidr be used as below for subnet id? Not really understanding usage syntax?
subnet_id = "${azurerm_subnet.subnet.id}"
subnet id = cidr
What exactly is the "Default" function when used with variables? See below:
variable "prefix" {
type = "string"
default = "my"
}
variable "tags" {
type = "map"
default = {
Environment = "Terraform GS"
Dept = "Engineering"
}
}
variable "sku" {
default = {
westus = "16.04-LTS"
eastus = "18.04-LTS"
}
}
There are several questions there. the easy one: default:
The variable declaration can also include a default argument. If
present, the variable is considered to be optional and the default
value will be used if no value is set when calling the module or
running Terraform. The default argument requires a literal value and
cannot reference other objects in the configuration.
For the other question, refer to this example: https://www.terraform.io/docs/providers/azurerm/r/subnet.html#attributes-reference
So in short, to use the existing subnet cidr, you need to refer to it like this:
azurerm_subnet.%subnetname%.address_prefix
subnet name, however, cannot be equals to a cidr, because it doesnt allow for / inside of the name. you can use something like this though: 10.0.0.0-24

How to list the inbound and outbound rules for aws security group using Terraform script?

Following is the terraform script.
variable "vpc_ids" {
default = [
"vpc-**********",
"vpc-**********",
"vpc-**********",
"vpc-**********",
]
type = "list"
}
data "aws_security_groups" "test" {
filter {
name = "vpc-id"
values = "${var.vpc_ids}"
}
}
data "aws_security_group" "selected" {
count = "${length(data.aws_security_groups.test.ids)}"
id = "${element(data.aws_security_groups.test.ids, count.index)}"
}
output "sec_groups" {
value = "${data.aws_security_group.selected.0.description}"
// value = "${join(",", data.aws_security_group.selected.*.description)}"
}
In the last, I am using description but it is not giving the inbound and outbound rules for the security group.
Is anyone know how to ge the inbound and outbound rules using datasource ?

Change a list of maps into a formatted string in terraform

I'm trying to set up a Google Cloud Load Balancer and one step requires updating the named ports on the managed instance groups for which I need a formatted string to generate the command-line call. I feel like this should be something that I can automate with terraform, but I'm struggling with mapping data formats.
I have two sets of source data.
From the instance group resource:
data "google_compute_instance_group" "all" {
count = "${length(var.backend)}"
self_link = "${element(var.backend, count.index)}"
}
I get the existing named ports from data.google_compute_instance_group.all.*.named_port in the format:
[
[
map[name:https port:30443],
map[name:http port:30080]
],
[
map[name:https port:30443],
map[port:30080 name:http]
]
]
I also have the ports that I want to make sure are defined in my own map:
variable "node_ports" {
type = "map"
default = {
"https" = "30443"
"monitor" = "30012"
}
}
There may be overlap between these; I want to select the value defined in the variable. (The named ports are the same for all instance groups.)
First, I want to combine the two maps into a single map to make sure that there is one port for each name. How can I convert the first list of lists of maps to a single map so that I get this?
{
"http" = "30080"
"https" = "30443"
"monitor" = "30012"
}
Second, I want to convert all of that to the format needed on the command line:
gcloud --project ${var.project} compute instance-groups set-named-ports ${basename(var.backend[count.index])} --named-ports=https:443,http:30080,monitor:30012
I think I could do that with a jsonencode hack but I'd be interested in better solutions:
"${replace(jsonencode(named_ports), "/[\\{\\}\"\\s]/", "")}"
I was able to use a for expression to rearrange the list of list of maps using terraform 0.12.x. To break down this statement
merge({ for l in local.lolom[0] : l.name => l.port }, { monitor = local.monitor })
It will go through the first list of maps as denoted by for l in local.lolom[0]
Then it will set the name of the map equal to the port's value returning a map itself so now we have { https = 30443, http = 30080 }
The merge() will then combine our map with the monitor map to give us our intended result
{
"http" = "30080"
"https" = "30443"
"monitor" = "30012"
}
I couldn't find a way to convert the above into the arguments without using jsonencode but the following seems to provide the intended result.
locals {
lolom = [
[
{ name : "https", port : 30443 },
{ name : "http", port : 30080 }
],
[
{ name : "https", port : 30443 },
{ port : 30080, name : "http" }
]
]
monitor = 30012
first = merge({ for l in local.lolom[0] : l.name => l.port }, { monitor = local.monitor })
second = merge({ for l in local.lolom[1] : l.name => l.port }, { monitor = local.monitor })
first_args = replace(jsonencode(local.first), "/[\\{\\}\"\\s]/", "")
second_args = replace(jsonencode(local.second), "/[\\{\\}\"\\s]/", "")
}
output "first_args" {
value = local.first_args
}
output "second_args" {
value = local.second_args
}
Apply
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
first_args = http:30080,https:30443,monitor:30012
second_args = http:30080,https:30443,monitor:30012

Resources