Best Practices
Recommended practices for using nx-terraform effectively in your infrastructure projects.
Project Organization
Project Structure
Organize projects logically:
packages/
├── terraform-setup/ # Backend (one per workspace/environment)
├── networking/ # Shared modules
├── security/ # Shared modules
├── terraform-infra/ # Infrastructure
Separation of Concerns
- Keep backends separate from infrastructure projects
- Use modules for reusable patterns and shared code
- Keep stateful projects focused on specific infrastructure domains
- Separate environments into different projects
Backend Management
Backend Selection
- Production: Use AWS S3 backend with DynamoDB state locking
- Development: Local backend is acceptable for single-user scenarios
- Teams: Always use remote backend (AWS S3) to avoid conflicts
Backend Best Practices
- Enable State Locking: Always use DynamoDB for state locking in production
- Use Separate Buckets: Consider separate buckets for different environments
- Enable Versioning: Keep state history for recovery
- Access Control: Use IAM policies to restrict access
- Backup Strategy: Consider cross-region replication for critical state
State Management
- One backend per workspace (or environment)
- Separate state files for each project
- Never share state between environments
- Backup state files regularly (especially for production)
Module Design
Reusable Modules
-
Clear Interface: Define clear inputs and outputs
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
} -
Documentation: Document your modules
- Purpose and use cases
- Input variables
- Output values
- Examples
-
Versioning: Consider versioning for shared modules
-
Testing: Test modules independently
nx run networking:terraform-validate
nx run networking:terraform-fmt -
Reusability: Design for reuse
- Avoid hardcoded values
- Use variables for customization
- Provide sensible defaults
- Support multiple use cases
Module Patterns
- Infrastructure Modules: Define infrastructure patterns
- Utility Modules: Provide utilities and helpers
- Composite Modules: Combine other modules
Environment Management
Environment Separation
- Separate projects for each environment (dev, staging, prod)
- Separate state files per environment
- Environment-specific variables via
tfvarsfiles - Clear naming to identify environments
Environment Configuration
-
Use tfvars files for environment-specific values:
packages/my-infra/
└── tfvars/
├── dev.tfvars
├── staging.tfvars
└── prod.tfvars -
Environment tags to identify resources:
tags = {
Environment = var.environment
ManagedBy = "nx-terraform"
} -
Access control: Restrict production access to senior engineers
-
Testing strategy:
- Test changes in dev first
- Promote to staging for integration testing
- Deploy to prod only after validation
Dependency Management
Dependency Best Practices
-
Clear Dependency Structure:
- Keep dependencies shallow when possible
- Avoid deep dependency chains
- Use clear naming
-
Module Organization:
- Create reusable modules for shared code
- Reference modules explicitly
- Document module dependencies
-
Backend Management:
- One backend per workspace (or environment)
- Clear backend → infrastructure relationships
- Document backend dependencies
-
Avoid Circular Dependencies:
- Design modules to avoid cycles
- Use data sources for cross-references
- Extract common code to shared modules
Configuration Management
Variable Management
- Clear variable names with descriptions
- Provide sensible defaults where appropriate
- Document variable purposes
- Use type constraints for validation
Secrets Management
- Don't commit secrets to
tfvarsfiles - Use environment variables for secrets
- Consider secret management tools (AWS Secrets Manager, HashiCorp Vault)
- Use
.gitignorefor sensitive files
Configuration Files
- Keep environment configs in version control
- Document environment-specific requirements
- Use consistent naming conventions
Security
Backend Security (AWS S3)
- IAM Roles: Use IAM roles with least privilege
- Encryption: Enable encryption at rest
- Bucket Policies: Use bucket policies for access control
- MFA Delete: Enable MFA delete (optional)
- Monitoring: Monitor access with CloudTrail
Local Backend Security
- Git Ignore: Add
*.tfstateto.gitignore - File Permissions: Use file system permissions
- Encryption: Encrypt sensitive state files
- Backups: Regular backups
- Version Control: Don't commit state to version control
General Security
- State files may contain secrets - handle with care
- Use remote backends for team collaboration
- Restrict access to production environments
- Audit changes regularly
Performance and Caching
Leverage Caching
-
Run cached operations frequently:
terraform-fmt(fast when cached)terraform-validate(cached when unchanged)
-
Understand cache invalidation:
- Know which operations are cached
- Understand when cache invalidates
- Don't rely on cache for state-dependent operations
-
Optimize for caching:
- Keep files organized
- Minimize unnecessary changes
- Use consistent Terraform versions
CI/CD Optimization
- Use Nx Cloud for distributed caching
- Run cached operations in parallel
- Leverage incremental builds
- Cache provider downloads
Development Workflow
Code Quality
-
Format code regularly:
nx run my-project:terraform-fmt -
Validate before committing:
nx run my-project:terraform-validate -
Review plans before applying:
nx run my-project:terraform-plan
Testing
- Test in dev first before promoting to production
- Use staging for integration testing
- Validate modules independently
- Test dependency chains
Documentation
- Document project purposes in README files
- Document dependencies and relationships
- Provide usage examples
- Keep documentation up to date
Common Patterns
Pattern 1: Environment Tags
Add tags to identify environments:
tags = {
Environment = var.environment
ManagedBy = "nx-terraform"
Project = var.project_name
}
Pattern 2: Environment-Specific Resources
Use conditionals for environment-specific resources:
resource "aws_instance" "monitoring" {
count = var.environment == "prod" ? 1 : 0
# ... monitoring instance config
}
Pattern 3: Shared Backend, Separate States
All environments can share the same backend (S3 bucket) but use different state keys:
dev-infra/terraform.tfstateprod-infra/terraform.tfstate
Pattern 4: Module Composition
Combine modules for complex infrastructure:
module "networking" { ... }
module "security" { ... }
module "compute" { ... }
Related Topics
- Project Types - Understanding project types
- Backend Types - Backend selection
- Dependencies - Managing dependencies
- Configuration - Configuration management
- Troubleshooting - Common issues and solutions