0

我是 terraform 的新手,并尝试在 terraform v0.14.0 中创建一个项目,该项目将执行以下步骤:

  1. 创建多台 ec2 机器(一台为主,其余为子)
  2. 创建 NLB 和 terget 组
  3. 通过引用步骤 1(ec2 模块)的输出变量,将子 ec2 实例私有 ip 附加到目标组。

但是当我运行 terraform 计划时,我收到以下消息。这可以解决吗?或任何其他一次性提供这些资源的方法?

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:     }

#项目级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 模块 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
        
}
4

1 回答 1

1

您的错误消息和代码示例似乎并不完整,但尽管如此,我认为我可以在这里看到导致问题的原因:您正在尝试使用目标组 ARN 作为实例键的一部分aws_lb_target_group_attachment.tgr_attachment,但是ARN 不适用于实例键,因为提供者在创建每个对象之后才知道 ARN 值,因此它无法在规划期间预测该值。

相反,我建议将您更改var.target_groups为对象映射而不是对象列表,然后使用for_each = var.target_groupsin aws_lb_target_group.main。因此,模块的调用者可以为每个目标组指定一个有意义的名称,您可以将其用作实例键的一部分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
    }
  }

  # ...
}

通过使模块的调用者为每个目标组指定一个键,您可以让调用者控制对目标组集合的更改是否应该被理解为就地更新现有目标组,创建一个新目标组,或删除现有的目标组。

作为一个很好的副作用,您还可以获得一个静态且稳定的标识符以用于您的每个实例,这对于查看terraform plan输出的人来说是有意义的,并且即使未来的更改导致目标组之一被替换。

于 2020-12-09T18:56:21.207 回答