EKS y las Zonas Locales usando Terraform

Hello World 🙂
Seguimos avanzando con nuestros ejercicios 100% prácticos sobre Terraform y la creación de recursos en AWS. En esta ocasión, nos enfocaremos en documentar un caso de uso muy interesante que tuve el placer de aplicar en un proyecto real. Durante este ejercicio, crearemos una VPC que extenderemos hacia las Zonas Locales de AWS.
Pero antes de entrar en detalle, es fundamental definir este concepto y entender qué son las Zonas Locales y cómo pueden beneficiar a los clientes.
Las zonas locales de AWS son un tipo de despliegue de infraestructura que coloca determinados servicios de AWS más cerca de sus usuarios finales y cargas de trabajo.
Nos permite ejecutar aplicaciones que requieran una latencia de milisegundos de un solo dígito o un procesamiento de datos local al acercar la infraestructura de AWS a sus usuarios finales y centros de negocios.
Tomado de AWS.
En muchas ocasiones, ya sea por requisitos de la aplicación o del cliente, es necesario que los datos o la aplicación estén en el país o lo más cerca posible del usuario final. Para cumplir con este objetivo, AWS nos ofrece las Zonas Locales, una solución que permite extender nuestras VPCs a ubicaciones geográficas cercanas. En esta ocasión, vamos a combinar una vez más la VPC con un clúster de EKS, pero esta vez nuestros nodos de trabajo o worker nodes estarán específicamente en una Zona Local. También crearemos un EFS, que se integra fácilmente con EKS para compartir información, y configuraremos un JumBox que nos permitirá administrar el clúster.
Es importante destacar que no todos los servicios están disponibles en las Zonas Locales, por lo que es fundamental revisar la documentación para verificar qué servicios están habilitados en la zona en la que queremos trabajar. Por ejemplo, EFS no está disponible en ninguna Zona Local y tampoco todos los tipos de instancias están soportados. Aunque EBS está disponible en todas las Zonas Locales, en muchas de ellas solo podemos usar volúmenes GP2. Por esto, es crucial consultar la documentación antes de comenzar.
Una nota aparte para EKS. Aunque la documentación indica que EKS es compatible con todas las Zonas Locales, es importante aclarar que las subredes principales del clúster deben estar ubicadas en las AZs (Availability Zones) principales de la región. Por ejemplo, en el caso de us-east-1, estas subredes serían us-east-1a, us-east-1b, us-east-1c, us-east-1d, us-east-1e, y us-east-1f. Los nodos trabajadores, en cambio, serán los que se ubiquen en la Zona Local.
También es importante señalar que los nodos trabajadores en las Zonas Locales son de tipo self_managed_node_groups, ya que la documentación de AWS sobre los managed_node_groups lo deja claro: Managed node groups can’t be deployed on AWS Outposts, AWS Wavelength, or AWS Local Zones. No se preocupen, veremos todo esto más adelante de forma práctica en el código. Sin embargo, es fundamental tener estos conceptos claros para evitar confusiones a lo largo del proceso.
-
Nota
- El 20/11/2024 AWS anuncio lo siguiente «Amazon EKS managed node groups now support AWS Local Zones»
En el ejercicio de hoy, trabajaremos con la Zona Local de Buenos Aires(us-east-1-bue-1a). Supongamos que tenemos un cliente en Argentina cuyo requisito es minimizar la latencia. Utilizando una Zona Local, podremos cumplir con esa necesidad de forma efectiva. En esta zona local los tipos de instancias permitidos son T3, C5, R5, G4dn y M5 y solo podemos usar GP2.
¡Prepárate para adentrarte en el mundo de la automatización de infraestructura con Terraform!
Requisitos:
- Tener una cuenta de AWS.
- Tener instalado Terraform.
En esta ocasión el proyecto va a estar formado por 4 carpetas;
- En la carpeta ‘1-iam-role-eks’, definiremos todo lo necesario para crear el rol que se encargará de gestionar la creación del clúster EKS y los otros recursos.
- En la carpeta ‘2-vpc’, configuraremos todo lo necesario para crear la VPC.
- En la carpeta ‘3-eks-efs-ec2’, estableceremos los elementos requeridos para desplegar el clúster EKS, el sistema de archivos EFS, y una instancia EC2 que servirá como Jumbox, además de otros recursos como los grupos de seguridad.
- Finalmente, en la carpeta ‘4-aws-auth’, configuraremos el módulo que editará el config map
aws-authdentro del clúster.
1- Primero debemos acceder a la UI de AWS para activar la zona local que queremos usar, ya que por defecto vienen deshabilitadas;
- Vamos a la consola de EC2
2- Necesitamos exportar nuestras credenciales de AWS en nuestra consola local;
export AWS_ACCESS_KEY_ID="XXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXXX"- Comprobamos que estemos conectados de forma correcta
3- Crear la carpeta 1-iam-role-eks y dentro de ella los siguientes ficheros;
- 1-iam.tf; definiremos el provider, el rol que vamos a crear y las políticas que le vamos a adjuntar.
-
provider "aws" {region = local.region}terraform {required_version = ">= 1.0"required_providers {aws = {source = "hashicorp/aws"version = ">= 4.47"}}}locals {region = "us-east-1"role_name = "TerraformRole-Eks"}resource "aws_iam_role" "terraform_role" {name = local.role_nameassume_role_policy = jsonencode({"Version" : "2012-10-17","Statement" : [{"Effect" : "Allow","Principal" : {"AWS" : ["arn:aws:iam::012345678901:user/poc", # To change"arn:aws:iam::012345678901:root" # To change]},"Action" : "sts:AssumeRole"}]})}resource "aws_iam_role_policy_attachment" "attachment_policy" {role = aws_iam_role.terraform_role.namepolicy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"}
-
- 2-output.tf, definiremos la salida del ARN del rol.
-
output "provisioner_role_arn" {value = aws_iam_role.terraform_role.arn}
-
- Después de crear esos ficheros ya estamos en condiciones de ejecutar Terraform
- Una vez que hemos creado el rol, es necesario editarlo para que pueda asumirse a sí mismo. Esto se logra tomando el ARN del rol y añadiendo assumed-role. El formato sería algo como:
arn:aws:sts::012345678901:assumed-role/TerraformRole-Eks/demo.Este ajuste puede hacerse desde la consola de AWS. Simplemente navega a IAM, busca el rol, y en la sección Trust relationships agrega una nueva entrada con la información mencionada. Alternativamente, también puedes realizar este cambio directamente desde Terraform.Este procedimiento es una buena práctica de seguridad, ya que permite evitar la creación de recursos mediante un usuario específico. Al usar este rol para la creación de recursos, obtenemos mucho más control y seguridad sobre nuestra infraestructura. - Nota: Este paso es crucial, ya que este rol será responsable de crear nuestro clúster de EKS y todos los recursos asociados. Si no lo configuramos correctamente, podríamos encontrarnos con problemas más adelante durante la creación de estos recursos. Asegurarnos de que esté bien configurado desde el principio nos evitará inconvenientes en el futuro.
4- Crear la carpeta 2-vpc y dentro de ella los siguientes ficheros;
- En el fichero 1-vpc.tf, definiremos todo lo necesario para crear nuestra VPC con sus respectivas subredes, incluyendo las subredes en la zona local.
-
rovider "aws" {region = "us-east-1"}locals {name = var.nameregion = var.regionvpc_cidr = var.vpc_cidrcluster_name = var.cluster_nameazs = ["${local.region}a", "${local.region}b", "${local.region}c"]lzs = var.lzs}module "vpc" {source = "terraform-aws-modules/vpc/aws"name = var.namecidr = var.vpc_cidrazs = local.azspublic_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k)]private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 10)]enable_nat_gateway = truesingle_nat_gateway = truecreate_igw = trueenable_dns_hostnames = truepublic_subnet_tags = {"kubernetes.io/cluster/${var.cluster_name}" = "shared""kubernetes.io/role/elb" = "1"}private_subnet_tags = {"kubernetes.io/cluster/${var.cluster_name}" = "shared""kubernetes.io/role/internal-elb" = "1"}}resource "aws_subnet" "public-subnet-lz" {vpc_id = module.vpc.vpc_idcidr_block = cidrsubnet(var.vpc_cidr, 8, 5)availability_zone = local.lzs[0]map_public_ip_on_launch = truetags = merge({ "Name" = "${module.vpc.name}-public-${local.lzs[0]}" },)}resource "aws_subnet" "private-subnet-lz" {vpc_id = module.vpc.vpc_idcidr_block = cidrsubnet(var.vpc_cidr, 8, 10 + 5)availability_zone = local.lzs[0]tags = merge({ "Name" = "${module.vpc.name}-private-${local.lzs[0]}" },)}resource "aws_route_table_association" "public-subnet-lz-rta" {subnet_id = aws_subnet.public-subnet-lz.idroute_table_id = module.vpc.public_route_table_ids[0]}resource "aws_route_table_association" "private-subnet-lz-rta" {subnet_id = aws_subnet.private-subnet-lz.idroute_table_id = module.vpc.private_route_table_ids[0]}
-
- 2-variables.tf, definiremos las variables necesarias para parametrizar nuestra VPC.
-
variable "name" {type = string}variable "vpc_cidr" {type = string}variable "cluster_name" {type = string}variable "region" {type = string}variable "lzs" {type = list(string)}
-
- 3-demo.auto.tfvars, definiremos todos los parámetros necesarios para crear la VPC en nuestro entorno específico. Este archivo es dinámico y puede variar según el entorno en el que estemos trabajando, permitiéndonos ajustar fácilmente configuraciones como el rango de CIDR, las subred de la local zone, region, etc.
-
name = "eks-lz"vpc_cidr = "10.0.0.0/16"cluster_name = "demo-lz"region = "us-east-1"lzs = ["us-east-1-bue-1a"] # Zona Local de Buenos Aires
-
- 4-outputs.tf, defineremos las salidas que queremos obtener al ejecutar terraform apply.
-
output "vpc_id" {description = "The ID of the VPC"value = try(module.vpc.vpc_id, "")}output "vpc_id_cidr" {value = try(module.vpc.vpc_cidr_block)}output "public_subnets" {description = "List of IDs of public subnets"value = module.vpc.public_subnets}output "private_subnets" {description = "List of IDs of private subnets"value = module.vpc.private_subnets}output "public_subnets_local_zone" {value = aws_subnet.public-subnet-lz.id}output "private_subnets_local_zone" {value = aws_subnet.private-subnet-lz.id}
-
- Después de crear esos ficheros ya estamos en condiciones de ejecutar Terraform
5- Crear la carpeta 3-eks-efs-ec2 y dentro de ella los siguientes ficheros;
- 0-provider.tf, definiremos los proveedores que utilizaremos en este proyecto y el rol con el cual vamos a crear el clúster. Estos proveedores son esenciales, ya que permiten a Terraform interactuar con los distintos servicios de infraestructura en la nube necesarios para desplegar los recursos correctamente.
-
terraform {required_version = ">= 1.0"required_providers {aws = {source = "hashicorp/aws"version = ">= 4.47"}kubernetes = {source = "hashicorp/kubernetes"version = ">= 2.10"}helm = {source = "hashicorp/helm"version = ">= 2.5.0"}kubectl = {source = "gavinbunney/kubectl"version = ">= 1.14"}}}provider "aws" {region = local.regiondefault_tags {tags = local.common_tags}assume_role {role_arn = var.terraformrole}}provider "helm" {kubernetes {host = module.eks_blueprints.eks_cluster_endpointcluster_ca_certificate = base64decode(module.eks_blueprints.eks_cluster_certificate_authority_data)exec {api_version = "client.authentication.k8s.io/v1beta1"command = "aws"# This requires the awscli to be installed locally where Terraform is executedargs = ["eks", "get-token", "--cluster-name", module.eks_blueprints.eks_cluster_id]}}}provider "kubernetes" {host = module.eks_blueprints.eks_cluster_endpointcluster_ca_certificate = base64decode(module.eks_blueprints.eks_cluster_certificate_authority_data)exec {api_version = "client.authentication.k8s.io/v1beta1"command = "aws"# This requires the awscli to be installed locally where Terraform is executedargs = ["eks", "get-token", "--cluster-name", module.eks_blueprints.eks_cluster_id]}}provider "kubectl" {apply_retry_count = 10host = module.eks_blueprints.eks_cluster_endpointcluster_ca_certificate = base64decode(module.eks_blueprints.eks_cluster_certificate_authority_data)load_config_file = falseexec {api_version = "client.authentication.k8s.io/v1beta1"command = "aws"# This requires the awscli to be installed locally where Terraform is executedargs = ["eks", "get-token", "--cluster-name", module.eks_blueprints.eks_cluster_id]}}
-
- 1-locals.tf, definiremos las variables locales del proyecto. Estas variables ayudan a centralizar valores que se utilizarán en múltiples recursos, simplificando la configuración y facilitando el mantenimiento del código
-
locals {ng_name = "demo-localzone"region = var.regioncluster_name = var.cluster_namename = basename(path.cwd)tags = var.tagscluster_version = var.cluster_versiondomain_for_route53 = var.domain_name_in_route53common_tags = {Environment = var.environmentTerraform = "true"}}
-
- 2-eks.tf, en este fichero, definiremos todo lo necesario para crear nuestro clúster de EKS. A diferencia de la entrada anterior donde hablamos de EKS y utilizamos el módulo
terraform-aws-modules/eks/aws, en esta ocasión haremos uso del móduloterraform-aws-eks-blueprints. Este módulo nos permitirá crear nuestros nodos de trabajo sin inconvenientes en la zona local utilizando el tipo self_managed_node_groups, ya que como mencionamos al principio, según la documentación de AWS, los nodos de tipo managed_node_groups no se pueden crear en una Zona Local.-
module "eks_blueprints" {source = "github.com/aws-ia/terraform-aws-eks-blueprints?ref=v4.25.0"cluster_name = var.cluster_namevpc_id = var.vpc_idprivate_subnet_ids = var.private_subnetscluster_version = var.cluster_versioncluster_service_ipv4_cidr = var.service_cidr_blockcloudwatch_log_group_retention_in_days = var.cloudwatch_log_group_retention_in_dayscluster_enabled_log_types = ["audit", "api", "authenticator", "controllerManager", "scheduler"]cluster_endpoint_public_access = var.cluster_endpoint_public_accesscluster_endpoint_private_access = var.cluster_endpoint_private_access#EKS LOCAL ZONE NODE GROUPself_managed_node_groups = {smng = {node_group_name = "smng"instance_type = "t3.medium" # Tipo de instancia soportado en la zona local de Buenos Airessubnet_ids = [var.private_subnets_local_zone]launch_template_os = "amazonlinux2eks" # amazonlinux2eks or bottlerocket or windowsenable_monitoring = truemin_size = "1"max_size = "5"desired_size = "3"block_device_mappings = [{device_name = "/dev/xvda"volume_type = "gp2"volume_size = "20"},]},}cluster_security_group_additional_rules = {ingress_nodes = {description = "Allow all connections from nodes"protocol = "-1"from_port = 0to_port = 0type = "ingress"source_node_security_group = true}}node_security_group_additional_rules = {# Extend node-to-node security group rules. Recommended and required for the Add-onsingress_self_all = {description = "Node to node all ports/protocols"protocol = "-1"from_port = 0to_port = 0type = "ingress"self = true}# Recommended outbound traffic for Node groupsegress_all = {description = "Node all egress"protocol = "-1"from_port = 0to_port = 0type = "egress"cidr_blocks = ["0.0.0.0/0"]ipv6_cidr_blocks = ["::/0"]}# Custom ruleegress_artifactory = {description = "CIDR artifactory"protocol = "tcp"from_port = 80to_port = 80type = "egress"cidr_blocks = ["10.2.0.0/24"]}ingress_cluster_to_node_all_traffic = {description = "Cluster API to Nodegroup all traffic"protocol = "-1"from_port = 0to_port = 0type = "ingress"source_cluster_security_group = true}}}#Agregar reglas al SG del clusterresource "aws_security_group_rule" "allow_node_sg_to_cluster_sg" {description = "Self-managed Nodegroup to Cluster API/Managed Nodegroup all traffic"source_security_group_id = module.eks_blueprints.worker_node_security_group_idsecurity_group_id = module.eks_blueprints.cluster_primary_security_group_idtype = "ingress"protocol = "-1"from_port = 0to_port = 0depends_on = [module.eks_blueprints]}resource "aws_security_group_rule" "allow_node_sg_from_cluster_sg" {description = "Cluster API/Managed Nodegroup to Self-Managed Nodegroup all traffic"source_security_group_id = module.eks_blueprints.cluster_primary_security_group_idsecurity_group_id = module.eks_blueprints.worker_node_security_group_idtype = "ingress"protocol = "-1"from_port = 0to_port = 0depends_on = [module.eks_blueprints]}#Agregar reglas al SG de los nodosresource "aws_security_group_rule" "worker_node_sg_update_egress" {depends_on = [module.eks_blueprints]description = "Allow artifactory.vficloud.net CIDR"from_port = 443to_port = 443protocol = "tcp"cidr_blocks = var.artifactorytype = "egress"security_group_id = module.eks_blueprints.worker_node_security_group_id}resource "aws_security_group_rule" "worker_node_sg_update_ingress" {depends_on = [module.eks_blueprints]description = "Allow artifactory.vficloud.net CIDR"from_port = 443to_port = 443protocol = "tcp"cidr_blocks = var.artifactorytype = "ingress"security_group_id = module.eks_blueprints.worker_node_security_group_id}# Aquí pasamos los roles o usuarios que queremos que interactúan con el clúster.module "eks_auth" {source = "../4-aws-auth/"eks = module.eks_blueprintsdepends_on = [null_resource.kubeconfig]map_roles = [{rolearn = "arn:aws:iam::012345678901:role/TerraformRole-Eks" # To changeusername = "admin-role"groups = ["system:masters"]}]map_users = [{userarn = "arn:aws:iam::012345678901:root" # To changeusername = "root"groups = ["system:masters"]},{userarn = "arn:aws:iam::012345678901:user/poc" # To changeusername = "poc"groups = ["system:masters"]}]map_accounts = []}resource "null_resource" "kubeconfig" {depends_on = [module.eks_blueprints]provisioner "local-exec" {interpreter = ["/bin/bash", "-c"]command = <<EOTset -eecho 'Updating Kube config file'# aws sts get-caller-identityexport $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \$(aws sts assume-role \--role-arn ${var.terraformrole} \--role-session-name TerraformRole \--query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]" \--output text))aws sts get-caller-identityaws eks wait cluster-active --name '${module.eks_blueprints.eks_cluster_id}' --region '${local.region}'aws eks --region ${local.region} update-kubeconfig --name ${module.eks_blueprints.eks_cluster_id} --alias ${module.eks_blueprints.eks_cluster_id}EOT}}
-
- 3-efs.tf, definiremos todo lo necesario para la creación del EFS, permitiendo que esté preparado para integrarse con el clúster EKS.
-
resource "aws_efs_file_system" "demo_efs" {creation_token = "demo"performance_mode = "generalPurpose"throughput_mode = "bursting"encrypted = truetags = {Name = "demo"}}resource "aws_efs_mount_target" "aws_efs" {for_each = toset(var.private_subnets)file_system_id = aws_efs_file_system.demo_efs.idsubnet_id = each.keysecurity_groups = [aws_security_group.efs.id]}
-
- 4-ec2.tf, configuraremos nuestro servidor JumpBox, que nos permitirá acceder al clúster de EKS. A través de user_data, instalaremos las herramientas necesarias para la gestión y administración del clúster, como Git, kubectl, Helm y Terraform. Este servidor funcionará como un punto de control seguro y centralizado para nuestras operaciones en el entorno de Kubernetes.
-
resource "aws_instance" "jumbox" {ami = data.aws_ami.amazon_linux.idinstance_type = "t2.micro"iam_instance_profile = "${aws_iam_instance_profile.demo_profile.name}"subnet_id = "subnet-id" # To changeassociate_public_ip_address= truevpc_security_group_ids = [aws_security_group.ec2.id]tags= {Name = "jumbox"}user_data = <<-EOF#!/bin/bashsudo yum updatesudo yum -y install telnet git# Install Kubectlcurl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.28.8/2024-04-19/bin/linux/amd64/kubectlcurl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.28.8/2024-04-19/bin/linux/amd64/kubectl.sha256openssl sha1 -sha256 kubectlchmod +x ./kubectlmkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$HOME/bin:$PATHecho 'export PATH=$HOME/bin:$PATH' >> ~/.bashrckubectl version --client# Install Helmcurl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 > get_helm.shchmod 700 get_helm.sh./get_helm.shhelm version# Install Terraformsudo yum install -y yum-utilssudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.reposudo yum -y install terraformEOF}data "aws_ami" "amazon_linux" {most_recent = truefilter {name = "name"values = ["al202*-ami-202*"]}filter {name = "architecture"values = ["x86_64"]}owners = ["amazon"]
-
- 5-security.tf, configuraremos los grupos de seguridad necesarios para el correcto funcionamiento del EFS y del servidor JumpBox. Estos grupos de seguridad permitirán gestionar de manera segura el acceso dentro de nuestra infraestructura.
-
resource "aws_security_group" "efs" {name = "efs-sg"description= "Allow inbound efs traffic from ec2"vpc_id = "vpc-id" # To changeingress {cidr_blocks = ["10.0.0.0/16"] # VPC CIDRfrom_port = 2049to_port=2049protocol = "tcp"}ingress {security_groups = [aws_security_group.ec2.id]from_port = 2049to_port=2049protocol = "tcp"}egress {cidr_blocks = ["10.0.0.0/16"] # VPC CIDRfrom_port = 0to_port = 0protocol = "-1"}egress {security_groups = [aws_security_group.ec2.id]from_port = 0to_port = 0protocol = "-1"}tags = {Name = "efs-sg"}}resource "aws_security_group" "ec2" {name = "ec2-sg"description = "Allow efs outbound traffic"vpc_id = "vpc-id" # To changeingress {cidr_blocks = ["0.0.0.0/0"]from_port = 22to_port = 22protocol = "tcp"}egress {from_port = 0to_port = 0protocol = "-1"cidr_blocks = ["0.0.0.0/0"]}tags = {Name = "ec2-sg"}}
-
- 6-iam.tf, en este fichero definiremos el rol, las políticas y el
instance_profileque se asociará al servidor Jumbox. Esto nos permitirá gestionar permisos y conectarnos a la instancia de manera segura desde la consola de AWS, asegurando así un acceso adecuado y controlado al servidor.-
resource "aws_iam_role" "demo_role" {name = "demo_role"assume_role_policy = <<EOF{"Version": "2012-10-17","Statement": [{"Action": "sts:AssumeRole","Principal": {"Service": "ec2.amazonaws.com"},"Effect": "Allow","Sid": ""}]}EOFtags = {tag-key = "demo"}}resource "aws_iam_policy_attachment" "attach_amazon_ssm_full_access-demo_role" {name = "attach-amazon-ssm-full-access-demo_role"roles = [aws_iam_role.demo_role.name]policy_arn = "arn:aws:iam::aws:policy/AmazonSSMFullAccess"}resource "aws_iam_policy_attachment" "attach_amazon_ec2_role_for_ssm-demo_role" {name = "attach-amazon-ec2-role-for-ssm-demo_role"roles = [aws_iam_role.demo_role.name]policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"}resource "aws_iam_instance_profile" "demo_profile" {name = "demo_profile"role = "${aws_iam_role.demo_role.name}"}
-
- 7-variables.tf, definiremos todas las variables que necesitaremos para parametrizar el proyecto. Estas variables permitirán una mayor flexibilidad y reutilización del código, facilitando la personalización de los recursos y haciendo que el despliegue sea más adaptable a diferentes entornos y requisitos específicos.
-
variable "region" {description = "Region"type = stringdefault = ""}variable "environment" {description = "Environment Name"type = stringdefault = "Dev"}variable "terraformrole" {description = "Terraform Role"type = stringdefault = ""}variable "vpc_id" {type = stringdescription = "The IP of the VPC"}variable "private_subnets" {type = list(string)description = "Private subnet of the AZs"}variable "private_subnets_local_zone" {type = stringdescription = "Private subnet of the Local zone"}variable "cluster_name" {type = stringdescription = "Cluste name"}variable "cluster_version" {description = "EKS cluster version to use"type = stringdefault = ""}variable "service_cidr_block" {type = stringdescription = "EKS Service CIDR Block for EKS"}variable "cloudwatch_log_group_retention_in_days" {type = numberdescription = "Number of days to retain EKS logs for control plane"}variable "domain_name_in_route53" {type = string}variable "cluster_endpoint_public_access" {description = "Default to EKS resource and it is true"type = booldefault = true}variable "cluster_endpoint_private_access" {description = "Default to EKS resource and it is false"type = booldefault = false}variable "artifactory" {type = listdescription = "Allow artifactory connection with to connect to eks"}variable "tags" {description = "A map of tags that get added to all resources"type = map(string)default = {}}
-
- 8-demo-eks.auto.tfvars, estableceremos todos los valores necesarios para generar nuestro entorno. Esto nos permite un enfoque más dinámico y flexible, ya que cualquier ajuste de valores en el futuro solo requerirá modificar este archivo, facilitando la administración y personalización de nuestro entorno según sea necesario.
-
region = "us-east-1"environment = "poc"terraformrole = "arn:aws:iam::012345678901:role/TerraformRole-Eks" # To changedomain_name_in_route53 = "demo.com"vpc_id = "vpc-id" # To changeprivate_subnets = ["subnet-id", # To change"subnet-id", # To change"subnet-id", # To change]private_subnets_local_zone = "subnet-id" # To changecluster_name = "demo-lz"cluster_version = "1.30"artifactory = ["172.16.200.0/24"]service_cidr_block = "172.25.200.0/22"cloudwatch_log_group_retention_in_days = 180tags = {App_Name = "Kubernetes"Country = "UY"Region = "AMERICA"}
-
- 9-outputs.tf, definiremos las salidas que Terraform nos mostrará tras ejecutar terraform apply. Estas salidas incluirán detalles de los recursos creados, que nos ayudarán a verificar rápidamente el estado del despliegue y a acceder fácilmente a los recursos configurados en el proyecto.
-
output "eks_cluster_id" {value = module.eks_blueprints.eks_cluster_id}output "eks_cluster_arn" {description = "Amazon EKS Cluster Name"value = module.eks_blueprints.eks_cluster_arn}output "eks_cluster_name" {description = "Amazon EKS Cluster Name"value = module.eks_blueprints.eks_cluster_id}output "eks_cluster_endpoint" {description = "Endpoint for your Kubernetes API server"value = module.eks_blueprints.eks_cluster_endpoint}output "oidc_provider" {description = "The OpenID Connect identity provider)"value = module.eks_blueprints.oidc_provider}output "eks_cluster_version" {description = "The Kubernetes version for the cluster"value = module.eks_blueprints.eks_cluster_version}output "cluster_primary_security_group_id" {description = "Cluster security group..."value = module.eks_blueprints.cluster_primary_security_group_id}output "cluster_security_group_id" {description = "EKS Control Plane Security Group ID"value = module.eks_blueprints.cluster_security_group_id}output "cluster_security_group_arn" {description = "Amazon Resource Name (ARN) of the cluster security group"value = module.eks_blueprints.cluster_security_group_arn}output "worker_node_security_group_arn" {description = "Amazon Resource Name (ARN) of the worker node shared security group"value = module.eks_blueprints.worker_node_security_group_arn}output "worker_node_security_group_id" {description = "ID of the worker node shared security group"value = module.eks_blueprints.worker_node_security_group_id}output "self_managed_node_group_iam_instance_profile_id" {description = "IAM instance profile id of managed node groups"value = module.eks_blueprints.self_managed_node_group_iam_instance_profile_id}output "configure_kubectl" {description = "Configure kubectl using AWS cli"value = "aws eks --region ${local.region} update-kubeconfig --name ${local.cluster_name} --alias ${local.cluster_name}"}output "efs_id" {description = "EFS ID"value = aws_efs_file_system.demo_efs.id}output "ec2_id" {description = "EC2 ID"value = aws_instance.jumbox.id}
-
6- Crear la carpeta 4-aws-auth y dentro de ella los siguientes ficheros;
- Este módulo nos va a permitir editar el configmap aws-auth, que es donde están todos los usuarios y roles autorizados a interactuar con el cluster.
- 1-main.tf
-
resource "kubernetes_config_map_v1_data" "aws_auth" {force = truemetadata {name = "aws-auth"namespace = "kube-system"}data = {mapRoles = yamlencode(local.merged_map_roles)mapUsers = yamlencode(var.map_users)mapAccounts = yamlencode(var.map_accounts)}}
-
- 2-locals.tf
-
locals {merged_map_roles = distinct(concat(try(yamldecode(yamldecode(var.eks.aws_auth_configmap_yaml).data.mapRoles), []),var.map_roles,))}
-
- 3-outputs.tf
-
output "map_accounts" {description = "The aws-auth map accounts."value = var.map_accounts}output "map_roles" {description = "The aws-auth map roles ..."value = local.merged_map_roles}output "map_users" {description = "The aws-auth map users."value = var.map_users}
-
- 4-variables.tf
-
variable "eks" {description = "The outputs from the `terraform-aws-eks` module."type = any}variable "map_accounts" {description = "Additional AWS account numbers to add to the aws-auth configmap."type = list(string)default = []}variable "map_roles" {description = "Additional IAM roles to add to the aws-auth configmap."type = list(object({rolearn = stringusername = stringgroups = list(string)}))default = []}variable "map_users" {description = "Additional IAM users to add to the aws-auth configmap."type = list(object({userarn = stringusername = stringgroups = list(string)}))default = []}
-
- 1-main.tf
- Antes de ejecutar terraform debemos exportar el rol creado anteriormente de la siguiente forma;
- credentials=$(aws sts assume-role –role-arn arn:aws:iam::012345678901:role/TerraformRole-Eks –role-session-name «demo» | jq «.Credentials»)
export AWS_ACCESS_KEY_ID=$(echo $credentials | jq -r «.AccessKeyId»)
export AWS_SECRET_ACCESS_KEY=$(echo $credentials | jq -r «.SecretAccessKey»)
export AWS_SESSION_TOKEN=$(echo $credentials | jq -r «.SessionToken»)
- credentials=$(aws sts assume-role –role-arn arn:aws:iam::012345678901:role/TerraformRole-Eks –role-session-name «demo» | jq «.Credentials»)
- Después de crear esos ficheros ya estamos en condiciones de ejecutar Terraform para crear nuestro clúster y demás recursos,

- Actualizar el kubeconfig
- Una ves validado que todo esta bien, vamos ejecutar un deployment en el clúster y validar que nuesta app esta funcionando sin problemas en los nodos trabajadores.
- Si queremos deployar una app de prueba que use el EFS podemos ir al articulo anterior sobre EKS y copiar el codigo relacionado con este apartado en la parte final del articulo.
- Terraform destroy
- Como siempre, al finalizar tus pruebas, recuerda eliminar todos los recursos creados ejecutando
terraform destroy --auto-approve. Este paso es crucial para evitar sorpresas desagradables en tu factura a final del mes. No subestimes la importancia de esta tarea, ya que la acumulación de recursos no utilizados puede generar costos innecesarios. Tómate el tiempo para limpiar tu entorno, eso te ahorrará dolores de cabeza en el futuro 🙂
- Como siempre, al finalizar tus pruebas, recuerda eliminar todos los recursos creados ejecutando
Conclusiones
Las Zonas Locales se presentan como una solución clave para muchos de nuestros clientes, especialmente en el contexto de instituciones financieras y entidades gubernamentales, donde la latencia y el cumplimiento normativo son esenciales. Mantener los datos dentro del país del cliente no solo mejora la experiencia del usuario, sino que también ayuda a cumplir en determinadas ocasiones con las regulaciones locales. Sin embargo, es fundamental estar al tanto de las particularidades de su implementación, como la necesidad de habilitar estas zonas y las limitaciones de ciertos servicios. Comprender estos aspectos nos permitirá ofrecer soluciones efectivas y adaptadas a las necesidades específicas de cada cliente.
Personalmente, cuando tuve la oportunidad de trabajar con las Zonas Locales, fue todo un desafío, ya que nunca había tenido experiencia previa con ellas. Me encontré con varios detalles pequeños pero significativos. Esta experiencia me enseñó que, antes de implementar cualquier funcionalidad en las Zonas Locales, es crucial consultar la documentación y verificar si el servicio que deseamos utilizar está habilitado, estar bien informado es clave para aprovechar al máximo estas configuraciones.
¡Nos vemos en la próxima entrada!
Nota: Todo el código lo pueden encontrar en el siguiente repositorio.
















