THEN

THEN is used to define the matching criteria. It is the decision making step where the Scenario fails or passes. It can also be used for drilling down on a multi-layered resource property, just like some WHEN cases. The main difference would be THEN cases would fail if it the property can not be found while drilling down.

Few examples can be ;

Then it must contain server_side_encryption
Then its value must match the "^(dev|test|uat|prod)" regex
Then its value must be greater than 20

Using THEN is optional. You might have some use cases that only a GIVEN directive could be enough for you for setting the initial definitions.

You can use AND with THEN.

Depending on use a Scenario Outline instead of a Scenario, it can also be a variable like ;

Then its value must be <pattern>

When a THEN step fails to complete, the next steps will not be executed and the scenario will be deemed to fail. If terraform-compliance is used with --early-exit parameter then other scenarios - if exists - won’t be executed. This is useful on use cases where you have lots of tests.

In step variables

As a scenario calls GIVEN steps, all resources that was once assigned to the scenario by GIVEN steps are accumulated in “cumulative stash.” Steps that support in step variables can access cumulative stash directly within the step definition.

In step variables can be accessed by surrounding curly braces around the related statement.

Example:

Scenario: Lambda functions must have a matching Cloudwatch Log Group
    Given I have aws_cloudwatch_log_group defined
    Given I have aws_lambda_function defined
    Then it must have function_name
    And it must be in {aws_cloudwatch_log_group.values.name}

Since two GIVEN steps were called, the cumulative stash contains all aws_cloudwatch_log_group and aws_lambda_function resources that were defined within the plan.

On the final step, the resources in stash (function_names that were collected via previous steps) are compared with properties under {aws_cloudwatch_log_group.values.name}.

List values can be indexed or sliced using {path_to_value.[0]}, {path_to_value.[:]} similar to how indexing rules works in python. (e.g. {path_to_value.[4:2]} would return an empty list)

Example

Scenario: slicing 1
    Given I have azurerm_postgresql_server defined
    Then it must have azurerm_postgresql_configuration
    Then it must have name
    Then it must be in {azurerm_postgresql_server.values.azurerm_postgresql_configuration.[:].name}

In this scenario, in step variables would contain the name of every element under values.azurerm_postgresql_configuration for every azurerm_postgresql_server resource.

The path to the desired value can be found through parsing the stash on debugging mode. Usually, the path will be in the format resource_name.value.value_name

Reference

Then it must contain something

Possible sentences :

Then it must contain something

Then it must have something

Then they must contain something

Then they must have something

key Description Examples
something any property within terraform resoruce/provider/etc access_key ingress "something with spaces"

Then it must not contain something

Possible sentences :

Then it must not contain something

Then it must not have something

Then they must not have something

Then they must not contain something

key Description Examples
something any property within terraform resoruce/provider/etc access_key ingress "something with spaces"

Then something is enabled

This step checks if the property has some value defined in it, except the values are False, {}, '' or None and []

Possible sentences :

Then something is enabled

Then something must be enabled

key Description Examples
something can be either a generic property from your terraform configuration or templated ones like below for some resources;
encryption at rest
encrytion in flight
server_side_encryption encryption at rest encryption in flight website

encryption at rest and encryption in flight are templated property types to help you to increase the readability of your BDD test. Please check templated entities section for more information.


Then it must condition have proto protocol and port port for cidr

This step is only valid for aws_security_group resources.

Possible sentences :

Then it
condition have proto protocol and port port for cidr

key Description Examples
condition defines the conditional search. Can only be must, must not, must only
proto defines the network transport protocol Can be tcp, udp, icmp, -1 or any
port defines the network port, list of ports or a port range. 80 443 8080-8090 443, 80, 22 or any
cidr defines the network ip cidr 0.0.0.0/0 192.168.0.0/24 8.8.8.8/32

This step will execute tests that is applicable for both per rule and per security group, depending on the condition ;

  • must: The port(s) given must be a subset of the configured ports in related Security Group.
  • must not: The port(s) given must not exist in ANY rule of the Security Group.
  • must only: The port(s) given must be exactly same like the ones defined in Security Group.

Please note that must not condition is executed per every Security Group Rule, while must not and must only is executed for ALL rules exist in a Security Group.


Then its value must be action than number

This step is for mathematical comparison. It requires to have a specific WHEN directive above this line.

Possible sentences :

Then its value must be action than number

Then I expect the result is action than number

Then its value must be action to number

Then I expect the result is action to number

key Description Examples
action mathematical operator Only supports for :
more greater bigger
more and equal greater and equal bigger and equal
less and equal lesser and equal smaller and equal
equal
number integer 1 20 -40

Then its value condition match the “search regex” regex

This step requires fundamental knowledge about regular expressions due the pattern matching algorithm. It is highly recommended to check for your patterns in regex101 before you implement your tests.

All values are compared with the regex. If the value referred by “it” on the plan is a dictionary or list, this step will fail if any element in the value fails the condition match.

Possible sentences :

Then its value condition match the
search_regex” regex

Then all of its values condition match the
search_regex” regex

key Description Examples
condition defines positive or negative comparison Only supports for :
▪ Leave it empty for positive comparison
▪ Use must not for negative comparison
search_regex any valid regular expression ^some_name$ ^(you|can|use|or|like|this)$ \d+

Please note that, in case you are using a Scenario Outline instead of a Scenario and if you need to use | (or) regular expression operator within your search_regex regex, then you must use escape characters (\) for not to interfere with Scenario Outline structure. In these situations use \| instead of |.

Warning: Terraform plan files may not always match the corresponding .tf files 1:1. In those cases, this step will match with the plan file and not the .tf file.


Then any of its values condition match the “search regex” regex

This step requires fundamental knowledge about regular expressions due the pattern matching algorithm. It is highly recommended to check for your patterns in regex101 before you implement your tests.

All values are compared with the regex. If the value referred by “it” on the plan is a dictionary or list, this step will pass if any element in the value passes the condition match.

Possible sentences :

Then any of its values condition match the
search_regex” regex

key Description Examples
condition defines positive or negative comparison Only supports for :
▪ Leave it empty for positive comparison
▪ Use must not for negative comparison
search_regex any valid regular expression ^some_name$ ^(you|can|use|or|like|this)$ \d+

Please note that, in case you are using a Scenario Outline instead of a Scenario and if you need to use | (or) regular expression operator within your search_regex regex, then you must use escape characters (\) for not to interfere with Scenario Outline structure. In these situations use \| instead of |.


Then its singular value condition match the “search regex” regex

This step requires fundamental knowledge about regular expressions due the pattern matching algorithm. It is highly recommended to check for your patterns in regex101 before you implement your tests.

Very similar to Then its value condition match the “search regex” regex, but fail if the corresponding value is not one of (bool, int, float, str).

Possible sentences :

Then its singular value condition match the
search_regex” regex

key Description Examples
condition defines positive or negative comparison Only supports for :
▪ Leave it empty for positive comparison
▪ Use must not for negative comparison
search_regex any valid regular expression ^some_name$ ^(you|can|use|or|like|this)$ \d+

Please note that, in case you are using a Scenario Outline instead of a Scenario and if you need to use | (or) regular expression operator within your search_regex regex, then you must use escape characters (\) for not to interfere with Scenario Outline structure. In these situations use \| instead of |.


Then its value condition be something

This step is a simplified step for searching something without writing any regular expression. This step will transform something into ^something$ and trigger the Then its value condition match the “search regex” regex” step.

Possible sentences :

Then its value condition be something

key Description Examples
condition defines positive or negative comparison Only supports for :
must
must not
something search value some_name "something with spaces"

Then its value condition contain something

Unlike the step supporting regular expression, this step does not support any regular expression.

Possible sentences :

Then its value condition contain something

key Description Examples
condition defines positive or negative comparison Only supports for :
must
must not
something search value some_name "something with spaces"

Then its value condition be null

This step will checks if filtered value from parent steps is null, `` or not assigned any value.

Possible sentences :

Then its value condition be null

key Description Examples
condition defines positive or negative comparison Only supports for :
must
must not

Then it fails

This steps will always makes the scenario fails. Thus, it requires that the test logic is defined on parent steps. If one of the parent steps declared a skip (e.g. When steps), then all child steps including this step - if exists in the same scenario - will be skipped and your scenario will pass.

Possible sentences :

Then it fails

Then the scenario fails

Then the scenario should fail

Then the scenario must fail

Then it should fail

Then it must fail


Then it must have address referenced

terraform-compliance mounts resources into each other if they are referenced. E.g. an aws_security_group_rule onto aws_security_group. Some use cases may require to find these references, mount points about which entity is mounted on top of which entity. This step can be used in these situations.

Possible sentences :

Then its must have address referenced

key Description Examples
address resource address within terraform aws_security_group.my_group "something with spaces"

Then its key condition be value

This is an optimised way of reading and matching a data without drilling down once more by using it contains steps. This step will match key = value or key != value depending on the condition

Possible sentences :

Then its key condition be value

Then its key property condition be value

Then its key key condition be value

key Description Examples
key The key name of the property encryption, private, name, id "something with spaces"
condition Defines if the match will be = or != Only must and must not
value The value of the property true, closed, my_bucket "something with spaces"

Then I flatten all values found

This will apply a union/combine/merge function for the values that has been found for the resources from the previous steps. A valid use case could be checking a value that exists in a list of values that has been combined into one. For example, checking a specific name that has been created by a for_each of resource iteration.

Possible sentences :

Then I flatten all values found


Then it condition be in haystack

This step compares the contents of the current stash to the in step variables. This step will pass or fail depending on the condition. It evaluates whether or not resources from the previous step form a subset of the resources within the in step variables. Only sets of bool, int, float, and string values are supported.

Possible sentences :

Then it condition be in haystack

Then it condition be a subset of haystack

key Description Examples
haystack The resources to be accessed via in step variables {aws_lambda_function.values.function_name}, {aws_cloudwatch_log_group.values.name}, {resource_name.path.to.property}
condition Defines whether the resources from the previous must be a subset of the in step variables Only must and must not

Then it must cover haystack

This step compares the contents of the current stash to the in step variables. This step will pass or fail depending on the condition. It evaluates whether or not resources from the previous step form a superset of the resources within the in step variables. Only sets of bool, int, float, and string values are supported.

Possible sentences :

Then it condition cover haystack

Then it condition be a superset of haystack

key Description Examples
haystack The resources to be accessed via in step variables {aws_lambda_function.values.function_name}, {aws_cloudwatch_log_group.values.name}, {resource_name.path.to.property}
condition Defines whether the resources from the previous must be a superset of the in step variables Only must and must not

terraform-compliance made with . Distributed by an MIT license.