Developer Guide¶
Guidance for developers working on TerraVision itself. This guide focuses on how the code works — architecture, pipeline, handler patterns, and critical constants. For how to install a dev environment, see Installation → Method 4. For contribution workflow (branching, PRs, code standards), see the Contributing Guide.
Project Overview¶
TerraVision is an AI-powered CLI tool that converts Terraform code into professional cloud architecture diagrams. It runs 100% client-side, securely parses Terraform plans, and generates visual representations of cloud infrastructure without requiring access to cloud environments.
Commands¶
All commands below assume poetry run as the prefix, e.g. poetry run terravision draw .... Alternatively activate the venv with eval $(poetry env activate) and drop the prefix.
Running TerraVision¶
# Basic diagram generation
poetry run terravision draw --source <path>
# With AI annotation generation
poetry run terravision draw --source <path> --ai-annotate bedrock
poetry run terravision draw --source <path> --ai-annotate ollama
# Export graph data
poetry run terravision graphdata --source <path> --outfile graph.json
# Debug mode (exports tfdata.json)
poetry run terravision draw --source <path> --debug
Testing¶
# Run all tests
poetry run pytest tests -v
# Run non-slow tests (for pre-commit)
poetry run pytest -m "not slow"
# Run specific test file
poetry run pytest tests/test_provider_detection.py -v
# Run with coverage
poetry run pytest tests --cov=modules
Linting and Formatting¶
# Check formatting (CI enforced)
poetry run black --check -v modules
# Auto-format code
poetry run black modules
# Run pre-commit hooks
pre-commit run --all-files
Building and Releasing¶
# Verify dependencies
dot --version # Graphviz
terraform version # Must be v1.x
git --version
# Clean cache (troubleshooting)
rm -rf ~/.terravision
Architecture¶
Multi-Cloud Provider Support¶
TerraVision supports AWS (full), GCP (partial), and Azure (partial). The architecture uses dynamic provider detection and configuration loading:
- Provider Detection (
modules/provider_detector.py): Analyzes Terraform resource prefixes (aws_,azurerm_,google_) to identify cloud providers - Configuration Loader (
modules/config_loader.py): Dynamically loads provider-specific configs at runtime - Provider Configs (
modules/config/cloud_config_*.py): Each provider has isolated configuration defining icons, relationships, special resources
Key Pattern:
# Detection happens once during compilation
provider_detection = detect_providers(tfdata)
tfdata["provider_detection"] = provider_detection
# Throughout codebase, load provider-specific config
provider = get_primary_provider_or_default(tfdata)
config = load_config(provider) # Returns cloud_config_aws, cloud_config_azure, or cloud_config_gcp
Core Pipeline (compile_tfdata)¶
The main processing pipeline in terravision.py follows this flow:
1. tf_initplan() → Initialize Terraform, run plan
2. tf_makegraph() → Generate Terraform graph
3. read_tfsource() → Parse .tf files with HCL2
4. prefix_module_names() → Handle Terraform modules
5. resolve_all_variables() → Interpolate variables
6. handle_special_cases() → Provider-specific resource processing
7. add_relations() → Detect resource dependencies
8. consolidate_nodes() → Merge similar resources
9. add_annotations() → Apply custom YAML annotations
10. handle_special_resources() → VPC, subnet, security group logic
11. handle_variants() → Add resource variants (Lambda runtime, EC2 type)
12. create_multiple_resources() → Handle count/for_each (resource~1, resource~2)
13. reverse_relations() → Fix arrow directions
14. (Optional) generate_ai_annotations() → Write terravision.ai.yml (--ai-annotate flag)
15. render_diagram() → Generate Graphviz output
Key Modules¶
tfwrapper.py: Terraform wrapper executing terraform init, plan, graph. Downloads remote modules, handles workspaces, processes terraform.tfvars.
fileparser.py: Parses .tf files using python-hcl2, extracts resources, variables, modules, outputs.
interpreter.py: Resolves Terraform variables, interpolations, expressions. Handles complex scenarios like count, for_each, module variables.
graphmaker.py: Core graph construction. Detects relationships by scanning resource attributes for references to other resources. Handles numbered resources (resource~1, resource~2) for count > 1.
resource_handlers.py: Dispatcher for provider-specific resource handlers. Routes to resource_handlers_aws.py, resource_handlers_gcp.py, or resource_handlers_azure.py.
resource_handlers_aws.py / _gcp.py / _azure.py: Provider-specific logic for VPCs, subnets, security groups, networks. Each has functions like handle_vpc(), handle_subnet(), handle_security_group().
drawing.py: Renders final Graphviz diagram. Uses provider-specific icon libraries, applies styling, handles subgraphs (VPCs, availability zones).
annotations.py: Processes custom YAML annotations (terravision.yml) to add/remove/update nodes and connections.
helpers.py: Utility functions for node manipulation, JSON extraction, resource matching.
Resource Handlers Pattern¶
TerraVision uses a hybrid configuration-driven approach for resource handlers. Handlers are defined in RESOURCE_HANDLER_CONFIGS dict in each cloud_config_<provider>.py:
Three handler types:
- Pure config-driven: Use only transformation building blocks (7 AWS handlers)
- Hybrid: Use transformations + custom Python function (3 AWS handlers)
- Pure function: Use only custom Python function (6 AWS handlers)
# modules/config/resource_handler_configs_aws.py
RESOURCE_HANDLER_CONFIGS = {
# Pure config-driven
"aws_vpc_endpoint": {
"description": "Move VPC endpoints into VPC parent and delete endpoint nodes",
"transformations": [
{"operation": "move_to_parent", "params": {...}},
{"operation": "delete_nodes", "params": {...}},
],
},
# Pure function
"aws_security_group": {
"description": "Process security group relationships and reverse connections",
"additional_handler_function": "aws_handle_sg",
},
}
The handle_special_resources() function in graphmaker.py executes:
1. Config-driven transformations (if transformations key exists)
2. Additional handler function (if additional_handler_function key exists)
Numbered Resources (Count/For_Each)¶
Resources with count > 1 or for_each are expanded into numbered instances with ~ suffix:
aws_instance.webwith count=3 becomesaws_instance.web~1,aws_instance.web~2,aws_instance.web~3- Connections are matched by suffix:
web~1 → db~1,web~2 → db~2 - Security groups are extended to match numbered resources they protect
AI Refinement Backends¶
Two AI backends refine diagrams:
Bedrock: AWS API Gateway + Lambda + Bedrock (infrastructure in ai-backend-terraform/)
Ollama: Local llama3 model (localhost:11434)
Both use provider-specific prompts from cloud_config_*.py (AWS_REFINEMENT_PROMPT, AZURE_REFINEMENT_PROMPT, etc.) to fix groupings and connections.
Icon Libraries¶
Icons are sourced from provider-specific directories defined in each config:
- AWS:
resource_classes/aws/(200+ services) - Azure:
resource_classes/azure/ - GCP:
resource_classes/gcp/ - Generic:
resource_classes/generic/(fallback icons)
Important Patterns¶
Configuration-Driven Handlers¶
TerraVision uses a hybrid approach for resource handlers:
Pure config-driven (simple operations):
"aws_vpc_endpoint": {
"transformations": [
{"operation": "move_to_parent", "params": {...}},
{"operation": "delete_nodes", "params": {...}},
],
}
Pure function (complex logic):
Hybrid (both config and custom logic):
"aws_complex_resource": {
"transformations": [{"operation": "expand_to_numbered_instances", "params": {...}}],
"additional_handler_function": "aws_handle_complex_custom",
}
See docs/CONFIGURATION_DRIVEN_HANDLERS.md for details.
Adding New Cloud Provider¶
- Create
modules/config/cloud_config_<provider>.pywith required constants - Create
modules/resource_handlers_<provider>.pywith handler functions - Add provider prefix to
PROVIDER_PREFIXESinprovider_detector.py - Add icons to
resource_classes/<provider>/ - Update tests in
tests/test_provider_detection.py
Testing Provider-Specific Code¶
When modifying provider-specific functionality, always run:
poetry run pytest tests/test_provider_detection.py -v
poetry run pytest tests/test_config_loader.py -v
poetry run pytest tests/test_<provider>_resources.py -v
Debug Workflow¶
When debugging Terraform parsing issues:
# Generate debug output
python terravision.py draw --source <path> --debug
# Inspect tfdata.json (contains all_resource, original_metadata, graphdict)
cat tfdata.json | jq '.graphdict'
# Replay from debug file (skips terraform init/plan)
python terravision.py draw --source tfdata.json
Pre-commit Hooks¶
The repo uses pre-commit to run pytest on non-slow tests before each commit. Hook defined in .pre-commit-config.yaml.
CI/CD¶
GitHub Actions workflow (.github/workflows/lint-and-test.yml) runs on push/PR to main:
- Installs Poetry, Python 3.11, Terraform, Graphviz
- Runs Black formatter check on
modules/directory - Executes pytest test suite
- Uses AWS OIDC for integration tests requiring AWS credentials
Configuration Files¶
pyproject.toml: Poetry dependencies, Black config (line-length 88), isort config .pre-commit-config.yaml: Pre-commit hook running pytest .github/workflows/lint-and-test.yml: CI pipeline
Critical Constants¶
Each cloud_config_*.py defines these provider-specific constants:
PROVIDER_PREFIX: Resource name prefix(es) (e.g.,["aws_"]or["azurerm_", "azuread_"])ICON_LIBRARY: Path to icon directorySPECIAL_RESOURCES: Dict mapping resource types to handler functionsGROUP_NODES: Resources that create subgraphs (VPCs, resource groups)CONSOLIDATED_NODES: Resources to merge into single nodesNODE_VARIANTS: Resources with variants (Lambda runtime, EC2 type)IMPLIED_CONNECTIONS: Keywords that imply connectionsREVERSE_ARROW_LIST: Resources requiring reversed arrow directionFORCED_DEST/FORCED_ORIGIN: Resources with fixed connection directionSHARED_SERVICES: Resources shared across modules (IAM roles, etc.)EDGE_NODES: Resources at diagram boundariesAUTO_ANNOTATIONS: Auto-applied labels
Testing Philosophy¶
- Unit tests: Test individual functions in isolation (
tests/*_unit_test.py) - Integration tests: Test full pipeline with real Terraform code (
tests/integration_test.py) - Provider tests: Validate provider detection and config loading (
tests/test_provider_detection.py,tests/test_config_loader.py) - Mark slow tests: Use
@pytest.mark.slowfor tests that run terraform commands
Known Constraints¶
- Terraform must be v1.x (v0.x not supported)
- Requires external dependencies: Graphviz (
dot), Git, Terraform - Security groups require special handling due to complex ingress/egress rules
- Module paths must be resolvable (Git URLs cloned to
~/.terravision/) - JSON debug files from different TerraVision versions may not be compatible