How do I pass provider information to tests?

terraform 1.6 was released yesterday and it includes new feature terraform test which I’ve been trying to use but getting stuck on how to specify providers.

I want to test a module that is used to create objects in Snowflake. The module is typically called like this:

module "resources" {
  providers = {
    snowflake.sys = snowflake.sys
    snowflake.sec = snowflake.sec
  }
  source                   = "../modules/resources"
  description              = local.description
  tag                      = local.tag
}

I have been loosely following the Write terraform tests tutorial therefore I have created:

  • a tests directory inside the directory that is the source of this module.
  • a tests/setup module that creates a random name that will be passed to the tag variable of my module
  • a test file tests/database.tftest.hcl
> tree
.
├── locals.tf
├── outputs.tf
├── README.md
├── resource_modules.tf
├── tests
│   ├── database.tftest.hcl
│   └── setup
│       └── main.tf
└── vars.tf

tests/setup/main.tf looks like this:

terraform {
  required_version = ">= 0.13"
  required_providers {
    random = {
      source = "hashicorp/random"
      version = "3.5.1"
    }
    snowflake = {
      source  = "snowflake-labs/snowflake"
      version = "0.51.0"
    }
    vault = {
      source  = "hashicorp/vault"
      version = "~> 3.0"
    }
  }
}

provider "vault" {
  namespace = "services/snowflake-automation"
}

data "vault_generic_secret" "vault_secrets_automation" {
  path = format("some/path/in/vault/namespace")
}

provider "snowflake" {
  username    = "user@company.net"
  alias       = "sec"
  account     = "XXXXX"
  region      = "eu-west-1"
  private_key = data.vault_generic_secret.vault_secrets_automation.data["key"]
  role        = "SECURITYADMIN"
}

provider "snowflake" {
  username    = "user@company.net"
  alias       = "sys"
  account     = "XXXXX"
  region      = "eu-west-1"
  private_key = data.vault_generic_secret.vault_secrets_automation.data["key"]
  role        = "SYSADMIN"
}

resource "random_pet" "tag" {
  length = 2
  prefix = "TESTDB"
}

output "tag" {
    value = lower(random_pet.tag.id)
}

tests/database.tftest.hcl looks like this:

run "setup_tests" {
    module {
        source = "./tests/setup"
    }
}

run "create_live_database_only" {
    command = apply
    variables {
        description              = "Temporary database created for the purpose of unit testing"
        tag                      = run.setup_tests.tag
    }
    # I have guessed at the syntax for specifying providers because
    # doing so is not covered in the tutorial
    providers = {
      snowflake.sys = snowflake.sys
      snowflake.sec = snowflake.sec
    }
}

When I run terraform test I get an error:

terraform test
tests/database.tftest.hcl… in progress
run “setup_tests”… pass
run “create_live_database_only”… fail

│ Error: Missing provider definition for snowflake.sys

│ on tests/database.tftest.hcl line 18, in run “create_live_database_only”:
│ 18: snowflake.sys = snowflake.sys

│ This provider block references a provider definition that does not exist.


│ Error: Missing provider definition for snowflake.sec

│ on tests/database.tftest.hcl line 19, in run “create_live_database_only”:
│ 19: snowflake.sec = snowflake.sec

│ This provider block references a provider definition that does not exist.

tests/database.tftest.hcl… tearing down
tests/database.tftest.hcl… fail

Clearly I am specifying the provider configuration incorrectly but I don’t know how to fix it. Can anyone help? thx.

Hi @jamiekt, thanks for trying out the new testing framework!

You should define your providers directly within the testing file instead of in your setup module. At the moment, the providers you defined within your setup module will only be available to that module and not externally.

As an aside, the testing framework will match up required providers based on names and aliases automatically. So, you only need to manually specify providers if you have given them different aliases within the test file and the modules you are using/testing during the test. In this case your providers block is redundant as the names and aliases match on both sides.

Hope this helps! I’ll keep an eye on this thread in case you have other questions!

Hi @jamiekt, I’ve looked at your config again and can see that you’re referencing data sources from within your provider definitions. Apologies, I missed this before. It’s not actually possible to define providers in this way within testing files yet. We don’t have the ability to execute data sources outside the context of a run block at the moment.

Again, apologies for that - you can largely ignore my first comment.

As a workaround, you could define a module that wraps the module you want to test and defines the providers directly. For example:

# ./tests/wrapper/main.tf

provider "snowflake" {
  # ... definition ...
}

provider "snowflake" {
  # ... definition ...
}

variable "description" {
  # ... definition ...
}

variable "tag" {
  # ... definition ...
}

module "resources" {
  providers = {
    snowflake.sys = snowflake.sys
    snowflake.sec = snowflake.sec
  }
  source                   = "../modules/resources"
  description              = var.description
  tag                      = var.tag
}

You’d then also need to forward any outputs from the module that you want to check for in assertions, so that they are available to the testing framework. Or, potentially I think module.resources.output == "whatever" would work from within assertions as well.

Apologies for the missing functionality here - this is something we are working on.

Thanks!

Hi Liam, thanks for the detailed responses,

I did go through a bit of trial and error and concluded that this might be the case, so I’m glad that you have confirmed it. And it is good to read that you are working on adding this missing capability (i.e. references to data sources in provider blocks).

I will now attempt your workaround.

OK, I tried your workaround and made some progress.

Here’s my dir structure now

> tree
.
├── locals.tf
├── outputs.tf
├── README.md
├── resource_modules.tf
├── tests
│   ├── database.tftest.hcl
│   ├── setup
│   │   └── main.tf
│   └── wrapper
│       └── main.tf
└── vars.tf

my new run block:

run "resources" {
    module {
        source = "./tests/wrapper"
    }
    variables {
        description              = "Temporary database created for the purpose of unit testing"
        tag                      = run.setup_tests.tag
    }
}

and upon running terraform test

 terraform test
tests/database.tftest.hcl... in progress
  run "setup_tests"... pass
  run "resources"... pass
tests/database.tftest.hcl... tearing down
Terraform encountered an error destroying resources created while executing tests/database.tftest.hcl/resources.
│
│ Error: error deleting database TESTDB-SQUARE-SPIDER: 003001 (42501): SQL access control error:
│ Insufficient privileges to operate on database 'TESTDB-SQUARE-SPIDER'
│ 
│ 
│

Terraform left the following resources in state after executing tests/database.tftest.hcl/resources, and they need to be cleaned up manually:
  - module.resources.module.live_database.snowflake_database.database
tests/database.tftest.hcl... fail

Failure! 2 passed, 0 failed.

clearly I now have a different problem to solve but I’d call that progress. Thank you Liam.

Two more thoughts:

  1. For debugging purposes it would be nice to have the option to see more verbose output from the apply being carried out on my wrapper configuration. I’d at least like to see the plan.
  2. I use the terraform extension for VSCode in order to get syntax highlighting but it doesn’t yet seem to recognise .tftest.hcl files. I’m sure that’s a different team to what you work on Liam but if you could pass that feedback on it would be appreciated.

Good to see some progress!

  1. There is a -verbose flag available already, for your run blocks it’ll print out the state after the apply operation has been completed. With command = plan, it would print out the plan for you. In both cases the idea is to show the values that any assertions you might have written will be compared against.
  2. Yes, we definitely want to support proper syntax highlighting and completion for the testing framework. If you are interested, you can follow the ticket here: meta: Support Terraform Test Framework · Issue #1534 · hashicorp/vscode-terraform · GitHub

Thanks!

perfect :slight_smile: thank you.

will do, thanks!

I’ve filed Allow providers defined in test files to reference variables and other providers · Issue #34007 · hashicorp/terraform · GitHub so we can track the work for this externally.

Thanks for submitting that, I’ve put a watch on it.

We just released an alpha that should make the request here possible.

More information here if you’re interested in trying this out or have any feedback.

good new, thank you Liam