Skip to main content

Terraform Plan Provider

required_provider: stackguardian/terraform_plan

Inspects a Terraform plan: attribute values of changed resources, the actions applied to them, resource counts, explicit dependencies, references between resources, provider configuration, and the Terraform version.

Input document

The JSON representation of a Terraform plan:

terraform plan -out=plan.out
terraform show -json plan.out > plan.json
tirith -policy-path policy.json -input-path plan.json

Most operations read the resource_changes array of the plan. If the plan contains no resource_changes at all, every operation reports an error with severity 0 (No Terraform resources changes are found), which is skipped at the default error_tolerance of 0.

Operation types

operation_typePurpose
attributeGet an attribute's planned value for every instance of a resource type
actionGet the plan actions (create, update, delete, ...) for a resource type
countCount the changed instances of a resource type
direct_dependenciesGet the resource types listed in a resource's depends_on
direct_referencesGet or check references between resources
terraform_versionGet the Terraform version that produced the plan
provider_configGet the configuration of a Terraform provider (version constraint or region)

Any other operation_type produces an error with severity 99, which fails the check.


attribute

Returns the planned (change.after) value of an attribute for every instance of a resource type.

ParameterRequiredDescription
terraform_resource_typeyesResource type to match (e.g. aws_s3_bucket), or * to match every type.
terraform_resource_attributeyesAttribute to read. A plain top-level key (force_destroy), a dotted path (tags.costcenter), or a path containing .*. to iterate over a list (ebs_block_device.*.encrypted).
exclude_resource_typesno (default [])List of resource types to skip. Only applied when terraform_resource_type is *.

Returns: one value per matching resource instance. With a .*. wildcard, one value per list element; list elements that lack the attribute contribute null, so they are still evaluated. The value is whatever the attribute holds in the plan — scalar, list, or dict.

On a miss:

  • No resource of the requested type in resource_changes — severity 1 (resource_type: '...' is not found).
  • Resource found, attribute absent — severity 2 (attribute: '...' is not found), reported per resource instance that lacks it.
  • Resource found but its change.after is empty (e.g. a destroy-only change) — severity 0 (No Terraform changes found for resource type: '...').

Example (adapted from a test fixture; requires every resource in the plan to carry a non-empty costcenter tag):

{
"meta": {
"version": "v1",
"required_provider": "stackguardian/terraform_plan"
},
"evaluators": [
{
"id": "every_resource_has_costcenter_tag",
"description": "All resources must have a 'costcenter' tag with a non-empty value",
"provider_args": {
"operation_type": "attribute",
"terraform_resource_type": "*",
"terraform_resource_attribute": "tags.costcenter",
"exclude_resource_types": ["aws_iam_role_policy_attachment"]
},
"condition": {
"type": "IsNotEmpty",
"value": "",
"error_tolerance": 1
}
}
],
"eval_expression": "every_resource_has_costcenter_tag"
}

action

Returns the actions Terraform plans to take on every instance of a resource type. Actions come straight from change.actions in the plan: create, update, delete, no-op, read (a replacement appears as both delete and create).

ParameterRequiredDescription
terraform_resource_typeyesResource type to match, or * for every type.
exclude_resource_typesno (default [])List of resource types to skip. Only applied when terraform_resource_type is *.

Returns: one string per action per matching resource instance (a resource with actions ["delete", "create"] yields two values).

On a miss: no resource of the requested type — severity 1.

Example (adapted from a test fixture; fails when a virtual network would be deleted):

{
"meta": {
"version": "v1",
"required_provider": "stackguardian/terraform_plan"
},
"evaluators": [
{
"id": "vnet_is_deleted",
"provider_args": {
"operation_type": "action",
"terraform_resource_type": "azurerm_virtual_network"
},
"condition": {
"type": "ContainedIn",
"value": ["delete"],
"error_tolerance": 2
}
}
],
"eval_expression": "!vnet_is_deleted"
}

count

Counts the instances of a resource type in resource_changes.

ParameterRequiredDescription
terraform_resource_typeyesResource type to count, or * for every type.
exclude_resource_typesno (default [])List of resource types to skip. Only applied when terraform_resource_type is *.

Returns: a single integer. A type with no instances returns 0 — this operation never produces a "not found" error.

Example:

{
"id": "at_most_ten_vpcs",
"provider_args": {
"operation_type": "count",
"terraform_resource_type": "aws_vpc"
},
"condition": {
"type": "LessThanEqualTo",
"value": 10
}
}

direct_dependencies

Returns, for each resource of a type, the resource types named in its explicit depends_on. Only resources declared in the root module of the configuration are inspected.

ParameterRequiredDescription
terraform_resource_typeyesResource type to inspect. Omitting it produces a severity 99 error.

Returns: one list of resource-type strings per matching resource (only the type part of each depends_on entry, i.e. aws_s3_bucket.example becomes aws_s3_bucket). A resource without depends_on yields an empty list.

On a miss: no resource of the requested type in the configuration — severity 1.

Example (verified against a test fixture; requires every EC2 instance to declare an explicit dependency on an S3 bucket):

{
"meta": {
"version": "v1",
"required_provider": "stackguardian/terraform_plan"
},
"evaluators": [
{
"id": "ec2_depends_on_s3",
"description": "Make sure that EC2 instances have explicit dependency on S3 bucket",
"provider_args": {
"operation_type": "direct_dependencies",
"terraform_resource_type": "aws_instance"
},
"condition": {
"type": "Contains",
"value": "aws_s3_bucket",
"error_tolerance": 2
}
}
],
"eval_expression": "ec2_depends_on_s3"
}

direct_references

Inspects references between resources (a reference is created when one resource's argument uses another resource's attribute, e.g. security_groups = [aws_security_group.sg.id]). It has three modes, chosen by which parameters are present:

ParameterRequiredDescription
terraform_resource_typeyesThe resource type under inspection.
referenced_bynoA resource type that should point at terraform_resource_type.
references_tonoA resource type that terraform_resource_type should point to.

referenced_by and references_to are mutually exclusive — supplying both produces a severity 99 error.

Plain mode (neither referenced_by nor references_to)

For each resource of terraform_resource_type declared in the root module, returns the list of resource types it references in its expressions.

Returns: one list of resource-type strings per matching resource.

On a miss: type not found in the configuration — severity 1. Omitting terraform_resource_type — severity 99.

Example (verified against a test fixture):

{
"id": "aws_elbs_have_direct_references_to_security_group",
"provider_args": {
"operation_type": "direct_references",
"terraform_resource_type": "aws_elb"
},
"condition": {
"type": "Contains",
"value": "aws_security_group",
"error_tolerance": 2
}
}

referenced_by mode

Checks that instances of terraform_resource_type are referenced by resources of type referenced_by. Instances that are only being destroyed are ignored. Unlike the plain mode, references are searched through the whole configuration, including child modules.

Returns: one boolean per instance of terraform_resource_typetrue if some referenced_by resource references it, false otherwise. Use "condition": {"type": "Equals", "value": true} to require that all instances are referenced.

On a miss: no (non-destroyed) instance of terraform_resource_type — severity 1.

Example (from a test fixture; every S3 bucket must have an intelligent-tiering configuration attached):

{
"meta": {
"required_provider": "stackguardian/terraform_plan",
"version": "v1"
},
"evaluators": [
{
"id": "s3HasLifeCycleIntelligentTiering",
"description": "Make sure all aws_s3_bucket are referenced by aws_s3_bucket_intelligent_tiering_configuration",
"provider_args": {
"operation_type": "direct_references",
"terraform_resource_type": "aws_s3_bucket",
"referenced_by": "aws_s3_bucket_intelligent_tiering_configuration"
},
"condition": {
"type": "Equals",
"value": true,
"error_tolerance": 0
}
}
],
"eval_expression": "s3HasLifeCycleIntelligentTiering"
}

references_to mode

Checks that every instance of terraform_resource_type references at least one resource of type references_to. Instances that are only being destroyed are ignored.

Returns: a single boolean — true only if all instances reference the target type.

On a miss: no (non-destroyed) instance of terraform_resource_type — severity 1.

Example (verified against a test fixture):

{
"id": "elbRefsToSecGroup",
"description": "Make sure ELBs references to security groups",
"provider_args": {
"operation_type": "direct_references",
"terraform_resource_type": "aws_elb",
"references_to": "aws_security_group"
},
"condition": {
"type": "Equals",
"value": true,
"error_tolerance": 0
}
}

terraform_version

Returns the Terraform version string recorded in the plan.

No parameters besides operation_type.

Returns: a single string (e.g. "1.4.5"), or null if the plan has no terraform_version key.

Example (verified end-to-end):

{
"id": "terraform_version_check",
"provider_args": {
"operation_type": "terraform_version"
},
"condition": {
"type": "RegexMatch",
"value": "^1\\."
}
}

provider_config

Reads the configuration of a Terraform provider from configuration.provider_config in the plan.

ParameterRequiredDescription
terraform_provider_full_nameyesThe provider's full registry name, e.g. registry.terraform.io/hashicorp/aws. Omitting it produces a severity 99 error.
attributeyesWhat to read. Must be version_constraint or region — anything else produces a severity 99 error.

Returns: one string per provider entry whose full_name matches: the version constraint (e.g. ">= 3.11.0, < 4.0.0") or the region. The region is only found when it is written as a constant in the configuration; a region supplied through a variable is reported as not found (severity 2).

On a miss:

  • Matching provider found but the attribute is absent — severity 2 (`region` is not found in the provider_config).
  • No provider with that full_name — severity 1.

Example (verified end-to-end):

{
"id": "aws_region_check",
"provider_args": {
"operation_type": "provider_config",
"terraform_provider_full_name": "registry.terraform.io/hashicorp/aws",
"attribute": "region"
},
"condition": {
"type": "ContainedIn",
"value": ["eu-central-1", "eu-west-1"]
}
}

Error severities used by this provider

SeveritySituation
0No resource_changes in the plan, or the matched resource has no planned values (destroy-only change).
1Resource type / provider name not found.
2Attribute not found on a matched resource or provider config.
99Invalid provider_args (unsupported operation or attribute, missing required parameter, both referenced_by and references_to given).

Whether a severity fails or skips the check depends on condition.error_tolerance — see the providers overview and error tolerance. Condition types are documented in the evaluators reference.