Day 7 - Improve the Terraform Module
Enhance your Terraform module with advanced features like testing, documentation, and CI/CD integration.
Description
Your S3 module from Day 6 works, but it lacks comprehensive documentation, automated testing, and CI/CD integration. To make it production-ready and shareable, you need to add these professional touches.
Task
Enhance your Terraform module with documentation, testing, and automation.
Requirements:
- Comprehensive README with examples
- Automated validation in CI/CD
- Input/output documentation
- Multiple usage examples
- Pre-commit hooks
Target
- ✅ Complete README.md with all module details
- ✅ CI/CD pipeline validates module
- ✅ Pre-commit hooks enforce standards
- ✅ Multiple example configurations
- ✅ Auto-generated documentation
Sample App
Enhanced Directory Structure
terraform-aws-s3-bucket/
├── .github/
│ └── workflows/
│ ├── validate.yml
│ └── release.yml
├── .pre-commit-config.yaml
├── examples/
│ ├── basic/
│ ├── with-lifecycle/
│ ├── encrypted/
│ └── complete/
├── test/
│ └── module_test.go
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
├── README.md
├── CHANGELOG.md
└── .terraform-docs.yml
View Solution
Solution
Enhanced Documentation
README.md
# AWS S3 Bucket Terraform Module
Production-ready Terraform module for creating AWS S3 buckets with security best practices.
## Features
- 🔒 **Security First**: Public access blocked by default
- 🔄 **Versioning**: Optional versioning support
- 🔐 **Encryption**: AES256 or KMS encryption
- 📋 **Lifecycle Rules**: Automated object management
- 🏷️ **Tagging**: Consistent resource tagging
- ✅ **Validated**: Automated testing and validation
## Usage
### Basic Example
```hcl
module "bucket" {
source = "git::https://github.com/yourorg/terraform-aws-s3-bucket.git?ref=v1.0.0"
bucket_name = "my-application-data"
environment = "production"
tags = {
Project = "MyApp"
}
}
With Lifecycle Rules
module "bucket_with_lifecycle" {
source = "git::https://github.com/yourorg/terraform-aws-s3-bucket.git?ref=v1.0.0"
bucket_name = "my-app-logs"
environment = "production"
lifecycle_rules = [
{
id = "archive-logs"
enabled = true
transition_days = 30
storage_class = "GLACIER"
expiration_days = 365
}
]
}
Requirements
| Name | Version |
|---|---|
| terraform | >= 1.5 |
| aws | ~> 5.0 |
Providers
| Name | Version |
|---|---|
| aws | ~> 5.0 |
Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| bucket_name | Name of the S3 bucket | string |
n/a | yes |
| environment | Environment name | string |
"dev" |
no |
| versioning_enabled | Enable versioning | bool |
true |
no |
| block_public_access | Block all public access | bool |
true |
no |
| kms_key_id | KMS key for encryption | string |
null |
no |
| lifecycle_rules | Lifecycle management rules | list(object) |
null |
no |
| tags | Additional resource tags | map(string) |
{} |
no |
Outputs
| Name | Description |
|---|---|
| bucket_id | The name of the bucket |
| bucket_arn | The ARN of the bucket |
| bucket_domain_name | The domain name of the bucket |
| bucket_regional_domain_name | The regional domain name |
| bucket_region | The AWS region |
Examples
- Basic - Simple bucket creation
- With Lifecycle - Lifecycle rules
- Encrypted - KMS encryption
- Complete - All features
Development
Prerequisites
# Install pre-commit
pip install pre-commit
pre-commit install
# Install terraform-docs
brew install terraform-docs
# Install tflint
brew install tflint
Testing
# Validate
terraform validate
# Format
terraform fmt -recursive
# Lint
tflint --init
tflint
# Run tests
cd test && go test -v
Contributing
See CONTRIBUTING.md
License
MIT Licensed. See LICENSE
### CI/CD Pipeline
#### .github/workflows/validate.yml
```yaml
name: Validate
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
validate:
name: Terraform Validate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init -backend=false
- name: Terraform Validate
run: terraform validate
lint:
name: TFLint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: terraform-linters/setup-tflint@v4
with:
tflint_version: latest
- name: Init TFLint
run: tflint --init
- name: Run TFLint
run: tflint --format compact
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform
quiet: false
soft_fail: true
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
- name: Generate Terraform docs
uses: terraform-docs/[email protected]
with:
working-dir: .
output-file: README.md
output-method: inject
git-push: true
examples:
name: Validate Examples
runs-on: ubuntu-latest
strategy:
matrix:
example: [basic, with-lifecycle, encrypted, complete]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Init Example
run: terraform init -backend=false
working-directory: examples/${{ matrix.example }}
- name: Validate Example
run: terraform validate
working-directory: examples/${{ matrix.example }}
Pre-commit Configuration
.pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.83.5
hooks:
- id: terraform_fmt
name: Terraform format
description: Format Terraform files
- id: terraform_validate
name: Terraform validate
description: Validate Terraform configuration
- id: terraform_docs
name: Terraform docs
description: Update documentation
args:
- --args=--lockfile=false
- id: terraform_tflint
name: TFLint
description: Lint Terraform files
args:
- --args=--config=__GIT_WORKING_DIR__/.tflint.hcl
- id: terraform_checkov
name: Checkov
description: Security scanning
args:
- --args=--quiet
- --args=--framework terraform
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
Terraform Docs Configuration
.terraform-docs.yml
formatter: "markdown table"
version: ""
header-from: main.tf
content: |-
{{ .Header }}
## Usage
{{ include "examples/basic/main.tf" }}
{{ .Requirements }}
{{ .Providers }}
{{ .Inputs }}
{{ .Outputs }}
## Examples
- [Basic](./examples/basic) - Simple bucket creation
- [With Lifecycle](./examples/with-lifecycle) - Lifecycle rules
- [Encrypted](./examples/encrypted) - KMS encryption
- [Complete](./examples/complete) - All features
{{ .Footer }}
output:
file: README.md
mode: inject
template: |-
<!-- BEGIN_TF_DOCS -->
{{ .Content }}
<!-- END_TF_DOCS -->
sort:
enabled: true
by: name
settings:
anchor: true
color: true
default: true
description: true
escape: true
hide-empty: false
html: true
indent: 2
lockfile: true
read-comments: true
required: true
sensitive: true
type: true
TFLint Configuration
.tflint.hcl
plugin "aws" {
enabled = true
version = "0.28.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}
config {
module = true
force = false
}
rule "terraform_naming_convention" {
enabled = true
}
rule "terraform_documented_variables" {
enabled = true
}
rule "terraform_documented_outputs" {
enabled = true
}
rule "terraform_unused_declarations" {
enabled = true
}
rule "terraform_deprecated_interpolation" {
enabled = true
}
Additional Examples
examples/encrypted/main.tf
provider "aws" {
region = "us-east-1"
}
# KMS key for encryption
resource "aws_kms_key" "bucket" {
description = "KMS key for S3 bucket encryption"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Purpose = "S3 Encryption"
}
}
resource "aws_kms_alias" "bucket" {
name = "alias/s3-bucket-key"
target_key_id = aws_kms_key.bucket.key_id
}
module "encrypted_bucket" {
source = "../../"
bucket_name = "encrypted-data-${var.environment}"
environment = var.environment
versioning_enabled = true
block_public_access = true
kms_key_id = aws_kms_key.bucket.arn
tags = {
Project = "SecureApp"
Encryption = "KMS"
}
}
output "bucket_arn" {
value = module.encrypted_bucket.bucket_arn
}
output "kms_key_id" {
value = aws_kms_key.bucket.id
}
examples/complete/main.tf
provider "aws" {
region = "us-east-1"
}
# Logging bucket
resource "aws_s3_bucket" "logs" {
bucket = "app-logs-${var.environment}"
}
resource "aws_s3_bucket_acl" "logs" {
bucket = aws_s3_bucket.logs.id
acl = "log-delivery-write"
}
# Complete configuration
module "complete_bucket" {
source = "../../"
bucket_name = "complete-example-${var.environment}"
environment = var.environment
versioning_enabled = true
block_public_access = true
lifecycle_rules = [
{
id = "transition-to-ia"
enabled = true
transition_days = 30
storage_class = "STANDARD_IA"
expiration_days = 0
},
{
id = "transition-to-glacier"
enabled = true
transition_days = 90
storage_class = "GLACIER"
expiration_days = 0
},
{
id = "expire-old-data"
enabled = true
transition_days = 0
storage_class = "GLACIER"
expiration_days = 365
}
]
tags = {
Project = "CompleteExample"
CostCenter = "Engineering"
Compliance = "Required"
}
}
# Enable logging
resource "aws_s3_bucket_logging" "complete" {
bucket = module.complete_bucket.bucket_id
target_bucket = aws_s3_bucket.logs.id
target_prefix = "access-logs/"
}
output "bucket_arn" {
value = module.complete_bucket.bucket_arn
}
Explanation
Why These Improvements Matter
1. Automated Validation
Before:
- Manual testing
- Inconsistent formatting
- Missed errors
After:
on: [push, pull_request] # Automatic validation
- Every change validated
- Consistent standards
- Early error detection
2. Documentation Generation
Manual docs drift over time
Automated docs stay current:
- name: Generate Terraform docs
uses: terraform-docs/[email protected]
Always reflects actual code.
3. Pre-commit Hooks
Catch issues before commit:
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_docs
Ensures quality at source.
4. Security Scanning
Checkov finds security issues:
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
Prevents misconfigurations.
Module Maturity Levels
| Level | Features |
|---|---|
| Basic | Working code, manual testing |
| Good | Documentation, examples |
| Better | Validation, formatting |
| Best | CI/CD, testing, security scans |
Try to solve the challenge yourself first!
Click "Reveal Solution" when you're ready to see the answer.
Result
Setup and Validate
# Install pre-commit
pip install pre-commit
pre-commit install
# Install dependencies
brew install terraform-docs tflint
# Run all checks locally
pre-commit run --all-files
# Output:
# Terraform format...........Passed
# Terraform validate.........Passed
# Terraform docs.............Passed
# TFLint.....................Passed
# Checkov....................Passed
CI/CD Pipeline Results
✓ Terraform Validate (15s)
✓ TFLint (12s)
✓ Security Scan (23s)
✓ Documentation (8s)
✓ Validate Examples (45s)
All checks passed!
Generated Documentation
README.md automatically updated with:
- Current inputs/outputs
- Version requirements
- Usage examples
- Provider information
Validation
Quality Checklist
# 1. Pre-commit hooks installed
pre-commit run --all-files
# Should show all checks passing
# 2. CI/CD pipeline configured
git push origin feature-branch
# Check GitHub Actions for green checkmarks
# 3. Documentation current
terraform-docs markdown table . --output-file README.md
git diff README.md
# Should show no changes (already current)
# 4. Examples validate
for dir in examples/*/; do
(cd "$dir" && terraform init -backend=false && terraform validate)
done
# All examples should validate
# 5. Security scan passes
checkov -d . --framework terraform
# Should show no HIGH or CRITICAL issues
# 6. Formatting consistent
terraform fmt -check -recursive
# Should return 0 (already formatted)
Best Practices
✅ Do's
- Automate everything: CI/CD for all checks
- Generate documentation: Keep docs in sync
- Use pre-commit hooks: Catch issues early
- Security scan: Find vulnerabilities
- Multiple examples: Show various use cases
- Version semantically: Clear version progression
❌ Don'ts
- Don't skip testing: Quality suffers
- Don't write docs manually: They'll drift
- Don't ignore security: Scan regularly
- Don't forget examples: Users need them
- Don't skip formatting: Consistency matters
Links
Share Your Success
Enhanced your module? Share the improvements!
Tag @thedevopsdaily on X with:
- Before/after comparison
- CI/CD pipeline screenshot
- Documentation quality improvement
- Link to module repo
Use hashtags: #AdventOfDevOps #Terraform #IaC #Day7
Ready to complete this challenge?
Mark this challenge as complete once you've finished the task. We'll track your progress!
Completed this challenge? Share your success!
Tag @thedevopsdaily on X (Twitter) and share your learning journey with the community!
We earn commissions when you shop through the links below.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
Acronis
The most secure backup
Acronis: the most secure backup solution for your data
Pluralsight
Technology skills platform
Expert-led courses in software development, IT ops, data, and cybersecurity
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?