Terraform test, validate resource created by a module

I am trying to use terraform test in a unit testing capacity (e.g. command = plan) to validate that an Azure Key Vault resource will be created correctly. This resource is created by an external module name kv. My issue is that it seems terraform test is not aware of the resources created by the module as it fails to find the azurerm_key_vault resource. Below is my configuration.

# dir layout

➜ tree
.
├── terraform
│   ├── environments
│   │   ├── dev
│   │   │   ├── omitted.tfbackend
│   │   │   └── terraform.tfvars
│   │   └── prod
│   │       ├── omitted.tfbackend
│   │       └── terraform.tfvars
│   └── module
│       ├── backend.tf
│       ├── key_vault.tf
│       ├── main.tf
│       ├── outputs.tf
│       ├── providers.tf
│       ├── tests
│       │   └── key_vault.tftest.hcl
│       ├── variables.tf
└──     └── versions.tf

The file key_vault.tf calls a module that creates the Azure Key Vault. A snippet of key_vault.tf is below.

# key_vault.tf

module "kv" {
  source   = "tf.myreg.com/terraform/azurerm//modules/key_vault"

  for_each = var.regions

  name_prefix                   = # omitted
  resource_group_name           = # omitted
  location                      = # omitted
  tags                          = # omitted
  log_analytics_id              = # omitted
  subnet_id                     = # omitted
  enable_rbac_authorization     = true
  default_action                = # omitted
  public_network_access_enabled = # omitted
  private_dns_zone_id           = # omitted
}

The test below validates that my root module (e.g. key_vault.tf) is creating the resource module.kv["omitted"].azurerm_key_vault.kv and has the attribute enable_rbac_authorization set to true.

# key_vault.tftest.hcl

run "test_kv" {
  command = plan

  assert {
    # condition = module.kv["omitted"].azurerm_key_vault.kv.enable_rbac_authorization == true
    condition = module.kv["omitted"].azurerm_key_vault.kv == true
    error_message = "RBAC Authorization should be enabled"
  }
}

Output from a terraform plan confirming the resource address used in the above test.

# terraform plan output snippet

# module.kv["omitted"].azurerm_key_vault.kv will be created
+ resource "azurerm_key_vault" "kv" {
    + access_policy                 = (known after apply)
    + enable_rbac_authorization     = true
    + enabled_for_disk_encryption   = false
    + id                            = (known after apply)
    + location                      = "omitted"
    + name                          = (known after apply)
    + public_network_access_enabled = false
    + purge_protection_enabled      = false
    + resource_group_name           = "omitted"
    + sku_name                      = "omitted"
    + soft_delete_retention_days    = "omitted"
    + tags                          = {
        + "environment" = "omitted"
        + "module"      = "omitted"
        + "owner"       = "omitted"
      }
    + tenant_id                     = "omitted"
    + vault_uri                     = (known after apply)

    + network_acls {
        + bypass         = "omitted"
        + default_action = "omitted"
      }
  }

The following is the error I get when running terraform test. The module kv does produce 6 outputs, which I think is all terraform test is aware of. How would I go about validating the resources created by an external module? Thanks!

│ Error: Unsupported attribute
│ 
│   on tests/key_vault.tftest.hcl line 5, in run "test_kv":
│    5:     condition = module.kv["omitted"].azurerm_key_vault.kv.enable_rbac_authorization == true
│     ├────────────────
│     │ module.kv["omitted"] is object with 6 attributes
│ 
│ This object does not have an attribute named "kv".
╵
tests/key_vault.tftest.hcl... tearing down
tests/key_vault.tftest.hcl... fail

Failure! 0 passed, 1 failed.

Hi @joeypiccola, the terraform test command respects the encapsulation of modules, so you can’t refer directly to the resources within a module from a test file that is testing the parent module.

You could use the module block within the run blocks to load and test that particular module directly to make sure it is behaving as expected.

The thinking behind this decision is that in this case you’re kind of trying to write a unit test for that module in the wrong place rather than testing your root module. From the perspective of the parent module you should be able to trust that your child modules behave as documented.

Hopefully that makes sense!

@joeypiccola So I think this is tricky. Not because its hard, but you are verifying that a module you include in your composition is doing what you expect. You could do what @liamcervante suggest, but that seems like testing an external module within your module.

I would think that if it is critical to know (trust the module) you have to run apply and do a data lookup and assert the resource is there. Because looking at the output from your plan what output does the module produce that you could assert on? Name and id are only know after apply.