in terraform how to set if condition for data resource - terraform

How do I set if statement for data aws_acm_certificate. I get the following error when I add count for data aws_acm_certificate. I do not get the same error for resource "aws_lb_listener".
Because data.aws_acm_certificate.acm_certificate has "count" set, its
attributes must be accessed on specific instances.
data "aws_acm_certificate" "acm_certificate" {
count = var.protocol!="TCP" ? 1 : 0
domain = var.certificate_domain_name
most_recent = true
}
resource "aws_lb_listener" "listener_https" {
count = var.protocol!="TCP" ? 1 : 0
load_balancer_arn = var.load_balancer_arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = data.aws_acm_certificate.acm_certificate.arn
default_action {
type = "forward"
target_group_arn = var.target_group_arn
}
}

Because you have a count on your data, you need to access it as an array
certificate_arn = data.aws_acm_certificate.acm_certificate[0].arn
Alternatively, you could use a for_each for your resource
Something like
resource "aws_lb_listener" "listener_https" {
for_each = data.aws_acm_certificate.acm_certificate
load_balancer_arn = var.load_balancer_arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = each.value.arn
default_action {
type = "forward"
target_group_arn = var.target_group_arn
}
}

Related

Terraform: How would I reference a variable in For_Each that is not included in a map, such as file_system_id?

Maybe this is possible, maybe it's not. I'm attempting to mount an EFS target using some of the values stored in a var.ec2_server map which includes subnets, EBS volumes, etc.
The issue I've run into is that I created the EFS File System using a for_each statement; since the efs_file_system was created with a for_each, I must reference the attributes within specified instances when referring to the resource in other variables.
The file_system_id is only known after creation so how would I reference it within a map or other variable inside other for_each statements, such as the aws_efs_mount_target resource defined below? Will what I'm doing even work?
I'm using the antiquated resource.tf > variable.tf > terraform.tfvars (config) style code :
...the ec2.tf file:
###############################################################################
# EC2 Instance
resource "aws_instance" "ec2" {
for_each = var.ec2_servers
ami = data.aws_ami.ec2[each.key].id
disable_api_termination = var.disable_api_termination
iam_instance_profile = aws_iam_instance_profile.ec2[each.key].id
instance_type = each.value.instance_type
monitoring = true
vpc_security_group_ids = [aws_security_group.ec2[each.key].id]
subnet_id = each.value.subnet_name != null ? aws_subnet.private["${each.value.vpc_name}.${each.value.subnet_name}.${each.value.availability_zone}"].id : null
key_name = aws_key_pair.ec2.key_name
user_data = each.value.user_data == "" ? null : templatefile("./${each.value.user_data}", { region = data.aws_region.current.name })
private_ip = each.value.private_ip
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
}
root_block_device {
delete_on_termination = true
encrypted = true
volume_size = each.value.root_volume_size
volume_type = "gp2"
tags = {
Name = replace("${var.project_name}-${each.value.vpc_name}-${each.key}-EBS", " ", "")
}
}
dynamic "ebs_block_device" {
for_each = each.value.ebs_volumes
content {
volume_type = ebs_block_device.value.volume_type
volume_size = ebs_block_device.value.volume_size
device_name = ebs_block_device.value.device_name
tags = {
Name = replace("${var.project_name}-${each.value.vpc_name}-${each.key}-EBS", " ", "") }
}
}
tags = {
Name = replace("${var.project_name}-${each.value.vpc_name}-${each.key}-EC2", " ", "")
Backup = "true"
}
}
...the efs.tf file:
###############################################################################
# Create EFS File System
resource "aws_efs_file_system" "efs" {
for_each = {
for object, property in var.efs_config : object => property if var.efs_config.efs_enabled
}
creation_token = var.efs_config.efs_creation_token
encrypted = var.efs_config.efs_encrypt
kms_key_id = aws_kms_key.efs_kms.arn
tags = {
Name = replace("${var.project_name}-${var.efs_config.efs_vpc}-EFS", " ", "")
}
}
resource "aws_efs_backup_policy" "efs_backup_policy" {
file_system_id = "NEEDS TO BE DETERMINED"
backup_policy {
status = "ENABLED"
}
}
resource "aws_efs_mount_target" "efs_mount_target" {
for_each = var.ec2_servers
file_system_id = "NEEDS TO BE DETERMINED"
subnet_id = each.value.subnet_name == "app" ? aws_subnet.private["${each.value.vpc_name}.${each.value.subnet_name}.${each.value.availability_zone}"].id : null
ip_address = lookup(var.efs_config, "efs_private_ip")
security_groups = [aws_security_group.ec2[each.key].id]
}
...the variables.tf file:
variable "ec2_servers" {
description = "A configurable map of EC2 settings."
type = map(any)
}
...the terraform.tfvars file:
###############################################################################
# EFS Configurations
efs_config = {
efs_enabled = true
efs_creation_token = "Prod_EFS"
efs_encrypt = true
efs_vpc = "Prod"
efs_private_ip = "10.200.0.5"
}
# Server Configurations
ec2_servers = {
EC201 = {
ami_owner = "XXXXXXXXXXXX"
ami_name = "xxxxx-xxxxxx"
instance_type = "t2.micro"
root_volume_size = "10"
ebs_volumes = [
{
volume_size = "20"
volume_type = "gp3"
device_name = "/dev/xvdba"
},
{
volume_size = "20"
volume_type = "gp3"
device_name = "/dev/xvdbb"
}
]
vpc_name = "Prod"
subnet_name = "web"
set_ec2_hostname = false
ec2_hostname = "xxxxxxxxx"
availability_zone = "a"
public_dns = false
private_dns = true
policy_names = []
s3_storage = false
transfer_files = false
user_data = "setup_ssm_linux.tftpl"
private_ip = "10.200.0.132"
ingress = {
ssh = {
description = "Internal address"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"10.200.0.0/22"
]
}
}
}
}
I've tried a number of things such as creating a data resource for aws_efs_mount_target and nothing I do seems to work. If anyone could provide a little insight, both my project leads and myself would be greatly appreciated!
If I missed anything here, please let me know and I will update the question with the relevant information.
Your aws_efs_backup_policy needs a for_each also, since you need to create one for each EFS volume:
resource "aws_efs_backup_policy" "efs_backup_policy" {
for_each = aws_efs_file_system.efs
file_system_id = each.id
backup_policy {
status = "ENABLED"
}
}
For your EFS mount target, I would use the same for_each you use for the EFS volumes:
resource "aws_efs_mount_target" "efs_mount_target" {
for_each = {
for object, property in var.efs_config : object => property if var.efs_config.efs_enabled
}
file_system_id = aws_efs_file_system.efs[each.key].id
...
}
I think you need to clean up those other lookups in aws_efs_mount_target by moving those values into the efs_config var.

terraform for loop list for target_groups with a combine variable

Is there a way to use the below list in a for loop and add in the target_groups ? I am trying to use the prefix with target_groups variable in a for-loop. I have tested also for_each. The target_groups expects the list format but the for_each does not give that expected result.
variable "prefix" {
description = "NLB Prefix"
type = any
default = "test-target"
}
variable "target_groups" {
description = "NLB"
type = any
default = {
tg1 = {
name_prefix = "test"
backend_protocol = "TCP"
backend_port = 443
target_type = "ip"
deregistration_delay = 10
preserve_client_ip = true
stickiness = {
enabled = true
type = "source_ip"
}
targets = {
appl1 = {
target_id = "191.11.11.11"
port = 443
}
}
},
}
}
}
I tried the list below for_each
module "g-appl_nlb" {
source = "../../modules/compute/lb"
name = format("%s-g-appl-nlb", var.name_prefix)
load_balancer_type = "network"
vpc_id = data.aws_vpc.target_vpc.id
...
target_groups = [
for_each = var.target_groups
name_previs = var.prefix
backend_protocol = each.value["backend_protocol"]
backend_port = each.value["backend_port"]
target_type = each.value["target_type"]
deregistration_delay = each.value["deregistration_delay"]
preserve_client_ip = each.value["preserve_client_ip"]
stickiness = each.value["stickiness"]
]
....
Basically, I managed the solved my request with the below approach.
locals {
target_groups = flatten([
for tg_data in var.target_groups: {
name_prefix = "var.name_prefix"
backend_protocol = tg_data.backend_protocol
backend_port = tg_data.backend_port
target_type = tg_data.target_type
deregistration_delay = tg_data.deregistration_delay
preserve_client_ip = tg_data.preserve_client_ip
....
])
}
module "g-appl_nlb" {
source = "../../modules/compute/lb"
name = format("%s-g-appl-nlb", var.name_prefix)
load_balancer_type = "network"
vpc_id = data.aws_vpc.target_vpc.id
...
target_groups = local.target_groups

Application gateway request_routing_rules does not exist

I am trying to deploy a azure application gateway. I set the configuration as follow:
resource "azurerm_application_gateway" "demo-app-gateway" {
location = var.location
resource_group_name = azurerm_resource_group.rg-hri-testing-env.name
name = "demo-app-gateway"
autoscale_configuration {
max_capacity = 10
min_capacity = 2
}
frontend_port {
name = "port_443"
port = 443
}
sku {
name = "Standard_v2"
tier = "Standard_v2"
}
frontend_ip_configuration {
name = "appGwPublicFrontendIp"
public_ip_address_id = azurerm_public_ip.demo-app-gateway-public-ip.id
private_ip_address_allocation = "Dynamic"
}
backend_http_settings {
cookie_based_affinity = "Disabled"
name = "demo-http-settings"
port = 443
protocol = "Https"
host_name = "apim.test.com"
pick_host_name_from_backend_address = false
path = "/external/"
request_timeout = 20
probe_name = "demo-apim-probe"
trusted_root_certificate_names = ["demo-trusted-root-ca-certificate"]
}
probe {
interval = 30
name = "demo-apim-probe"
path = "/status-0123456789abcdef"
protocol = "Https"
timeout = 30
unhealthy_threshold = 3
pick_host_name_from_backend_http_settings = true
match {
body = ""
status_code = [
"200-399"
]
}
}
gateway_ip_configuration {
name = "appGatewayIpConfig"
subnet_id = azurerm_subnet.GatewaSubnet.id
}
backend_address_pool {
name = "demo-backend-pool"
}
http_listener {
frontend_ip_configuration_name = "appGwPublicFrontendIp"
frontend_port_name = "port_443"
name = "demo-app-gateway-listener"
protocol = "Https"
require_sni = false
ssl_certificate_name = "demo-app-gateway-certificate"
}
ssl_certificate {
data = filebase64(var.ssl_certificate_path)
name = "demo-app-gateway-certificate"
password = var.ssl_certificate_password
}
trusted_root_certificate {
data = filebase64(var.ssl_certificate_path)
name = "demo-trusted-root-ca-certificate"
}
request_routing_rule {
http_listener_name = "demo-app-gateway-listener"
name = "demo-rule"
rule_type = "Basic"
backend_address_pool_name = "demo-backend-pool"
backend_http_settings_name = "demo-http-setting"
}
}
But when I run terraform apply I get this error.
Error: creating/updating Application Gateway: (Name "demo-app-gateway" / Resource Group "rg-hri-testing-apim"): network.ApplicationGatewaysClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: Code="InvalidResourceReference" Message="Resource /subscriptions/my-sub/resourceGroups/rg-hri-testing-apim/providers/Microsoft.Network/applicationGateways/demo-app-gateway/backendHttpSettingsCollection/demo-http-setting referenced by resource /subscriptions/mysub/resourceGroups/rg-hri-testing-apim/providers/Microsoft.Network/applicationGateways/demo-app-gateway/requestRoutingRules/demo-rule was not found. Please make sure that the referenced resource exists, and that both resources are in the same region." Details=[]
on app-gateway-main.tf line 1, in resource "azurerm_application_gateway" "demo-app-gateway":
1: resource "azurerm_application_gateway" "demo-app-gateway" {
The resource causing the error is the request_routing_rule not being found, but what it confuses me is that is looking for it before to create it?
Can anyone please help me to understand what am I doing wrong here?
Please if you need more infos, just let me know.
Thank you very much
Please check the Backend HTTP settings name which is being referenced by request routing rule block. You have to change it to demo-http-settings in request_routing_rule to resolve the error.
Issue:
You are using below as backend http setting :
backend_http_settings {
cookie_based_affinity = "Disabled"
name = "demo-http-settings"
port = 443
protocol = "Https"
host_name = "apim.test.com"
pick_host_name_from_backend_address = false
path = "/external/"
request_timeout = 20
probe_name = "demo-apim-probe"
trusted_root_certificate_names = ["demo-trusted-root-ca-certificate"]
}
But while referencing it in request request routing rule you are using :
request_routing_rule {
http_listener_name = "demo-app-gateway-listener"
name = "demo-rule"
rule_type = "Basic"
backend_address_pool_name = "demo-backend-pool"
backend_http_settings_name = "demo-http-setting"
As you have given the name of backend_http_setting_name = demo-http-settings and giving it as demo-http-setting in request_routing_rule. It will error out as it can't find the backend http setting.

Terraform for aws_alb_listener how to add multiple target_group_arn in default action for type="forward"

My Approach: I have to create an "aws_lb_listener" resource , in the default action I have type = " forward" but I don't have one target_group_arn . I have more than one target_group_arn values.
Below snippet shows frontend-listener but the target_group_arn should include more than one arn values.
resource "aws_alb_listener" "frontend-listener" {
load_balancer_arn = aws_alb.ss_alb.arn
port = "443" #"80"
protocol = "HTTPS"
depends_on = [aws_alb_target_group.aws_alb_target_group]
default_action {
#target_group_arn = aws_alb_target_group.aws_alb_target_group.arn
type = "forward"
}
}
The aws_alb_target_group resource shows multiple target-group being created .
resource "aws_alb_target_group" "aws_alb_target_group" {
for_each = local.ebapp_name
name = "${each.value.name}-tg"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
}
I have looked at the terraform documentation but couldn't get a solution. (https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener)
resource "aws_alb_listener_rule" "aws_alb_listener_rule"{
for_each = local.ebapp_name
listener_arn = aws_alb_listener.frontend-listener.arn
action {
type = "forward"
target_group_arn = aws_alb_target_group.aws_alb_target_group[each.value.name].arn
}
condition {
path_pattern {
values = ["/${each.value.name}/*"]
}
}
}
I have also mentioned the Listener rules .
The error is shown with terraform apply command and it is as below:
for actions of type 'forward', you must specify a 'forward' block or 'target_group_arn'
What changes to make to solve this error?
I think you could achieve that using dynamic blocks. For example:
resource "aws_alb_listener" "frontend-listener" {
load_balancer_arn = aws_alb.ss_alb.arn
port = "443" #"80"
protocol = "HTTPS"
depends_on = [aws_alb_target_group.aws_alb_target_group]
default_action {
type = "forward"
forward {
dynamic "target_group" {
for_each = aws_alb_target_group.aws_alb_target_group
content {
arn = target_group.value["arn"]
}
}
}
}
}
The above is example only, and some adjustment may still be required to make it work as expected.
This worked for me. It adds now all three target groups to the defualt action of the listener.
locals {
target_groups = ["1", "2", "3"]
}
resource "aws_lb_listener" "https_to_target_group" {
count = length(local.target_groups)
certificate_arn = aws_acm_certificate.cd.arn
load_balancer_arn = aws_lb.cd.arn
port = var.alb.port
protocol = var.alb.protocol
ssl_policy = var.alb.ssl_policy
default_action {
type = "forward"
forward {
dynamic "target_group" {
for_each = local.target_groups
content {
arn = aws_lb_target_group.cd[target_group.key].arn
}
}
}
}

ECS and Application Load Balancer Ephemeral Ports using Terraform

I tried to build an ECS cluster with ALB in front using terraform. As I used dynamic port mappping the targets will not be registerd as healthy. I played with the healthcheck and Success codes if I set it to 301 everything is fine.
ECS
data "template_file" "mb_task_template" {
template = file("${path.module}/templates/marketplace-backend.json.tpl")
vars = {
name = "${var.mb_image_name}"
port = "${var.mb_port}"
image = "${aws_ecr_repository.mb.repository_url}"
log_group = "${aws_cloudwatch_log_group.mb.name}"
region = "${var.region}"
}
}
resource "aws_ecs_cluster" "mb" {
name = var.mb_image_name
}
resource "aws_ecs_task_definition" "mb" {
family = var.mb_image_name
container_definitions = data.template_file.mb_task_template.rendered
volume {
name = "mb-home"
host_path = "/ecs/mb-home"
}
}
resource "aws_ecs_service" "mb" {
name = var.mb_repository_url
cluster = aws_ecs_cluster.mb.id
task_definition = aws_ecs_task_definition.mb.arn
desired_count = 2
iam_role = var.aws_iam_role_ecs
depends_on = [aws_autoscaling_group.mb]
load_balancer {
target_group_arn = var.target_group_arn
container_name = var.mb_image_name
container_port = var.mb_port
}
}
resource "aws_autoscaling_group" "mb" {
name = var.mb_image_name
availability_zones = ["${var.availability_zone}"]
min_size = var.min_instance_size
max_size = var.max_instance_size
desired_capacity = var.desired_instance_capacity
health_check_type = "EC2"
health_check_grace_period = 300
launch_configuration = aws_launch_configuration.mb.name
vpc_zone_identifier = flatten([var.vpc_zone_identifier])
lifecycle {
create_before_destroy = true
}
}
data "template_file" "user_data" {
template = file("${path.module}/templates/user_data.tpl")
vars = {
ecs_cluster_name = "${var.mb_image_name}"
}
}
resource "aws_launch_configuration" "mb" {
name_prefix = var.mb_image_name
image_id = var.ami
instance_type = var.instance_type
security_groups = ["${var.aws_security_group}"]
iam_instance_profile = var.aws_iam_instance_profile
key_name = var.key_name
associate_public_ip_address = true
user_data = data.template_file.user_data.rendered
lifecycle {
create_before_destroy = true
}
}
resource "aws_cloudwatch_log_group" "mb" {
name = var.mb_image_name
retention_in_days = 14
}
ALB
locals {
target_groups = ["1", "2"]
}
resource "aws_alb" "mb" {
name = "${var.mb_image_name}-alb"
internal = false
load_balancer_type = "application"
security_groups = ["${aws_security_group.mb_alb.id}"]
subnets = var.subnets
tags = {
Name = var.mb_image_name
}
}
resource "aws_alb_target_group" "mb" {
count = length(local.target_groups)
name = "${var.mb_image_name}-tg-${element(local.target_groups, count.index)}"
port = var.mb_port
protocol = "HTTP"
vpc_id = var.vpc_id
target_type = "instance"
health_check {
path = "/health"
protocol = "HTTP"
timeout = "10"
interval = "15"
healthy_threshold = "3"
unhealthy_threshold = "3"
matcher = "200-299"
}
lifecycle {
create_before_destroy = true
}
tags = {
Name = var.mb_image_name
}
}
resource "aws_alb_listener" "mb_https" {
load_balancer_arn = aws_alb.mb.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = module.dns.certificate_arn
default_action {
type = "forward"
target_group_arn = aws_alb_target_group.mb.0.arn
}
}
resource "aws_alb_listener_rule" "mb_https" {
listener_arn = aws_alb_listener.mb_https.arn
priority = 100
action {
type = "forward"
target_group_arn = aws_alb_target_group.mb.0.arn
}
condition {
field = "path-pattern"
values = ["/health/"]
}
}
Okay. Looks like the code above is working. I had different issue with networking.

Resources