# Terraform AWS Infrastructure for mockupAWS terraform { required_version = ">= 1.5.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } random = { source = "hashicorp/random" version = "~> 3.0" } } backend "s3" { bucket = "mockupaws-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "mockupaws-terraform-locks" } } provider "aws" { region = var.region default_tags { tags = { Project = "mockupAWS" Environment = var.environment ManagedBy = "Terraform" } } } # Data sources data "aws_caller_identity" "current" {} data "aws_availability_zones" "available" { state = "available" } # Random suffix for unique resource names resource "random_id" "suffix" { byte_length = 4 } #------------------------------------------------------------------------------ # VPC & Networking #------------------------------------------------------------------------------ module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.0" name = "${var.project_name}-${var.environment}" cidr = var.vpc_cidr azs = var.availability_zones private_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i)] public_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i + 100)] database_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i + 200)] enable_nat_gateway = true single_nat_gateway = var.environment != "production" enable_dns_hostnames = true enable_dns_support = true # VPC Flow Logs enable_flow_log = true create_flow_log_cloudwatch_iam_role = true create_flow_log_cloudwatch_log_group = true tags = { Environment = var.environment } } #------------------------------------------------------------------------------ # Security Groups #------------------------------------------------------------------------------ resource "aws_security_group" "alb" { name_prefix = "${var.project_name}-alb-" description = "Security group for ALB" vpc_id = module.vpc.vpc_id ingress { description = "HTTPS from anywhere" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "HTTP from anywhere (redirect)" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { description = "Allow all outbound" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.project_name}-alb-sg" } lifecycle { create_before_destroy = true } } resource "aws_security_group" "ecs_tasks" { name_prefix = "${var.project_name}-ecs-tasks-" description = "Security group for ECS tasks" vpc_id = module.vpc.vpc_id ingress { description = "HTTP from ALB" from_port = 8000 to_port = 8000 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 = { Name = "${var.project_name}-ecs-tasks-sg" } lifecycle { create_before_destroy = true } } resource "aws_security_group" "rds" { name_prefix = "${var.project_name}-rds-" description = "Security group for RDS" vpc_id = module.vpc.vpc_id ingress { description = "PostgreSQL from ECS tasks" from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [aws_security_group.ecs_tasks.id] } tags = { Name = "${var.project_name}-rds-sg" } lifecycle { create_before_destroy = true } } resource "aws_security_group" "elasticache" { name_prefix = "${var.project_name}-elasticache-" description = "Security group for ElastiCache" vpc_id = module.vpc.vpc_id ingress { description = "Redis from ECS tasks" from_port = 6379 to_port = 6379 protocol = "tcp" security_groups = [aws_security_group.ecs_tasks.id] } tags = { Name = "${var.project_name}-elasticache-sg" } lifecycle { create_before_destroy = true } } #------------------------------------------------------------------------------ # RDS PostgreSQL #------------------------------------------------------------------------------ resource "aws_db_subnet_group" "main" { name = "${var.project_name}-${var.environment}" subnet_ids = module.vpc.database_subnets tags = { Name = "${var.project_name}-db-subnet-group" } } resource "aws_db_parameter_group" "main" { family = "postgres15" name = "${var.project_name}-${var.environment}" parameter { name = "log_connections" value = "1" } parameter { name = "log_disconnections" value = "1" } parameter { name = "log_duration" value = "1" } tags = { Name = "${var.project_name}-db-params" } } resource "random_password" "db_password" { length = 32 special = true override_special = "!#$%&*()-_=+[]{}<>:?" } resource "aws_secretsmanager_secret" "db_password" { name = "${var.project_name}/${var.environment}/database-password" description = "Database password for ${var.project_name}" recovery_window_in_days = 7 tags = { Name = "${var.project_name}-db-secret" } } resource "aws_secretsmanager_secret_version" "db_password" { secret_id = aws_secretsmanager_secret.db_password.id secret_string = random_password.db_password.result } resource "aws_db_instance" "main" { identifier = "${var.project_name}-${var.environment}" engine = "postgres" engine_version = "15.4" instance_class = var.db_instance_class allocated_storage = var.db_allocated_storage max_allocated_storage = var.db_max_allocated_storage storage_type = "gp3" storage_encrypted = true db_name = replace(var.project_name, "-", "_") username = "mockupaws_admin" password = random_password.db_password.result multi_az = var.db_multi_az db_subnet_group_name = aws_db_subnet_group.main.name vpc_security_group_ids = [aws_security_group.rds.id] parameter_group_name = aws_db_parameter_group.main.name backup_retention_period = var.db_backup_retention_days backup_window = "03:00-04:00" maintenance_window = "Mon:04:00-Mon:05:00" deletion_protection = var.environment == "production" skip_final_snapshot = var.environment != "production" performance_insights_enabled = true performance_insights_retention_period = 7 monitoring_interval = 60 monitoring_role_arn = aws_iam_role.rds_monitoring.arn enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] tags = { Name = "${var.project_name}-postgres" } } #------------------------------------------------------------------------------ # ElastiCache Redis #------------------------------------------------------------------------------ resource "aws_elasticache_subnet_group" "main" { name = "${var.project_name}-${var.environment}" subnet_ids = module.vpc.private_subnets tags = { Name = "${var.project_name}-elasticache-subnet" } } resource "aws_elasticache_parameter_group" "main" { family = "redis7" name = "${var.project_name}-${var.environment}" parameter { name = "maxmemory-policy" value = "allkeys-lru" } parameter { name = "activedefrag" value = "yes" } tags = { Name = "${var.project_name}-redis-params" } } resource "aws_elasticache_replication_group" "main" { replication_group_id = "${var.project_name}-${var.environment}" description = "Redis cluster for ${var.project_name}" node_type = var.redis_node_type num_cache_clusters = var.redis_num_cache_clusters port = 6379 parameter_group_name = aws_elasticache_parameter_group.main.name subnet_group_name = aws_elasticache_subnet_group.main.name security_group_ids = [aws_security_group.elasticache.id] automatic_failover_enabled = var.environment == "production" multi_az_enabled = var.environment == "production" at_rest_encryption_enabled = true transit_encryption_enabled = true snapshot_retention_limit = 7 snapshot_window = "05:00-06:00" maintenance_window = "sun:06:00-sun:07:00" apply_immediately = false tags = { Name = "${var.project_name}-redis" } } #------------------------------------------------------------------------------ # S3 Buckets #------------------------------------------------------------------------------ resource "aws_s3_bucket" "reports" { bucket = "${var.project_name}-reports-${var.environment}-${random_id.suffix.hex}" tags = { Name = "${var.project_name}-reports" } } resource "aws_s3_bucket_versioning" "reports" { bucket = aws_s3_bucket.reports.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_server_side_encryption_configuration" "reports" { bucket = aws_s3_bucket.reports.id rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } } } resource "aws_s3_bucket_lifecycle_configuration" "reports" { bucket = aws_s3_bucket.reports.id rule { id = "archive-old-reports" status = "Enabled" transition { days = 30 storage_class = "STANDARD_IA" } transition { days = 90 storage_class = "GLACIER" } expiration { days = 365 } } } resource "aws_s3_bucket" "backups" { bucket = "${var.project_name}-backups-${var.environment}-${random_id.suffix.hex}" tags = { Name = "${var.project_name}-backups" } } resource "aws_s3_bucket_versioning" "backups" { bucket = aws_s3_bucket.backups.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_server_side_encryption_configuration" "backups" { bucket = aws_s3_bucket.backups.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" kms_master_key_id = aws_kms_key.main.arn } bucket_key_enabled = true } } resource "aws_s3_bucket_lifecycle_configuration" "backups" { bucket = aws_s3_bucket.backups.id rule { id = "backup-lifecycle" status = "Enabled" transition { days = 30 storage_class = "GLACIER" } noncurrent_version_transition { noncurrent_days = 7 storage_class = "GLACIER" } noncurrent_version_expiration { noncurrent_days = 90 } } } resource "aws_s3_bucket_public_access_block" "all" { for_each = toset([aws_s3_bucket.reports.id, aws_s3_bucket.backups.id]) bucket = each.value block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } #------------------------------------------------------------------------------ # KMS Key #------------------------------------------------------------------------------ resource "aws_kms_key" "main" { description = "KMS key for ${var.project_name}" deletion_window_in_days = 7 enable_key_rotation = true tags = { Name = "${var.project_name}-kms" } } resource "aws_kms_alias" "main" { name = "alias/${var.project_name}-${var.environment}" target_key_id = aws_kms_key.main.key_id } #------------------------------------------------------------------------------ # Application Load Balancer #------------------------------------------------------------------------------ resource "aws_lb" "main" { name = "${var.project_name}-${var.environment}" internal = false load_balancer_type = "application" security_groups = [aws_security_group.alb.id] subnets = module.vpc.public_subnets enable_deletion_protection = var.environment == "production" enable_http2 = true access_logs { bucket = aws_s3_bucket.logs.id prefix = "alb-logs" enabled = true } tags = { Name = "${var.project_name}-alb" } } resource "aws_lb_target_group" "backend" { name = "${var.project_name}-backend-${var.environment}" port = 8000 protocol = "HTTP" vpc_id = module.vpc.vpc_id target_type = "ip" health_check { enabled = true healthy_threshold = 2 interval = 30 matcher = "200" path = "/api/v1/health" port = "traffic-port" protocol = "HTTP" timeout = 5 unhealthy_threshold = 3 } tags = { Name = "${var.project_name}-backend-tg" } } resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.main.arn port = "443" protocol = "HTTPS" ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" certificate_arn = var.certificate_arn default_action { type = "forward" target_group_arn = aws_lb_target_group.backend.arn } } resource "aws_lb_listener" "http" { load_balancer_arn = aws_lb.main.arn port = "80" protocol = "HTTP" default_action { type = "redirect" redirect { port = "443" protocol = "HTTPS" status_code = "HTTP_301" } } } #------------------------------------------------------------------------------ # ECS Cluster & Service #------------------------------------------------------------------------------ resource "aws_ecs_cluster" "main" { name = "${var.project_name}-${var.environment}" setting { name = "containerInsights" value = "enabled" } tags = { Name = "${var.project_name}-ecs-cluster" } } resource "aws_ecs_cluster_capacity_providers" "main" { cluster_name = aws_ecs_cluster.main.name capacity_providers = ["FARGATE", "FARGATE_SPOT"] default_capacity_provider_strategy { base = 1 weight = 1 capacity_provider = "FARGATE" } } resource "aws_cloudwatch_log_group" "ecs" { name = "/ecs/${var.project_name}-${var.environment}" retention_in_days = 30 tags = { Name = "${var.project_name}-ecs-logs" } } resource "aws_ecs_task_definition" "backend" { family = "${var.project_name}-backend" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] cpu = var.ecs_task_cpu memory = var.ecs_task_memory execution_role_arn = aws_iam_role.ecs_execution.arn task_role_arn = aws_iam_role.ecs_task.arn container_definitions = jsonencode([ { name = "backend" image = "${var.ecr_repository_url}:v1.0.0" essential = true portMappings = [ { containerPort = 8000 protocol = "tcp" } ] environment = [ { name = "APP_ENV" value = var.environment }, { name = "APP_NAME" value = var.project_name }, { name = "DEBUG" value = "false" }, { name = "API_V1_STR" value = "/api/v1" }, { name = "DATABASE_URL" value = "postgresql+asyncpg://${aws_db_instance.main.username}:@${aws_db_instance.main.endpoint}/${aws_db_instance.main.db_name}" }, { name = "REDIS_URL" value = "redis://${aws_elasticache_replication_group.main.primary_endpoint_address}:6379/0" }, { name = "FRONTEND_URL" value = "https://${var.domain_name}" }, { name = "S3_REPORTS_BUCKET" value = aws_s3_bucket.reports.id } ] secrets = [ { name = "JWT_SECRET_KEY" valueFrom = aws_secretsmanager_secret.jwt_secret.arn }, { name = "DATABASE_PASSWORD" valueFrom = aws_secretsmanager_secret.db_password.arn } ] logConfiguration = { logDriver = "awslogs" options = { awslogs-group = aws_cloudwatch_log_group.ecs.name awslogs-region = var.region awslogs-stream-prefix = "backend" } } healthCheck = { command = ["CMD-SHELL", "curl -f http://localhost:8000/api/v1/health || exit 1"] interval = 30 timeout = 5 retries = 3 startPeriod = 60 } } ]) tags = { Name = "${var.project_name}-backend-task" } } resource "aws_ecs_service" "backend" { name = "backend" cluster = aws_ecs_cluster.main.id task_definition = aws_ecs_task_definition.backend.arn desired_count = var.ecs_desired_count launch_type = "FARGATE" network_configuration { subnets = module.vpc.private_subnets security_groups = [aws_security_group.ecs_tasks.id] assign_public_ip = false } load_balancer { target_group_arn = aws_lb_target_group.backend.arn container_name = "backend" container_port = 8000 } deployment_controller { type = "ECS" } deployment_circuit_breaker { enable = true rollback = true } propagate_tags = "SERVICE" tags = { Name = "${var.project_name}-backend-service" } } resource "aws_appautoscaling_target" "ecs" { max_capacity = var.ecs_max_count min_capacity = var.ecs_desired_count resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.backend.name}" scalable_dimension = "ecs:service:DesiredCount" service_namespace = "ecs" } resource "aws_appautoscaling_policy" "ecs_cpu" { name = "${var.project_name}-cpu-autoscaling" policy_type = "TargetTrackingScaling" resource_id = aws_appautoscaling_target.ecs.resource_id scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension service_namespace = aws_appautoscaling_target.ecs.service_namespace target_tracking_scaling_policy_configuration { predefined_metric_specification { predefined_metric_type = "ECSServiceAverageCPUUtilization" } target_value = 70.0 scale_in_cooldown = 300 scale_out_cooldown = 60 } } resource "aws_appautoscaling_policy" "ecs_memory" { name = "${var.project_name}-memory-autoscaling" policy_type = "TargetTrackingScaling" resource_id = aws_appautoscaling_target.ecs.resource_id scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension service_namespace = aws_appautoscaling_target.ecs.service_namespace target_tracking_scaling_policy_configuration { predefined_metric_specification { predefined_metric_type = "ECSServiceAverageMemoryUtilization" } target_value = 75.0 scale_in_cooldown = 300 scale_out_cooldown = 60 } } #------------------------------------------------------------------------------ # CloudFront CDN #------------------------------------------------------------------------------ resource "aws_cloudfront_distribution" "main" { enabled = true is_ipv6_enabled = true comment = "${var.project_name} CDN" default_root_object = "index.html" price_class = "PriceClass_100" aliases = [var.domain_name, "www.${var.domain_name}"] origin { domain_name = aws_lb.main.dns_name origin_id = "ALB-${var.project_name}" custom_origin_config { http_port = 80 https_port = 443 origin_protocol_policy = "https-only" origin_ssl_protocols = ["TLSv1.2"] } } origin { domain_name = aws_s3_bucket.reports.bucket_regional_domain_name origin_id = "S3-${var.project_name}-reports" s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.main.cloudfront_access_identity_path } } default_cache_behavior { allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] cached_methods = ["GET", "HEAD"] target_origin_id = "ALB-${var.project_name}" forwarded_values { query_string = true headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"] cookies { forward = "all" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 0 max_ttl = 86400 } ordered_cache_behavior { path_pattern = "/reports/*" allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD"] target_origin_id = "S3-${var.project_name}-reports" forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 } restrictions { geo_restriction { restriction_type = "none" } } viewer_certificate { acm_certificate_arn = var.certificate_arn ssl_support_method = "sni-only" minimum_protocol_version = "TLSv1.2_2021" } logging_config { include_cookies = false bucket = aws_s3_bucket.logs.bucket_domain_name prefix = "cdn-logs/" } web_acl_id = aws_wafv2_web_acl.main.arn tags = { Name = "${var.project_name}-cdn" } } resource "aws_cloudfront_origin_access_identity" "main" { comment = "OAI for ${var.project_name}" } #------------------------------------------------------------------------------ # WAF Web ACL #------------------------------------------------------------------------------ resource "aws_wafv2_web_acl" "main" { name = "${var.project_name}-${var.environment}" description = "WAF rules for ${var.project_name}" scope = "CLOUDFRONT" default_action { allow {} } rule { name = "AWSManagedRulesCommonRuleSet" priority = 1 override_action { none {} } statement { managed_rule_group_statement { name = "AWSManagedRulesCommonRuleSet" vendor_name = "AWS" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "AWSManagedRulesCommonRuleSetMetric" sampled_requests_enabled = true } } rule { name = "AWSManagedRulesSQLiRuleSet" priority = 2 override_action { none {} } statement { managed_rule_group_statement { name = "AWSManagedRulesSQLiRuleSet" vendor_name = "AWS" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "AWSManagedRulesSQLiRuleSetMetric" sampled_requests_enabled = true } } 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 = "AWSManagedRulesKnownBadInputsRuleSetMetric" sampled_requests_enabled = true } } rule { name = "RateLimitRule" priority = 4 action { block {} } statement { rate_based_statement { limit = 2000 aggregate_key_type = "IP" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "RateLimitRuleMetric" sampled_requests_enabled = true } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "${var.project_name}-waf-metric" sampled_requests_enabled = true } tags = { Name = "${var.project_name}-waf" } } #------------------------------------------------------------------------------ # Route53 DNS #------------------------------------------------------------------------------ resource "aws_route53_zone" "main" { count = var.create_route53_zone ? 1 : 0 name = var.domain_name tags = { Name = "${var.project_name}-zone" } } resource "aws_route53_record" "main" { zone_id = var.create_route53_zone ? aws_route53_zone.main[0].zone_id : var.hosted_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 } } resource "aws_route53_record" "www" { zone_id = var.create_route53_zone ? aws_route53_zone.main[0].zone_id : var.hosted_zone_id name = "www.${var.domain_name}" type = "CNAME" ttl = 300 records = [var.domain_name] } resource "aws_route53_health_check" "main" { fqdn = var.domain_name port = 443 type = "HTTPS" resource_path = "/api/v1/health" failure_threshold = 3 request_interval = 30 tags = { Name = "${var.project_name}-health-check" } } #------------------------------------------------------------------------------ # IAM Roles & Policies #------------------------------------------------------------------------------ resource "aws_iam_role" "ecs_execution" { name = "${var.project_name}-ecs-execution-${var.environment}" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } } ] }) tags = { Name = "${var.project_name}-ecs-execution-role" } } resource "aws_iam_role_policy_attachment" "ecs_execution_managed" { role = aws_iam_role.ecs_execution.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } resource "aws_iam_role_policy" "ecs_execution_secrets" { name = "${var.project_name}-ecs-secrets-policy" role = aws_iam_role.ecs_execution.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "secretsmanager:GetSecretValue" ] Resource = [ aws_secretsmanager_secret.db_password.arn, aws_secretsmanager_secret.jwt_secret.arn ] } ] }) } resource "aws_iam_role" "ecs_task" { name = "${var.project_name}-ecs-task-${var.environment}" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } } ] }) tags = { Name = "${var.project_name}-ecs-task-role" } } resource "aws_iam_role_policy" "ecs_task_s3" { name = "${var.project_name}-ecs-s3-policy" role = aws_iam_role.ecs_task.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ] Resource = [ "${aws_s3_bucket.reports.arn}/*", "${aws_s3_bucket.backups.arn}/*" ] }, { Effect = "Allow" Action = [ "s3:ListBucket" ] Resource = [ aws_s3_bucket.reports.arn, aws_s3_bucket.backups.arn ] } ] }) } resource "aws_iam_role" "rds_monitoring" { name = "${var.project_name}-rds-monitoring-${var.environment}" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "monitoring.rds.amazonaws.com" } } ] }) tags = { Name = "${var.project_name}-rds-monitoring-role" } } resource "aws_iam_role_policy_attachment" "rds_monitoring" { role = aws_iam_role.rds_monitoring.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole" } #------------------------------------------------------------------------------ # Secrets Manager #------------------------------------------------------------------------------ resource "random_password" "jwt_secret" { length = 64 special = false } resource "aws_secretsmanager_secret" "jwt_secret" { name = "${var.project_name}/${var.environment}/jwt-secret" description = "JWT signing secret for ${var.project_name}" recovery_window_in_days = 7 tags = { Name = "${var.project_name}-jwt-secret" } } resource "aws_secretsmanager_secret_version" "jwt_secret" { secret_id = aws_secretsmanager_secret.jwt_secret.id secret_string = random_password.jwt_secret.result } #------------------------------------------------------------------------------ # S3 Logs Bucket #------------------------------------------------------------------------------ resource "aws_s3_bucket" "logs" { bucket = "${var.project_name}-logs-${var.environment}-${random_id.suffix.hex}" tags = { Name = "${var.project_name}-logs" } } resource "aws_s3_bucket_policy" "logs" { bucket = aws_s3_bucket.logs.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { AWS = "arn:aws:iam::127311923021:root" # us-east-1 ELB account } Action = "s3:PutObject" Resource = "${aws_s3_bucket.logs.arn}/alb-logs/*" } ] }) } resource "aws_s3_bucket_lifecycle_configuration" "logs" { bucket = aws_s3_bucket.logs.id rule { id = "expire-logs" status = "Enabled" expiration { days = 90 } } }