How to create multiple target groups with specific naming conventions terraform - terraform

I have a resource that creates a target group. I would like to modify the resource with a capability of creating n number of target groups with a specific naming convention.
resource "aws_lb_target_group" "main" {
name = "${var.name}-tg"
port = var.forward_port
protocol = var.forward_protocol
target_type = var.target_type
deregistration_delay = var.deregistration_delay
health_check {
interval = var.interval
path = var.path
}
stickiness {
type = var.cookie_type
}
}
I want to modify it in such a way that if only one target group is created it should name it as test-tg, but if there are more than one target group then it should add index to name like test-tg1. Also each target group can have their own specific set of variable value. How can I achieve that?

You can use for_each in that case as you want to create multiple TGs with different configurations. Create a variable with map(object()) type which allows you to put the values the way you expect. I have put a sample code. Take a reference of below.
Please put the values of below vars in terraform.tfvars file
cookie_type, path, deregistration_delay
var.tf
variable "TG_conf" {
type = map(object({
port = string
protocol = string
target_type = string
interval = string
cookie_type = string
path = string
deregistration_delay = string
}))
}
terrafor.tfvars
TG_conf = {
"test-tg" = {
port = 80
protocol = HTTP
target_type = "instance
interval = "5"
cookie_type = string
path = string
deregistration_delay = string
}
alb.tf
resource "aws_lb_target_group" "main" {
for_each = var.TG_conf
name = "${each.key}"
port = each.value.port
protocol = each.value.protocol
target_type = each.value.target_type
deregistration_delay = each.value.deregistration_delay
health_check {
interval = each.value.interval
path = each.value.path
}
stickiness {
type = each.value.cookie_type
}
}

Related

Terraform nested dynamic blocks

I'm trying to deploy an Azure application gateway in Terraform, in particular I need to create a nested dynamic blocks.
I have tried to implement this (this part of the code is in a file called application_gateway.tf):
dynamic "url_path_map" {
for_each = var.path_maps
content {
name = outer_block.value["name"]
default_backend_address_pool_name = outer_block.value["backend"]
default_backend_http_settings_name = outer_block.value["backend_set"]
dynamic "url_path_rule" {
for_each = url_path_map.value["upm"]
content{
name = url_path_rule.value["name_rule"]
paths = url_path_rule.value["path"]
backend_address_pool_name = url_path_rule.value["backend"]
backend_http_settings_name = url_path_rule.value["backend_set"]
}
}
}
}
The correspective variables.tf file is:
variable "path_maps" {
default = []
type = list(object({
name = string
backend = string
backend_set = string
upm = list(object({
name_rule = string
path = string
backend = string
backend_set = string
}))
}))
}
With the following module call (this part of the script is in another file called main.tf):
module "application_gateway" {
source = "../modules/resources-hub/application_gateway"
resource_group_name = module.resource_group.name
resource_group_location = module.resource_group.location
subnet_id = module.agw_subnet.id
public_ip_address_id = module.app_gw_pip.id
firewall_policy_id = module.agw_web_application_firewall.id
log_analytics_workspace_id = module.log_analytics_workspace.id
path_maps = [{name = "dev_url_path_name", backend = "devBackend", backend_set = "devHttpSetting", name_rule = "dev_path_rule_name_app", path = "/app/*"},
{name = "tst_url_path_name", backend = "tstBackend", backend_set = "tstHttpSetting", name_rule = "dev_path_rule_name_edp", path = "/edp/*"},
{name = "uat_url_path_name", backend = "uatBackend", backend_set = "uatHttpSetting", name_rule = "dev_path_rule_name_internal", path = "/internal/*"}]
}
At the end, what I would like to obtain is this but using the nested dynamic blocks:
url_path_map {
name = "dev_url_path_name"
default_backend_address_pool_name = "devBackend"
default_backend_http_settings_name = "devHttpSetting"
path_rule {
name = "dev_path_rule_name_app_edp"
paths = ["/app/*"]
backend_address_pool_name = "devBackend"
backend_http_settings_name = "devHttpSetting"
}
path_rule {
name = "dev_path_rule_name_internal"
paths = ["/edp/*"]
backend_address_pool_name = "devBackend"
backend_http_settings_name = "devHttpSetting"
}
path_rule {
name = "dev_path_rule_name_internal"
paths = ["/internal/*"]
backend_address_pool_name = "sinkPool"
backend_http_settings_name = "devHttpSetting"
}
}
This is the error that I get if I run "terraform validate":
enter image description here
Thank you in advance!
I have tried the code above but I got the error in the image.
The first problem is on the definition of the variable "path_maps", because is different as the path_maps format that you are passing to the module.
You can modify the path_maps before passing to the module with the correct format, or you can change variable to fit the format that you define.
thats why you are getting the error that "upm" is required

Multiple For Each in Terraform

I am trying to deploy multiple for_each in terraform. I have two variables:
variable "VirtualNetworks" is for two VLANS, deployed in two different locations.
variable "VirtualMachines" is for six VM's, 3 in each of the VLANS.
Following are variable types:
variable "VirtualNetworks" {
type = list(object({
VirtualNetworksName = string
VirtualNetworksLocation = string
SubnetName = string
NGSName = string
}))...}
variable "VirtualMachines" {
type = list(object({
OS = string
location = string
hostname = string
interfaceid = string
size = string
environment = string
publisher = string
offer = string
sku = string
version = string
}))...}
The issue is when I am trying to assign 6 network interfaces dynamically, I don't know how to. Following is the way I have tried.
resource "azurerm_network_interface" "tf_example_nic" {
for_each = { for i, v in var.VirtualMachines : i => v }
# for_each = { for j, w in var.VirtualNetworks : j => w }
name = each.value.interfaceid
location = each.value.location
resource_group_name = azurerm_resource_group.tf_example_rg.name
ip_configuration {
name = "internal"
subnet_id = each.key != "0||1||2" ? azurerm_subnet.tf_example_subnet[0].id : azurerm_subnet.tf_example_subnet[1].id
# subnet_id = azurerm_subnet.tf_example_subnet[each.key].id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.tf_example_public_ip[each.key].id
}
}
Is there a way I can try and make this dynamic. Any assistance will be really helpful.

Construct a object list from multiple instances of a terraform module

I'm trying to figure out how to turn multiple module instances into a map of objects. I have a map of "Customer". I then want the public IP of each of the created customers to build a list of firewall rules. The Customer module has many outputs including "name" and "public_ip_address" The AzureSQLFirewall module expects a map of objects each attributes "name" and "ipAddress".
Main Module:
module "Customer"{
for_each = var.customers
source = "./customer"
customerName = "${each.value["name"]}"
}
module "AzureSQLFirewall" {
source = "./azureSQLFirewall"
azureSQLServerID = module.BaseInfrastructure.sql_server_id
azureSQLAllowedIPs = { for v in module.Customer:
{
name = v.name,
ipAddress = v.public_ip
}
}
}
AzureSQLFirewall.tf:
terraform {
required_version = ">= 0.14.9"
}
resource "azurerm_mssql_firewall_rule" "azureSQLFirewall" {
for_each = var.azureSQLAllowedIPs
name = each.value.name
server_id = var.azureSQLServerID
start_ip_address = each.value.ipAddress
end_ip_address = each.value.ipAddress
}
variable "azureSQLServerID"{
type = string
}
variable "azureSQLAllowedIPs" {
type = map(object({
name = string
ipAddress = string
}))
}
EDIT: Turns out, I was quite close:
azureSQLAllowedIPs = { for k,v in module.Customer: k => {
name = v.customerName,
ipAddress = v.public_ip}
}

Creation of Terraform Variables for Nested Objects

Following is the variable declaration for my terraform module which is used in our cloud and inputs for these variables are obtained via one of the automation solutions. Now, I would like to reproduce one of the issues for which I would like to create a tfvars file from the below variable definition.
variables.tf:
variable "docker" {
type = object({
image_name = string
image_location = string
docker_ports = object({
internal = number
external = number
})
rmodelling = object({
lang = object({
version = number
enabled = bool
policy = object({
identification = string
})
})
impl = object({
version = number
enabled = bool
policy = object({
identification = string
})
})
})
})
}
I have tried something like this, but for the next nested objected I am not quite sure on how those can be put down. Can someone guide or shed some pointers?
terraform.tfvars:
docker = {
image_name = "Ubuntu 18.04"
image_location = "https://registry.jd.com/ubuntu/<custom_location>"
docker_ports = {
internal = 80
external = 443
}
rmodelling = { ??
???
An example of a valid value for your var.docker is:
docker = {
image_name = "Ubuntu 18.04"
image_location = "https://registry.jd.com/ubuntu/<custom_location>"
docker_ports = {
internal = 80
external = 443
}
rmodelling = {
lang = {
version = 3
enabled = true
policy = {
identification = "test"
}
}
impl = {
version = 4
enabled = false
policy = {
identification = "test2"
}
}
}
}

Terraform: How to set variables in a module based on a conditional?

I would like to pass a variable that will allow me to specify the list of VPC and subnet settings for an AWS instance. There are fixed VPC and subnet settings that make sense so I just want to allow a user to pick one using a single variable, i.e. use A or B.
For instance, let's say I have two available VPCs, and these are specified in a variables.tf file for a module my_instance:
variable "a_vpc_cidr_block" { default = "105.191.44.0/22" }
variable "a_vpc_id" { default = "id_a"}
variable "a_vpc_name" { default = "vpc_a" }
variable "a_subnet_availability_zone" { default = "us-east-1a" }
variable "a_subnet_cidr_block" { default = "105.191.25.0/25" }
variable "a_subnet_name" { default = "instance_A" }
variable "b_vpc_cidr_block" { default = "105.191.45.0/22" }
variable "b_vpc_id" { default = "id_b"}
variable "b_vpc_name" { default = "vpc_b" }
variable "b_subnet_availability_zone" { default = "us-east-1a" }
variable "b_subnet_cidr_block" { default = "105.191.35.0/25" }
variable "b_subnet_name" { default = "instance_B" }
The my_instance module will take a single input variable that an environment will specify, with a value of either 'A' or 'B' (is there a way to limit options for a variable to a list of values such as options=['A', 'B']?), and will be called like so in the terraform.tf for a Terraform configuration with a single instance:
module "my_instance" {
source = "../../modules/my_instance"
option = "A"
}
I want to now implement some logic within the module's main file (modules/my_instance/my_instance.tf) where it decides on which of the two collections of VPC and subnet settings it should use from the ones in modules/my_instance/variables.tf. I want to something like this (pseudocode):
if var.option == 'A'
vpc_cidr_block = var.a_vpc_cidr_block
vpc_id = var.a_vpc_id
vpc_name = var.a_vpc_name
subnet_availability_zone = var.a_subnet_availability_zone
subnet_cidr_block = var.a_subnet_cidr_block
subnet_name = var.a_subnet_name
else if var.option == 'B'
vpc_cidr_block = var.b_vpc_cidr_block
vpc_id = var.b_vpc_id
vpc_name = var.b_vpc_name
subnet_availability_zone = var.b_subnet_availability_zone
subnet_cidr_block = var.b_subnet_cidr_block
subnet_name = var.b_subnet_name
else
raise an error
# get a data resource identified by the VPC variables
data "aws_vpc" "instance_vpc" {
cidr_block = var.vpc_cidr_block
tags = {
Name = var.vpc_name
}
}
# get a data resource identified by the VPC variables
data "aws_subnet" "instance_subnet" {
vpc_id = var.vpc_id
cidr_block = var.subnet_cidr_block
availability_zone = var.subnet_availability_zone
tags = {
Name = var.subnet_name
}
}
# create an AWS key pair resource
resource "aws_key_pair" "instance_aws_key_pair" {
key_name = "component_key_${terraform.workspace}"
public_key = file("~/.ssh/terraform.pub")
}
# create the AWS EC2 instance
resource "aws_instance" "my_aws_instance" {
key_name = aws_key_pair.instance_aws_key_pair.key_name
ami = "ami-b12345"
instance_type = "t2.micro"
subnet_id = data.aws_subnet.instance_subnet.id
connection {
type = "ssh"
user = "terraform"
private_key = file("~/.ssh/terraform")
host = self.public_ip
}
tags = {
"Name" : "my_instance_name"
"Terraform" : "true"
}
}
Is this a matter of somehow using a count, something like this:
count = var.option == 'A'? 1 : 0
Is there a way to do this, or is there a better approach? I am very new to Terraform so I may be missing something obvious.
You have a couple of questions here.
Firstly, you should be able to use the newer, experimental custom validation rules to assert that a value is in a specific list of values.
Secondly, for determining which set of variables to use, I'd recommend going with a good old map in a local value.
For example,
locals {
vpc_info = {
"A" = {
vpc_cidr_block = var.a_vpc_cidr_block
vpc_id = var.a_vpc_id
vpc_name = var.a_vpc_name
subnet_availability_zone = var.a_subnet_availability_zone
subnet_cidr_block = var.a_subnet_cidr_block
subnet_name = var.a_subnet_name
}
"B" = {
vpc_cidr_block = var.b_vpc_cidr_block
vpc_id = var.b_vpc_id
vpc_name = var.b_vpc_name
subnet_availability_zone = var.b_subnet_availability_zone
subnet_cidr_block = var.b_subnet_cidr_block
subnet_name = var.b_subnet_name
}
}
}
Then you should be able to reference a specific field, within the chose option like the following
local.vpc_info[var.option].vpc_name
Let me know if this hits all your questions.

Resources