How to use both the "or_statement" and "and_statement" in single statement block in terraform

HI Team,

In one of my requirement i wanna use “or_statement” and “and_statement” in single statement block.

==>in or_statement i want to use the ip address retrieval by using for_each loop.

==>in and_statement i want to use the geo_match_statement and their country codes.

So, please suggest any possibilities/example if it is there.

Thanks in advance.

Hi @sadik13,

The main Terraform language doesn’t have anything named or_statement or and_statement, so I assume you’re asking about features of a particular provider here.

It would help if you could specify which provider and resource you are talking about, and ideally also show a configuration you’ve tried already and what problems you faced when you tried it.

Hi @apparentlymart ,

I am using the “AWS” provider and below is my resource code example:

resource "aws_wafv2_ip_set" "sadik-ip" {
  name               = "sadik-ip"
  scope              = "CLOUDFRONT"
  ip_address_version = "IPV4"
  addresses          = ["1.2.3.4/32"]

}
resource "aws_wafv2_ip_set" "syed-ip" {
  name               = "syed-ip"
  scope              = "CLOUDFRONT"
  ip_address_version = "IPV4"
  addresses          = ["5.6.7.8/32"]

}
resource "aws_wafv2_web_acl" "test-web-acl" {
  name  = "test-web-acl"
  scope = "CLOUDFRONT"

  default_action {
    allow {}
  }

  rule {
    name     = "test-rule"
    priority = 10

    action {
      block {}
    }

    statement {
      or_statement {
        statement {

          ip_set_reference_statement {
            arn = aws_wafv2_ip_set.sadik-ip.arn
          }

        }
        statement {

          ip_set_reference_statement {
            arn = aws_wafv2_ip_set.syed-ip.arn
          }
        }


      }
 #or-statement end in above closing brases

      and_statement {
        statement {
          geo_match_statement {
            country_codes = ["US", "CA", "MX"]
          }
        }
      }

    }

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "rule-metric"
      sampled_requests_enabled   = false
    }
  }

  tags = merge(
    module.tags.default_tags,
    {
      Name = module.naming.name
    },
  )

  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "webacl-metric"
    sampled_requests_enabled   = false
  }

}

while run the terraform apply i am getting the below error message:

Error: Error updating WAFv2 WebACL: WAFInvalidParameterException: Error reason: You have used none or multiple values for a field that requires exactly one value., field: STATEMENT, parameter: Statement
│ {
│   RespMetadata: {
│     StatusCode: 400,
│     RequestID: "97444770-bdd7-4530-b3ed-651873f09ff8"
│   },
│   Field: "STATEMENT",
│   Message_: "Error reason: You have used none or multiple values for a field that requires exactly one value., field: STATEMENT, parameter: Statement",
│   Parameter: "Statement",
│   Reason: "You have used none or multiple values for a field that requires exactly one value."
│ }
│
│   with aws_wafv2_web_acl.test-web-acl,
│   on main.tf line 46, in resource "aws_wafv2_web_acl" "test-web-acl":
│   46: resource "aws_wafv2_web_acl" "test-web-acl" {
│

Hi @sadik13! Thanks for the extra information.

I’ve moved your topic into the AWS provider category because it seems like this would be best answered by someone with AWS-specific knowledge; this error message is coming from the AWS API rather than directly from Terraform.

It’s unfortunate that because this is an API error rather than a provider error, the message is written in terms that make sense for the underlying API rather than for the Terraform provider schema, and so it’s hard to understand what exactly the error message is referring to.

I’m not personally familiar with WAF so I can’t give any specific advice here, but hopefully someone else with more familiarity will be able to give some specific advice.

An initial idea I have, based on some skimming of the relevant API documentation, is that it seems like the intent here is that each statement should only have one nested block to specify what kind of statement it is, and so perhaps it’s not valid to have or_statement and and_statement as sibling blocks. It might work to nest one inside of the other so that the precedence is clear:

statement {
  and_statement {
    statement {
      or_statement {
        statement {
          ip_set_reference_statement { }
        }
        statement {
          ip_set_reference_statement { }
        }
      }
    }
    statement {
      geo_match_statement {}
    }
  }
}

This is based on nothing other than my general intuition about how boolean combinators typically work in other similar systems, but I’d expect the above to be understood something like the following boolean expression:

( \text{ip_set} = \text{sadik_ip} \lor \text{ip_set} = \text{syed_ip} ) \land (\text{country_code} \in \{ \mathrm{US},\mathrm{CA},\mathrm{MX} \})

Where ∧ means “and”, ∨ means “or”, and ∈ means “is a member of”. Notice that the “or” expression is one of the operands of the “and” expression, and so I nested it inside the and_statement block above to represent that.