Add specify Subnet for EC2 in another module - AWS Terraform - - amazon-ec2

I'm Terraform newbie.
I have vpc module and ec2 module.
VPC module creates 3 public subnets.
Ec2 module creates 2 instances.
-> So I want that public subnet 1 used by the 1st instance, public subnet 3 used by the 2nd instance. I used output variables, but I don't know how to work with output. Pls, help me
The provider is AWS
VPC module
#/module/vpc/main.tf
resource "aws_subnet" "pub_subnet" {
count = var.create_vpc && length(var.public_subnet) > 0 ? length(var.public_subnet) : 0
vpc_id = local.vpc_id
availability_zone = element(var.azs, count.index)
cidr_block = element(var.public_subnet, count.index)
map_public_ip_on_launch = true
# network_acls = aws_network_acl.nacls
tags = merge ({
Name = format("Pub_subnet %s", element(var.public_subnet, count.index))
},
var.tags
)
}
#module/vpc/output
output "public_subnet_id" {
value = aws_subnet.pub_subnet[0].id
}
EC2 Module
#/module/ec2/variables
variable "ec2_instance" {
type = map(object({
ec2_name = string
ec2_ami = string
ec2_instance_type = string
ec2_subnet_id = string
ec2_associate_public_ip_address = string
ec2_key_name = string
ec2_security_groups = list(string)
ec2_user_data = string
root_block_device_volume_type = string
root_block_device_volume_size = number
root_block_device_iops = number
root_block_device_throughput = number
}))
}
#/module/vpc/main.tf
resource "aws_instance" "ec2" {
for_each = var.ec2_instance
ami = each.value.ec2_ami
instance_type = each.value.ec2_instance_type
subnet_id = each.value.ec2_subnet_id
associate_public_ip_address = each.value.ec2_associate_public_ip_address
key_name = each.value.ec2_key_name
security_groups = each.value.ec2_security_groups
user_data = each.value.ec2_user_data
root_block_device {
volume_type = each.value.root_block_device_volume_type
volume_size = each.value.root_block_device_volume_size
iops = each.value.root_block_device_iops
throughput = each.value.root_block_device_throughput
}
tags = merge ({
Name = format("%s", each.value.ec2_name)
},
var.tags
)
}
Root module
#/main.tf
module "vpc" {
source = "../../modules/ec2"
public_subnet = [ "13.9.1.0/24", "13.9.2.0/24", "13.9.7.0/24" ]
}
module "ec2"{
source = "../../modules/ec2"
ec2_instance = {
"bastion_pub_sub_1" = {
ec2_name = "bastion_pub_sub_1"
ec2_ami = data.aws_ami.ubuntu_20_04.id
ec2_associate_public_ip_address = true
ec2_instance_type = "t2.micro"
ec2_key_name = #secret
ec2_subnet_id = module.vpc.public_subnet_id
ec2_user_data = "value"
root_block_device_iops = 3000
root_block_device_throughput = 125
root_block_device_volume_size = 8
root_block_device_volume_type = "gp3"
},
source = "../../qa_modules/ec2"
ec2_instance = {
"bastion_pub_sub_2" = {
ec2_name = "bastion_pub_sub_2"
ec2_ami = data.aws_ami.ubuntu_20_04.id
ec2_associate_public_ip_address = true
ec2_instance_type = "t2.micro"
ec2_key_name = #secret
ec2_subnet_id = module.vpc.public_subnet_id
ec2_user_data = "value"
root_block_device_iops = 3000
root_block_device_throughput = 125
root_block_device_volume_size = 8
root_block_device_volume_type = "gp3"
}
}
So, at ec2_subnet_id = module.vpc.public_subnet_id, how can i add the subnet "13.9.1.0/24" for instance "bastion_pub_sub_1", subnet "13.9.7.0/24" for instance "bastion_pub_sub_2"

Your outputs should be:
output "public_subnet_id" {
value = aws_subnet.pub_subnet[*].id
}
then:
module "ec2"{
source = "../../modules/ec2"
ec2_instance = {
"bastion_pub_sub_1" = {
ec2_name = "bastion_pub_sub_1"
ec2_ami = data.aws_ami.ubuntu_20_04.id
ec2_associate_public_ip_address = true
ec2_instance_type = "t2.micro"
ec2_key_name = #secret
ec2_subnet_id = module.vpc.public_subnet_id[0]
ec2_user_data = "value"
root_block_device_iops = 3000
root_block_device_throughput = 125
root_block_device_volume_size = 8
root_block_device_volume_type = "gp3"
},
source = "../../qa_modules/ec2"
ec2_instance = {
"bastion_pub_sub_2" = {
ec2_name = "bastion_pub_sub_2"
ec2_ami = data.aws_ami.ubuntu_20_04.id
ec2_associate_public_ip_address = true
ec2_instance_type = "t2.micro"
ec2_key_name = #secret
ec2_subnet_id = module.vpc.public_subnet_id[2]
ec2_user_data = "value"
root_block_device_iops = 3000
root_block_device_throughput = 125
root_block_device_volume_size = 8
root_block_device_volume_type = "gp3"
}
}

Related

Unable to register EC2 instances with ECS (EC2) cluster using terraform

I am unable to register EC2 instances with ECS cluster through terraform by using AWS as provider. I have followed all the links and tried everything but still facing issue. Can any one please assist me in reviewing my code and assist me where am I wrong and why my EC2 instance not registered with ECS cluster?
#1. Frontend security group
resource "aws_security_group" "sample_web_sg" {
name = "${var.name}-web-sg"
description = "sample-web-sg"
vpc_id = var.vpc_id
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTP"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.name}-web-sg"
Environment = var.environment
}
}
#2. IAM role
resource "aws_iam_role" "ecs-instance-role" {
name = "ecs-instance-role-test-web-1"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": ["ecs.amazonaws.com"]
},
"Effect": "Allow"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "ecs-instance-role-attachment" {
role = aws_iam_role.ecs-instance-role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}
resource "aws_iam_instance_profile" "ecs_service_role" {
role = aws_iam_role.ecs-instance-role.name
}
#3. Launch Configuration
resource "aws_launch_configuration" "sample_web_server_lc" {
name = "sample-web-server-lc"
image_id = "ami-08e0b00e3616220d8"
instance_type = "t2.micro"
iam_instance_profile = aws_iam_instance_profile.ecs_service_role.name
security_groups = [aws_security_group.sample_web_sg.id]
key_name = "sample-key-us-east-2"
associate_public_ip_address = "true"
user_data = <<-EOF
#!/bin/bash
sudo yum update -y
sudo echo "ECS_CLUSTER=my-cluster" >> /etc/ecs/ecs.config
EOF
}
#4. Autoscaling
resource "aws_autoscaling_group" "sample_asg" {
name = "sample-asg"
max_size = 1
min_size = 1
health_check_grace_period = 300
desired_capacity = 1
launch_configuration = aws_launch_configuration.sample_web_server_lc.id
vpc_zone_identifier = [var.subnet_id]
health_check_type = "EC2"
}
#5. ECS cluster
resource "aws_ecs_cluster" "my-cluster" {
name = "my-cluster"
capacity_providers = [aws_ecs_capacity_provider.sample-ecs-capacity-provider.name]
}
#6. ECS task definition
resource "aws_ecs_task_definition" "sample_ecs_task_definition" {
network_mode = "awsvpc"
requires_compatibilities = ["EC2"]
cpu = 1024
memory = 2048
family = "sample_ecs_service"
container_definitions = <<DEFINITION
[
{
"image": "wordpress:latest",
"cpu": 1024,
"memory": 512,
"name": "sample-wordpress-task-definition",
"networkMode": "awsvpc",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
]
}
]
DEFINITION
}
#7. ECS Service
resource "aws_ecs_service" "sample_ecs_service" {
name = "sample_ecs_service"
cluster = aws_ecs_cluster.my-cluster.id
launch_type = "EC2"
task_definition = aws_ecs_task_definition.sample_ecs_task_definition.arn
desired_count = 1
network_configuration {
subnets = [var.subnet_id]
}
}
resource "aws_ecs_capacity_provider" "sample-ecs-capacity-provider" {
name = "sample-ecs-capacity-provider"
auto_scaling_group_provider {
auto_scaling_group_arn = aws_autoscaling_group.sample_asg.arn
managed_scaling {
maximum_scaling_step_size = 1000
minimum_scaling_step_size = 1
status = "ENABLED"
target_capacity = 10
}
}
Is ECS agent is installed in your EC2?? Is your EC2 have internet access??

Terraform set a variable with conditional another variable present

I would like to set a variable only if a value is present.
My variables are:
variable "http_tcp_listeners" {
description = "aws_lb_listener"
type = map(any)
default = {
http = {
# load_balancer_arn = aws_lb.nlb_test.arn
port = "80"
protocol = "TCP"
action_type = "forward"
certificate_arn = ""
alpn_policy = ""
},
https = {
# load_balancer_arn = aws_lb.nlb_test.arn
port = "443"
protocol = "TLS"
action_type = "forward"
certificate_arn = "arn:aws:acm:us-east-1:b447fa7953be"
alpn_policy = "HTTP2Preferred"
}
}
}
In case http listener the string alpn_policy = each.value.alpn_policy should be absent. If the string is just empty alpn_policy = "" we will have got error Error: expected alpn_policy to be one of [HTTP1Only HTTP2Only HTTP2Optional HTTP2Preferred None], got
If we set any value we will have the error message that ALPN policy cannot be set for non secure listeners
I would like something like this. Pseudocode.
...
If val.alpn_policy != empty then
certificate_arn = try(each.value.certificate_arn, false)
alpn_policy = each.value.alpn_policy
default_action {
else
certificate_arn = try(each.value.certificate_arn, false)
default_action {
...
resource "aws_lb_listener" "frontend_http_tcp" {
for_each = var.http_tcp_listeners
load_balancer_arn = aws_lb.main.arn
port = each.value.port
protocol = each.value.protocol
certificate_arn = try(each.value.certificate_arn, false)
alpn_policy = each.value.alpn_policy
default_action {
type = each.value.action_type
target_group_arn = aws_lb_target_group.main[each.key].arn
}
depends_on = [
aws_lb.main,
aws_lb_target_group.main,
]
}
Thank #RafaP and #Marcin for the ideas. Finally, the code looks like this.
I have deleted unwanted variables in VAR and uses try Function alpn_policy= try(each.value.alpn_policy, null)
instead of
alpn_policy = lookup(var.https_listeners[count.index], "alpn_policy", null)
variable "http_tcp_listeners" {
description = "aws_lb_listener"
type = map(any)
default = {
http = {
port = "80"
protocol = "TCP"
action_type = "forward"
},
https = {
port = "443"
protocol = "TLS"
action_type = "forward"
certificate_arn = "arn:aws:acm:us-east-1:714154805721:certificate/c3be"
alpn_policy = "HTTP2Preferred"
}
}
}
resource "aws_lb_listener" "frontend_http_tcp" {
for_each = var.http_tcp_listeners
load_balancer_arn = aws_lb.main.arn
port = each.value.port
protocol = each.value.protocol
certificate_arn = try(each.value.certificate_arn, null)
alpn_policy = try(each.value.alpn_policy, null)
default_action {
type = each.value.action_type
target_group_arn = aws_lb_target_group.main[each.key].arn
}
depends_on = [
aws_lb.main,
aws_lb_target_group.main,
]
}

Terraform : How to loop aws_instances inside aws_lb for subnets

I have the following Terraform variable that defines instances
variable "instance_types" {
default = {
k8_east_1_control_plane = {
count = 1
role = "control-plane"
ami = "ami-xxx"
instance_type = "t2.large"
iam_instance_profile = "xxx-user"
subnet_id = "subnet-xxx-0"
}
k8_east_2_control_plane = {
count = 3
role = "contro-plane"
ami = "ami-xxx"
instance_type = "t2.large"
iam_instance_profile = "xxx-user"
subnet_id = "subnet-xxx-1"
}
...
}
I have N many aws_instances (example below)
locals {
instance_types = flatten([
for instance_key, instance in var.instance_types : [
for type_count in range(1, instance.count + 1) : {
new_key = "${instance_key}-${type_count}"
type = instance_key
role = instance.role
ami = instance.ami
instance_type = instance.instance_type
iam_instance_profile = instance.iam_instance_profile
subnet_id = instance.subnet_id
}
]
])
}
resource "aws_instance" "k8s-node" {
for_each = { for instance_type in local.instance_types : instance_type.new_key => instance_type }
ami = each.value.ami
instance_type = each.value.instance_type
iam_instance_profile = each.value.iam_instance_profile
subnet_id = each.value.subnet_id
...
}
Question : How to loop over these aws_instances to populate subnets from role type of "control-plane"
resource "aws_lb" "k8s_load_balancer" {
depends_on = [aws_instance.k8s-node]
name = "k8_load_balancer"
load_balancer_type = "network"
dynamic "subnet_mapping" {
// PROBLEM HERE :: How do you create for loop to populate `subnet_mapping`
// for_each = [for i in aws_instance.k8s-node: i.private_ip if i.tags.Role == "control-plane" {
// subnet_id = control_planes[i].subnet_id
// private_ip = control_planes[i].private_ip
// }]
content {
subnet_id = subnet_mapping.value.subnet_id
private_ipv4_address = subnet_mapping.value.private_ip
}
}
}
Goal in pseudo code, traditional for-loop
resource "aws_lb" "k8s_load_balancer" {
depends_on = [aws_instance.k8s-node]
name = "k8_load_balancer"
load_balancer_type = "network"
dynamic "subnet_mapping" {
for(aws_instance instance : aws_instance.k8s-node) {
if(instance.role.equls("control-plane")){
subnet_mapping {
subnet_id = instance.subnet_id
private_ipv4_address = instance.private_ip
}
}
}
}
Since your var.instance_types is used both in your aws_instance and for dynamic block, the following should be possible:
dynamic "subnet_mapping" {
for_each = { for key, value in variable.instance_types:
key => {
subnet_id = value.subnet_id
} if value.role == "control-plane"
}
content {
subnet_id = subnet_mapping.value.subnet_id
private_ipv4_address = aws_instance.k8s-node[subnet_mapping.key].private_ip
}
}

Error when referring output of one terraform module into another module

I am new to terraform and trying to create a project in terraform v0.14.0 that will perform following steps:
Create a n number of ec2 machines (one will be master and rest will be child)
Create a NLB and terget group
Attach child ec2 instance private ip to target group by referencing a output variable of step 1 (ec2 module).
But when I am running terraform plan I am getting below message. Can this be fixed? or any other approach to provision these resources in one go?
Error: Invalid for_each argument
on ../../../../modules/nlb_new2/main.tf line 493, in resource "aws_lb_target_group_attachment" "tgr_attachment":
493: for_each = {
494: for pair in setproduct(keys(tomap({arn = lookup(aws_lb_target_group.main[0],"arn")})), **var.target_id**) :
495: "${pair[0]},${pair[1]}" => {
496: target_group = aws_lb_target_group.main[0].arn
497: target = length(split(":",pair[1])) > 0 ? split(":",pair[1])[0]: null
498: port = length(split(":",pair[1])) == 2 ? split(":",pair[1])[1] : null
499: }
500: }
#project level main.tf
region = lookup(var.lb_common_prop, "region", "us-east-1")
}
module "ec2_detail" {
source = "../../../../modules/ec2_new2"
for_each = var.ec2_detail
instance_count = each.value.instance_count
name = format("%s-${var.ec2_name.suffix}",each.key)
ami_name = var.ec2_common_prop.ami_name
instance_type = var.ec2_common_prop.instance_type
subnet_id = ""
vpc_name = var.ec2_common_prop.vpc_name
subnet_names = var.security.subnet_names
vpc_security_group_names = var.security.vpc_security_group_names
associate_public_ip_address = var.ec2_common_prop.associate_public_ip_address
tags = var.ec2_tags
volume_tags = var.ec2_volume_tags
key_name = var.ec2_common_prop.key_name
user_data = ""
iam_instance_profile = var.ec2_common_prop.iam_instance_profile
monitoring = var.ec2_common_prop.monitoring
source_dest_check = var.ec2_common_prop.source_dest_check
placement_group = var.ec2_common_prop.placement_group
ebs_optimized = var.ec2_common_prop.ebs_optimized
disable_api_termination = var.ec2_common_prop.disable_api_termination
root_block_device = var.hardware.root_block_device
ebs_block_device = var.hardware.ebs_block_device
}
module "network_load_balancer" {
depends_on=[module.ec2_detail]
source = "../../../../modules/nlb_new2"
for_each = var.all_load_balancer
name = format("%s-${var.ec2_name.suffix}",each.key)
load_balancer_type = each.value.load_balancer_type
vpc_name = var.ec2_common_prop.vpc_name
subnet_names = var.security.subnet_names
internal = lookup(merge(var.lb_tags, each.value.lb_tags), "internal", true)
target_id = lookup(each.value.target_groups[0],"target_id",[])==[] ? [for pair in setproduct(module.ec2_detail["memsql-child"].private_ip, [lookup(var.lb_common_prop,"target_port")]) : "${pair[0]}:${pair[1]}" ] : lookup(each.value.target_groups[0],"target_id",[])
target_groups = each.value.target_groups
http_tcp_listeners = each.value.http_tcp_listeners
tags = merge(var.lb_tags, each.value.lb_tags)
}
#NLB module main.tf
data aws_vpc "selected" {
filter {
name = "tag:Name"
values = [var.vpc_name]
}
}
data "aws_subnet_ids" "selected" {
vpc_id=data.aws_vpc.selected.id
filter {
name = "tag:Name"
values = var.subnet_names
}
}
#data "aws_subnet" "selected" {
# for_each = data.aws_subnet_ids.selected.ids
# id = each.value
#}
resource "aws_lb" "this" {
count = var.create_lb ? 1 : 0
name = var.name
name_prefix = var.name_prefix
load_balancer_type = var.load_balancer_type
internal = var.internal
#security_groups = var.security_groups
#security_groups = [for i in range(length(var.vpc_security_group_names)) : data.aws_security_group.selected.*.id[i]]
#subnets = var.subnets
#subnets = [for s in data.aws_subnet_ids.selected : s.ids]
subnets = data.aws_subnet_ids.selected.ids
idle_timeout = var.idle_timeout
enable_cross_zone_load_balancing = var.enable_cross_zone_load_balancing
enable_deletion_protection = var.enable_deletion_protection
enable_http2 = var.enable_http2
ip_address_type = var.ip_address_type
drop_invalid_header_fields = var.drop_invalid_header_fields
# See notes in README (ref: https://github.com/terraform-providers/terraform-provider-aws/issues/7987)
dynamic "access_logs" {
for_each = length(keys(var.access_logs)) == 0 ? [] : [var.access_logs]
content {
enabled = lookup(access_logs.value, "enabled", lookup(access_logs.value, "bucket", null) != null)
bucket = lookup(access_logs.value, "bucket", null)
prefix = lookup(access_logs.value, "prefix", null)
}
}
dynamic "subnet_mapping" {
for_each = var.subnet_mapping
content {
subnet_id = subnet_mapping.value.subnet_id
allocation_id = lookup(subnet_mapping.value, "allocation_id", null)
}
}
tags = merge(
var.tags,
var.lb_tags,
{
Name = var.name != null ? var.name : var.name_prefix
"name" = var.name != null ? var.name : var.name_prefix
},
)
timeouts {
create = var.load_balancer_create_timeout
update = var.load_balancer_update_timeout
delete = var.load_balancer_delete_timeout
}
}
resource "aws_lb_target_group" "main" {
count = var.create_lb ? length(var.target_groups) : 0
name = lookup(var.target_groups[count.index], "name", null)
name_prefix = lookup(var.target_groups[count.index], "name_prefix", null)
#vpc_id = var.vpc_id
vpc_id = data.aws_vpc.selected.id
port = lookup(var.target_groups[count.index], "backend_port", null)
protocol = lookup(var.target_groups[count.index], "backend_protocol", null) != null ? upper(lookup(var.target_groups[count.index], "backend_protocol")) : null
target_type = lookup(var.target_groups[count.index], "target_type", null)
deregistration_delay = lookup(var.target_groups[count.index], "deregistration_delay", null)
slow_start = lookup(var.target_groups[count.index], "slow_start", null)
proxy_protocol_v2 = lookup(var.target_groups[count.index], "proxy_protocol_v2", false)
lambda_multi_value_headers_enabled = lookup(var.target_groups[count.index], "lambda_multi_value_headers_enabled", false)
load_balancing_algorithm_type = lookup(var.target_groups[count.index], "load_balancing_algorithm_type", null)
dynamic "health_check" {
for_each = length(keys(lookup(var.target_groups[count.index], "health_check", {}))) == 0 ? [] : [lookup(var.target_groups[count.index], "health_check", {})]
content {
enabled = lookup(health_check.value, "enabled", null)
interval = lookup(health_check.value, "interval", null)
path = lookup(health_check.value, "path", null)
port = lookup(health_check.value, "port", null)
healthy_threshold = lookup(health_check.value, "healthy_threshold", null)
unhealthy_threshold = lookup(health_check.value, "unhealthy_threshold", null)
timeout = lookup(health_check.value, "timeout", null)
protocol = lookup(health_check.value, "protocol", null)
matcher = lookup(health_check.value, "matcher", null)
}
}
dynamic "stickiness" {
for_each = length(keys(lookup(var.target_groups[count.index], "stickiness", {}))) == 0 ? [] : [lookup(var.target_groups[count.index], "stickiness", {})]
content {
enabled = lookup(stickiness.value, "enabled", null)
cookie_duration = lookup(stickiness.value, "cookie_duration", null)
type = lookup(stickiness.value, "type", null)
}
}
tags = merge(
var.tags,
{"stack-technology" = "target-group"},
var.target_group_tags,
#lookup(var.target_groups[count.index], "tags", {}),
{
"Name" = format("%s${var.name}", lookup(var.target_groups[count.index], "name_prefix", ""))
"name" = format("%s${var.name}", lookup(var.target_groups[count.index], "name_prefix", ""))
},
)
depends_on = [aws_lb.this]
lifecycle {
create_before_destroy = true
}
}
resource "aws_lb_listener_rule" "https_listener_rule" {
count = var.create_lb ? length(var.https_listener_rules) : 0
listener_arn = aws_lb_listener.frontend_https[lookup(var.https_listener_rules[count.index], "https_listener_index", count.index)].arn
priority = lookup(var.https_listener_rules[count.index], "priority", null)
# authenticate-cognito actions
dynamic "action" {
for_each = [
for action_rule in var.https_listener_rules[count.index].actions :
action_rule
if action_rule.type == "authenticate-cognito"
]
content {
type = action.value["type"]
authenticate_cognito {
authentication_request_extra_params = lookup(action.value, "authentication_request_extra_params", null)
on_unauthenticated_request = lookup(action.value, "on_authenticated_request", null)
scope = lookup(action.value, "scope", null)
session_cookie_name = lookup(action.value, "session_cookie_name", null)
session_timeout = lookup(action.value, "session_timeout", null)
user_pool_arn = action.value["user_pool_arn"]
user_pool_client_id = action.value["user_pool_client_id"]
user_pool_domain = action.value["user_pool_domain"]
}
}
}
# authenticate-oidc actions
dynamic "action" {
for_each = [
for action_rule in var.https_listener_rules[count.index].actions :
action_rule
if action_rule.type == "authenticate-oidc"
]
content {
type = action.value["type"]
authenticate_oidc {
# Max 10 extra params
authentication_request_extra_params = lookup(action.value, "authentication_request_extra_params", null)
authorization_endpoint = action.value["authorization_endpoint"]
client_id = action.value["client_id"]
client_secret = action.value["client_secret"]
issuer = action.value["issuer"]
on_unauthenticated_request = lookup(action.value, "on_unauthenticated_request", null)
scope = lookup(action.value, "scope", null)
session_cookie_name = lookup(action.value, "session_cookie_name", null)
session_timeout = lookup(action.value, "session_timeout", null)
token_endpoint = action.value["token_endpoint"]
user_info_endpoint = action.value["user_info_endpoint"]
}
}
}
# redirect actions
dynamic "action" {
for_each = [
for action_rule in var.https_listener_rules[count.index].actions :
action_rule
if action_rule.type == "redirect"
]
content {
type = action.value["type"]
redirect {
host = lookup(action.value, "host", null)
path = lookup(action.value, "path", null)
port = lookup(action.value, "port", null)
protocol = lookup(action.value, "protocol", null)
query = lookup(action.value, "query", null)
status_code = action.value["status_code"]
}
}
}
# fixed-response actions
dynamic "action" {
for_each = [
for action_rule in var.https_listener_rules[count.index].actions :
action_rule
if action_rule.type == "fixed-response"
]
content {
type = action.value["type"]
fixed_response {
message_body = lookup(action.value, "message_body", null)
status_code = lookup(action.value, "status_code", null)
content_type = action.value["content_type"]
}
}
}
# forward actions
dynamic "action" {
for_each = [
for action_rule in var.https_listener_rules[count.index].actions :
action_rule
if action_rule.type == "forward"
]
content {
type = action.value["type"]
target_group_arn = aws_lb_target_group.main[lookup(action.value, "target_group_index", count.index)].id
}
}
# Path Pattern condition
dynamic "condition" {
for_each = [
for condition_rule in var.https_listener_rules[count.index].conditions :
condition_rule
if length(lookup(condition_rule, "path_patterns", [])) > 0
]
content {
path_pattern {
values = condition.value["path_patterns"]
}
}
}
# Host header condition
dynamic "condition" {
for_each = [
for condition_rule in var.https_listener_rules[count.index].conditions :
condition_rule
if length(lookup(condition_rule, "host_headers", [])) > 0
]
content {
host_header {
values = condition.value["host_headers"]
}
}
}
# Http header condition
dynamic "condition" {
for_each = [
for condition_rule in var.https_listener_rules[count.index].conditions :
condition_rule
if length(lookup(condition_rule, "http_headers", [])) > 0
]
content {
dynamic "http_header" {
for_each = condition.value["http_headers"]
content {
http_header_name = http_header.value["http_header_name"]
values = http_header.value["values"]
}
}
}
}
# Http request method condition
dynamic "condition" {
for_each = [
for condition_rule in var.https_listener_rules[count.index].conditions :
condition_rule
if length(lookup(condition_rule, "http_request_methods", [])) > 0
]
content {
http_request_method {
values = condition.value["http_request_methods"]
}
}
}
# Query string condition
dynamic "condition" {
for_each = [
for condition_rule in var.https_listener_rules[count.index].conditions :
condition_rule
if length(lookup(condition_rule, "query_strings", [])) > 0
]
content {
dynamic "query_string" {
for_each = condition.value["query_strings"]
content {
key = lookup(query_string.value, "key", null)
value = query_string.value["value"]
}
}
}
}
# Source IP address condition
dynamic "condition" {
for_each = [
for condition_rule in var.https_listener_rules[count.index].conditions :
condition_rule
if length(lookup(condition_rule, "source_ips", [])) > 0
]
content {
source_ip {
values = condition.value["source_ips"]
}
}
}
}
resource "aws_lb_listener" "frontend_http_tcp" {
count = var.create_lb ? length(var.http_tcp_listeners) : 0
load_balancer_arn = aws_lb.this[0].arn
port = var.http_tcp_listeners[count.index]["port"]
protocol = var.http_tcp_listeners[count.index]["protocol"]
dynamic "default_action" {
for_each = length(keys(var.http_tcp_listeners[count.index])) == 0 ? [] : [var.http_tcp_listeners[count.index]]
# Defaults to forward action if action_type not specified
content {
type = lookup(default_action.value, "action_type", "forward")
target_group_arn = contains([null, "", "forward"], lookup(default_action.value, "action_type", "")) ? aws_lb_target_group.main[lookup(default_action.value, "target_group_index", count.index)].id : null
dynamic "redirect" {
for_each = length(keys(lookup(default_action.value, "redirect", {}))) == 0 ? [] : [lookup(default_action.value, "redirect", {})]
content {
path = lookup(redirect.value, "path", null)
host = lookup(redirect.value, "host", null)
port = lookup(redirect.value, "port", null)
protocol = lookup(redirect.value, "protocol", null)
query = lookup(redirect.value, "query", null)
status_code = redirect.value["status_code"]
}
}
dynamic "fixed_response" {
for_each = length(keys(lookup(default_action.value, "fixed_response", {}))) == 0 ? [] : [lookup(default_action.value, "fixed_response", {})]
content {
content_type = fixed_response.value["content_type"]
message_body = lookup(fixed_response.value, "message_body", null)
status_code = lookup(fixed_response.value, "status_code", null)
}
}
}
}
}
resource "aws_lb_listener" "frontend_https" {
count = var.create_lb ? length(var.https_listeners) : 0
load_balancer_arn = aws_lb.this[0].arn
port = var.https_listeners[count.index]["port"]
protocol = lookup(var.https_listeners[count.index], "protocol", "HTTPS")
certificate_arn = var.https_listeners[count.index]["certificate_arn"]
ssl_policy = lookup(var.https_listeners[count.index], "ssl_policy", var.listener_ssl_policy_default)
dynamic "default_action" {
for_each = length(keys(var.https_listeners[count.index])) == 0 ? [] : [var.https_listeners[count.index]]
# Defaults to forward action if action_type not specified
content {
type = lookup(default_action.value, "action_type", "forward")
target_group_arn = contains([null, "", "forward"], lookup(default_action.value, "action_type", "")) ? aws_lb_target_group.main[lookup(default_action.value, "target_group_index", count.index)].id : null
dynamic "redirect" {
for_each = length(keys(lookup(default_action.value, "redirect", {}))) == 0 ? [] : [lookup(default_action.value, "redirect", {})]
content {
path = lookup(redirect.value, "path", null)
host = lookup(redirect.value, "host", null)
port = lookup(redirect.value, "port", null)
protocol = lookup(redirect.value, "protocol", null)
query = lookup(redirect.value, "query", null)
status_code = redirect.value["status_code"]
}
}
dynamic "fixed_response" {
for_each = length(keys(lookup(default_action.value, "fixed_response", {}))) == 0 ? [] : [lookup(default_action.value, "fixed_response", {})]
content {
content_type = fixed_response.value["content_type"]
message_body = lookup(fixed_response.value, "message_body", null)
status_code = lookup(fixed_response.value, "status_code", null)
}
}
# Authentication actions only available with HTTPS listeners
dynamic "authenticate_cognito" {
for_each = length(keys(lookup(default_action.value, "authenticate_cognito", {}))) == 0 ? [] : [lookup(default_action.value, "authenticate_cognito", {})]
content {
# Max 10 extra params
authentication_request_extra_params = lookup(authenticate_cognito.value, "authentication_request_extra_params", null)
on_unauthenticated_request = lookup(authenticate_cognito.value, "on_authenticated_request", null)
scope = lookup(authenticate_cognito.value, "scope", null)
session_cookie_name = lookup(authenticate_cognito.value, "session_cookie_name", null)
session_timeout = lookup(authenticate_cognito.value, "session_timeout", null)
user_pool_arn = authenticate_cognito.value["user_pool_arn"]
user_pool_client_id = authenticate_cognito.value["user_pool_client_id"]
user_pool_domain = authenticate_cognito.value["user_pool_domain"]
}
}
dynamic "authenticate_oidc" {
for_each = length(keys(lookup(default_action.value, "authenticate_oidc", {}))) == 0 ? [] : [lookup(default_action.value, "authenticate_oidc", {})]
content {
# Max 10 extra params
authentication_request_extra_params = lookup(authenticate_oidc.value, "authentication_request_extra_params", null)
authorization_endpoint = authenticate_oidc.value["authorization_endpoint"]
client_id = authenticate_oidc.value["client_id"]
client_secret = authenticate_oidc.value["client_secret"]
issuer = authenticate_oidc.value["issuer"]
on_unauthenticated_request = lookup(authenticate_oidc.value, "on_unauthenticated_request", null)
scope = lookup(authenticate_oidc.value, "scope", null)
session_cookie_name = lookup(authenticate_oidc.value, "session_cookie_name", null)
session_timeout = lookup(authenticate_oidc.value, "session_timeout", null)
token_endpoint = authenticate_oidc.value["token_endpoint"]
user_info_endpoint = authenticate_oidc.value["user_info_endpoint"]
}
}
}
}
dynamic "default_action" {
for_each = contains(["authenticate-oidc", "authenticate-cognito"], lookup(var.https_listeners[count.index], "action_type", {})) ? [var.https_listeners[count.index]] : []
content {
type = "forward"
target_group_arn = aws_lb_target_group.main[lookup(default_action.value, "target_group_index", count.index)].id
}
}
}
resource "aws_lb_listener_certificate" "https_listener" {
count = var.create_lb ? length(var.extra_ssl_certs) : 0
listener_arn = aws_lb_listener.frontend_https[var.extra_ssl_certs[count.index]["https_listener_index"]].arn
certificate_arn = var.extra_ssl_certs[count.index]["certificate_arn"]
}
resource "aws_lb_target_group_attachment" "tgr_attachment" {
depends_on = [aws_lb.this]
for_each = {
for pair in setproduct(keys(tomap({arn = lookup(aws_lb_target_group.main[0],"arn")})), var.target_id) :
"${pair[0]},${pair[1]}" => {
target_group = aws_lb_target_group.main[0].arn
target = length(split(":",pair[1])) > 0 ? split(":",pair[1])[0]: null
port = length(split(":",pair[1])) == 2 ? split(":",pair[1])[1] : null
}
}
target_group_arn = var.create_lb ? each.value.target_group : null
target_id = var.create_lb ? each.value.target : null
port = var.create_lb ? each.value.port : null
}
Your error message and your code examples don't seem to be complete, but in spite of that I think I can see what's causing the problem here: you're trying to use the target group ARNs as part of the instance keys for aws_lb_target_group_attachment.tgr_attachment, but the ARNs are not appropriate to use for instance keys because the provider doesn't know the ARN value until after each object is created, and so it can't predict the value during planning.
Instead, I'd suggest to change your var.target_groups to be a map of objects instead of a list of objects, and then use for_each = var.target_groups in aws_lb_target_group.main. The caller of your module can therefore specify a meaningful name for each of the target groups, which you can use as part of the instance keys for aws_lb_target_group_attachment.tgr_attachment:
resource "aws_lb_target_group_attachment" "tgr_attachment" {
for_each = {
for pair in setproduct(keys(aws_lb_target_group.main), var.target_id) :
"${pair[0]},${pair[1]}" => {
target_group_arn = aws_lb_target_group.main[pair[0]].arn
target = length(split(":",pair[1])) > 0 ? split(":",pair[1])[0]: null
port = length(split(":",pair[1])) == 2 ? split(":",pair[1])[1] : null
}
}
# ...
}
By making the caller of the module specify a key for each of the target groups, you put the caller in control of whether a change to the collection of target groups should be understood as updating an existing target group in-place, creating a new target group, or removing an existing target group.
You also, as a nice side-effect, get a static and stable identifier to use for each of your instances which would be meaningful to someone reviewing the terraform plan output, and that will stay consistent even if a future change causes one of the target groups to be replaced.

Building LINQ Dynamic Order Clause, But Cast Field

The following code works great of I want to dynamically build an orderby:
public static IQueryable<TEntity> OrderByAnyField<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc, Type propertyType)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
So, I want to be able to change the CAST of the sort. So, as an example, I would like to take:
.OrderBy(x => x.Something)
and do this instead:
.OrderBy(x => double.Parse(x.Something))
Any help is greatly appreciated
I am sharing one simpler approach to do the same. You can add generics as per your requirement. You can play on data any way you want
static object GetOrder(Table tb, string propertyName, bool desc)
{
if (desc)
return 0;
PropertyInfo pI = typeof(Table).GetProperty(propertyName);
var val = pI.GetValue(tb);
return val;
}
static object GetOrderDesc(Table tb, string propertyName, bool desc)
{
if (!desc)
return 0;
PropertyInfo pI = typeof(Table).GetProperty(propertyName);
var val = pI.GetValue(tb);
return val;
}
static void Main(string[] args)
{
bool desc = false;
List<Table> table = new List<Table>() {
new Table() { ID = "03", X = "Str1", Y = "C1", P = 10 },
new Table() { ID = "04", X = "Str1", Y = "C1", P = 5 },
new Table() { ID = "05", X = "Str1", Y = "C1", P = 1 },
new Table() { ID = "06", X = "Str1", Y = "C1", P = 2 },
new Table() { ID = "07", X = "Str2", Y = "C1", P = 25 },
new Table() { ID = "08", X = "Str2", Y = "C1", P = 4 },
new Table() { ID = "09", X = "Str1", Y = "C2", P = 411 },
new Table() { ID = "10", X = "Str1", Y = "C2", P = 2356 },
new Table() { ID = "11", X = "Str2", Y = "C2", P = 12 },
new Table() { ID = "12", X = "Str2", Y = "C2", P = 33 },
};
var sortedTable = table.OrderBy(x => GetOrder(x, "P", desc)).OrderByDescending(x => GetOrderDesc(x, "P", desc));
}

Resources