From 8ef4a0fc65c001d6238670a41411074af17304f9 Mon Sep 17 00:00:00 2001 From: Gabriel Batista <141974958+gabebatista@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:30:59 -0400 Subject: [PATCH] Gabeaws/jenkins/complete example (#240) --- modules/jenkins/examples/complete/dns.tf | 60 ++++++++ modules/jenkins/examples/complete/local.tf | 32 +++++ modules/jenkins/examples/complete/main.tf | 64 +++++++++ .../jenkins/examples/complete/variables.tf | 10 ++ modules/jenkins/examples/complete/versions.tf | 10 ++ modules/jenkins/examples/complete/vpc.tf | 130 ++++++++++++++++++ samples/simple-build-pipeline/README.md | 1 + samples/simple-build-pipeline/dns.tf | 2 +- 8 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 modules/jenkins/examples/complete/dns.tf create mode 100644 modules/jenkins/examples/complete/local.tf create mode 100644 modules/jenkins/examples/complete/main.tf create mode 100644 modules/jenkins/examples/complete/variables.tf create mode 100644 modules/jenkins/examples/complete/versions.tf create mode 100644 modules/jenkins/examples/complete/vpc.tf diff --git a/modules/jenkins/examples/complete/dns.tf b/modules/jenkins/examples/complete/dns.tf new file mode 100644 index 00000000..c2a984d1 --- /dev/null +++ b/modules/jenkins/examples/complete/dns.tf @@ -0,0 +1,60 @@ +########################################## +# Route53 Hosted Zone for FQDN +########################################## +data "aws_route53_zone" "root" { + name = var.fully_qualified_domain_name + private_zone = false +} + +resource "aws_route53_record" "jenkins" { + zone_id = data.aws_route53_zone.root.id + name = "jenkins.${data.aws_route53_zone.root.name}" + type = "A" + alias { + name = module.jenkins.jenkins_alb_dns_name + zone_id = module.jenkins.jenkins_alb_zone_id + evaluate_target_health = true + } +} + +########################################## +# Jenkins Certificate Management +########################################## + +resource "aws_acm_certificate" "jenkins" { + domain_name = "jenkins.${data.aws_route53_zone.root.name}" + validation_method = "DNS" + + tags = { + Environment = "dev" + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_route53_record" "jenkins_cert" { + for_each = { + for dvo in aws_acm_certificate.jenkins.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.root.id +} + +resource "aws_acm_certificate_validation" "jenkins" { + timeouts { + create = "15m" + } + certificate_arn = aws_acm_certificate.jenkins.arn + validation_record_fqdns = [for record in aws_route53_record.jenkins_cert : record.fqdn] +} diff --git a/modules/jenkins/examples/complete/local.tf b/modules/jenkins/examples/complete/local.tf new file mode 100644 index 00000000..ed5d6d00 --- /dev/null +++ b/modules/jenkins/examples/complete/local.tf @@ -0,0 +1,32 @@ +data "aws_availability_zones" "available" {} + + +locals { + + build_farm_compute = { + example_builders : { + ami = "ami-066784287e358dad1" // Amazon Linux 2023 (64-bit x86) + instance_type = "t3.medium" + } + } + + build_farm_fsx_openzfs_storage = { + cache : { + storage_type = "SSD" + throughput_capacity = 160 + storage_capacity = 256 + deployment_type = "MULTI_AZ_1" + route_table_ids = [aws_route_table.private_rt.id] + } + } + + # VPC Configuration + vpc_cidr_block = "10.0.0.0/16" + public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"] + private_subnet_cidrs = ["10.0.3.0/24", "10.0.4.0/24"] + + tags = { + environment = "cgd" + } + azs = slice(data.aws_availability_zones.available.names, 0, 2) +} diff --git a/modules/jenkins/examples/complete/main.tf b/modules/jenkins/examples/complete/main.tf new file mode 100644 index 00000000..d9f4fbcb --- /dev/null +++ b/modules/jenkins/examples/complete/main.tf @@ -0,0 +1,64 @@ +# tflint-ignore: terraform_required_version + +########################################## +# Shared ECS Cluster for Services +########################################## + +resource "aws_ecs_cluster" "jenkins_cluster" { + name = "jenkins-cluster" + + setting { + name = "containerInsights" + value = "enabled" + } +} + +resource "aws_ecs_cluster_capacity_providers" "providers" { + cluster_name = aws_ecs_cluster.jenkins_cluster.name + + capacity_providers = ["FARGATE"] + + default_capacity_provider_strategy { + base = 1 + weight = 100 + capacity_provider = "FARGATE" + } +} + +########################################## +# Jenkins +########################################## + +module "jenkins" { + source = "../.." + + cluster_name = aws_ecs_cluster.jenkins_cluster.name + vpc_id = aws_vpc.jenkins_vpc.id + jenkins_alb_subnets = aws_subnet.public_subnets[*].id + jenkins_service_subnets = aws_subnet.private_subnets[*].id + existing_security_groups = [] + internal = false + certificate_arn = aws_acm_certificate.jenkins.arn + jenkins_agent_secret_arns = var.jenkins_agent_secret_arns + create_ec2_fleet_plugin_policy = true + + # Build Farms + build_farm_subnets = aws_subnet.private_subnets[*].id + + build_farm_compute = local.build_farm_compute + + build_farm_fsx_openzfs_storage = local.build_farm_fsx_openzfs_storage + # Artifacts + artifact_buckets = { + builds : { + name = "game-builds" + enable_force_destroy = true + + tags = { + Name = "game-builds" + } + }, + } + + depends_on = [aws_ecs_cluster.jenkins_cluster, aws_acm_certificate_validation.jenkins] +} diff --git a/modules/jenkins/examples/complete/variables.tf b/modules/jenkins/examples/complete/variables.tf new file mode 100644 index 00000000..dfa6e63f --- /dev/null +++ b/modules/jenkins/examples/complete/variables.tf @@ -0,0 +1,10 @@ +variable "fully_qualified_domain_name" { + type = string + description = "A fully qualified domain name (FQDN) to be used for jenkins. A record will be created on the hosted zone with the following patterns 'jenkins.'" +} + +variable "jenkins_agent_secret_arns" { + type = list(string) + description = "A list of secretmanager ARNs (wildcards allowed) that contain any secrets which need to be accessed by the Jenkins service." + default = [] +} diff --git a/modules/jenkins/examples/complete/versions.tf b/modules/jenkins/examples/complete/versions.tf new file mode 100644 index 00000000..a49b9edb --- /dev/null +++ b/modules/jenkins/examples/complete/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "5.66.0" + } + } +} diff --git a/modules/jenkins/examples/complete/vpc.tf b/modules/jenkins/examples/complete/vpc.tf new file mode 100644 index 00000000..afb10f45 --- /dev/null +++ b/modules/jenkins/examples/complete/vpc.tf @@ -0,0 +1,130 @@ +########################################## +# VPC +########################################## + +resource "aws_vpc" "jenkins_vpc" { + cidr_block = local.vpc_cidr_block + tags = merge(local.tags, + { + Name = "jenkins-vpc" + } + ) + enable_dns_hostnames = true + #checkov:skip=CKV2_AWS_11: VPC flow logging disabled by design +} + +# Set default SG to restrict all traffic +resource "aws_default_security_group" "default" { + vpc_id = aws_vpc.jenkins_vpc.id +} + +########################################## +# Subnets +########################################## + +resource "aws_subnet" "public_subnets" { + count = length(local.public_subnet_cidrs) + vpc_id = aws_vpc.jenkins_vpc.id + cidr_block = element(local.public_subnet_cidrs, count.index) + availability_zone = element(local.azs, count.index) + + tags = merge(local.tags, + { + Name = "pub-subnet-${count.index + 1}" + } + ) +} + +resource "aws_subnet" "private_subnets" { + count = length(local.private_subnet_cidrs) + vpc_id = aws_vpc.jenkins_vpc.id + cidr_block = element(local.private_subnet_cidrs, count.index) + availability_zone = element(local.azs, count.index) + + tags = merge(local.tags, + { + Name = "pvt-subnet-${count.index + 1}" + } + ) +} + +########################################## +# Internet Gateway +########################################## + +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.jenkins_vpc.id + tags = merge(local.tags, + { + Name = "build-pipeline-igw" + } + ) +} + +########################################## +# Route Tables & NAT Gateway +########################################## + +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.jenkins_vpc.id + + # public route to the internet + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + + tags = merge(local.tags, + { + Name = "jenkins-public-rt" + } + ) +} + +resource "aws_route_table_association" "public_rt_asso" { + count = length(aws_subnet.public_subnets) + route_table_id = aws_route_table.public_rt.id + subnet_id = aws_subnet.public_subnets[count.index].id +} + +resource "aws_eip" "nat_gateway_eip" { + depends_on = [aws_internet_gateway.igw] + #checkov:skip=CKV2_AWS_19:EIP associated with NAT Gateway through association ID + tags = merge(local.tags, + { + Name = "jenkins-nat-eip" + } + ) +} + +resource "aws_route_table" "private_rt" { + vpc_id = aws_vpc.jenkins_vpc.id + + # route to the internet through NAT gateway + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat_gateway.id + } + + tags = merge(local.tags, + { + Name = "jenkins-private-rt" + } + ) +} + +resource "aws_route_table_association" "private_rt_asso" { + count = length(aws_subnet.private_subnets) + route_table_id = aws_route_table.private_rt.id + subnet_id = aws_subnet.private_subnets[count.index].id +} + +resource "aws_nat_gateway" "nat_gateway" { + allocation_id = aws_eip.nat_gateway_eip.id + subnet_id = aws_subnet.public_subnets[0].id + tags = merge(local.tags, + { + Name = "jenkins-nat" + } + ) +} diff --git a/samples/simple-build-pipeline/README.md b/samples/simple-build-pipeline/README.md index 7abf0abe..91579696 100644 --- a/samples/simple-build-pipeline/README.md +++ b/samples/simple-build-pipeline/README.md @@ -6,6 +6,7 @@ | [terraform](#requirement\_terraform) | >= 1.0 | | [aws](#requirement\_aws) | 5.66.0 | + ## Providers | Name | Version | diff --git a/samples/simple-build-pipeline/dns.tf b/samples/simple-build-pipeline/dns.tf index a4ae3487..05ee4b70 100644 --- a/samples/simple-build-pipeline/dns.tf +++ b/samples/simple-build-pipeline/dns.tf @@ -9,7 +9,7 @@ data "aws_route53_zone" "root" { resource "aws_route53_record" "jenkins" { zone_id = data.aws_route53_zone.root.id - name = data.aws_route53_zone.root.name + name = "jenkins.${data.aws_route53_zone.root.name}" type = "A" alias { name = module.jenkins.jenkins_alb_dns_name