AWS DevOps Infrastructure Code: los 9 pasos clave para pasar de cero a operativo

AWS DevOps Infrastructure Code: lo esencial en un artículo — código real, diagramas y pasos concretos, extractos de un curso de 39 lecciones.

AWS DevOps Infrastructure Code: los 9 pasos clave para pasar de cero a operativo

Todo el mundo puede aprender AWS DevOps Infrastructure Code — siempre que siga los pasos en el orden correcto. Hemos condensado un curso completo de 39 lecciones en un recorrido claro, con los extractos de código más útiles.

tl;dr
  • Introducción y Configuración de AWS
  • DevOps e Infraestructura como Código
  • AWS CloudFormation
  • AWS CDK Cloud Development Kit
  • CICD con AWS CodePipeline
~$ cat ./parcours.md # AWS DevOps Infrastructure Code — 10 capítulos
01
Introducción y Configuración AWS
→ Capítulo 00 — Crear una cuenta AWS Free Tier→ Capítulo 00 — IAM : Usuarios, Roles y Políticas+ 2 más lecciones
02
DevOps e Infraestructura como Código
→ Capítulo 01 — La Filosofía DevOps→ Capítulo 01 — Infraestructura como Código (IaC)+ 2 más lecciones
03
AWS CloudFormation
→ Introducción a AWS CloudFormation→ Parameters, Outputs, Mappings y Condiciones+ 2 más lecciones
04
AWS CDK Cloud Development Kit
→ Introducción a AWS CDK — Cloud Development Kit→ CDK en profundidad — Crear una infraestructura real+ 2 más lecciones
05
CICD con AWS CodePipeline
→ CodeCommit y CodeBuild→ CodeDeploy y CodePipeline+ 2 más lecciones
06
Contenedores en AWS Docker ECS Fargate
→ Docker y Amazon ECR→ Amazon ECS y Fargate+ 2 más lecciones
07
Monitorización y Observabilidad
→ CloudWatch — Logs, Métricas y Alarmas→ AWS X-Ray y CloudTrail+ 2 más lecciones
08
Terraform en AWS
→ Introducción a Terraform en AWS→ Terraform — State, Variables y Módulos+ 2 más lecciones
🏁
Proyecto final (+ 2 capítulos en el camino)
→ Te vas con un proyecto concreto y demostrable

Introducción a AWS CDK — Cloud Development Kit

Capítulo 03 — Lección 01 · AWS DevOps : Infraestructura como Código

1. ¿Qué es AWS CDK?

CloudFormation es como rellenar un formulario administrativo CERFA a mano, casilla por casilla, respetando reglas de sintaxis estrictas. Es tedioso pero funciona. El CDK es tener un programa informático que rellena ese formulario por ti: le dices en Python «quiero un servidor de tamaño medio con una base de datos» y genera automáticamente el formulario CERFA completo, correctamente rellenado, con todas las casillas obligatorias. Escribes mucho menos, haces mucho más y aprovechas toda la potencia de un lenguaje de programación real como bucles, funciones y reutilización de código.

El AWS Cloud Development Kit (CDK) es un framework de código abierto que permite definir tu infraestructura AWS con un lenguaje de programación real: Python, TypeScript, Java, C# o Go. El CDK genera automáticamente plantillas CloudFormation a partir de tu código.

NOTEEl principio: escribes Python (o TypeScript, etc.), el CDK compila ese código en una plantilla CloudFormation válida y luego CloudFormation despliega los recursos AWS. Aprovechas toda la potencia de un lenguaje real (bucles, condiciones, funciones, herencia) para describir tu infraestructura.

CDK vs CloudFormation — comparación

CriterioCloudFormation (YAML)AWS CDK (Python)
LenguajeYAML / JSON declarativoPython, TypeScript, Java, C#, Go
ReutilizaciónCopiar-pegar, Nested StacksClases, módulos, herencia
Autocompletado IDELimitado (YAML)Completo con tipos y documentación
LógicaCondiciones y mapeos limitadosif/else, bucles for, funciones completas
PruebasDifícilPruebas unitarias con pytest, jest
Curva de aprendizajeBaja para YAML, pero verbosoRequiere conocimiento de un lenguaje de programación
Bajo el capóEjecutado directamente por CloudFormationGenera CloudFormation y luego lo despliega

Los cuatro conceptos fundamentales del CDK

App (Aplicación CDK)

El punto de entrada de todo proyecto CDK. Una App contiene una o varias Stacks. Es el contenedor raíz de tu infraestructura.

Stack (Pila CDK)

Corresponde a una stack CloudFormation. Contiene Constructs (recursos AWS). Una App puede tener varias Stacks para diferentes entornos.

Construct

El bloque de construcción básico del CDK. Representa un recurso AWS (o un grupo de recursos). Existe en tres niveles: L1, L2, L3.

Environment (Entorno)

La combinación de cuenta AWS + región de destino para el despliegue. Permite desplegar la misma stack en varios entornos.

2. Instalación — Node.js, CDK CLI y Python

El CDK CLI es una herramienta Node.js. Aunque programes en Python, debes tener Node.js instalado.

Requisitos previos

bash
# 1. Verificar Node.js (versión 18+ recomendada)
node --version
bash
# Bootstrap en la región actual
cdk bootstrap

# Bootstrap en una región específica
cdk bootstrap aws://123456789012/eu-west-1

Los Constructs son los bloques básicos del CDK. Existen en tres niveles de abstracción, cada uno ofreciendo un compromiso diferente entre control y simplicidad.

L1 — Constructs de bajo nivel (CfnXxx)

Los constructs L1 (prefijados con Cfn) son envoltorios directos de los recursos CloudFormation. Dan acceso a todas las propiedades pero sin valores predeterminados: debes configurarlo todo explícitamente.

output
from aws_cdk import aws_s3 as s3
import aws_cdk as cdk

class MaStack(cdk.Stack):
    def __init__(self, scope, id, **kwargs):
        super().__init__(scope, id, **kwargs)

        # L1 : CfnBucket = wrapper direct CloudFormation AWS::S3::Bucket
        bucket_l1 = s3.CfnBucket(self, "BucketL1",
            bucket_name="mon-bucket-l1",
            versioning_configuration=s3.CfnBucket.VersioningConfigurationProperty(
                status="Enabled"
            ),
            lifecycle_configuration=s3.CfnBucket.LifecycleConfigurationProperty(
                rules=[s3.CfnBucket.RuleProperty(
                    id="expiration-anciens-objets",
                    status="Enabled",
                    expiration_in_days=90
                )]
            )
        )

L2 — Constructs de alto nivel (recomendados)

Nested Stacks, StackSets, Change Sets y Buenas Prácticas

Capítulo 02 — Lección 03 · AWS DevOps : Infraestructura como Código

1. Nested Stacks — descomponer grandes infraestructuras

Imagina la construcción de un edificio de oficinas. En lugar de que un solo contratista lo haga todo de principio a fin y se vea rápidamente superado, se recurre a especialistas independientes: un electricista para toda la red eléctrica, un fontanero para la fontanería, un carpintero para los tabiques, un técnico de redes para la informática. Cada uno trabaja en su ámbito, produce su informe de trabajo y un jefe de obra coordina el conjunto. Las Nested Stacks funcionan de la misma manera: cada módulo (red, seguridad, base de datos, aplicación) es una plantilla independiente gestionada por un equipo diferente, y una stack padre coordina a todos.

Cuando una infraestructura se vuelve compleja (VPC, bases de datos, aplicaciones, seguridad...), agrupar todo en una sola plantilla se vuelve inmanejable. Las Nested Stacks (pilas anidadas) permiten dividir la infraestructura en módulos reutilizables, cada uno en su propia plantilla.

Arquitectura en capas con las Nested Stacks

output
infrastructure/
├── stack-parent.yaml         # Stack racine — orchestre tout
├── modules/
│   ├── reseau.yaml           # VPC, subnets, Internet Gateway
│   ├── securite.yaml         # Security Groups, IAM Roles
│   ├── base-de-donnees.yaml  # RDS, cache ElastiCache
│   └── application.yaml      # EC2, Load Balancer, Auto Scaling

La stack padre que referencia los módulos

output
AWSTemplateFormatVersion: "2010-09-09"
Description: "Stack parent — orchestre toute l'infrastructure"

Parameters:
  Environnement:
    Type: String
    AllowedValues: [dev, prod]
  URLTemplates:
    Type: String
    Description: "URL S3 de base où sont stockés les templates"
    Default: "https://s3.amazonaws.com/mon-bucket-templates/v1"

Resources:
  # Module réseau
  StackReseau:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub "${URLTemplates}/reseau.yaml"
      Parameters:
        Environnement: !Ref Environnement
        CIDRVpc: "10.0.0.0/16"
      TimeoutInMinutes: 15

  # Module sécurité — dépend du réseau
  StackSecurite:
    Type: AWS::CloudFormation::Stack
    DependsOn: StackReseau
    Properties:
      TemplateURL: !Sub "${URLTemplates}/securite.yaml"
      Parameters:
        Environnement: !Ref Environnement
        VpcId: !GetAtt StackReseau.Outputs.VpcId

  # Module base de données
  StackBDD:
    Type: AWS::CloudFormation::Stack
    DependsOn: StackSecurite
    Properties:
      TemplateURL: !Sub "${URLTemplates}/base-de-donnees.yaml"
      Parameters:
        Environnement: !Ref Environnement
        VpcId: !GetAtt StackReseau.Outputs.VpcId
        SubnetIds: !GetAtt StackReseau.Outputs.SubnetsPrives
        SGBaseDeDonnees: !GetAtt StackSecurite.Outputs.SGBaseDeDonnees
      TimeoutInMinutes: 30

  # Module application
  StackApplication:
    Type: AWS::CloudFormation::Stack
    DependsOn: StackBDD
    Properties:
      TemplateURL: !Sub "${URLTemplates}/application.yaml"
      Parameters:
        Environnement: !Ref Environnement
        VpcId: !GetAtt StackReseau.Outputs.VpcId
        SubnetPublics: !GetAtt StackReseau.Outputs.SubnetsPublics
        URLBDD: !GetAtt StackBDD.Outputs.EndpointBDD
        SGApplication: !GetAtt StackSecurite.Outputs.SGApplication

Outputs:
  URLApplication:
    Description: "URL du Load Balancer de l'application"
    Value: !GetAtt StackApplication.Outputs.URLLoadBalancer

Ejemplo de la plantilla módulo reseau.yaml

output
AWSTemplateFormatVersion: "2010-09-09"
Description: "Module réseau — VPC, subnets publics et privés"

Parameters:
  Environnement:
    Type: String
  CIDRVpc:
    Type: String

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CIDRVpc
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub "vpc-${Environnement}"

  SubnetPublic1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.1.0/24"
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: true

  SubnetPrive1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.10.0/24"
      AvailabilityZone: !Select [0, !GetAZs ""]

Outputs:
  VpcId:
    Value: !Ref VPC
    Export:
      Name: !Sub "${AWS::StackName}-VpcId"
  SubnetsPublics:
    Value: !Join [",", [!Ref SubnetPublic1]]
    Export:
      Name: !Sub "${AWS::StackName}-SubnetsPublics"
  SubnetsPrives:
    Value: !Join [",", [!Ref SubnetPrive1]]
    Export:
      Name: !Sub "${AWS::StackName}-SubnetsPrives"

Desplegar con las plantillas almacenadas en S3

bash
# Uploader les templates dans S3 d'abord
aws s3 sync ./modules/ s3://mon-bucket-templates/v1/ --exclude "*.md"

# Déployer la stack parent
aws cloudformation deploy \
  --stack-name infra-prod \
  --template-file stack-parent.yaml \
  --parameter-overrides \
    Environnement=prod \
    URLTemplates=https://s3.amazonaws.com/mon-bucket-templates/v1 \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM
TIPVentajas de las Nested Stacks: modularidad (cada módulo es independiente), reutilización (el mismo módulo de red para dev y prod), legibilidad (las plantillas siguen siendo cortas) y límite de CloudFormation (500 recursos máx. por stack — las nested stacks sortean este límite).

2. StackSets — desplegar en varias cuentas y regiones

Imagina una cadena de restaurantes que abre sucursales en todo el mundo. El director general crea una receta de menú estándar, una carta gráfica y procedimientos de funcionamiento que se aplican en todos los restaurantes, ya estén en París, Nueva York o Tokio. Cuando se añade una nueva norma (nueva alergia que mostrar, nuevo reglamento sanitario), se aplica simultáneamente en todos los restaurantes de la cadena con una única decisión central. Los StackSets funcionan igual: defines tu infraestructura una sola vez y la despliegas en todas tus cuentas AWS y todas tus regiones en una sola operación.

Los StackSets permiten desplegar la misma plantilla CloudFormation en varias cuentas AWS y/o varias regiones en una sola operación. Es la herramienta ideal para organizaciones con varias cuentas (Landing Zone, Control Tower).

Casos de uso típicos

Gobernanza centralizada

Desplegar reglas de AWS Config, CloudTrail, GuardDuty en todas las cuentas de la organización automáticamente.

Conformidad uniforme

Aplicar las mismas políticas IAM, grupos de seguridad básicos y notificaciones de alerta en cada cuenta.

Multirregión

Desplegar una infraestructura idéntica en us-east-1, eu-west-1 y ap-southeast-1 para la redundancia global.

Requisitos previos: configurar los permisos

bash
# Deux modes de fonctionnement :

# Mode 1 : Self-Managed (rôles IAM manuels dans chaque compte)
# Créer AWSCloudFormationStackSetAdministrationRole dans le compte admin
# Créer AWSCloudFormationStackSetExecutionRole dans chaque compte cible

# Mode 2 : Service-Managed (via AWS Organizations — recommandé)
# Activer la confiance avec Organizations dans CloudFormation
aws organizations enable-aws-service-access \
  --service-principal cloudformation.amazonaws.com

Crear y desplegar un StackSet

Desplegar y gestionar aplicaciones en EKS

Capítulo 08 — Kubernetes en AWS EKS | AWS DevOps Infrastructure as Code

1. Desplegar una aplicación — Deployment YAML

Un Deployment de Kubernetes es como una cadena de comida rápida. Defines cuántos «mostradores» (replicas) quieres abiertos al mismo tiempo. Si uno falla, Kubernetes abre uno nuevo automáticamente sin que los clientes se den cuenta. Cuando quieres cambiar el menú (nueva versión de la imagen), Kubernetes actualiza los mostradores uno a uno (rolling update) para no cerrar nunca todos los mostradores al mismo tiempo. Si el nuevo menú no gusta, se puede volver al menú anterior inmediatamente (rollback).

Un Deployment de Kubernetes declara el estado deseado de tu aplicación: qué imagen usar, cuántas réplicas, cómo realizar las actualizaciones. Kubernetes mantiene ese estado permanentemente.

Deployment completo con todas las buenas prácticas

output
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mon-api-fastapi
  namespace: mon-application
  labels:
    app: mon-api-fastapi
    version: "1.0.0"
    equipe: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mon-api-fastapi
  # Stratégie de déploiement rolling update
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1      # Au plus 1 Pod indisponible pendant la mise à jour
      maxSurge: 1            # Au plus 1 Pod en surplus pendant la mise à jour
  template:
    metadata:
      labels:
        app: mon-api-fastapi
        version: "1.0.0"
    spec:
      serviceAccountName: sa-mon-api  # IRSA — IAM Role for Service Account
      terminationGracePeriodSeconds: 30
      # Sécurité au niveau du Pod
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
        - name: mon-api-fastapi
          image: 123456789012.dkr.ecr.ca-central-1.amazonaws.com/mon-api-fastapi:1.0.0
          ports:
            - containerPort: 8080
              protocol: TCP
          # Limites de ressources obligatoires en production
          resources:
            requests:
              cpu: "250m"        # 0.25 vCPU garanti
              memory: "256Mi"    # 256 Mo garantis
            limits:
              cpu: "500m"        # Maximum 0.5 vCPU
              memory: "512Mi"    # Maximum 512 Mo
          # Variables d'environnement depuis ConfigMap et Secret
          env:
            - name: ENVIRONNEMENT
              value: production
            - name: PORT
              value: "8080"
          envFrom:
            - configMapRef:
                name: config-mon-api
            - secretRef:
                name: secrets-mon-api
          # Sondes de santé
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
            successThreshold: 1
          startupProbe:
            httpGet:
              path: /health
              port: 8080
            failureThreshold: 30
            periodSeconds: 5
          # Sécurité du conteneur
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
      # Distribution entre les zones de disponibilité
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: mon-api-fastapi

Comandos de despliegue esenciales

bash
# Appliquer le manifeste
kubectl apply -f deployment.yaml -n mon-application

# Suivre le déploiement en temps réel
kubectl rollout status deployment/mon-api-fastapi -n mon-application

# Voir l'historique des déploiements
kubectl rollout history deployment/mon-api-fastapi -n mon-application

# Mettre à jour l'image (déclenche un rolling update)
kubectl set image deployment/mon-api-fastapi \
  mon-api-fastapi=123456789012.dkr.ecr.ca-central-1.amazonaws.com/mon-api-fastapi:1.1.0 \
  -n mon-application

# Revenir à la version précédente (rollback)
kubectl rollout undo deployment/mon-api-fastapi -n mon-application

# Revenir à une révision spécifique
kubectl rollout undo deployment/mon-api-fastapi --to-revision=2 -n mon-application

2. Servicios Kubernetes: ClusterIP, NodePort, LoadBalancer

En Kubernetes, los Services son como direcciones postales estables en un edificio cuyos inquilinos cambian a menudo. ClusterIP es una oficina interna en el edificio: nadie del exterior puede acceder, es para la comunicación entre empleados. LoadBalancer es la puerta principal del edificio con un agente de seguridad (el Load Balancer AWS) que dirige a los visitantes externos a la oficina disponible correcta. Aunque los inquilinos cambien (los Pods se reinician), la dirección del edificio sigue siendo la misma.

Un Service expone un conjunto de Pods mediante una IP estable y un nombre DNS interno, independientemente del ciclo de vida de los Pods individuales.

TipoAccesibilidadCaso de uso
ClusterIP Solo dentro del clúster Comunicación entre servicios (base de datos, caché)
NodePort Desde el exterior mediante la IP del Node + un puerto fijo (30000-32767) Pruebas, desarrollo (no recomendado en producción)
LoadBalancer Desde el exterior mediante un Load Balancer AWS (CLB/NLB) aprovisionado automáticamente Exponer un servicio directamente (sin Ingress)
ExternalName Resuelve a un nombre DNS externo Acceder a servicios RDS, ElastiCache mediante un nombre interno
output
---
# Service ClusterIP — pour la communication interne
apiVersion: v1
kind: Service
metadata:
  name: svc-mon-api
  namespace: mon-application
  labels:
    app: mon-api-fastapi
spec:
  type: ClusterIP
  selector:
    app: mon-api-fastapi      # Correspond aux labels du Deployment
  ports:
    - name: http
      port: 80                # Port du Service (interne)
      targetPort: 8080        # Port du conteneur
      protocol: TCP

---
# Service LoadBalancer — expose via un NLB AWS
apiVersion: v1
kind: Service
metadata:
  name: svc-mon-api-public
  namespace: mon-application
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "external"
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
  type: LoadBalancer
  selector:
    app: mon-api-fastapi
  ports:
    - name: http
      port: 80
      targetPort: 8080

3. ConfigMaps y Secrets de Kubernetes

ConfigMap — configuración no sensible

output
apiVersion: v1
kind: ConfigMap
metadata:
  name: config-mon-api
  namespace: mon-application
data:
  # Variables d'environnement
  LOG_LEVEL: "info"
  WORKERS: "4"
  TIMEOUT: "30"
  DATABASE_HOST: "rds-mon-app.ca-central-1.rds.amazonaws.com"
  DATABASE_NAME: "production"
  # Fichier de configuration complet
  application.yaml: |
    server:
      host: 0.0.0.0
      port: 8080
    database:
      pool_size: 10
      max_overflow: 5
    cache:
      backend: redis
      ttl: 300

Secret de Kubernetes

output
apiVersion: v1
kind: Secret
metadata:
  name: secrets-mon-api
  namespace: mon-application
type: Opaque
stringData:
  # stringData accepte les valeurs en clair — K8s les encode en base64 automatiquement
  DATABASE_PASSWORD: "MonMotDePasseSecret"
  API_KEY: "sk-abc123..."
  JWT_SECRET: "ma-cle-jwt-super-secrete"
WARNINGAtención: Por defecto, los Secrets de Kubernetes están simplemente codificados en base64 (no cifrados) en etcd. Para una seguridad reforzada en EKS, activa el cifrado de Secrets con una clave KMS y considera usar AWS Secrets Manager + External Secrets Operator para sincronizar los secretos automáticamente.

External Secrets Operator — sincronizar desde Secrets Manager

output
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: secrets-aws-synchronises
  namespace: mon-application
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: secrets-mon-api   # Nom du Secret Kubernetes à créer
    creationPolicy: Owner
  data:
    - secretKey: DATABASE_PASSWORD
      remoteRef:
        key: "mon-app/production/secrets"
        property: "database_password"
    - secretKey: API_KEY
      remoteRef:
        key: "mon-app/production/secrets"
        property: "api_key"
va-plus-loin

Este artículo cubre los extractos más útiles: el curso completo AWS DevOps Infrastructure Code (10 capítulos, 39 lecciones, ejercicios corregidos y proyecto final) te lleva hasta el final.

./acceder-au-cours-complet curso gratuito: Dominar Claude Code

FAQ

¿Cuánto tiempo se tarda en aprender AWS DevOps Infrastructure Code?
Con una progresión estructurada (10 capítulos, 39 lecciones cortas y prácticas), se alcanza un nivel operativo en unas semanas dedicando 30 a 60 minutos al día. Lo importante es practicar cada concepto de inmediato.
¿Se necesitan requisitos previos?
Es mejor estar cómodo con los fundamentos del área: este contenido profundiza, con casos reales.
¿Por dónde empezar concretamente?
Reproduce los comandos de este artículo y luego sigue el curso completo AWS DevOps Infrastructure Code: encadena las 39 lecciones en orden, con ejercicios y proyecto final.

📬 ¿Quieres recibir este tipo de guía cada semana? Suscríbete gratis — código real, cero palabrería.