Initial commit: AWS CloudFront with OpenTofu infrastructure
- Complete CloudFront distribution setup with origin.servidor.it.com - WAF v2 integration for security protection - S3 backend for Terraform state management - CloudFront logging to S3 - HTTP-only origin protocol configuration (resolves 504 Gateway Timeout) - Comprehensive documentation with deployment guide 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Terraform/OpenTofu files
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.*
|
||||||
|
*.tfvars
|
||||||
|
!terraform.tfvars.example
|
||||||
|
.terraform/
|
||||||
|
.terraform.lock.hcl
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# AWS credentials
|
||||||
|
.aws/
|
||||||
|
credentials
|
||||||
|
config
|
||||||
|
|
||||||
|
# S3 backend files (already stored remotely)
|
||||||
|
terraform.tfstate
|
||||||
|
terraform.tfstate.backup
|
||||||
241
DEPLOYMENT_GUIDE.md
Normal file
241
DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# AWS CloudFront 배포 가이드
|
||||||
|
|
||||||
|
## 📋 프로젝트 개요
|
||||||
|
AWS CloudFront CDN을 OpenTofu(Terraform fork)로 구성하여 `origin.servidor.it.com`을 origin으로 사용하는 인프라스트럭처 프로젝트입니다.
|
||||||
|
|
||||||
|
## 🏗️ 현재 인프라 상태
|
||||||
|
|
||||||
|
### CloudFront Distribution
|
||||||
|
- **Distribution ID**: E1XR8P4ENGP8RU
|
||||||
|
- **CloudFront URL**: https://dspki4yrh5oy1.cloudfront.net
|
||||||
|
- **Origin**: origin.servidor.it.com (HTTP-only)
|
||||||
|
- **Status**: ✅ Deployed and Working
|
||||||
|
|
||||||
|
### 보안 설정
|
||||||
|
- **WAF**: AWS WAF v2 활성화
|
||||||
|
- WAF ID: d21d84c1-edb9-40af-9cdd-27f42f09c499
|
||||||
|
- Rate Limiting: 10,000 requests/5min per IP
|
||||||
|
- AWS Managed Rules - Common Rule Set (SQL Injection, XSS 방어)
|
||||||
|
- AWS Managed Rules - Known Bad Inputs (악성 패턴 차단)
|
||||||
|
- **Viewer Protocol**: HTTPS (redirect-to-https)
|
||||||
|
- **Origin Protocol**: HTTP-only (중요: HTTPS 사용 시 504 에러)
|
||||||
|
|
||||||
|
### 로깅 및 상태 관리
|
||||||
|
- **CloudFront Logs**: `s3://aws-cf-cloudfront-logs-535294143817/cloudfront-logs/`
|
||||||
|
- **Terraform State**: `s3://aws-cf-terraform-state-535294143817/aws-cf/terraform.tfstate`
|
||||||
|
- **State Locking**: 비활성화 (DynamoDB 권한 없음)
|
||||||
|
|
||||||
|
## 🚀 배포 방법
|
||||||
|
|
||||||
|
### 사전 요구사항
|
||||||
|
- OpenTofu 또는 Terraform 설치
|
||||||
|
- AWS CLI 구성
|
||||||
|
- 필요한 IAM 권한:
|
||||||
|
- CloudFrontFullAccess
|
||||||
|
- S3FullAccess
|
||||||
|
- Route53FullAccess
|
||||||
|
- AWSCertificateManagerFullAccess
|
||||||
|
- AWSWAFFullAccess (WAFv2 포함)
|
||||||
|
- AWSCloudFormationFullAccess
|
||||||
|
|
||||||
|
### 초기 설정
|
||||||
|
```bash
|
||||||
|
# S3 Backend 설정 (이미 완료됨)
|
||||||
|
./setup-backend.sh
|
||||||
|
|
||||||
|
# State 마이그레이션 (이미 완료됨)
|
||||||
|
echo "yes" | tofu init -migrate-state
|
||||||
|
```
|
||||||
|
|
||||||
|
### 배포 명령어
|
||||||
|
```bash
|
||||||
|
# 초기화
|
||||||
|
tofu init
|
||||||
|
|
||||||
|
# 계획 확인
|
||||||
|
tofu plan
|
||||||
|
|
||||||
|
# 배포 실행
|
||||||
|
tofu apply -auto-approve
|
||||||
|
|
||||||
|
# 상태 확인
|
||||||
|
tofu state list
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 프로젝트 구조
|
||||||
|
```
|
||||||
|
aws-cf/
|
||||||
|
├── main.tf # CloudFront 메인 구성
|
||||||
|
├── variables.tf # 변수 정의
|
||||||
|
├── terraform.tfvars # 변수 값 설정
|
||||||
|
├── outputs.tf # 출력 정의
|
||||||
|
├── versions.tf # Provider 버전 관리
|
||||||
|
├── backend.tf # S3 Backend 설정
|
||||||
|
├── security.tf # WAF 및 보안 그룹
|
||||||
|
├── acm.tf # ACM 인증서 (비활성화)
|
||||||
|
├── setup-backend.sh # S3 Backend 설정 스크립트
|
||||||
|
├── README.md # 기존 문서
|
||||||
|
└── DEPLOYMENT_GUIDE.md # 이 문서
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ 중요 설정 내용
|
||||||
|
|
||||||
|
### terraform.tfvars 핵심 설정
|
||||||
|
```hcl
|
||||||
|
# Origin 설정 (매우 중요)
|
||||||
|
origin_domain = "origin.servidor.it.com"
|
||||||
|
origin_protocol_policy = "http-only" # HTTPS 사용 시 504 에러!
|
||||||
|
|
||||||
|
# CloudFront 설정
|
||||||
|
viewer_protocol_policy = "redirect-to-https"
|
||||||
|
price_class = "PriceClass_100"
|
||||||
|
|
||||||
|
# 보안 설정
|
||||||
|
enable_waf = true
|
||||||
|
|
||||||
|
# 로깅 설정
|
||||||
|
enable_cloudfront_logging = true
|
||||||
|
cloudfront_logs_bucket = "aws-cf-cloudfront-logs-535294143817"
|
||||||
|
|
||||||
|
# 비활성화된 기능 (권한/제한 사항)
|
||||||
|
create_route53_records = false # CAA 제한으로 비활성화
|
||||||
|
create_acm_certificate = false # CloudFront 기본 인증서 사용
|
||||||
|
enable_cloudformation_stack = false # CloudFormation 권한 없음
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 문제 해결 히스토리
|
||||||
|
|
||||||
|
### 1. 504 Gateway Timeout 해결 ✅
|
||||||
|
**문제**: CloudFront가 origin 서버에 연결할 수 없음
|
||||||
|
**원인**: origin.servidor.it.com이 HTTP(80)만 지원하는데 HTTPS(443)로 연결 시도
|
||||||
|
**해결**:
|
||||||
|
```hcl
|
||||||
|
origin_protocol_policy = "http-only"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. ACM 인증서 CAA 제한 ✅
|
||||||
|
**문제**: *.servidor.it.com 도메인에 대한 ACM 인증서 발급 실패
|
||||||
|
**원인**: CAA DNS 레코드가 AWS 인증서 발급을 제한
|
||||||
|
**해결**: CloudFront 기본 인증서 사용
|
||||||
|
```hcl
|
||||||
|
create_acm_certificate = false
|
||||||
|
viewer_certificate {
|
||||||
|
cloudfront_default_certificate = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. DynamoDB State Locking 권한 ✅
|
||||||
|
**문제**: Terraform state locking을 위한 DynamoDB 테이블 생성/접근 불가
|
||||||
|
**해결**: S3만 사용 (1인 개발 시 충분)
|
||||||
|
```hcl
|
||||||
|
backend "s3" {
|
||||||
|
bucket = "aws-cf-terraform-state-535294143817"
|
||||||
|
key = "aws-cf/terraform.tfstate"
|
||||||
|
# dynamodb_table = "terraform-state-lock" # 비활성화
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 중복 CloudFront Distribution 정리 ✅
|
||||||
|
**문제**: 여러 배포 시도로 3개의 distribution 생성
|
||||||
|
**해결**:
|
||||||
|
- E18GW141CX7I8C - 비활성화 중 (삭제 예정)
|
||||||
|
- E32FD742KMW2YY - 비활성화 중 (삭제 예정)
|
||||||
|
- E1XR8P4ENGP8RU - 현재 사용 중 ✅
|
||||||
|
|
||||||
|
### 5. WAF 권한 문제 ✅
|
||||||
|
**초기 문제**: WAFv2 권한 없다고 오류 발생
|
||||||
|
**해결**: AWSWAFFullAccess 정책에 wafv2:* 권한 포함되어 있음 확인
|
||||||
|
|
||||||
|
## 📊 모니터링 및 관리
|
||||||
|
|
||||||
|
### 상태 확인 명령어
|
||||||
|
```bash
|
||||||
|
# CloudFront 상태
|
||||||
|
aws cloudfront get-distribution --id E1XR8P4ENGP8RU \
|
||||||
|
--query 'Distribution.Status' --output text
|
||||||
|
|
||||||
|
# WAF 연결 확인
|
||||||
|
aws cloudfront get-distribution --id E1XR8P4ENGP8RU \
|
||||||
|
--query 'Distribution.DistributionConfig.WebACLId' --output text
|
||||||
|
|
||||||
|
# 로그 확인
|
||||||
|
aws s3 ls s3://aws-cf-cloudfront-logs-535294143817/cloudfront-logs/ --recursive
|
||||||
|
|
||||||
|
# State 파일 확인
|
||||||
|
aws s3 ls s3://aws-cf-terraform-state-535294143817/aws-cf/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 성능 테스트
|
||||||
|
```bash
|
||||||
|
# CloudFront 응답 확인
|
||||||
|
curl -I https://dspki4yrh5oy1.cloudfront.net
|
||||||
|
|
||||||
|
# 출력 예시:
|
||||||
|
# HTTP/2 200
|
||||||
|
# content-type: text/html
|
||||||
|
# x-cache: Miss from cloudfront (첫 요청) / Hit from cloudfront (캐시된 요청)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 비활성화된 Distribution 삭제 (완전 비활성화 후)
|
||||||
|
```bash
|
||||||
|
# 상태 확인
|
||||||
|
aws cloudfront list-distributions \
|
||||||
|
--query 'DistributionList.Items[*].[Id,Status,DistributionConfig.Enabled]' \
|
||||||
|
--output table
|
||||||
|
|
||||||
|
# 삭제 (Deployed 상태에서 Enabled=false 일 때)
|
||||||
|
aws cloudfront get-distribution-config --id E18GW141CX7I8C > dist-config.json
|
||||||
|
ETAG=$(jq -r '.ETag' dist-config.json)
|
||||||
|
aws cloudfront delete-distribution --id E18GW141CX7I8C --if-match $ETAG
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔒 보안 고려사항
|
||||||
|
|
||||||
|
1. **WAF 설정**
|
||||||
|
- 현재 기본 AWS Managed Rules 사용
|
||||||
|
- 필요시 커스텀 룰 추가 가능
|
||||||
|
- Rate limiting 조정 가능 (security.tf)
|
||||||
|
|
||||||
|
2. **Origin 보안**
|
||||||
|
- ⚠️ Origin이 HTTP만 지원하므로 CloudFront-Origin 간 트래픽 암호화 안됨
|
||||||
|
- 권장: Origin 서버에 HTTPS 지원 추가
|
||||||
|
|
||||||
|
3. **로그 관리**
|
||||||
|
- CloudFront 로그 90일 자동 삭제 설정
|
||||||
|
- 장기 보관 필요시 S3 lifecycle 수정
|
||||||
|
|
||||||
|
## 📝 향후 개선 사항
|
||||||
|
|
||||||
|
### 권한 획득 시 활성화 가능한 기능
|
||||||
|
1. **DynamoDB State Locking**
|
||||||
|
- 팀 협업 시 필수
|
||||||
|
- backend.tf에서 주석 해제
|
||||||
|
|
||||||
|
2. **Custom Domain with ACM**
|
||||||
|
- CAA 레코드 수정 후 가능
|
||||||
|
- terraform.tfvars에서 `create_acm_certificate = true`
|
||||||
|
|
||||||
|
3. **CloudFormation Stack**
|
||||||
|
- VPC 및 네트워킹 리소스 관리
|
||||||
|
- terraform.tfvars에서 `enable_cloudformation_stack = true`
|
||||||
|
|
||||||
|
### 성능 최적화
|
||||||
|
1. Cache Policy 최적화
|
||||||
|
2. Origin Request Policy 조정
|
||||||
|
3. CloudFront Edge Location 선택 (Price Class)
|
||||||
|
|
||||||
|
## 🏷️ 태그 및 메타데이터
|
||||||
|
- **Project**: aws-cf
|
||||||
|
- **Environment**: dev
|
||||||
|
- **ManagedBy**: OpenTofu
|
||||||
|
- **Owner**: kaffa
|
||||||
|
- **Created**: 2025-09-08
|
||||||
|
- **LastUpdated**: 2025-09-09
|
||||||
|
|
||||||
|
## 📚 참고 자료
|
||||||
|
- [OpenTofu Documentation](https://opentofu.org/docs/)
|
||||||
|
- [AWS CloudFront Best Practices](https://docs.aws.amazon.com/cloudfront/latest/developerguide/best-practices.html)
|
||||||
|
- [AWS WAF Documentation](https://docs.aws.amazon.com/waf/latest/developerguide/)
|
||||||
|
|
||||||
|
---
|
||||||
|
*이 문서는 실제 배포 경험을 바탕으로 작성되었습니다.*
|
||||||
151
README.md
Normal file
151
README.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# AWS CloudFront with OpenTofu
|
||||||
|
|
||||||
|
이 프로젝트는 OpenTofu를 사용하여 AWS CloudFront 배포와 CloudFormation 스택을 관리합니다.
|
||||||
|
|
||||||
|
## 구조
|
||||||
|
|
||||||
|
- `versions.tf` - Provider 및 Terraform 버전 설정
|
||||||
|
- `variables.tf` - 입력 변수 정의
|
||||||
|
- `main.tf` - CloudFront 배포 및 CloudFormation 스택
|
||||||
|
- `security.tf` - 보안 그룹 및 WAF 설정
|
||||||
|
- `outputs.tf` - 출력 변수
|
||||||
|
- `terraform.tfvars.example` - 변수 설정 예시
|
||||||
|
|
||||||
|
## 주요 기능
|
||||||
|
|
||||||
|
### CloudFront 배포
|
||||||
|
- `origin.servidor.it.com`을 원본 서버로 사용
|
||||||
|
- HTTPS 리디렉션 및 압축 지원
|
||||||
|
- 사용자 정의 도메인(CNAME) 지원
|
||||||
|
- 캐시 정책 및 원본 요청 정책 설정
|
||||||
|
- 커스텀 오류 페이지 설정
|
||||||
|
|
||||||
|
### CloudFormation 스택
|
||||||
|
- VPC 및 네트워킹 리소스 생성
|
||||||
|
- 퍼블릭 서브넷 및 인터넷 게이트웨이
|
||||||
|
- 스택 출력을 통한 리소스 ID 공유
|
||||||
|
|
||||||
|
### 보안 기능
|
||||||
|
- ALB 및 웹 서버용 보안 그룹 (선택사항)
|
||||||
|
- AWS WAF v2 웹 ACL (선택사항)
|
||||||
|
- 레이트 제한 및 관리 규칙 세트
|
||||||
|
|
||||||
|
## 사용 방법
|
||||||
|
|
||||||
|
### 1. 환경 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# OpenTofu 설치 확인
|
||||||
|
tofu version
|
||||||
|
|
||||||
|
# AWS 자격 증명 설정
|
||||||
|
export AWS_ACCESS_KEY_ID="your-access-key"
|
||||||
|
export AWS_SECRET_ACCESS_KEY="your-secret-key"
|
||||||
|
export AWS_DEFAULT_REGION="us-east-1"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 변수 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 설정 파일 복사
|
||||||
|
cp terraform.tfvars.example terraform.tfvars
|
||||||
|
|
||||||
|
# 필요한 값들 수정
|
||||||
|
vim terraform.tfvars
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 배포
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 초기화
|
||||||
|
tofu init
|
||||||
|
|
||||||
|
# 계획 확인
|
||||||
|
tofu plan
|
||||||
|
|
||||||
|
# 배포 실행
|
||||||
|
tofu apply
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 확인
|
||||||
|
|
||||||
|
배포 후 출력된 CloudFront URL을 통해 서비스에 접근할 수 있습니다:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CloudFront 배포 상태 확인
|
||||||
|
aws cloudfront get-distribution --id <distribution-id>
|
||||||
|
|
||||||
|
# CloudFormation 스택 상태 확인
|
||||||
|
aws cloudformation describe-stacks --stack-name <stack-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 중요 설정
|
||||||
|
|
||||||
|
### SSL 인증서
|
||||||
|
|
||||||
|
이 설정은 **ACM(AWS Certificate Manager) 인증서**를 사용합니다:
|
||||||
|
|
||||||
|
- `servidor.it.com`과 `*.servidor.it.com` 도메인 지원
|
||||||
|
- 자동으로 us-east-1 리전에 ACM 인증서 생성
|
||||||
|
- DNS 검증을 통한 자동 인증서 발급
|
||||||
|
- Route53을 통한 자동 DNS 레코드 관리
|
||||||
|
- CloudFront와 완전 통합된 HTTPS 설정
|
||||||
|
|
||||||
|
### 캐시 정책
|
||||||
|
|
||||||
|
기본 제공되는 AWS 관리 정책:
|
||||||
|
|
||||||
|
- `4135ea2d-6df8-44a3-9df3-4b5a84be39ad` - CachingDisabled
|
||||||
|
- `725fe1d6-8a84-4f3e-8ab1-bc2d5bb10e12` - CachingOptimized
|
||||||
|
- `df3c6b6c-4e41-4d9c-a8c7-6b8b2b8e6b8b` - CachingOptimizedForUncompressedObjects
|
||||||
|
|
||||||
|
### 보안 고려사항
|
||||||
|
|
||||||
|
프로덕션 환경에서는:
|
||||||
|
|
||||||
|
- `ssh_allowed_cidrs`를 특정 IP로 제한
|
||||||
|
- WAF 활성화 (`enable_waf = true`)
|
||||||
|
- 적절한 캐시 정책 선택
|
||||||
|
- CloudTrail 및 CloudWatch 로깅 활성화
|
||||||
|
|
||||||
|
### 도메인 설정
|
||||||
|
|
||||||
|
**전제 조건**:
|
||||||
|
- `servidor.it.com` 도메인의 Route53 호스팅 영역이 존재해야 함
|
||||||
|
- 도메인의 네임서버가 Route53으로 설정되어 있어야 함
|
||||||
|
|
||||||
|
**자동 설정**:
|
||||||
|
- ACM 인증서 자동 생성 및 검증
|
||||||
|
- Route53 A 레코드 자동 생성 (`servidor.it.com` → CloudFront)
|
||||||
|
- Route53 A 레코드 자동 생성 (`www.servidor.it.com` → CloudFront)
|
||||||
|
|
||||||
|
## 출력 정보
|
||||||
|
|
||||||
|
배포 완료 후 다음 정보들이 출력됩니다:
|
||||||
|
|
||||||
|
- CloudFront 배포 ID 및 도메인
|
||||||
|
- CloudFormation 스택 정보
|
||||||
|
- 보안 그룹 ID (활성화된 경우)
|
||||||
|
- WAF Web ACL ARN (활성화된 경우)
|
||||||
|
- VPC 및 서브넷 정보
|
||||||
|
|
||||||
|
## 정리
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 리소스 삭제
|
||||||
|
tofu destroy
|
||||||
|
```
|
||||||
|
|
||||||
|
## 문제 해결
|
||||||
|
|
||||||
|
### 일반적인 오류
|
||||||
|
|
||||||
|
1. **인증서 오류**: CloudFront용 인증서는 us-east-1에서만 생성 가능
|
||||||
|
2. **도메인 검증**: CNAME 설정 전에 도메인 소유권 확인 필요
|
||||||
|
3. **원본 서버**: `origin.servidor.it.com`이 HTTPS를 지원하는지 확인
|
||||||
|
|
||||||
|
### 로그 확인
|
||||||
|
|
||||||
|
- CloudFront 액세스 로그 활성화
|
||||||
|
- CloudWatch 메트릭 모니터링
|
||||||
|
- WAF 로그 분석 (활성화된 경우)
|
||||||
82
acm.tf
Normal file
82
acm.tf
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Data source to get Route53 hosted zone
|
||||||
|
data "aws_route53_zone" "main" {
|
||||||
|
count = var.create_route53_records ? 1 : 0
|
||||||
|
name = var.domain_name
|
||||||
|
private_zone = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# ACM Certificate for CloudFront (must be in us-east-1) - conditional
|
||||||
|
resource "aws_acm_certificate" "main" {
|
||||||
|
count = var.create_acm_certificate ? 1 : 0
|
||||||
|
provider = aws.us_east_1
|
||||||
|
domain_name = var.domain_name
|
||||||
|
subject_alternative_names = ["*.${var.domain_name}"]
|
||||||
|
validation_method = var.certificate_domain_validation_options
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
create_before_destroy = true
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-${var.environment}-certificate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Route53 records for ACM certificate validation
|
||||||
|
resource "aws_route53_record" "cert_validation" {
|
||||||
|
provider = aws.us_east_1
|
||||||
|
for_each = var.create_route53_records && var.create_acm_certificate ? {
|
||||||
|
for dvo in aws_acm_certificate.main[0].domain_validation_options : dvo.domain_name => {
|
||||||
|
name = dvo.resource_record_name
|
||||||
|
record = dvo.resource_record_value
|
||||||
|
type = dvo.resource_record_type
|
||||||
|
}
|
||||||
|
} : {}
|
||||||
|
|
||||||
|
zone_id = data.aws_route53_zone.main[0].zone_id
|
||||||
|
name = each.value.name
|
||||||
|
type = each.value.type
|
||||||
|
records = [each.value.record]
|
||||||
|
ttl = 60
|
||||||
|
allow_overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ACM Certificate validation
|
||||||
|
resource "aws_acm_certificate_validation" "main" {
|
||||||
|
count = var.create_acm_certificate ? 1 : 0
|
||||||
|
provider = aws.us_east_1
|
||||||
|
certificate_arn = aws_acm_certificate.main[0].arn
|
||||||
|
validation_record_fqdns = var.create_route53_records ? [for record in aws_route53_record.cert_validation : record.fqdn] : null
|
||||||
|
|
||||||
|
timeouts {
|
||||||
|
create = "10m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Route53 A record for main domain (CloudFront alias)
|
||||||
|
resource "aws_route53_record" "main" {
|
||||||
|
count = var.create_route53_records ? 1 : 0
|
||||||
|
zone_id = data.aws_route53_zone.main[0].zone_id
|
||||||
|
name = var.domain_name
|
||||||
|
type = "A"
|
||||||
|
|
||||||
|
alias {
|
||||||
|
name = aws_cloudfront_distribution.main.domain_name
|
||||||
|
zone_id = aws_cloudfront_distribution.main.hosted_zone_id
|
||||||
|
evaluate_target_health = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Route53 A record for www subdomain (CloudFront alias)
|
||||||
|
resource "aws_route53_record" "www" {
|
||||||
|
count = var.create_route53_records ? 1 : 0
|
||||||
|
zone_id = data.aws_route53_zone.main[0].zone_id
|
||||||
|
name = "www.${var.domain_name}"
|
||||||
|
type = "A"
|
||||||
|
|
||||||
|
alias {
|
||||||
|
name = aws_cloudfront_distribution.main.domain_name
|
||||||
|
zone_id = aws_cloudfront_distribution.main.hosted_zone_id
|
||||||
|
evaluate_target_health = false
|
||||||
|
}
|
||||||
|
}
|
||||||
15
backend.tf
Normal file
15
backend.tf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# S3 Backend Configuration for Terraform State
|
||||||
|
# This file configures remote state storage in S3 with DynamoDB for state locking
|
||||||
|
|
||||||
|
terraform {
|
||||||
|
backend "s3" {
|
||||||
|
bucket = "aws-cf-terraform-state-535294143817"
|
||||||
|
key = "aws-cf/terraform.tfstate"
|
||||||
|
region = "us-east-1"
|
||||||
|
encrypt = true
|
||||||
|
# dynamodb_table = "terraform-state-lock" # Disabled due to permission issues
|
||||||
|
|
||||||
|
# Optional: Add versioning for state file history
|
||||||
|
# versioning = true
|
||||||
|
}
|
||||||
|
}
|
||||||
224
main.tf
Normal file
224
main.tf
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# CloudFront Origin Access Control
|
||||||
|
resource "aws_cloudfront_origin_access_control" "oac" {
|
||||||
|
name = "${var.project_name}-${var.environment}-oac"
|
||||||
|
description = "OAC for ${var.project_name}"
|
||||||
|
origin_access_control_origin_type = "s3"
|
||||||
|
signing_behavior = "always"
|
||||||
|
signing_protocol = "sigv4"
|
||||||
|
}
|
||||||
|
|
||||||
|
# CloudFront Distribution
|
||||||
|
resource "aws_cloudfront_distribution" "main" {
|
||||||
|
# Origin configuration for custom domain
|
||||||
|
origin {
|
||||||
|
domain_name = var.origin_domain
|
||||||
|
origin_id = "${var.project_name}-${var.environment}-origin"
|
||||||
|
|
||||||
|
custom_origin_config {
|
||||||
|
http_port = 80
|
||||||
|
https_port = 443
|
||||||
|
origin_protocol_policy = var.origin_protocol_policy
|
||||||
|
origin_ssl_protocols = ["TLSv1.2"]
|
||||||
|
origin_read_timeout = 30
|
||||||
|
origin_keepalive_timeout = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
# Custom headers (optional)
|
||||||
|
# custom_header {
|
||||||
|
# name = "User-Agent"
|
||||||
|
# value = "CloudFront-${var.project_name}"
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = true
|
||||||
|
is_ipv6_enabled = true
|
||||||
|
comment = "CloudFront distribution for ${var.project_name} - ${var.environment}"
|
||||||
|
default_root_object = "index.html"
|
||||||
|
|
||||||
|
# Aliases (custom domain names) - Disabled for default certificate
|
||||||
|
# aliases = var.cloudfront_aliases
|
||||||
|
|
||||||
|
# Default cache behavior
|
||||||
|
default_cache_behavior {
|
||||||
|
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
|
||||||
|
cached_methods = ["GET", "HEAD"]
|
||||||
|
target_origin_id = "${var.project_name}-${var.environment}-origin"
|
||||||
|
cache_policy_id = var.cache_policy_id
|
||||||
|
origin_request_policy_id = var.origin_request_policy_id
|
||||||
|
viewer_protocol_policy = var.viewer_protocol_policy
|
||||||
|
compress = true
|
||||||
|
|
||||||
|
# Forward headers, query strings, and cookies
|
||||||
|
# Use cache policies instead for better performance
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ordered cache behaviors (optional)
|
||||||
|
ordered_cache_behavior {
|
||||||
|
path_pattern = "/api/*"
|
||||||
|
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
|
||||||
|
cached_methods = ["GET", "HEAD"]
|
||||||
|
target_origin_id = "${var.project_name}-${var.environment}-origin"
|
||||||
|
cache_policy_id = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # CachingDisabled
|
||||||
|
viewer_protocol_policy = "https-only"
|
||||||
|
compress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Price class
|
||||||
|
price_class = var.price_class
|
||||||
|
|
||||||
|
# Geographic restrictions
|
||||||
|
restrictions {
|
||||||
|
geo_restriction {
|
||||||
|
restriction_type = "none"
|
||||||
|
# locations = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# SSL/TLS certificate - Use CloudFront default certificate (temporary)
|
||||||
|
viewer_certificate {
|
||||||
|
cloudfront_default_certificate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Custom error responses
|
||||||
|
custom_error_response {
|
||||||
|
error_code = 404
|
||||||
|
response_code = 200
|
||||||
|
response_page_path = "/index.html"
|
||||||
|
error_caching_min_ttl = 300
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_error_response {
|
||||||
|
error_code = 403
|
||||||
|
response_code = 200
|
||||||
|
response_page_path = "/index.html"
|
||||||
|
error_caching_min_ttl = 300
|
||||||
|
}
|
||||||
|
|
||||||
|
# Web Application Firewall (optional)
|
||||||
|
web_acl_id = var.enable_waf ? aws_wafv2_web_acl.cloudfront[0].arn : null
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
dynamic "logging_config" {
|
||||||
|
for_each = var.enable_cloudfront_logging ? [1] : []
|
||||||
|
content {
|
||||||
|
bucket = "${var.cloudfront_logs_bucket}.s3.amazonaws.com"
|
||||||
|
prefix = var.cloudfront_logs_prefix
|
||||||
|
include_cookies = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-${var.environment}-distribution"
|
||||||
|
Origin = var.origin_domain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# CloudFormation Stack (optional - for additional AWS resources)
|
||||||
|
resource "aws_cloudformation_stack" "network" {
|
||||||
|
count = var.enable_cloudformation_stack ? 1 : 0
|
||||||
|
name = "${var.project_name}-${var.environment}-network-stack"
|
||||||
|
|
||||||
|
parameters = {
|
||||||
|
VPCCidr = "10.0.0.0/16"
|
||||||
|
Environment = var.environment
|
||||||
|
ProjectName = var.project_name
|
||||||
|
}
|
||||||
|
|
||||||
|
template_body = jsonencode({
|
||||||
|
AWSTemplateFormatVersion = "2010-09-09"
|
||||||
|
Description = "Network resources for ${var.project_name}"
|
||||||
|
|
||||||
|
Parameters = {
|
||||||
|
VPCCidr = {
|
||||||
|
Type = "String"
|
||||||
|
Default = "10.0.0.0/16"
|
||||||
|
Description = "CIDR block for the VPC"
|
||||||
|
}
|
||||||
|
Environment = {
|
||||||
|
Type = "String"
|
||||||
|
Description = "Environment name"
|
||||||
|
}
|
||||||
|
ProjectName = {
|
||||||
|
Type = "String"
|
||||||
|
Description = "Project name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Resources = {
|
||||||
|
VPC = {
|
||||||
|
Type = "AWS::EC2::VPC"
|
||||||
|
Properties = {
|
||||||
|
CidrBlock = { Ref = "VPCCidr" }
|
||||||
|
EnableDnsHostnames = true
|
||||||
|
EnableDnsSupport = true
|
||||||
|
Tags = [
|
||||||
|
{
|
||||||
|
Key = "Name"
|
||||||
|
Value = { "Fn::Sub" = "$${ProjectName}-$${Environment}-vpc" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InternetGateway = {
|
||||||
|
Type = "AWS::EC2::InternetGateway"
|
||||||
|
Properties = {
|
||||||
|
Tags = [
|
||||||
|
{
|
||||||
|
Key = "Name"
|
||||||
|
Value = { "Fn::Sub" = "$${ProjectName}-$${Environment}-igw" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachGateway = {
|
||||||
|
Type = "AWS::EC2::VPCGatewayAttachment"
|
||||||
|
Properties = {
|
||||||
|
VpcId = { Ref = "VPC" }
|
||||||
|
InternetGatewayId = { Ref = "InternetGateway" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicSubnet = {
|
||||||
|
Type = "AWS::EC2::Subnet"
|
||||||
|
Properties = {
|
||||||
|
VpcId = { Ref = "VPC" }
|
||||||
|
CidrBlock = "10.0.1.0/24"
|
||||||
|
AvailabilityZone = { "Fn::Select" = [0, { "Fn::GetAZs" = "" }] }
|
||||||
|
MapPublicIpOnLaunch = true
|
||||||
|
Tags = [
|
||||||
|
{
|
||||||
|
Key = "Name"
|
||||||
|
Value = { "Fn::Sub" = "$${ProjectName}-$${Environment}-public-subnet" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Outputs = {
|
||||||
|
VPCId = {
|
||||||
|
Description = "VPC ID"
|
||||||
|
Value = { Ref = "VPC" }
|
||||||
|
Export = {
|
||||||
|
Name = { "Fn::Sub" = "$${ProjectName}-$${Environment}-VPC-ID" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PublicSubnetId = {
|
||||||
|
Description = "Public Subnet ID"
|
||||||
|
Value = { Ref = "PublicSubnet" }
|
||||||
|
Export = {
|
||||||
|
Name = { "Fn::Sub" = "$${ProjectName}-$${Environment}-PublicSubnet-ID" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
capabilities = ["CAPABILITY_IAM"]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-${var.environment}-network"
|
||||||
|
ManagedBy = "OpenTofu"
|
||||||
|
}
|
||||||
|
}
|
||||||
113
outputs.tf
Normal file
113
outputs.tf
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# CloudFront Distribution Outputs
|
||||||
|
output "cloudfront_distribution_id" {
|
||||||
|
description = "CloudFront distribution ID"
|
||||||
|
value = aws_cloudfront_distribution.main.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_distribution_arn" {
|
||||||
|
description = "CloudFront distribution ARN"
|
||||||
|
value = aws_cloudfront_distribution.main.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_domain_name" {
|
||||||
|
description = "CloudFront distribution domain name"
|
||||||
|
value = aws_cloudfront_distribution.main.domain_name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_hosted_zone_id" {
|
||||||
|
description = "CloudFront distribution hosted zone ID"
|
||||||
|
value = aws_cloudfront_distribution.main.hosted_zone_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_status" {
|
||||||
|
description = "CloudFront distribution status"
|
||||||
|
value = aws_cloudfront_distribution.main.status
|
||||||
|
}
|
||||||
|
|
||||||
|
# CloudFormation Stack Outputs (conditional)
|
||||||
|
output "cloudformation_stack_id" {
|
||||||
|
description = "CloudFormation stack ID"
|
||||||
|
value = var.enable_cloudformation_stack ? aws_cloudformation_stack.network[0].id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudformation_stack_name" {
|
||||||
|
description = "CloudFormation stack name"
|
||||||
|
value = var.enable_cloudformation_stack ? aws_cloudformation_stack.network[0].name : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vpc_id" {
|
||||||
|
description = "VPC ID from CloudFormation stack"
|
||||||
|
value = var.enable_cloudformation_stack ? data.aws_cloudformation_stack.network[0].outputs["VPCId"] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "public_subnet_id" {
|
||||||
|
description = "Public subnet ID from CloudFormation stack"
|
||||||
|
value = var.enable_cloudformation_stack ? data.aws_cloudformation_stack.network[0].outputs["PublicSubnetId"] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security Group Outputs (conditional)
|
||||||
|
output "alb_security_group_id" {
|
||||||
|
description = "ALB security group ID"
|
||||||
|
value = var.create_alb_security_group ? aws_security_group.alb[0].id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "web_security_group_id" {
|
||||||
|
description = "Web server security group ID"
|
||||||
|
value = var.create_web_security_group ? aws_security_group.web[0].id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
# WAF Outputs (conditional)
|
||||||
|
output "waf_web_acl_arn" {
|
||||||
|
description = "WAF Web ACL ARN"
|
||||||
|
value = var.enable_waf ? aws_wafv2_web_acl.cloudfront[0].arn : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "waf_web_acl_id" {
|
||||||
|
description = "WAF Web ACL ID"
|
||||||
|
value = var.enable_waf ? aws_wafv2_web_acl.cloudfront[0].id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Origin Information
|
||||||
|
output "origin_domain" {
|
||||||
|
description = "Origin domain name"
|
||||||
|
value = var.origin_domain
|
||||||
|
}
|
||||||
|
|
||||||
|
# ACM Certificate Outputs
|
||||||
|
output "acm_certificate_arn" {
|
||||||
|
description = "ACM certificate ARN"
|
||||||
|
value = var.create_acm_certificate ? aws_acm_certificate.main[0].arn : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "acm_certificate_domain_validation_options" {
|
||||||
|
description = "ACM certificate domain validation options"
|
||||||
|
value = var.create_acm_certificate ? aws_acm_certificate.main[0].domain_validation_options : null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Route53 Outputs
|
||||||
|
output "route53_zone_id" {
|
||||||
|
description = "Route53 hosted zone ID"
|
||||||
|
value = var.create_route53_records ? data.aws_route53_zone.main[0].zone_id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
# CloudFront URLs for testing
|
||||||
|
output "cloudfront_url" {
|
||||||
|
description = "CloudFront distribution URL"
|
||||||
|
value = "https://${aws_cloudfront_distribution.main.domain_name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "custom_domain_urls" {
|
||||||
|
description = "Custom domain URLs"
|
||||||
|
value = [for alias in var.cloudfront_aliases : "https://${alias}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "domain_validation_records" {
|
||||||
|
description = "DNS records needed for domain validation (if not using Route53)"
|
||||||
|
value = var.create_route53_records || !var.create_acm_certificate ? null : [
|
||||||
|
for dvo in aws_acm_certificate.main[0].domain_validation_options : {
|
||||||
|
name = dvo.resource_record_name
|
||||||
|
type = dvo.resource_record_type
|
||||||
|
value = dvo.resource_record_value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
181
security.tf
Normal file
181
security.tf
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# Security Group for ALB/ELB (if needed for origin server)
|
||||||
|
resource "aws_security_group" "alb" {
|
||||||
|
count = var.create_alb_security_group ? 1 : 0
|
||||||
|
name = "${var.project_name}-${var.environment}-alb-sg"
|
||||||
|
description = "Security group for ALB"
|
||||||
|
vpc_id = var.enable_cloudformation_stack ? data.aws_cloudformation_stack.network[0].outputs["VPCId"] : null
|
||||||
|
|
||||||
|
# HTTP access from anywhere
|
||||||
|
ingress {
|
||||||
|
description = "HTTP"
|
||||||
|
from_port = 80
|
||||||
|
to_port = 80
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS access from anywhere
|
||||||
|
ingress {
|
||||||
|
description = "HTTPS"
|
||||||
|
from_port = 443
|
||||||
|
to_port = 443
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# All outbound traffic
|
||||||
|
egress {
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-${var.environment}-alb-sg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security Group for EC2 instances (if needed)
|
||||||
|
resource "aws_security_group" "web" {
|
||||||
|
count = var.create_web_security_group ? 1 : 0
|
||||||
|
name = "${var.project_name}-${var.environment}-web-sg"
|
||||||
|
description = "Security group for web servers"
|
||||||
|
vpc_id = var.enable_cloudformation_stack ? data.aws_cloudformation_stack.network[0].outputs["VPCId"] : null
|
||||||
|
|
||||||
|
# HTTP access from ALB security group
|
||||||
|
ingress {
|
||||||
|
description = "HTTP from ALB"
|
||||||
|
from_port = 80
|
||||||
|
to_port = 80
|
||||||
|
protocol = "tcp"
|
||||||
|
security_groups = var.create_alb_security_group ? [aws_security_group.alb[0].id] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS access from ALB security group
|
||||||
|
ingress {
|
||||||
|
description = "HTTPS from ALB"
|
||||||
|
from_port = 443
|
||||||
|
to_port = 443
|
||||||
|
protocol = "tcp"
|
||||||
|
security_groups = var.create_alb_security_group ? [aws_security_group.alb[0].id] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
# SSH access (restrict as needed)
|
||||||
|
ingress {
|
||||||
|
description = "SSH"
|
||||||
|
from_port = 22
|
||||||
|
to_port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = var.ssh_allowed_cidrs
|
||||||
|
}
|
||||||
|
|
||||||
|
# All outbound traffic
|
||||||
|
egress {
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-${var.environment}-web-sg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# WAF Web ACL for CloudFront (optional)
|
||||||
|
resource "aws_wafv2_web_acl" "cloudfront" {
|
||||||
|
provider = aws.us_east_1 # CloudFront WAF must be in us-east-1
|
||||||
|
count = var.enable_waf ? 1 : 0
|
||||||
|
name = "${var.project_name}-${var.environment}-waf"
|
||||||
|
scope = "CLOUDFRONT"
|
||||||
|
|
||||||
|
default_action {
|
||||||
|
allow {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rate limiting rule
|
||||||
|
rule {
|
||||||
|
name = "RateLimitRule"
|
||||||
|
priority = 1
|
||||||
|
|
||||||
|
action {
|
||||||
|
block {}
|
||||||
|
}
|
||||||
|
|
||||||
|
statement {
|
||||||
|
rate_based_statement {
|
||||||
|
limit = 10000
|
||||||
|
aggregate_key_type = "IP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visibility_config {
|
||||||
|
cloudwatch_metrics_enabled = true
|
||||||
|
metric_name = "RateLimitRule"
|
||||||
|
sampled_requests_enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# AWS Managed Rules - Core Rule Set
|
||||||
|
rule {
|
||||||
|
name = "AWSManagedRulesCommonRuleSet"
|
||||||
|
priority = 2
|
||||||
|
|
||||||
|
override_action {
|
||||||
|
none {}
|
||||||
|
}
|
||||||
|
|
||||||
|
statement {
|
||||||
|
managed_rule_group_statement {
|
||||||
|
name = "AWSManagedRulesCommonRuleSet"
|
||||||
|
vendor_name = "AWS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visibility_config {
|
||||||
|
cloudwatch_metrics_enabled = true
|
||||||
|
metric_name = "CommonRuleSetMetric"
|
||||||
|
sampled_requests_enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# AWS Managed Rules - Known Bad Inputs
|
||||||
|
rule {
|
||||||
|
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
||||||
|
priority = 3
|
||||||
|
|
||||||
|
override_action {
|
||||||
|
none {}
|
||||||
|
}
|
||||||
|
|
||||||
|
statement {
|
||||||
|
managed_rule_group_statement {
|
||||||
|
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
||||||
|
vendor_name = "AWS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visibility_config {
|
||||||
|
cloudwatch_metrics_enabled = true
|
||||||
|
metric_name = "KnownBadInputsRuleSetMetric"
|
||||||
|
sampled_requests_enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-${var.environment}-waf"
|
||||||
|
}
|
||||||
|
|
||||||
|
visibility_config {
|
||||||
|
cloudwatch_metrics_enabled = true
|
||||||
|
metric_name = "${var.project_name}-${var.environment}-waf"
|
||||||
|
sampled_requests_enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Data source to get CloudFormation stack outputs (conditional)
|
||||||
|
data "aws_cloudformation_stack" "network" {
|
||||||
|
count = var.enable_cloudformation_stack ? 1 : 0
|
||||||
|
name = aws_cloudformation_stack.network[0].name
|
||||||
|
}
|
||||||
100
setup-backend.sh
Executable file
100
setup-backend.sh
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Setup script for S3 backend and CloudFront logging
|
||||||
|
# This creates the necessary S3 buckets and DynamoDB table
|
||||||
|
|
||||||
|
AWS_REGION="us-east-1"
|
||||||
|
AWS_ACCOUNT_ID="535294143817"
|
||||||
|
STATE_BUCKET="aws-cf-terraform-state-${AWS_ACCOUNT_ID}"
|
||||||
|
LOGS_BUCKET="aws-cf-cloudfront-logs-${AWS_ACCOUNT_ID}"
|
||||||
|
DYNAMODB_TABLE="terraform-state-lock"
|
||||||
|
|
||||||
|
echo "Setting up S3 backend and CloudFront logging infrastructure..."
|
||||||
|
|
||||||
|
# Create S3 bucket for Terraform state
|
||||||
|
echo "Creating S3 bucket for Terraform state: ${STATE_BUCKET}"
|
||||||
|
aws s3api create-bucket \
|
||||||
|
--bucket ${STATE_BUCKET} \
|
||||||
|
--region ${AWS_REGION} \
|
||||||
|
2>/dev/null || echo "State bucket already exists or error occurred"
|
||||||
|
|
||||||
|
# Enable versioning on state bucket
|
||||||
|
echo "Enabling versioning on state bucket..."
|
||||||
|
aws s3api put-bucket-versioning \
|
||||||
|
--bucket ${STATE_BUCKET} \
|
||||||
|
--versioning-configuration Status=Enabled
|
||||||
|
|
||||||
|
# Enable encryption on state bucket
|
||||||
|
echo "Enabling encryption on state bucket..."
|
||||||
|
aws s3api put-bucket-encryption \
|
||||||
|
--bucket ${STATE_BUCKET} \
|
||||||
|
--server-side-encryption-configuration '{
|
||||||
|
"Rules": [
|
||||||
|
{
|
||||||
|
"ApplyServerSideEncryptionByDefault": {
|
||||||
|
"SSEAlgorithm": "AES256"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Block public access on state bucket
|
||||||
|
echo "Blocking public access on state bucket..."
|
||||||
|
aws s3api put-public-access-block \
|
||||||
|
--bucket ${STATE_BUCKET} \
|
||||||
|
--public-access-block-configuration \
|
||||||
|
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
|
||||||
|
|
||||||
|
# Create S3 bucket for CloudFront logs
|
||||||
|
echo "Creating S3 bucket for CloudFront logs: ${LOGS_BUCKET}"
|
||||||
|
aws s3api create-bucket \
|
||||||
|
--bucket ${LOGS_BUCKET} \
|
||||||
|
--region ${AWS_REGION} \
|
||||||
|
2>/dev/null || echo "Logs bucket already exists or error occurred"
|
||||||
|
|
||||||
|
# Set bucket ACL for CloudFront logging
|
||||||
|
echo "Setting ACL for CloudFront logs bucket..."
|
||||||
|
aws s3api put-bucket-acl \
|
||||||
|
--bucket ${LOGS_BUCKET} \
|
||||||
|
--grant-write 'URI="http://acs.amazonaws.com/groups/s3/LogDelivery"' \
|
||||||
|
--grant-read-acp 'URI="http://acs.amazonaws.com/groups/s3/LogDelivery"'
|
||||||
|
|
||||||
|
# Add lifecycle policy to logs bucket (optional - delete old logs after 90 days)
|
||||||
|
echo "Adding lifecycle policy to logs bucket..."
|
||||||
|
aws s3api put-bucket-lifecycle-configuration \
|
||||||
|
--bucket ${LOGS_BUCKET} \
|
||||||
|
--lifecycle-configuration '{
|
||||||
|
"Rules": [
|
||||||
|
{
|
||||||
|
"Id": "DeleteOldLogs",
|
||||||
|
"Status": "Enabled",
|
||||||
|
"Expiration": {
|
||||||
|
"Days": 90
|
||||||
|
},
|
||||||
|
"NoncurrentVersionExpiration": {
|
||||||
|
"NoncurrentDays": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Create DynamoDB table for state locking
|
||||||
|
echo "Creating DynamoDB table for state locking: ${DYNAMODB_TABLE}"
|
||||||
|
aws dynamodb create-table \
|
||||||
|
--table-name ${DYNAMODB_TABLE} \
|
||||||
|
--attribute-definitions AttributeName=LockID,AttributeType=S \
|
||||||
|
--key-schema AttributeName=LockID,KeyType=HASH \
|
||||||
|
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
|
||||||
|
--region ${AWS_REGION} \
|
||||||
|
2>/dev/null || echo "DynamoDB table already exists or error occurred"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Backend setup complete!"
|
||||||
|
echo ""
|
||||||
|
echo "📦 S3 State Bucket: ${STATE_BUCKET}"
|
||||||
|
echo "📊 S3 Logs Bucket: ${LOGS_BUCKET}"
|
||||||
|
echo "🔒 DynamoDB Table: ${DYNAMODB_TABLE}"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Run: tofu init -migrate-state"
|
||||||
|
echo "2. Run: tofu apply to update CloudFront with logging"
|
||||||
39
terraform.tfvars.example
Normal file
39
terraform.tfvars.example
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# AWS Configuration
|
||||||
|
aws_region = "us-east-1"
|
||||||
|
project_name = "aws-cf"
|
||||||
|
environment = "dev"
|
||||||
|
|
||||||
|
# Origin Configuration
|
||||||
|
origin_domain = "origin.servidor.it.com"
|
||||||
|
|
||||||
|
# Domain Configuration
|
||||||
|
domain_name = "servidor.it.com"
|
||||||
|
|
||||||
|
# CloudFront Configuration
|
||||||
|
cloudfront_aliases = [
|
||||||
|
"servidor.it.com",
|
||||||
|
"www.servidor.it.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Route53 Configuration
|
||||||
|
create_route53_records = true
|
||||||
|
certificate_domain_validation_options = "DNS"
|
||||||
|
|
||||||
|
# CloudFront Settings
|
||||||
|
price_class = "PriceClass_100" # PriceClass_All, PriceClass_200, PriceClass_100
|
||||||
|
origin_protocol_policy = "https-only" # http-only, https-only, match-viewer
|
||||||
|
viewer_protocol_policy = "redirect-to-https" # allow-all, https-only, redirect-to-https
|
||||||
|
|
||||||
|
# Cache Policies (AWS Managed Policies)
|
||||||
|
cache_policy_id = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # CachingDisabled
|
||||||
|
origin_request_policy_id = "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf" # CORS-S3Origin
|
||||||
|
|
||||||
|
# Security Configuration
|
||||||
|
create_alb_security_group = false
|
||||||
|
create_web_security_group = false
|
||||||
|
enable_waf = false
|
||||||
|
|
||||||
|
# SSH Access (if creating EC2 security groups)
|
||||||
|
ssh_allowed_cidrs = [
|
||||||
|
"0.0.0.0/0" # Restrict this to your IP in production
|
||||||
|
]
|
||||||
159
variables.tf
Normal file
159
variables.tf
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
variable "aws_region" {
|
||||||
|
description = "AWS region"
|
||||||
|
type = string
|
||||||
|
default = "us-east-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project_name" {
|
||||||
|
description = "Name of the project"
|
||||||
|
type = string
|
||||||
|
default = "aws-cf"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "environment" {
|
||||||
|
description = "Environment (dev, staging, prod)"
|
||||||
|
type = string
|
||||||
|
default = "dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "origin_domain" {
|
||||||
|
description = "Origin domain name"
|
||||||
|
type = string
|
||||||
|
default = "origin.servidor.it.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cloudfront_aliases" {
|
||||||
|
description = "List of aliases for CloudFront distribution"
|
||||||
|
type = list(string)
|
||||||
|
default = ["servidor.it.com", "www.servidor.it.com"]
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "domain_name" {
|
||||||
|
description = "Main domain name for ACM certificate"
|
||||||
|
type = string
|
||||||
|
default = "servidor.it.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "certificate_domain_validation_options" {
|
||||||
|
description = "Domain validation method for ACM certificate"
|
||||||
|
type = string
|
||||||
|
default = "DNS"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "create_route53_records" {
|
||||||
|
description = "Whether to create Route53 records for domain validation and alias"
|
||||||
|
type = bool
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "price_class" {
|
||||||
|
description = "CloudFront distribution price class"
|
||||||
|
type = string
|
||||||
|
default = "PriceClass_All"
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = contains([
|
||||||
|
"PriceClass_All",
|
||||||
|
"PriceClass_200",
|
||||||
|
"PriceClass_100"
|
||||||
|
], var.price_class)
|
||||||
|
error_message = "Price class must be one of: PriceClass_All, PriceClass_200, PriceClass_100"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "origin_protocol_policy" {
|
||||||
|
description = "Origin protocol policy"
|
||||||
|
type = string
|
||||||
|
default = "https-only"
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = contains([
|
||||||
|
"http-only",
|
||||||
|
"https-only",
|
||||||
|
"match-viewer"
|
||||||
|
], var.origin_protocol_policy)
|
||||||
|
error_message = "Origin protocol policy must be one of: http-only, https-only, match-viewer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "viewer_protocol_policy" {
|
||||||
|
description = "Viewer protocol policy"
|
||||||
|
type = string
|
||||||
|
default = "redirect-to-https"
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = contains([
|
||||||
|
"allow-all",
|
||||||
|
"https-only",
|
||||||
|
"redirect-to-https"
|
||||||
|
], var.viewer_protocol_policy)
|
||||||
|
error_message = "Viewer protocol policy must be one of: allow-all, https-only, redirect-to-https"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cache_policy_id" {
|
||||||
|
description = "CloudFront cache policy ID (managed or custom)"
|
||||||
|
type = string
|
||||||
|
default = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # CachingDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "origin_request_policy_id" {
|
||||||
|
description = "CloudFront origin request policy ID"
|
||||||
|
type = string
|
||||||
|
default = "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf" # CORS-S3Origin
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security-related variables
|
||||||
|
variable "create_alb_security_group" {
|
||||||
|
description = "Whether to create ALB security group"
|
||||||
|
type = bool
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "create_web_security_group" {
|
||||||
|
description = "Whether to create web server security group"
|
||||||
|
type = bool
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_allowed_cidrs" {
|
||||||
|
description = "CIDR blocks allowed for SSH access"
|
||||||
|
type = list(string)
|
||||||
|
default = ["0.0.0.0/0"] # Restrict this in production
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "enable_waf" {
|
||||||
|
description = "Whether to enable WAF for CloudFront"
|
||||||
|
type = bool
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "enable_cloudformation_stack" {
|
||||||
|
description = "Whether to create CloudFormation stack"
|
||||||
|
type = bool
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "create_acm_certificate" {
|
||||||
|
description = "Whether to create ACM certificate"
|
||||||
|
type = bool
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "enable_cloudfront_logging" {
|
||||||
|
description = "Whether to enable CloudFront access logging"
|
||||||
|
type = bool
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cloudfront_logs_bucket" {
|
||||||
|
description = "S3 bucket for CloudFront logs"
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cloudfront_logs_prefix" {
|
||||||
|
description = "Prefix for CloudFront logs in S3"
|
||||||
|
type = string
|
||||||
|
default = "cloudfront-logs/"
|
||||||
|
}
|
||||||
43
versions.tf
Normal file
43
versions.tf
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Uncomment and configure if you want to use remote state
|
||||||
|
# backend "s3" {
|
||||||
|
# bucket = "your-tofu-state-bucket"
|
||||||
|
# key = "cloudfront/terraform.tfstate"
|
||||||
|
# region = "us-east-1"
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
region = var.aws_region
|
||||||
|
|
||||||
|
default_tags {
|
||||||
|
tags = {
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
ManagedBy = "OpenTofu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional provider for ACM certificate (must be in us-east-1 for CloudFront)
|
||||||
|
provider "aws" {
|
||||||
|
alias = "us_east_1"
|
||||||
|
region = "us-east-1"
|
||||||
|
|
||||||
|
default_tags {
|
||||||
|
tags = {
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
ManagedBy = "OpenTofu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user