Module Resource Validation
Terratags can validate resources created by external modules (from Terraform Registry, Git repositories, etc.) by analyzing Terraform plan output. This provides comprehensive tag compliance checking across your entire infrastructure.
Overview
When using external modules in Terraform, the actual resources created by those modules are not visible during static analysis of your Terraform files. Terratags solves this by analyzing the Terraform plan output, which contains the expanded resource tree including all module-created resources.
How It Works
Static Analysis Limitation
When analyzing Terraform files directly (-dir
flag), terratags can only see: - Direct resource blocks in your .tf
files - Module calls and their input variables/tags - Provider configurations
It cannot see: - Resources created inside external modules - Conditional resources based on module logic - Dynamic resource creation patterns
Plan-Based Analysis
When analyzing Terraform plan JSON (-plan
flag), terratags can see: - All direct resources from your .tf
files - All resources that will be created by external modules - The complete resource dependency tree - Actual tag values after variable resolution
Tag Inheritance
Tags passed to module calls are automatically inherited by resources created within those modules:
- Module Call Tags: Tags defined in the module block
- Resource Tags: Tags defined directly on resources within modules
- Provider Default Tags: Tags from provider
default_tags
configuration - Inheritance Priority: Resource tags > Module tags > Provider default tags
Validation Modes
Mode | Command | Scope | Use Case |
---|---|---|---|
Directory | terratags -config config.yaml -dir ./infra | Direct resources + module calls | Quick validation, CI/CD for file changes |
Plan | terratags -config config.yaml -plan plan.json | All resources (direct + module-created) | Comprehensive validation, pre-deployment |
Examples
Module Validation Scenario
Consider this Terraform configuration:
# main.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
tags = {
Environment = "production"
Owner = "platform-team"
Project = "infrastructure"
# All required tags present for module call
}
}
resource "aws_s3_bucket" "app_data" {
bucket = "my-app-data"
tags = {
Environment = "production"
Owner = "app-team"
# Missing Project tag - will be flagged
}
}
Directory Validation
Results: - ✅ Module vpc
call has all required tags - ❌ Resource aws_s3_bucket.app_data
missing Project
tag - ❓ Unknown: What about the VPC, subnets, route tables, etc. created by the module?
Plan Validation
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
terratags -config config.yaml -plan plan.json
Results: - ✅ Module vpc
call has all required tags - ❌ Resource aws_s3_bucket.app_data
missing Project
tag - ✅ module.vpc.aws_vpc.this[0]
inherits tags from module call - ✅ module.vpc.aws_subnet.private[0]
inherits tags from module call - ✅ module.vpc.aws_subnet.private[1]
inherits tags from module call - ✅ module.vpc.aws_subnet.public[0]
inherits tags from module call - ✅ module.vpc.aws_subnet.public[1]
inherits tags from module call - ✅ module.vpc.aws_route_table.private[0]
inherits tags from module call - ... and so on for all module-created resources
Configuration
Module validation works with all existing terratags configuration options:
Required Tags
Pattern Validation
required_tags:
Environment:
pattern: "^(dev|test|staging|prod)$"
Owner:
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
Project: {}
Name: {}
Exemptions
exemptions:
- resource_type: aws_vpc
resource_name: "*"
exempt_tags: [Name]
reason: "VPC names are managed by module"
- resource_type: aws_subnet
resource_name: "*"
exempt_tags: [Name]
reason: "Subnet names are auto-generated by module"
Reporting
Console Output
Plan validation provides detailed output distinguishing between direct and module resources:
INFO Found 1 direct resources and 12 module resources
INFO Direct resources: 0/1 compliant (0.0%)
INFO Module resources: 12/12 compliant
INFO Overall: 12/13 compliant (92.3%)
Resource aws_s3_bucket 'app_data' is missing required tags: Project
HTML Reports
HTML reports include separate sections for: - Direct Resources: Resources defined in your .tf
files - Module Resources: Resources created by external modules
Module resources show additional information: - Module path (e.g., module.vpc
) - Module source (e.g., terraform-aws-modules/vpc/aws@3.14.0
) - Tag inheritance details
Best Practices
1. Use Plan Validation for Production
For production deployments, always use plan validation to ensure comprehensive coverage:
# Production deployment pipeline
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
terratags -config config.yaml -plan plan.json
2. Use Directory Validation for Development
For quick feedback during development, directory validation is sufficient:
3. Configure Module-Specific Exemptions
Many modules auto-generate resource names or have specific tagging patterns:
exemptions:
# VPC module resources often have auto-generated names
- resource_type: aws_vpc
resource_name: "*"
exempt_tags: [Name]
reason: "VPC names managed by terraform-aws-modules/vpc"
# Security group rules don't typically need all tags
- resource_type: aws_security_group_rule
resource_name: "*"
exempt_tags: [Name, Project]
reason: "Security group rules inherit context from parent"
4. Standardize Module Tags
Ensure consistent tagging across modules by standardizing tag inputs:
# Standard module tags variable
variable "common_tags" {
description = "Common tags to apply to all resources"
type = map(string)
default = {
Environment = "production"
Owner = "platform-team"
Project = "infrastructure"
ManagedBy = "terraform"
}
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
# Module-specific configuration
name = "my-vpc"
cidr = "10.0.0.0/16"
# Apply standard tags
tags = var.common_tags
}
module "rds" {
source = "terraform-aws-modules/rds/aws"
# Module-specific configuration
identifier = "my-database"
# Apply standard tags
tags = var.common_tags
}
Limitations
1. Plan Generation Required
Module validation requires generating a Terraform plan, which means: - AWS/Azure credentials must be available - Terraform must be able to connect to providers - Plan generation time adds to validation time
2. Module Source Access
For private modules, ensure the validation environment has access: - Private Git repositories require SSH keys or tokens - Private Terraform registries require authentication - Network access to module sources
3. Dynamic Module Behavior
Some advanced module patterns may not be fully captured: - Modules that create resources based on external data sources - Modules with complex conditional logic - Modules that use provider aliases extensively
Troubleshooting
Common Issues
Issue: Module resources not appearing in validation
Solution: Ensure the plan includes module resources:
# Check plan contains module resources
terraform show -json tfplan | jq '.resource_changes[] | select(.module_address != null)'
Issue: Tag inheritance not working
Solution: Verify module call tags are being loaded:
Issue: Module source showing as "unknown"
Solution: Ensure plan includes configuration section:
# Generate plan with configuration
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
# Check configuration section exists
jq '.configuration.root_module.module_calls' plan.json
Integration Examples
GitHub Actions
name: Validate All Resources
on:
pull_request:
paths: ['**.tf']
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install Terratags
run: go install github.com/terratags/terratags@latest
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
- name: Terraform Init
run: terraform init
- name: Generate Plan
run: |
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
- name: Validate Tags
run: terratags -config .terratags.yaml -plan plan.json -report report.html
- name: Upload Report
uses: actions/upload-artifact@v4
if: always()
with:
name: tag-validation-report
path: report.html
GitLab CI
validate-tags:
stage: validate
image: hashicorp/terraform:latest
before_script:
- apk add --no-cache go
- go install github.com/terratags/terratags@latest
script:
- terraform init
- terraform plan -out=tfplan
- terraform show -json tfplan > plan.json
- terratags -config .terratags.yaml -plan plan.json
artifacts:
when: always
reports:
junit: report.xml
paths:
- plan.json
- report.html
This comprehensive module validation capability ensures that your tag compliance policies are enforced across your entire Terraform-managed infrastructure, not just the resources you define directly.