Multiple conditions in load balancer rules throw the error "Only one of host_header, http_header, http_request_method, path_pattern, query_string or source_ip can be set in a condition block"
cdhesse opened this issue · 9 comments
Description
Currently, multiple conditions, such as path_pattern and host_header together, generate a single condition block, which gives the error
"Only one of host_header, http_header, http_request_method, path_pattern, query_string or source_ip can be set in a condition block"
This should look like this - taken from the terraform aws provider documentation: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener_rule
resource "aws_lb_listener_rule" "static" {
listener_arn = aws_lb_listener.front_end.arn
priority = 100
action {
type = "forward"
target_group_arn = aws_lb_target_group.static.arn
}
condition {
path_pattern {
values = ["/static/*"]
}
}
condition {
host_header {
values = ["example.com"]
}
}
}
- [ x ] ✋ I have searched the open/closed issues and my issue is not listed.
⚠️ Note
Before you submit an issue, please perform the following first:
- Remove the local
.terraform
directory (! ONLY if state is stored remotely, which hopefully you are following that best practice!):rm -rf .terraform/
- Re-initialize the project root to pull down modules:
terraform init
- Re-attempt your terraform plan or apply and check if the issue still persists
Versions
- Module version [Required]: 9.2.0
- Terraform version: 1.5.5
- Provider version(s): 5.26.0
Reproduction Code [Required]
Steps to reproduce the behavior:
No Yes terraform plan, terraform applyExpected behavior
Multiple conditions in listener rules should work as expected
Actual behavior
Multiple conditions render incorrectly as a single condition block
Terminal Output Screenshot(s)
Additional context
do you have a reproduction?
Just modify your example ex-cognito rule to include a second condition, as below.
module "alb" {
source = "../../"
name = local.name
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
# For example only
enable_deletion_protection = false
# Security Group
security_group_ingress_rules = {
all_http = {
from_port = 80
to_port = 82
ip_protocol = "tcp"
description = "HTTP web traffic"
cidr_ipv4 = "0.0.0.0/0"
}
all_https = {
from_port = 443
to_port = 445
ip_protocol = "tcp"
description = "HTTPS web traffic"
cidr_ipv4 = "0.0.0.0/0"
}
}
security_group_egress_rules = {
all = {
ip_protocol = "-1"
cidr_ipv4 = module.vpc.vpc_cidr_block
}
}
access_logs = {
bucket = module.log_bucket.s3_bucket_id
}
listeners = {
ex-http-https-redirect = {
port = 80
protocol = "HTTP"
redirect = {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
rules = {
ex-fixed-response = {
priority = 3
actions = [{
type = "fixed-response"
content_type = "text/plain"
status_code = 200
message_body = "This is a fixed response"
}]
conditions = [{
http_header = {
http_header_name = "x-Gimme-Fixed-Response"
values = ["yes", "please", "right now"]
}
}]
}
ex-weighted-forward = {
priority = 4
actions = [{
type = "weighted-forward"
target_groups = [
{
target_group_key = "ex-lambda-with-trigger"
weight = 2
},
{
target_group_key = "ex-instance"
weight = 1
}
]
stickiness = {
enabled = true
duration = 3600
}
}]
conditions = [{
query_string = {
key = "weighted"
value = "true"
}
}]
}
ex-redirect = {
priority = 5000
actions = [{
type = "redirect"
status_code = "HTTP_302"
host = "www.youtube.com"
path = "/watch"
query = "v=dQw4w9WgXcQ"
protocol = "HTTPS"
}]
conditions = [{
query_string = {
key = "video"
value = "random"
}
}]
}
}
}
ex-http-weighted-target = {
port = 81
protocol = "HTTP"
weighted_forward = {
target_groups = [
{
target_group_key = "ex-lambda-with-trigger"
weight = 60
},
{
target_group_key = "ex-instance"
weight = 40
}
]
}
}
ex-fixed-response = {
port = 82
protocol = "HTTP"
fixed_response = {
content_type = "text/plain"
message_body = "Fixed message"
status_code = "200"
}
}
ex-https = {
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-Res-2021-06"
certificate_arn = module.acm.acm_certificate_arn
additional_certificate_arns = [module.wildcard_cert.acm_certificate_arn]
forward = {
target_group_key = "ex-instance"
}
rules = {
ex-cognito = {
actions = [
{
type = "authenticate-cognito"
on_unauthenticated_request = "authenticate"
session_cookie_name = "session-${local.name}"
session_timeout = 3600
user_pool_arn = aws_cognito_user_pool.this.arn
user_pool_client_id = aws_cognito_user_pool_client.this.id
user_pool_domain = aws_cognito_user_pool_domain.this.domain
},
{
type = "forward"
target_group_key = "ex-instance"
}
]
conditions = [{
host_header = {
values = ["foobar.com"]
}
path_pattern = {
values = ["/some/auth/required/route"]
}
}]
}
ex-fixed-response = {
priority = 3
actions = [{
type = "fixed-response"
content_type = "text/plain"
status_code = 200
message_body = "This is a fixed response"
}]
conditions = [{
http_header = {
http_header_name = "x-Gimme-Fixed-Response"
values = ["yes", "please", "right now"]
}
}]
}
ex-weighted-forward = {
priority = 4
actions = [{
type = "weighted-forward"
target_groups = [
{
target_group_key = "ex-instance"
weight = 2
},
{
target_group_key = "ex-lambda-with-trigger"
weight = 1
}
]
stickiness = {
enabled = true
duration = 3600
}
}]
conditions = [{
query_string = {
key = "weighted"
value = "true"
}
}]
}
ex-redirect = {
priority = 5000
actions = [{
type = "redirect"
status_code = "HTTP_302"
host = "www.youtube.com"
path = "/watch"
query = "v=dQw4w9WgXcQ"
protocol = "HTTPS"
}]
conditions = [{
query_string = {
key = "video"
value = "random"
}
}]
}
}
}
ex-cognito = {
port = 444
protocol = "HTTPS"
certificate_arn = module.acm.acm_certificate_arn
authenticate_cognito = {
authentication_request_extra_params = {
display = "page"
prompt = "login"
}
on_unauthenticated_request = "authenticate"
session_cookie_name = "session-${local.name}"
session_timeout = 3600
user_pool_arn = aws_cognito_user_pool.this.arn
user_pool_client_id = aws_cognito_user_pool_client.this.id
user_pool_domain = aws_cognito_user_pool_domain.this.domain
}
forward = {
target_group_key = "ex-instance"
}
rules = {
ex-oidc = {
priority = 2
actions = [
{
type = "authenticate-oidc"
authentication_request_extra_params = {
display = "page"
prompt = "login"
}
authorization_endpoint = "https://${var.domain_name}/auth"
client_id = "client_id"
client_secret = "client_secret"
issuer = "https://${var.domain_name}"
token_endpoint = "https://${var.domain_name}/token"
user_info_endpoint = "https://${var.domain_name}/user_info"
},
{
type = "forward"
target_group_key = "ex-lambda-with-trigger"
}
]
conditions = [{
host_header = {
values = ["foobar.com"]
}
}]
}
}
}
ex-oidc = {
port = 445
protocol = "HTTPS"
certificate_arn = module.acm.acm_certificate_arn
action_type = "authenticate-oidc"
authenticate_oidc = {
authentication_request_extra_params = {
display = "page"
prompt = "login"
}
authorization_endpoint = "https://${var.domain_name}/auth"
client_id = "client_id"
client_secret = "client_secret"
issuer = "https://${var.domain_name}"
token_endpoint = "https://${var.domain_name}/token"
user_info_endpoint = "https://${var.domain_name}/user_info"
}
forward = {
target_group_key = "ex-instance"
}
}
}
target_groups = {
ex-instance = {
name_prefix = "h1"
protocol = "HTTP"
port = 80
target_type = "instance"
deregistration_delay = 10
load_balancing_cross_zone_enabled = false
health_check = {
enabled = true
interval = 30
path = "/healthz"
port = "traffic-port"
healthy_threshold = 3
unhealthy_threshold = 3
timeout = 6
protocol = "HTTP"
matcher = "200-399"
}
protocol_version = "HTTP1"
target_id = aws_instance.this.id
port = 80
tags = {
InstanceTargetGroupTag = "baz"
}
}
ex-lambda-with-trigger = {
name_prefix = "l1-"
target_type = "lambda"
lambda_multi_value_headers_enabled = true
target_id = module.lambda_with_allowed_triggers.lambda_function_arn
}
ex-lambda-without-trigger = {
name_prefix = "l2-"
target_type = "lambda"
target_id = module.lambda_without_allowed_triggers.lambda_function_arn
attach_lambda_permission = true
}
}
# Route53 Record(s)
route53_records = {
A = {
name = local.name
type = "A"
zone_id = data.aws_route53_zone.this.id
}
AAAA = {
name = local.name
type = "AAAA"
zone_id = data.aws_route53_zone.this.id
}
}
tags = local.tags
}
I have not had time to fork and test, but it looks to be simple fix:
dynamic "condition" {
for_each = try(each.value.conditions, [])
content {
...
Needs to be repeated for each type instead of one big for_each. Not sure if you prefer to do an "if" in the for to try for each sub key, or just let it be []
do you have a minimum reproducible example? Lets focus on just the bare minimum example and work from there
I can delete things from your example if you like...
provider "aws" {
region = local.region
}
data "aws_availability_zones" "available" {}
locals {
region = "eu-west-1"
name = "ex-${basename(path.cwd)}"
vpc_cidr = "10.0.0.0/16"
azs = slice(data.aws_availability_zones.available.names, 0, 3)
tags = {
Name = local.name
Example = local.name
Repository = "https://github.com/terraform-aws-modules/terraform-aws-alb"
}
}
##################################################################
# Application Load Balancer
##################################################################
module "alb" {
source = "../../"
name = local.name
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
# For example only
enable_deletion_protection = false
# Security Group
security_group_ingress_rules = {
all_https = {
from_port = 443
to_port = 445
ip_protocol = "tcp"
description = "HTTPS web traffic"
cidr_ipv4 = "0.0.0.0/0"
}
}
security_group_egress_rules = {
all = {
ip_protocol = "-1"
cidr_ipv4 = module.vpc.vpc_cidr_block
}
}
listeners = {
ex-https = {
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-Res-2021-06"
certificate_arn = module.acm.acm_certificate_arn
additional_certificate_arns = [module.wildcard_cert.acm_certificate_arn]
forward = {
target_group_key = "ex-instance"
}
rules = {
ex-fixed-response = {
priority = 3
actions = [{
type = "fixed-response"
content_type = "text/plain"
status_code = 200
message_body = "This is a fixed response"
}]
conditions = [{
host_header = {
values = ["foobar.com"]
}
http_header = {
http_header_name = "x-Gimme-Fixed-Response"
values = ["yes", "please", "right now"]
}
}]
}
}
}
}
target_groups = {
ex-instance = {
name_prefix = "h1"
protocol = "HTTP"
port = 80
target_type = "instance"
deregistration_delay = 10
load_balancing_cross_zone_enabled = false
health_check = {
enabled = true
interval = 30
path = "/healthz"
port = "traffic-port"
healthy_threshold = 3
unhealthy_threshold = 3
timeout = 6
protocol = "HTTP"
matcher = "200-399"
}
protocol_version = "HTTP1"
target_id = aws_instance.this.id
port = 80
tags = {
InstanceTargetGroupTag = "baz"
}
}
tags = local.tags
}
################################################################################
# Supporting resources
################################################################################
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = local.name
cidr = local.vpc_cidr
azs = local.azs
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)]
tags = local.tags
}
data "aws_route53_zone" "this" {
name = var.domain_name
}
module "acm" {
source = "terraform-aws-modules/acm/aws"
version = "~> 4.0"
domain_name = var.domain_name
zone_id = data.aws_route53_zone.this.id
}
module "wildcard_cert" {
source = "terraform-aws-modules/acm/aws"
version = "~> 4.0"
domain_name = "*.${var.domain_name}"
zone_id = data.aws_route53_zone.this.id
}
data "aws_ssm_parameter" "al2" {
name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
resource "aws_instance" "this" {
ami = data.aws_ssm_parameter.al2.value
instance_type = "t3.nano"
subnet_id = element(module.vpc.private_subnets, 0)
}
I don't need the example that we provide, I just need a minimal reproducible example and a detailed explanation of what is trying to be accomplished, steps to reproduce, and the current result vs the expected result. Jumping straight into the code or suggested fixes tend to lead to these long back and forths because the proper details weren't provided from the beginning so that we can properly triage and troubleshoot
I don't need the example that we provide, I just need a minimal reproducible example and a detailed explanation of what is trying to be accomplished, steps to reproduce, and the current result vs the expected result. Jumping straight into the code or suggested fixes tend to lead to these long back and forths because the proper details weren't provided from the beginning so that we can properly triage and troubleshoot
How is what i provided not meeting this request? If you use the example I provided, do terraform plan, terraform apply, you will see the error I indicated in the bug report.
What I'm trying to accomplish:
create a listener rule with multiple conditions
If you want JUST the code snippet, it's the below from the example... it's "any multiple condition in any rule"...
rules = {
ex-fixed-response = {
priority = 3
actions = [{
type = "fixed-response"
content_type = "text/plain"
status_code = 200
message_body = "This is a fixed response"
}]
conditions = [{
host_header = {
values = ["foobar.com"]
}
http_header = {
http_header_name = "x-Gimme-Fixed-Response"
values = ["yes", "please", "right now"]
}
}]
}
}
Now we're cookin with Crisco!
Your syntax is incorrect - you are passing a single map to conditions
but should be passing multiple maps like:
rules = {
ex-fixed-response = {
priority = 3
actions = [{
type = "fixed-response"
content_type = "text/plain"
status_code = 200
message_body = "This is a fixed response"
}]
conditions = [
{
host_header = {
values = ["foobar.com"]
}
},
{
http_header = {
http_header_name = "x-Gimme-Fixed-Response"
values = ["yes", "please", "right now"]
}
}
]
}
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.