• This repository has been archived on 01/Nov/2023
  • Stars
    star
    257
  • Rank 158,728 (Top 4 %)
  • Language
    Python
  • License
    GNU General Publi...
  • Created over 8 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Assists in the enforcement of user-defined standards in Terraform

Terraform Validate

Linux: Linux Build Status

Windows: Windows Build status

A python package that allows users to define Policy as Code for Terraform configurations.

By parsing a directory of .tf files using pyhcl, each defined resource can be tested using this module.

Example Usages

Check that all AWS EBS volumes are encrypted

import terraform_validate

class TestEncryptionAtRest(unittest.TestCase):

    def setUp(self):
        # Tell the module where to find your terraform configuration folder
        self.path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../terraform")
        self.v = terraform_validate.Validator(self.path)

    def test_aws_ebs_volume(self):
        # Assert that all resources of type 'aws_ebs_volume' are encrypted
        self.v.error_if_property_missing() # Fail any tests if the property does not exist on a resource
        self.v.resources('aws_ebs_volume').property('encrypted').should_equal(True)

    def test_instance_ebs_block_device(self):
        # Assert that all resources of type 'ebs_block_device' that are inside a 'aws_instance' are encrypted
        self.v.error_if_property_missing()
        self.v.resources('aws_instance').property('ebs_block_device').property('encrypted').should_equal(True)

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(TestEncryptionAtRest)
    unittest.TextTestRunner(verbosity=0).run(suite)

resource "aws_instance" "foo" {
  # This would fail the test
  ebs_block_device{
    encrypted = false
  }
}

resource "aws_ebs_volume" "bar" {
  # This would fail the test
  encrypted = false
}

Check that AWS resources are tagged correctly

import terraform_validate

class TestEncryptionAtRest(unittest.TestCase):

    def setUp(self):
        # Tell the module where to find your terraform configuration folder
        self.path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../terraform")
        self.v = terraform_validate.Validator(self.path)

    def test_aws_ebs_volume(self):
        # Assert that all resources of type 'aws_instance' and 'aws_ebs_volume' have the correct tags
        tagged_resources = ["aws_instance","aws_ebs_volume"]
        required_tags = ["name","version","owner"]
        self.v.resources(tagged_resources).property('tags').should_have_properties(required_tags)

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(TestEncryptionAtRest)
    unittest.TextTestRunner(verbosity=0).run(suite)

Behaviour functions

These affect the results of the Validation functions in a way that may be required for your tests.

Validator.error_if_property_missing()

By default, no errors will be raised if a property value is missing on a resource. This changes the behavior of .property() calls to raise an error if a property is not found on a resource.

Validator.enable_variable_expansion()

By default, variables in property values will not be calculated against their default values. This changes the behaviour of all Validation functions, to work out the value of a string when the variables have default values.

eg. string = "${var.foo}" will be read as string = "1" by the validator if the default value of foo is 1.

Search functions

These are used to gather property values together so that they can be validated.

Validator.resources([resource_types])

Searches for all resources of the required types and outputs a TerraformResourceList.

Can be chained with a .property() function.

If passed a string as an argument, search through all resource types and list the ones that match the string as a regex. If passed a list as an argument, only use the types that are inside the list.

Outputs: TerraformResourceList

TerraformResourceList.property(property_name)

Collects all top-level properties in a TerraformResourceList and exposes methods that can be used to validate the property values.

Can be chained with another .property() call to fetch nested properties.

eg. .resource('aws_instance').property('name')

TerraformResourceList.find_property(regex)

Similar to TerraformResourceList.property(), except that it will attempt to use a regex string to search for the property.

eg. .resource('aws_instance').find_property('tag[a-z]')

TerraformPropertyList.property(property_name)

Collects all nested properties in TerraformPropertyList and exposes methods that can be used to validate the property values.

eg. .resource('aws_instance').property('tags').property('name')

TerraformPropertyList.find_property(regex)

Similar to TerraformPropertyList.property(), except that it will attempt to use a regex string to search for the property.

eg. .resource('aws_instance').find_property('tag[a-z]')

Validation functions

If there are any errors, these functions will print the error and raise an AssertionError. The purpose of these functions is to validate the property values of different resources.

TerraformResourceList.should_have_properties([required_properties])

Will raise an AssertionError if any of the properties in required_properties are missing from a TerraformResourceList.

TerraformPropertyList.should_have_properties([required_properties])

Will raise an AssertionError if any of the properties in required_properties are missing from a TerraformPropertyList.

TerraformResourceList.should_not_have_properties([excluded_properties])

Will raise an AssertionError if any of the properties in required_properties are missing from a TerraformResourceList.

TerraformPropertyList.should_not_have_properties([excluded_properties])

Will raise an AssertionError if any of the properties in required_properties are missing from a TerraformPropertyList.

TerraformResourceList.name_should_match_regex(regex)

Will raise an AssertionError if the Terraform resource name does not match the value of regex

TerraformPropertyList.should_equal(expected_value)

Will raise an AssertionError if the value of the property does not equal expected_value

TerraformPropertyList.should_not_equal(unexpected_value)

Will raise an AssertionError if the value of the property equals unexpected_value

TerraformPropertyList.should_match_regex(regex)

Will raise an AssertionError if the value of the property does not match the value of regex

TerraformPropertyList.list_should_contain([value])

Will raise an AssertionError if the list value does not contain any of the [value]

TerraformPropertyList.list_should_not_contain([value])

Will raise an AssertionError if the list value does contain any of the [value]

Run with Docker

Build the terraform_validate daemon using:

docker build -t terraform_validate .

Then, on a different location, place your tests on your tests.py.

To run:

docker run -v `pwd`:/terraform_validate terraform_validate

Example output (All tests passing):

$ docker run -v `pwd`:/terraform_validate terraform_validate
----------------------------------------------------------------------
Ran 3 tests in 1.607s

OK