345 lines
8.0 KiB
Markdown
345 lines
8.0 KiB
Markdown
---
|
|
name: terraform-reviewer
|
|
description: |
|
|
WHEN: Terraform code review, module structure, state management, security policies
|
|
WHAT: Module organization + State backend + Security policies + Variable validation + Best practices
|
|
WHEN NOT: Kubernetes → k8s-reviewer, Docker → docker-reviewer
|
|
---
|
|
|
|
# Terraform Reviewer Skill
|
|
|
|
## Purpose
|
|
Reviews Terraform code for module structure, state management, security, and best practices.
|
|
|
|
## When to Use
|
|
- Terraform code review
|
|
- Module structure review
|
|
- State backend configuration
|
|
- Security policy review
|
|
- Variable and output review
|
|
|
|
## Project Detection
|
|
- `*.tf` files in project
|
|
- `main.tf`, `variables.tf`, `outputs.tf`
|
|
- `modules/` directory
|
|
- `terraform.tfvars`
|
|
|
|
## Workflow
|
|
|
|
### Step 1: Analyze Project
|
|
```
|
|
**Terraform**: 1.6+
|
|
**Provider**: AWS/GCP/Azure
|
|
**Backend**: S3/GCS/Azure Blob
|
|
**Modules**: Custom + Registry
|
|
```
|
|
|
|
### Step 2: Select Review Areas
|
|
**AskUserQuestion:**
|
|
```
|
|
"Which areas to review?"
|
|
Options:
|
|
- Full Terraform review (recommended)
|
|
- Module structure
|
|
- State management
|
|
- Security and compliance
|
|
- Variable validation
|
|
multiSelect: true
|
|
```
|
|
|
|
## Detection Rules
|
|
|
|
### Module Structure
|
|
| Check | Recommendation | Severity |
|
|
|-------|----------------|----------|
|
|
| All resources in main.tf | Split by resource type | MEDIUM |
|
|
| No modules | Extract reusable modules | MEDIUM |
|
|
| Hardcoded values | Use variables | HIGH |
|
|
| No outputs | Add relevant outputs | MEDIUM |
|
|
|
|
```
|
|
# GOOD: Project structure
|
|
terraform/
|
|
├── environments/
|
|
│ ├── dev/
|
|
│ │ ├── main.tf
|
|
│ │ ├── variables.tf
|
|
│ │ └── terraform.tfvars
|
|
│ └── prod/
|
|
│ ├── main.tf
|
|
│ ├── variables.tf
|
|
│ └── terraform.tfvars
|
|
├── modules/
|
|
│ ├── vpc/
|
|
│ │ ├── main.tf
|
|
│ │ ├── variables.tf
|
|
│ │ └── outputs.tf
|
|
│ └── eks/
|
|
│ ├── main.tf
|
|
│ ├── variables.tf
|
|
│ └── outputs.tf
|
|
└── README.md
|
|
```
|
|
|
|
```hcl
|
|
# BAD: Hardcoded values
|
|
resource "aws_instance" "web" {
|
|
ami = "ami-12345678"
|
|
instance_type = "t3.micro"
|
|
|
|
tags = {
|
|
Name = "web-server"
|
|
}
|
|
}
|
|
|
|
# GOOD: Parameterized with variables
|
|
variable "instance_type" {
|
|
description = "EC2 instance type"
|
|
type = string
|
|
default = "t3.micro"
|
|
|
|
validation {
|
|
condition = can(regex("^t3\\.", var.instance_type))
|
|
error_message = "Instance type must be t3 family."
|
|
}
|
|
}
|
|
|
|
variable "environment" {
|
|
description = "Environment name"
|
|
type = string
|
|
|
|
validation {
|
|
condition = contains(["dev", "staging", "prod"], var.environment)
|
|
error_message = "Environment must be dev, staging, or prod."
|
|
}
|
|
}
|
|
|
|
resource "aws_instance" "web" {
|
|
ami = data.aws_ami.amazon_linux.id
|
|
instance_type = var.instance_type
|
|
|
|
tags = merge(local.common_tags, {
|
|
Name = "${var.project}-${var.environment}-web"
|
|
})
|
|
}
|
|
```
|
|
|
|
### State Management
|
|
| Check | Recommendation | Severity |
|
|
|-------|----------------|----------|
|
|
| Local state | Use remote backend | CRITICAL |
|
|
| No state locking | Enable DynamoDB/GCS lock | HIGH |
|
|
| No state encryption | Enable encryption | HIGH |
|
|
| Shared state file | Split by environment | MEDIUM |
|
|
|
|
```hcl
|
|
# BAD: Local state (default)
|
|
# No backend configuration
|
|
|
|
# GOOD: Remote backend with locking
|
|
terraform {
|
|
backend "s3" {
|
|
bucket = "mycompany-terraform-state"
|
|
key = "prod/vpc/terraform.tfstate"
|
|
region = "us-east-1"
|
|
encrypt = true
|
|
dynamodb_table = "terraform-state-lock"
|
|
}
|
|
}
|
|
|
|
# For GCP
|
|
terraform {
|
|
backend "gcs" {
|
|
bucket = "mycompany-terraform-state"
|
|
prefix = "prod/vpc"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Security
|
|
| Check | Recommendation | Severity |
|
|
|-------|----------------|----------|
|
|
| Secrets in tfvars | Use secret manager | CRITICAL |
|
|
| Public S3 bucket | Set ACL private | CRITICAL |
|
|
| Open security group | Restrict CIDR | CRITICAL |
|
|
| No encryption | Enable encryption | HIGH |
|
|
|
|
```hcl
|
|
# BAD: Security issues
|
|
resource "aws_security_group" "web" {
|
|
ingress {
|
|
from_port = 0
|
|
to_port = 65535
|
|
protocol = "tcp"
|
|
cidr_blocks = ["0.0.0.0/0"] # Wide open!
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket" "data" {
|
|
bucket = "my-data-bucket"
|
|
acl = "public-read" # Public!
|
|
}
|
|
|
|
# GOOD: Secure configuration
|
|
resource "aws_security_group" "web" {
|
|
name = "${var.project}-web-sg"
|
|
description = "Security group for web servers"
|
|
vpc_id = var.vpc_id
|
|
|
|
ingress {
|
|
description = "HTTPS from load balancer"
|
|
from_port = 443
|
|
to_port = 443
|
|
protocol = "tcp"
|
|
security_groups = [aws_security_group.alb.id]
|
|
}
|
|
|
|
egress {
|
|
description = "Allow all outbound"
|
|
from_port = 0
|
|
to_port = 0
|
|
protocol = "-1"
|
|
cidr_blocks = ["0.0.0.0/0"]
|
|
}
|
|
|
|
tags = local.common_tags
|
|
}
|
|
|
|
resource "aws_s3_bucket" "data" {
|
|
bucket = "${var.project}-${var.environment}-data"
|
|
}
|
|
|
|
resource "aws_s3_bucket_public_access_block" "data" {
|
|
bucket = aws_s3_bucket.data.id
|
|
|
|
block_public_acls = true
|
|
block_public_policy = true
|
|
ignore_public_acls = true
|
|
restrict_public_buckets = true
|
|
}
|
|
|
|
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
|
|
bucket = aws_s3_bucket.data.id
|
|
|
|
rule {
|
|
apply_server_side_encryption_by_default {
|
|
sse_algorithm = "aws:kms"
|
|
kms_master_key_id = aws_kms_key.s3.arn
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Variable Validation
|
|
| Check | Recommendation | Severity |
|
|
|-------|----------------|----------|
|
|
| No type constraint | Add type | MEDIUM |
|
|
| No validation | Add validation block | MEDIUM |
|
|
| No description | Add description | LOW |
|
|
| Sensitive not marked | Add sensitive = true | HIGH |
|
|
|
|
```hcl
|
|
# GOOD: Well-defined variables
|
|
variable "vpc_cidr" {
|
|
description = "CIDR block for VPC"
|
|
type = string
|
|
default = "10.0.0.0/16"
|
|
|
|
validation {
|
|
condition = can(cidrnetmask(var.vpc_cidr))
|
|
error_message = "Must be a valid CIDR block."
|
|
}
|
|
}
|
|
|
|
variable "db_password" {
|
|
description = "Database password"
|
|
type = string
|
|
sensitive = true # Won't show in logs
|
|
|
|
validation {
|
|
condition = length(var.db_password) >= 16
|
|
error_message = "Password must be at least 16 characters."
|
|
}
|
|
}
|
|
|
|
variable "allowed_environments" {
|
|
description = "List of allowed environment names"
|
|
type = list(string)
|
|
default = ["dev", "staging", "prod"]
|
|
}
|
|
```
|
|
|
|
### Resource Naming
|
|
| Check | Recommendation | Severity |
|
|
|-------|----------------|----------|
|
|
| Inconsistent naming | Use naming convention | MEDIUM |
|
|
| No tags | Add standard tags | MEDIUM |
|
|
|
|
```hcl
|
|
# GOOD: Consistent naming and tagging
|
|
locals {
|
|
name_prefix = "${var.project}-${var.environment}"
|
|
|
|
common_tags = {
|
|
Project = var.project
|
|
Environment = var.environment
|
|
ManagedBy = "terraform"
|
|
Owner = var.owner
|
|
}
|
|
}
|
|
|
|
resource "aws_vpc" "main" {
|
|
cidr_block = var.vpc_cidr
|
|
|
|
tags = merge(local.common_tags, {
|
|
Name = "${local.name_prefix}-vpc"
|
|
})
|
|
}
|
|
```
|
|
|
|
## Response Template
|
|
```
|
|
## Terraform Review Results
|
|
|
|
**Project**: [name]
|
|
**Terraform**: 1.6 | **Provider**: AWS
|
|
|
|
### Module Structure
|
|
| Status | File | Issue |
|
|
|--------|------|-------|
|
|
| MEDIUM | main.tf | 500+ lines, should split |
|
|
|
|
### State Management
|
|
| Status | File | Issue |
|
|
|--------|------|-------|
|
|
| CRITICAL | - | Using local state |
|
|
|
|
### Security
|
|
| Status | File | Issue |
|
|
|--------|------|-------|
|
|
| CRITICAL | security.tf:23 | Security group allows 0.0.0.0/0 |
|
|
|
|
### Variables
|
|
| Status | File | Issue |
|
|
|--------|------|-------|
|
|
| HIGH | variables.tf | db_password not marked sensitive |
|
|
|
|
### Recommended Actions
|
|
1. [ ] Configure remote state backend with locking
|
|
2. [ ] Restrict security group ingress rules
|
|
3. [ ] Mark sensitive variables
|
|
4. [ ] Split main.tf into logical files
|
|
```
|
|
|
|
## Best Practices
|
|
1. **Structure**: Separate by environment, use modules
|
|
2. **State**: Remote backend with locking and encryption
|
|
3. **Security**: No secrets in code, least privilege
|
|
4. **Variables**: Type constraints, validation, descriptions
|
|
5. **Naming**: Consistent convention, standard tags
|
|
|
|
## Integration
|
|
- `k8s-reviewer`: EKS/GKE cluster configs
|
|
- `infra-security-reviewer`: Compliance checks
|
|
- `ci-cd-reviewer`: Terraform in pipelines
|