terraform - how to update route_table attribute conditionally - terraform

any ideas how to conditionally insert 'propagating_vgws = some_vgw' ??
I see lots of ways to conditionally create a resource, but what about a resources optional attribute?
resource "aws_route_table" "internal" {
count = "${length(var.internal_subnets)}"
vpc_id = "${aws_vpc.main.id}"
# propagating_vgws = ["${aws_vpn_gateway.main.id}"]
tags {
Name = "${var.name}-${format("internal-%03d", count.index+1)}"
}
}
the route_table is created by a vpc module and I need to conditionally place that vpc on a direct-connect virtual interface or not.
thanks for any feedback.

Maybe this will help you. (scroll to the conditinals section).
The idea is that you can do a conditional interpolation, and you can create an empty list rendering the attribute empty, thus in a way optional (has meaningful value or not):
propagating_vgws = "${var.decide_based_on_this_value == var.something ?
var.list_in_case_yes : list() }"
${list()} returns an empty list, for more refer to the above linked page, at the bottom there are many built-in functions like lists...
I hope it will solve your problem.

Related

Can you retrieve an item from a list using regex in Terraform?

The problem I am trying to solve is I need to identify one of the Azure subnets in a virtual network by part of it's name. This is so I can then later retrieve it's CIDR. I only know part beforehand such as "mgmt-1" or "egress-1". The actual name of the subnet is much longer but will end in something like that. This was my process:
I have the vnet name so I pull all subnets:
data "azurerm_virtual_network" "this" {
name = local.vnet
resource_group_name = "myrg"
}
Now what I wish I could do is this:
locals {
mgmt_index = index(data.azurerm_virtual_network.this.subnets, "*mgmt-1")
mgmt_subnet = data.azurerm_virtual_network.this.subnets[local.mgmt_index]
}
However index wants an exact match, not a regex. Is this possible to do? Perhaps a better way?
Thank you,
It is not possible to directly look up a list item using a regex match, but you can use for expressions to apply arbitrary filters to a collection when constructing a new collection:
locals {
mgmt_subnets = toset([
for s in data.azurerm_virtual_network.this.subnets : s
if can(regex(".*?mgmt-1", s.name))
])
}
In principle an expression like the above could match more than one object, so I wrote this to produce a set of objects that match.
If you expect that there will never be more than one object whose name matches the pattern then you can use Terraform's one function to assert that and then Terraform will check to confirm that there's no more than one element (returning an error if not) and then return that one value.
locals {
mgmt_subnet = one([
for s in data.azurerm_virtual_network.this.subnets : s
if can(regex(".*?mgmt-1", s.name))
])
}
If the condition doesn't match any of the subnet objects then in the first case you'll have an empty set and in the second case you'll have the value null.

Is there a condition in terraform same as CloudFormation?

I see people using count to block resource creation in terraform. I want to create some resources if a condition is set to true. Is there such a thing same as in CloudFormation?
You answered yourself, the most similar thing is the count
You can use it combined with a conditional expression, like
resource "x" "y"{
count = var.tag == "to_deploy" ? 1 : 0
}
But this is just a stupid example, you can put everything, also use functions
count = max(var.array) >= 3 ? 1 : 0
And if you need to put a condition on something more complex, you can evaluate to use a locals block where do all elaboration you need, and just use some bool, or what you want, resultant from that in conditional expression.
I would like to help you more, but I should know your specific case, what are the conditions you would have.
In CloudFormation a "condition" is a top-level object type alongside resources, outputs, mappings, etc.
The Terraform language takes a slightly more general approach of just having values of various data types, combining and transforming them using expressions. Therefore there isn't a concept exactly equivalent to CloudFormation's "conditions", but you can achieve a similar effect in other ways using Terraform.
For example, if you want to encode the decision rule in only a single place and then refer to it many times then you can define a Local Value of boolean type and then refer to that from multiple resource blocks. A local value of boolean type is essentially equivalent to a condition object in CloudFormation. The CloudFormation documentation page you linked to has, at the time of writing, an example titled "Simple condition" and the following is a roughly-equivalent version of that example in the Terraform language:
variable "environment_type" {
type = string
validation {
condition = contains(["prod", "test"], var.environment_type)
error_message = "Must be either 'prod' or 'test'."
}
}
locals {
create_prod_resources = (var.environment_type == "prod")
}
resource "aws_instance" "example" {
ami = "ami-0ff8a91507f77f867"
instance_type = "..."
}
resource "aws_ebs_volume" "example" {
count = local.create_prod_resources ? 1 : 0
availability_zone = aws_instance.example.availability_zone
}
resource "aws_volume_attachment" "example" {
count = local.create_prod_resources ? 1 : 0
volume_id = aws_ebs_volume.example[count.index].id
instance_id = aws_instance.example.id
device = "/dev/sdh"
}
Two different resource blocks can both refer to local.create_prod_resources, in the same way that the two resources MountPoint and NewVolume can refer to the shared condition CreateProdResources in the CloudFormation example.

Is there a way to pass attributes to data source in terraform?

I'm trying to tell data.github_ip_ranges to what name to use so I could create a list of CIDRs and my code look cleaner. I was trying to find answers, but no luck so far.
And I'm trying to see if there is a way of passing my variables to it...
variable "git_services" {
default = ["hooks_ipv4", "dependabot_ipv4", "dependabot_ipv6", "git_ipv4", "hooks_ipv6"]
}
locals {
github_ips = concat(data.github_ip_ranges.git.name) # name is my custom variable
}
Here is my original approach
locals {
github_ips = concat(data.github_ip_ranges.git.hooks_ipv4, data.github_ip_ranges.git.hooks_ipv6,
data.github_ip_ranges.git.dependabot_ipv4, data.github_ip_ranges.git.dependabot_ipv6)
}
Please help if you could. Thank you!
I think I understand what you're trying to accomplish. You would do it like so:
variable "git_services" {
default = ["hooks_ipv4", "dependabot_ipv4", "dependabot_ipv6", "git_ipv4", "hooks_ipv6"]
}
locals {
github_ips = distinct(flatten([
for service in var.git_services:
data.github_ip_ranges.git[service]
]))
}
What this is doing is creating a list of lists, where each element in the first level is for a service, and each element in the second level is a CIDR bock for that service. The flatten function turns this list of lists into a flat list of CIDR blocks. Since the same CIDR might be used for multiple services, and we probably don't want duplicates if we're using this for something like security group rules, we use the distinct function to remove any duplicate CIDR blocks.

Passing multiple values to a variable in Terraform

I have a question about passing multiple values to a variable in Terraform. I can't find the answer anywhere and I am not sure if this is even possible. In our environment when we create AWS resources such VPC and add a tag name to it like project-environment-VPC e.g. cvs-production-VPC. How would I do the same when I am trying to create the resource using Terraform? I tried the below approach and it didn't work:
resource "aws_vpc" "main" {
cidr_block = var.aws_cidr
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = ${var.project}-${var.environment}-${"VPC"}
}
}
If it is not possible - perhaps there is a workaround? Thanks in advance for any replies.
The current Name tag value causes Invalid Character error using Terraform 0.14.6.
Change the Name tag value as below to resolve the issue.
Name = "${var.project}-${var.environment}-${"VPC"}"

Combine Variable Values and Explicitly Defined Variable Values in Terraform Tags for AWS

Currently, I'm working on a requirement to make Terraform Tags for AWS resources more modular. In this instance, there will be one tag 'Function' that will be unique to each resource and the rest of the tags to be attached will apply to all resources. What I'm trying to do is combine the unique 'Function' value with the other tags for each resource.
Here's what I've got so far:
tags = {
Resource = "Example",
"${var.tags}
This tags value is defined as a map in the variables.tf file like so:
variable "tags" {
type = map
description = "Tags for infrastructure resources."
}
and populated in the tfvars file with:
tags = {
"Product" = "Name",
"Application" = "App",
"Owner" = "Email"
}
When I run TF Plan, however, I'm getting an error:
Expected an attribute value, introduced by an equals sign ("=").
How can variables be combined like this in Terraform? Thanks in advance for your help.
Figured this one out after further testing. Here you go:
tags = "${merge(var.tags,
map("Product", "Product Name",
"App", "${var.environment}")
)
}"
So, to reiterate: this code will merge a map variable of tags that (in my case) are applicable to many resources with the tag (Product and App) that are unique to each infrastructure resource. Hope this helps someone in the future. Happy Terraforming.
I tried to use map, it does work with new versions.
The lines below works for me:
tags = "${merge(var.resource_tags, {a="bb"})}"
Creating values in my tfvars file did not work for me...
Here is my approach....
I created a separate variable in my variables.tf file to call during the tagging process..
my default variable for tags are imported/pass from a parent module.
So therefore it doesnt need to specify any default data.
the extra tagging in the child module is done in the sub_tags variable..
imported/passed from parent/root module
variable "tags" {
type = "map"
}
tags in the child module
variable "sub_tags"{
type = "map"
default = {
Extra_Tags_key = "extra tagging value"
}
}
in the resource that needs the extra tagging.. i call it like this
tags = "${merge(var.tags, var.sub_tags)}"
this worked great for me

Resources