terraform-compliance / cli

a lightweight, security focused, BDD test framework against terraform.
https://terraform-compliance.com
MIT License
1.35k stars 152 forks source link

THEN's condition matches for child properties in absence of a property at expected level. Is this expected? #724

Open Yakuza-UA opened 11 months ago

Yakuza-UA commented 11 months ago

Question:

I have the following Scenario Outline to test for naming convention:

@case-sensitive
Scenario Outline: Ensure resource names follow naming convention
  Given I have <resource_type> defined
  When it has name
  Then it must contain name
  And its value must match the "<pattern>" regex

  Examples:
  | resource_type | pattern |
  | azurerm_virtual_network | ^vnet-[a-z0-9\-]+$ |
  | azurerm_virtual_machine | ^vm[a-z0-9]{1,16}$ |
  | azurerm_linux_virtual_machine | ^vm[a-z0-9]{1,16}$ |
  | azurerm_windows_virtual_machine | ^vm[a-z0-9]{1,16}$ |
  | azurerm_firewall | afw-[a-z0-9\-]+$ |
  | azurerm_route_table | rt-[a-z0-9\-]+$ |

It fails when I try to deploy a NEW resource where a random ID is part of a name. In this case the name property isn't populated/known, but there is another child property - route which is a list of objects where each object has a name property. As Terraform-Compliance cannot find name property at the top level it drills down to the route property level and finds irrelevant name properties forcing the rule to FAIL. Here's the sample JSON for the resource-to-be:

{
  "address": "module.intl_pre_prod_01_default_route_table[\"jpe_main.preprod-01\"].azurerm_route_table.this",
  "mode": "managed",
  "type": "azurerm_route_table",
  "name": "this",
  "provider_name": "registry.terraform.io/hashicorp/azurerm",
  "schema_version": 0,
  "values": {
    "disable_bgp_route_propagation": true,
    "location": "japaneast",
    "route": [
      {
        "address_prefix": "0.0.0.0/0",
        "name": "default-0-0-0-0-0",
        "next_hop_in_ip_address": "x.x.x.x",
        "next_hop_type": "VirtualAppliance"
      },
      {
        "address_prefix": "10.0.0.0/8",
        "name": "default-10-0-0-0-8",
        "next_hop_in_ip_address": "x.x.x.x",
        "next_hop_type": "VirtualAppliance"
      },
      {
        "address_prefix": "172.16.0.0/12",
        "name": "default-172-16-0-0-12",
        "next_hop_in_ip_address": "x.x.x.x",
        "next_hop_type": "VirtualAppliance"
      },
      {
        "address_prefix": "192.168.0.0/16",
        "name": "default-192-168-0-0-16",
        "next_hop_in_ip_address": "x.x.x.x",
        "next_hop_type": "VirtualAppliance"
      }
    ],
    "tags": null,
    "timeouts": null
  },
  "sensitive_values": {
    "route": [
      {},
      {},
      {},
      {}
    ],
    "subnets": []
  }
}

The error I get is the following:

Failure: name property in module.intl_pre_prod_01_default_route_table["jpe_main.preprod-01"].azurerm_route_table.this resource does not match with rt-[a-z0-9-]+$ case sensitive regex. It is set to default-0-0-0-0-0.
                Failure: name property in module.intl_pre_prod_01_default_route_table["jpe_main.preprod-01"].azurerm_route_table.this resource does not match with rt-[a-z0-9-]+$ case sensitive regex. It is set to default-10-0-0-0-8.
                Failure: name property in module.intl_pre_prod_01_default_route_table["jpe_main.preprod-01"].azurerm_route_table.this resource does not match with rt-[a-z0-9-]+$ case sensitive regex. It is set to default-172-16-0-0-12.
                Failure: name property in module.intl_pre_prod_01_default_route_table["jpe_main.preprod-01"].azurerm_route_table.this resource does not match with rt-[a-z0-9-]+$ case sensitive regex. It is set to default-192-168-0-0-16.
        | azurerm_route_table             | rt-[a-z0-9-]+$     |
          Failure:

If this logic is expected, then... is there a way to overcome the behavior and lock the scope for WHEN/THEN (force them to look within current scope)? Maybe a \@lock-scope tag or something?

Many thanks!

Yakuza-UA commented 11 months ago

Just to let you know that for now I've tackled this scenario by changing my WHEN to look for id property which ONLY exists in deployed resources and only at the top level:

@case-sensitive
Scenario Outline: Ensure resource names follow naming convention
  Given I have <resource_type> defined
  When it has id
  Then it must contain name
  And its value must match the "<pattern>" regex

With this I no longer hit name in a child property. But this feels like a workaround, rather than a proper solution.