From 6edad4aaf21cbb5bb6d9eeb023b068d1e8c14eeb Mon Sep 17 00:00:00 2001 From: Patrick de Ruiter Date: Sat, 1 Nov 2025 10:19:33 +0100 Subject: [PATCH] Add comprehensive README and update module documentation --- .gitignore | 0 README.md | 323 +++++++++++++++++++++++++++++++++++++- examples/outputs.tf | 2 + examples/provider.tf | 3 + examples/s3backend.tf | 4 + examples/terraform.tfvars | 2 + examples/variables.tf | 0 iam.tf | 0 main.tf | 0 outputs.tf | 0 variables.tf | 0 versions.tf | 0 12 files changed, 330 insertions(+), 4 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 README.md create mode 100755 examples/outputs.tf create mode 100755 examples/provider.tf create mode 100755 examples/s3backend.tf create mode 100755 examples/terraform.tfvars create mode 100755 examples/variables.tf mode change 100644 => 100755 iam.tf mode change 100644 => 100755 main.tf mode change 100644 => 100755 outputs.tf mode change 100644 => 100755 variables.tf mode change 100644 => 100755 versions.tf diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 97bc2dc..f2c1bbc --- a/README.md +++ b/README.md @@ -1,6 +1,321 @@ -# S3 Backend Module -This module will deply an s3 remote backend for Terraform +# Terraform AWS S3 Backend Module -Locking is handled by a serverless provisioned DynamoDB Table -All contents of the s3 bucket is encrypted via a KMS key and privileges are set in such a way that it only has the least ammount of privileges. +## Overview +This Terraform module creates a secure S3 backend for Terraform state management with DynamoDB state locking. It provides encrypted state storage, version control, and concurrent access protection using AWS best practices. + +## Features + +- S3 bucket with versioning enabled +- KMS encryption for state files +- DynamoDB table for state locking +- IAM role for secure access +- Public access blocking +- Resource grouping for easy management +- Configurable force destroy option +- Least privilege IAM policies + +## Resources Created + +### Storage +- S3 Bucket with: + - Versioning enabled + - KMS encryption + - Public access blocked + - Unique naming with random suffix + +### State Locking +- DynamoDB Table with: + - Pay-per-request billing + - LockID hash key + +### Security +- KMS Key for encryption +- IAM Role for state access +- IAM Policy with minimal permissions +- Resource Group for organization + +## Usage + +### Basic Example + +```hcl +module "terraform_backend" { + source = "git@github.com:webuildyourcloud/terraform-aws-s3-backend.git" + + namespace = "myorg-prod" + force_destroy_state = false +} +``` + +### With Custom IAM Principals + +```hcl +module "terraform_backend" { + source = "git@github.com:webuildyourcloud/terraform-aws-s3-backend.git" + + namespace = "myorg-dev" + force_destroy_state = true + + principle_arns = [ + "arn:aws:iam::123456789012:user/terraform", + "arn:aws:iam::123456789012:role/TerraformExecutionRole" + ] +} +``` + +### For CI/CD Pipeline + +```hcl +module "terraform_backend" { + source = "git@github.com:webuildyourcloud/terraform-aws-s3-backend.git" + + namespace = "cicd-terraform" + force_destroy_state = false + + principle_arns = [ + "arn:aws:iam::123456789012:role/GitHubActionsRole", + "arn:aws:iam::123456789012:role/JenkinsRole" + ] +} + +# Output the backend configuration +output "terraform_backend_config" { + value = module.terraform_backend.config +} +``` + +## Variables + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|----------| +| namespace | Project namespace for unique resource naming | `string` | `"s3backend"` | no | +| principle_arns | List of principal ARNs allowed to assume the IAM role | `list(string)` | `null` | no | +| force_destroy_state | Force destroy the S3 bucket containing state files | `bool` | `true` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| config | Complete backend configuration object containing bucket, region, role_arn, and dynamodb_table | + +The `config` output provides: +```hcl +{ + bucket = "namespace-randomstring-state-bucket" + region = "current-region" + role_arn = "arn:aws:iam::account-id:role/namespace-randomstring-tf-assume-role" + dynamodb_table = "namespace-randomstring-state-lock" +} +``` + +## Requirements + +| Name | Version | +|------|---------| +| terraform | >= 0.15 | +| aws | ~> 3.28 | +| random | ~> 3.0 | + +## Configuring Terraform Backend + +After creating the backend, configure Terraform to use it: + +### Step 1: Create the Backend Resources + +```bash +terraform init +terraform apply +``` + +### Step 2: Note the Outputs + +```bash +terraform output config +``` + +### Step 3: Configure Your Terraform Backend + +Create or update `backend.tf`: + +```hcl +terraform { + backend "s3" { + bucket = "myorg-prod-xyz123-state-bucket" + key = "path/to/statefile.tfstate" + region = "us-east-1" + dynamodb_table = "myorg-prod-xyz123-state-lock" + encrypt = true + role_arn = "arn:aws:iam::123456789012:role/myorg-prod-xyz123-tf-assume-role" + } +} +``` + +### Step 4: Migrate Existing State (if applicable) + +```bash +terraform init -migrate-state +``` + +## IAM Permissions + +The module creates an IAM role with the following permissions: + +### S3 Permissions +- `s3:ListBucket` - List bucket contents +- `s3:GetObject` - Read state files +- `s3:PutObject` - Write state files +- `s3:DeleteObject` - Delete old state versions + +### DynamoDB Permissions +- `dynamodb:GetItem` - Read lock status +- `dynamodb:PutItem` - Acquire lock +- `dynamodb:DeleteItem` - Release lock + +## Security Features + +### Encryption at Rest +All state files are encrypted using AWS KMS with a dedicated encryption key. + +### Encryption in Transit +All S3 API calls use HTTPS (TLS/SSL). + +### Public Access Blocked +The module explicitly blocks: +- Public ACLs +- Public bucket policies +- Public object access +- Public bucket access + +### Versioning +S3 bucket versioning is enabled to: +- Protect against accidental deletion +- Allow state recovery +- Maintain state history + +### State Locking +DynamoDB table prevents: +- Concurrent state modifications +- State corruption +- Race conditions + +## Important Notes + +1. **Unique Naming**: The module generates unique bucket names using random strings +2. **Bootstrapping**: This module must be deployed without a backend initially +3. **State Migration**: After creation, migrate your state to the new backend +4. **Force Destroy**: Set `force_destroy_state = false` for production +5. **IAM Principals**: If not specified, defaults to the current caller's ARN +6. **KMS Costs**: KMS key incurs monthly charges +7. **Region Locked**: Backend resources are created in the current region + +## Best Practices + +1. **Separate Backends**: Use different backends for different environments +2. **Least Privilege**: Only grant access to necessary IAM principals +3. **State File Paths**: Use descriptive key paths (e.g., `env/prod/vpc/terraform.tfstate`) +4. **Backup**: Enable S3 Cross-Region Replication for critical state files +5. **Monitoring**: Set up CloudWatch alarms for bucket access +6. **Lifecycle Policies**: Configure S3 lifecycle policies for old versions +7. **Resource Groups**: Use the created resource group for cost tracking +8. **Documentation**: Document backend configuration for team members + +## Example: Complete Setup + +```hcl +# Step 1: Create backend infrastructure +module "backend" { + source = "git@github.com:webuildyourcloud/terraform-aws-s3-backend.git" + + namespace = "myapp-prod" + force_destroy_state = false + + principle_arns = [ + "arn:aws:iam::123456789012:role/TerraformRole" + ] +} + +# Step 2: Output configuration for easy reference +output "backend_bucket" { + value = module.backend.config.bucket + description = "S3 bucket name for Terraform state" +} + +output "backend_dynamodb_table" { + value = module.backend.config.dynamodb_table + description = "DynamoDB table name for state locking" +} + +output "backend_role_arn" { + value = module.backend.config.role_arn + description = "IAM role ARN for backend access" +} + +output "backend_region" { + value = module.backend.config.region + description = "AWS region for backend resources" +} +``` + +## Troubleshooting + +### Cannot access state file +- Verify IAM role has correct permissions +- Check if `role_arn` is correctly specified in backend configuration +- Ensure principal is allowed in `principle_arns` + +### State locking errors +- Verify DynamoDB table exists +- Check for orphaned locks in DynamoDB +- Ensure sufficient DynamoDB capacity (should not be an issue with PAY_PER_REQUEST) + +### Backend initialization fails +- Verify bucket and table names are correct +- Check AWS credentials have appropriate permissions +- Ensure region matches where resources were created + +### Bucket name conflicts +- The module uses random suffixes to prevent conflicts +- If conflicts occur, destroy and recreate with a new namespace + +## State File Organization + +Recommended key structure: + +``` +///terraform.tfstate + +Examples: +prod/networking/vpc/terraform.tfstate +prod/compute/eks/terraform.tfstate +prod/data/rds/terraform.tfstate +staging/networking/vpc/terraform.tfstate +dev/compute/eks/terraform.tfstate +``` + +## Migration Guide + +### From Local State + +```bash +# 1. Create backend +terraform apply -target=module.backend + +# 2. Add backend configuration to your code +# 3. Initialize with migration +terraform init -migrate-state + +# 4. Verify +terraform state list +``` + +### From Another S3 Backend + +Update backend configuration and run: +```bash +terraform init -migrate-state -reconfigure +``` + +## License + +This module is provided as-is for use within your organization. diff --git a/examples/outputs.tf b/examples/outputs.tf new file mode 100755 index 0000000..cc66674 --- /dev/null +++ b/examples/outputs.tf @@ -0,0 +1,2 @@ +output "s3backend" { + value = module.s3backend.config diff --git a/examples/provider.tf b/examples/provider.tf new file mode 100755 index 0000000..dc58d9a --- /dev/null +++ b/examples/provider.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = var.region +} diff --git a/examples/s3backend.tf b/examples/s3backend.tf new file mode 100755 index 0000000..d7457cf --- /dev/null +++ b/examples/s3backend.tf @@ -0,0 +1,4 @@ +modules "s3backend" { + source = "git::git@github.com:webuildyourcloud/terraform-aws-s3-backend.git?ref=tags/0.0.1" + namespace = var.namespace +} diff --git a/examples/terraform.tfvars b/examples/terraform.tfvars new file mode 100755 index 0000000..e8b9504 --- /dev/null +++ b/examples/terraform.tfvars @@ -0,0 +1,2 @@ +region = "eu-west-1" +namspace = "example" diff --git a/examples/variables.tf b/examples/variables.tf new file mode 100755 index 0000000..e69de29 diff --git a/iam.tf b/iam.tf old mode 100644 new mode 100755 diff --git a/main.tf b/main.tf old mode 100644 new mode 100755 diff --git a/outputs.tf b/outputs.tf old mode 100644 new mode 100755 diff --git a/variables.tf b/variables.tf old mode 100644 new mode 100755 diff --git a/versions.tf b/versions.tf old mode 100644 new mode 100755