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_name
s 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
- In step variables
- Reference
- Then it must contain something
- Then it must not contain something
- Then something is enabled
- Then it must condition have proto protocol and port port for cidr
- Then its value must be action than number
- Then its value condition match the “search regex” regex
- Then any of its values condition match the “search regex” regex
- Then its singular value condition match the “search regex” regex
- Then its value condition be something
- Then its value condition contain something
- Then its value condition be null
- Then it fails
- Then it must have address referenced
- Then its key condition be value
- Then I flatten all values found
- Then it condition be in haystack
- Then it must cover haystack
Then it must contain something
Possible sentences :
▪ Then it must contain 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
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 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
orany
port defines the network port, list of ports or a port range. 80
443
8080-8090
443, 80, 22
orany
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 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” regexThen 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
▪ Usemust not
for negative comparisonsearch_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
▪ Usemust not
for negative comparisonsearch_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
▪ Usemust not
for negative comparisonsearch_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 contain something
Unlike the step supporting regular expression, this step does not support any regular expression.
Possible sentences :
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 :
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
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 :
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 :
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 |