feat(ci): integra Terraform en pipeline de Bitbucket Pipelines
- Agrega paso 03_terraform para DEV y PROD con init, plan y apply - Crea backend.dev.hcl para configuración explícita de estado DEV - Refactoriza Route53/ACM en main.tf para soportar PROD cross-account usando count condicional sin romper estado de DEV - Descomenta provider aws.route53 en provider.tf - Añade domain_name faltante en prod.tfvars y confirma dev.tfvars - Corrige output route53_record para recursos con count - Elimina errored.tfstate corrupto local - Incluye permiso sts:AssumeRole en IAM policy para Route53 cross-account
This commit is contained in:
+38
-10
@@ -1,8 +1,8 @@
|
|||||||
# ===============================================================================================================
|
# ===============================================================================================================
|
||||||
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
|
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
|
||||||
# Descripción:
|
# Descripción:
|
||||||
# Pipeline de 7 pasos estándar de CCsoft para desplegar el frontend
|
# Pipeline de 7 pasos estándar de CCsoft para desplegar infraestructura (Terraform),
|
||||||
# React (S3+CloudFront) y la API backend (EC2) de SACC.
|
# frontend React (S3+CloudFront) y API backend (EC2) de SACC.
|
||||||
#
|
#
|
||||||
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
# ===============================================================================================================
|
# ===============================================================================================================
|
||||||
@@ -42,7 +42,7 @@ pipelines:
|
|||||||
name: 01_image-setup
|
name: 01_image-setup
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk awscli
|
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk awscli wget unzip
|
||||||
- mkdir -p ~/.ssh
|
- mkdir -p ~/.ssh
|
||||||
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
- chmod 600 ~/.ssh/sacc4_key
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
@@ -59,11 +59,24 @@ pipelines:
|
|||||||
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-saac4.git" ci-cd-saac4
|
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-saac4.git" ci-cd-saac4
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 03_dependencies
|
name: 03_terraform
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- npm ci
|
- cd terraform
|
||||||
- ./gradlew dependencies
|
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
|
||||||
|
- unzip -q terraform_1.11.4_linux_amd64.zip
|
||||||
|
- mv terraform /usr/local/bin/terraform
|
||||||
|
- terraform version
|
||||||
|
- export AWS_ACCESS_KEY_ID="${DEV_AWS_ACCESS_KEY_ID}"
|
||||||
|
- export AWS_SECRET_ACCESS_KEY="${DEV_AWS_SECRET_ACCESS_KEY}"
|
||||||
|
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||||
|
- terraform init -backend-config=backend.dev.hcl
|
||||||
|
- terraform plan -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
|
||||||
|
- terraform apply -auto-approve dev.tfplan
|
||||||
|
- terraform output -json > terraform-outputs.json
|
||||||
|
- cat terraform-outputs.json
|
||||||
|
artifacts:
|
||||||
|
- terraform/terraform-outputs.json
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 04_build
|
name: 04_build
|
||||||
@@ -108,6 +121,7 @@ pipelines:
|
|||||||
-o StrictHostKeyChecking=no \
|
-o StrictHostKeyChecking=no \
|
||||||
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
|
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
|
||||||
"bash /home/thoth/deploy/setup/deploy.sh"
|
"bash /home/thoth/deploy/setup/deploy.sh"
|
||||||
|
- export CLOUDFRONT_DISTRIBUTION_ID=$(python3 -c "import json; print(json.load(open('terraform/terraform-outputs.json'))['cloudfront_distribution_id']['value'])")
|
||||||
- aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"
|
- aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"
|
||||||
- bash ci-cd-commons/telegram_alert.sh "✅ Deploy DEV de proyectosacc completado exitosamente"
|
- bash ci-cd-commons/telegram_alert.sh "✅ Deploy DEV de proyectosacc completado exitosamente"
|
||||||
|
|
||||||
@@ -116,7 +130,7 @@ pipelines:
|
|||||||
name: 01_image-setup
|
name: 01_image-setup
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk awscli
|
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk awscli wget unzip
|
||||||
- mkdir -p ~/.ssh
|
- mkdir -p ~/.ssh
|
||||||
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
- chmod 600 ~/.ssh/sacc4_key
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
@@ -133,11 +147,24 @@ pipelines:
|
|||||||
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-saac4.git" ci-cd-saac4
|
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-saac4.git" ci-cd-saac4
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 03_dependencies
|
name: 03_terraform
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- npm ci
|
- cd terraform
|
||||||
- ./gradlew dependencies
|
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
|
||||||
|
- unzip -q terraform_1.11.4_linux_amd64.zip
|
||||||
|
- mv terraform /usr/local/bin/terraform
|
||||||
|
- terraform version
|
||||||
|
- export AWS_ACCESS_KEY_ID="${PROD_AWS_ACCESS_KEY_ID}"
|
||||||
|
- export AWS_SECRET_ACCESS_KEY="${PROD_AWS_SECRET_ACCESS_KEY}"
|
||||||
|
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||||
|
- terraform init -backend-config=backend.prod.hcl
|
||||||
|
- terraform plan -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
|
||||||
|
- terraform apply -auto-approve prod.tfplan
|
||||||
|
- terraform output -json > terraform-outputs.json
|
||||||
|
- cat terraform-outputs.json
|
||||||
|
artifacts:
|
||||||
|
- terraform/terraform-outputs.json
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 04_build
|
name: 04_build
|
||||||
@@ -182,5 +209,6 @@ pipelines:
|
|||||||
-o StrictHostKeyChecking=no \
|
-o StrictHostKeyChecking=no \
|
||||||
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
|
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
|
||||||
"bash /home/thoth/deploy/setup/deploy.sh"
|
"bash /home/thoth/deploy/setup/deploy.sh"
|
||||||
|
- export CLOUDFRONT_DISTRIBUTION_ID=$(python3 -c "import json; print(json.load(open('terraform/terraform-outputs.json'))['cloudfront_distribution_id']['value'])")
|
||||||
- aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"
|
- aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"
|
||||||
- bash ci-cd-commons/telegram_alert.sh "✅ Deploy PROD de proyectosacc completado exitosamente"
|
- bash ci-cd-commons/telegram_alert.sh "✅ Deploy PROD de proyectosacc completado exitosamente"
|
||||||
|
|||||||
@@ -153,6 +153,14 @@
|
|||||||
],
|
],
|
||||||
"Resource": "*"
|
"Resource": "*"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Sid": "AssumeRoute53CrossAccountRole",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"sts:AssumeRole"
|
||||||
|
],
|
||||||
|
"Resource": "arn:aws:iam::262270938827:role/Route53ProyectosaccCrossAccountRole"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Sid": "ACMCertificateManagement",
|
"Sid": "ACMCertificateManagement",
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
bucket = "ccsoft-terraform-state"
|
||||||
|
key = "proyectosacc/terraform.tfstate"
|
||||||
|
region = "mx-central-1"
|
||||||
|
encrypt = true
|
||||||
|
dynamodb_table = "terraform-locks"
|
||||||
@@ -21,4 +21,5 @@ db_username = "sacc_admin_dev"
|
|||||||
db_password = "<cambiar-por-secret-real>"
|
db_password = "<cambiar-por-secret-real>"
|
||||||
s3_frontend_bucket = "ccsoft-proyectosacc-frontend-dev"
|
s3_frontend_bucket = "ccsoft-proyectosacc-frontend-dev"
|
||||||
s3_artifacts_bucket = "ccsoft-proyectosacc-artifacts-dev"
|
s3_artifacts_bucket = "ccsoft-proyectosacc-artifacts-dev"
|
||||||
|
domain_name = "dev-sacc.ccsoft.mx"
|
||||||
cloudfront_price_class = "PriceClass_100"
|
cloudfront_price_class = "PriceClass_100"
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ db_username = "sacc_admin_prod"
|
|||||||
db_password = "<cambiar-por-secret-real>"
|
db_password = "<cambiar-por-secret-real>"
|
||||||
s3_frontend_bucket = "ccsoft-proyectosacc-frontend-prod"
|
s3_frontend_bucket = "ccsoft-proyectosacc-frontend-prod"
|
||||||
s3_artifacts_bucket = "ccsoft-proyectosacc-artifacts-prod"
|
s3_artifacts_bucket = "ccsoft-proyectosacc-artifacts-prod"
|
||||||
|
domain_name = "sacc.ccsoft.mx"
|
||||||
cloudfront_price_class = "PriceClass_100"
|
cloudfront_price_class = "PriceClass_100"
|
||||||
|
|||||||
+55
-15
@@ -445,38 +445,78 @@ resource "aws_acm_certificate" "main" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_route53_record" "cert_validation" {
|
resource "aws_route53_record" "cert_validation" {
|
||||||
for_each = {
|
count = var.environment != "prod" ? 1 : 0
|
||||||
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
|
allow_overwrite = true
|
||||||
name = each.value.name
|
name = tolist(aws_acm_certificate.main.domain_validation_options)[0].resource_record_name
|
||||||
records = [each.value.record]
|
records = [tolist(aws_acm_certificate.main.domain_validation_options)[0].resource_record_value]
|
||||||
ttl = 60
|
ttl = 60
|
||||||
type = each.value.type
|
type = tolist(aws_acm_certificate.main.domain_validation_options)[0].resource_record_type
|
||||||
zone_id = data.aws_route53_zone.main.zone_id
|
zone_id = local.route53_zone_id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route53_record" "cert_validation_prod" {
|
||||||
|
provider = aws.route53
|
||||||
|
count = var.environment == "prod" ? 1 : 0
|
||||||
|
allow_overwrite = true
|
||||||
|
name = tolist(aws_acm_certificate.main.domain_validation_options)[0].resource_record_name
|
||||||
|
records = [tolist(aws_acm_certificate.main.domain_validation_options)[0].resource_record_value]
|
||||||
|
ttl = 60
|
||||||
|
type = tolist(aws_acm_certificate.main.domain_validation_options)[0].resource_record_type
|
||||||
|
zone_id = local.route53_zone_id
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_acm_certificate_validation" "main" {
|
resource "aws_acm_certificate_validation" "main" {
|
||||||
provider = aws.us_east_1
|
provider = aws.us_east_1
|
||||||
certificate_arn = aws_acm_certificate.main.arn
|
certificate_arn = aws_acm_certificate.main.arn
|
||||||
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
|
validation_record_fqdns = local.cert_validation_fqdns
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# Route 53
|
# Route 53
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
data "aws_route53_zone" "main" {
|
data "aws_route53_zone" "main" {
|
||||||
name = "ccsoft.mx"
|
count = var.environment != "prod" ? 1 : 0
|
||||||
|
name = var.domain_name
|
||||||
private_zone = false
|
private_zone = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data "aws_route53_zone" "main_prod" {
|
||||||
|
provider = aws.route53
|
||||||
|
count = var.environment == "prod" ? 1 : 0
|
||||||
|
name = var.domain_name
|
||||||
|
private_zone = false
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
route53_zone_id = coalesce(
|
||||||
|
try(data.aws_route53_zone.main[0].zone_id, ""),
|
||||||
|
try(data.aws_route53_zone.main_prod[0].zone_id, "")
|
||||||
|
)
|
||||||
|
|
||||||
|
cert_validation_fqdns = compact(try(
|
||||||
|
[aws_route53_record.cert_validation[0].fqdn],
|
||||||
|
[aws_route53_record.cert_validation_prod[0].fqdn],
|
||||||
|
[]
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
resource "aws_route53_record" "main" {
|
resource "aws_route53_record" "main" {
|
||||||
zone_id = data.aws_route53_zone.main.zone_id
|
count = var.environment != "prod" ? 1 : 0
|
||||||
|
zone_id = local.route53_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" "main_prod" {
|
||||||
|
provider = aws.route53
|
||||||
|
count = var.environment == "prod" ? 1 : 0
|
||||||
|
zone_id = local.route53_zone_id
|
||||||
name = var.domain_name
|
name = var.domain_name
|
||||||
type = "A"
|
type = "A"
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ output "cloudfront_distribution_id" {
|
|||||||
|
|
||||||
output "route53_record" {
|
output "route53_record" {
|
||||||
description = "Registro DNS creado en Route 53"
|
description = "Registro DNS creado en Route 53"
|
||||||
value = aws_route53_record.main.name
|
value = try(aws_route53_record.main[0].name, aws_route53_record.main_prod[0].name, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
output "acm_certificate_arn" {
|
output "acm_certificate_arn" {
|
||||||
|
|||||||
@@ -45,3 +45,22 @@ provider "aws" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Provider para Route 53 en cuenta cross-account (262270938827)
|
||||||
|
# Solo se usa en PROD mediante count condicional en los recursos de Route 53.
|
||||||
|
provider "aws" {
|
||||||
|
alias = "route53"
|
||||||
|
region = "us-east-1"
|
||||||
|
|
||||||
|
assume_role {
|
||||||
|
role_arn = "arn:aws:iam::262270938827:role/Route53ProyectosaccCrossAccountRole"
|
||||||
|
}
|
||||||
|
|
||||||
|
default_tags {
|
||||||
|
tags = {
|
||||||
|
Project = var.project_name
|
||||||
|
ManagedBy = "terraform"
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user