Initial commit: Terraform infrastructure, pipelines, docs and scripts
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user