Initial commit: Terraform infrastructure, pipelines, docs and scripts

This commit is contained in:
Evert Daniel Romero Garrido
2026-04-14 14:53:05 -06:00
commit 85297b12a2
31 changed files with 4015 additions and 0 deletions
+576
View File
@@ -0,0 +1,576 @@
# ===============================================================================================================
# main.tf - Recursos principales de infraestructura para proyectosacc
# Descripción:
# Crea VPC, EC2, RDS, S3, CloudFront, Route 53, ACM e IAM para la
# arquitectura híbrida S3+CloudFront+EC2 de SACC.
#
# Autor: Área de Tecnología y Desarrollo - CCsoft
# ===============================================================================================================
# -------------------------------------------------------------------------------
# VPC y Networking
# -------------------------------------------------------------------------------
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.project_name}-vpc-${var.environment}"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw-${var.environment}"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${var.availability_zones[count.index]}"
}
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 10)
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.project_name}-private-${var.availability_zones[count.index]}"
}
}
resource "aws_eip" "nat" {
domain = "vpc"
tags = {
Name = "${var.project_name}-nat-eip-${var.environment}"
}
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.project_name}-nat-${var.environment}"
}
depends_on = [aws_internet_gateway.main]
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-public-rt-${var.environment}"
}
}
resource "aws_route_table_association" "public" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
tags = {
Name = "${var.project_name}-private-rt-${var.environment}"
}
}
resource "aws_route_table_association" "private" {
count = length(aws_subnet.private)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
# -------------------------------------------------------------------------------
# Security Groups
# -------------------------------------------------------------------------------
resource "aws_security_group" "ec2_api" {
name_prefix = "${var.project_name}-ec2-api-"
vpc_id = aws_vpc.main.id
description = "Security Group para la API backend de ${var.project_name}"
ingress {
description = "SSH desde IPs confiables"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"] # Ajustar a IP/VPN real del pipeline
}
ingress {
description = "HTTP API"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS API"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "API backend directo"
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
egress {
description = "Salida libre"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-sg-ec2-api"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group" "rds" {
name_prefix = "${var.project_name}-rds-"
vpc_id = aws_vpc.main.id
description = "Security Group para RDS MariaDB de ${var.project_name}"
ingress {
description = "MariaDB desde EC2 API"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.ec2_api.id]
}
egress {
description = "Salida libre"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-sg-rds"
}
lifecycle {
create_before_destroy = true
}
}
# -------------------------------------------------------------------------------
# IAM Role / Instance Profile para EC2
# -------------------------------------------------------------------------------
resource "aws_iam_role" "ec2_role" {
name = "${var.project_name}-ec2-role-${var.environment}"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}]
})
tags = {
Name = "${var.project_name}-ec2-role"
}
}
resource "aws_iam_role_policy" "ec2_policy" {
name = "${var.project_name}-ec2-policy-${var.environment}"
role = aws_iam_role.ec2_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket"
]
Resource = [
aws_s3_bucket.artifacts.arn,
"${aws_s3_bucket.artifacts.arn}/*"
]
},
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "arn:aws:logs:*:*:*"
},
{
Effect = "Allow"
Action = [
"cloudfront:CreateInvalidation"
]
Resource = "*"
}
]
})
}
resource "aws_iam_instance_profile" "ec2_profile" {
name = "${var.project_name}-ec2-profile-${var.environment}"
role = aws_iam_role.ec2_role.name
tags = {
Name = "${var.project_name}-ec2-profile"
}
}
# -------------------------------------------------------------------------------
# EC2 (API Backend)
# -------------------------------------------------------------------------------
resource "aws_instance" "api" {
ami = var.ec2_ami
instance_type = var.ec2_instance_type
subnet_id = aws_subnet.public[0].id
vpc_security_group_ids = [aws_security_group.ec2_api.id]
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
key_name = var.ec2_key_name
root_block_device {
volume_type = "gp3"
volume_size = var.ec2_root_volume_size
encrypted = true
delete_on_termination = true
}
user_data = base64encode(templatefile("${path.module}/user-data.sh", {
pipeline_public_key = var.pipeline_public_key
}))
tags = {
Name = "${var.project_name}-api-${var.environment}"
Environment = var.environment
}
}
# -------------------------------------------------------------------------------
# RDS MariaDB
# -------------------------------------------------------------------------------
resource "aws_db_subnet_group" "rds" {
name = "${var.project_name}-rds-subnet-group-${var.environment}"
subnet_ids = aws_subnet.private[*].id
tags = {
Name = "${var.project_name}-rds-subnet-group"
}
}
resource "aws_db_instance" "main" {
identifier = "${var.project_name}-db-${var.environment}"
engine = var.db_engine
engine_version = var.db_engine_version
instance_class = var.db_instance_class
allocated_storage = var.db_allocated_storage
storage_type = "gp3"
storage_encrypted = true
db_name = var.db_name
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.rds.name
vpc_security_group_ids = [aws_security_group.rds.id]
publicly_accessible = false
skip_final_snapshot = true
backup_retention_period = 7
tags = {
Name = "${var.project_name}-rds"
Environment = var.environment
}
}
# -------------------------------------------------------------------------------
# S3 Buckets
# -------------------------------------------------------------------------------
resource "aws_s3_bucket" "frontend" {
bucket = var.s3_frontend_bucket
tags = {
Name = "${var.project_name}-frontend"
}
}
resource "aws_s3_bucket_versioning" "frontend" {
bucket = aws_s3_bucket.frontend.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_public_access_block" "frontend" {
bucket = aws_s3_bucket.frontend.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_website_configuration" "frontend" {
bucket = aws_s3_bucket.frontend.id
index_document {
suffix = "index.html"
}
error_document {
key = "index.html"
}
}
resource "aws_s3_bucket" "artifacts" {
bucket = var.s3_artifacts_bucket
tags = {
Name = "${var.project_name}-artifacts"
}
}
resource "aws_s3_bucket_versioning" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_public_access_block" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# -------------------------------------------------------------------------------
# CloudFront Origin Access Control
# -------------------------------------------------------------------------------
resource "aws_cloudfront_origin_access_control" "frontend" {
name = "${var.project_name}-oac-${var.environment}"
description = "OAC para bucket frontend de ${var.project_name}"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
resource "aws_s3_bucket_policy" "frontend" {
bucket = aws_s3_bucket.frontend.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCloudFrontOAC"
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.frontend.arn}/*"
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.main.arn
}
}
}]
})
depends_on = [aws_s3_bucket_public_access_block.frontend]
}
# -------------------------------------------------------------------------------
# ACM Certificate (us-east-1 obligatorio para CloudFront)
# -------------------------------------------------------------------------------
resource "aws_acm_certificate" "main" {
provider = aws.us_east_1
domain_name = var.domain_name
validation_method = "DNS"
tags = {
Name = "${var.project_name}-cert"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = data.aws_route53_zone.main.zone_id
}
resource "aws_acm_certificate_validation" "main" {
provider = aws.us_east_1
certificate_arn = aws_acm_certificate.main.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
# -------------------------------------------------------------------------------
# Route 53
# -------------------------------------------------------------------------------
data "aws_route53_zone" "main" {
name = "ccsoft.mx"
private_zone = false
}
resource "aws_route53_record" "main" {
zone_id = data.aws_route53_zone.main.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
}
}
# -------------------------------------------------------------------------------
# CloudFront Distribution
# -------------------------------------------------------------------------------
resource "aws_cloudfront_distribution" "main" {
enabled = true
is_ipv6_enabled = true
comment = "CDN para ${var.project_name}"
default_root_object = "index.html"
aliases = [var.domain_name]
price_class = var.cloudfront_price_class
origin {
domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
origin_id = "saccS3Origin"
origin_access_control_id = aws_cloudfront_origin_access_control.frontend.id
}
origin {
domain_name = aws_instance.api.public_dns
origin_id = "saccApiOrigin"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "saccS3Origin"
viewer_protocol_policy = "redirect-to-https"
compress = true
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
ordered_cache_behavior {
path_pattern = "/api/*"
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "saccApiOrigin"
viewer_protocol_policy = "redirect-to-https"
compress = true
forwarded_values {
query_string = true
headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
cookies {
forward = "all"
}
}
min_ttl = 0
default_ttl = 0
max_ttl = 0
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
acm_certificate_arn = aws_acm_certificate_validation.main.certificate_arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
depends_on = [aws_acm_certificate_validation.main]
tags = {
Name = "${var.project_name}-cdn"
}
}