La revisión Well-Architected dejó cinco riesgos altos en el pilar de costes, y el primero de todos es el que bloquea a los demás: la factura de MercadoFresco no se puede leer. Llega un importe consolidado de 2.237,60 USD al mes y, debajo, una lista de servicios. Nada dice qué parte es producción y qué parte es el entorno de pruebas que Luis dejó encendido, ni cuánto cuesta el catálogo comparado con los pedidos, ni si Madrid sale más caro por cliente que Sevilla.

Esta lección resuelve ese problema antes de tocar ninguna herramienta de análisis. Verás cómo se diseña una estrategia de etiquetado que sirva de verdad, cómo se activan las etiquetas de asignación de costes —y el detalle que arruina a mucha gente: no son retroactivas—, cómo se impone el etiquetado en lugar de pedirlo por correo, cómo se auditan los recursos sin etiquetar, cuándo la cuenta separa mejor que la etiqueta, cómo agrupar conceptos de negocio con categorías de coste, y cómo llegar al dato definitivo con el informe de costes y uso consultado desde Athena. El final es la métrica que cambia la conversación con el gerente: el coste por pedido.

Aviso de coste. Etiquetar es gratis: las etiquetas no cuestan dinero, ni activarlas como dimensiones de asignación de costes, ni las categorías de coste. Lo que sí cuesta es el informe de costes y uso: el almacenamiento en S3 (céntimos con este volumen) y, sobre todo, las consultas de Athena a 5 USD por TB escaneado, que con particiones y formato Parquet se quedan en menos de 1 USD al mes y sin ellas pueden dispararse. Datos, cuentas e identificadores ficticios.

Contenido

  1. El problema real: una factura que nadie puede leer
  2. Qué preguntas debe poder responder MercadoFresco
  3. Diseñar la estrategia de etiquetado
  4. Convenciones de nombres y valores cerrados
  5. Cuántas etiquetas son demasiadas y qué recursos no admiten etiquetas
  6. Activar las etiquetas de asignación de costes
  7. El detalle crítico: las etiquetas no son retroactivas
  8. Imponer el etiquetado en lugar de pedirlo
  9. Políticas de etiquetas de Organizations
  10. Condiciones IAM: aws:RequestTag y aws:TagKeys
  11. Reglas de Config con remediación
  12. Aspectos del CDK, ECS y el pipeline
  13. Qué mecanismo cubre qué: la tabla de decisión
  14. Auditar lo que no está etiquetado y fijar un objetivo realista
  15. La cuenta como dimensión de coste
  16. Categorías de coste: del recurso al concepto de negocio
  17. El informe de costes y uso (CUR)
  18. Consultar el CUR con Athena
  19. Costes compartidos y costes no asignables
  20. Coste unitario: el coste por pedido de MercadoFresco
  21. Mostrar y reintegrar costes: showback y chargeback
  22. Errores comunes y consejos
  23. Ejercicios
  24. Conclusión

El problema real: una factura que nadie puede leer

Marta abre la consola de facturación por primera vez con intención de entenderla. Lo que ve es esto:

Factura de agosto de 2026 — Organizacion o-a1b2c3d4e5
Total antes de impuestos ................................ 2.237,60 USD
  Amazon Relational Database Service .................... 342,60
  Amazon Elastic Container Service ...................... 262,40
  Amazon Virtual Private Cloud .......................... 362,40
  Amazon CloudWatch ..................................... 214,90
  Amazon ElastiCache .................................... 135,40
  Amazon Simple Storage Service ......................... 128,90
  ...

Es información, pero no sirve para decidir nada, porque ninguna pregunta del gerente se responde con esa lista. «¿Cuánto cuesta el entorno de desarrollo?»: la factura no lo sabe, sabe cuánto cuesta RDS en total. «¿La analítica de Sara se paga sola?»: no hay ninguna línea llamada «analítica». «Estos 362 USD de VPC, ¿de qué son?»: NAT Gateway y endpoints repartidos entre todos los componentes. «¿Cuánto cuesta servir a Sevilla?»: esa dimensión directamente no existe.

El diagnóstico es sencillo: la factura está organizada según la estructura de AWS, no según la estructura del negocio. El etiquetado es el mecanismo para traducir de una a la otra.

Qué preguntas debe poder responder MercadoFresco

Antes de decidir qué etiquetas poner, hay que escribir las preguntas. Es el orden correcto y casi nadie lo sigue: la mayoría de las estrategias de etiquetado fracasan porque se diseñan «por si acaso» y acaban con veinte etiquetas que no responden a nada. Las preguntas de MercadoFresco, acordadas entre Marta, Sara y el gerente:

Pregunta Dimensión que la responde Quién pregunta
¿Cuánto cuesta producción frente a preproducción y desarrollo? Entorno y cuenta Gerencia
¿Cuánto cuesta cada parte del sistema? Componente Marta
¿Quién es responsable de cada gasto? Propietario Marta
¿Cuánto imputamos a operaciones y cuánto a marketing? CentroCoste Administración
¿Cuánto cuesta servir cada ciudad? Derivada: no se etiqueta, se calcula Gerencia
¿Cuánto cuesta cada pedido? Derivada: coste total / pedidos Gerencia
¿Qué gasto no está asignado a nadie? Ausencia de etiquetas Marta

Las dos últimas filas contienen la lección más importante de esta sección: no todo se resuelve con una etiqueta. La ciudad no se puede etiquetar porque la infraestructura es compartida: el mismo ALB, el mismo clúster y la misma base de datos sirven a las cuatro ciudades. El coste por ciudad es un reparto, no una medición, y se calcula a partir del número de pedidos. Intentar forzarlo con una etiqueta Ciudad produciría un dato falso con apariencia de dato verdadero, que es peor que no tener el dato.

Diseñar la estrategia de etiquetado

Una etiqueta es un par clave-valor que se adjunta a un recurso. Suena trivial y no lo es: es el único mecanismo transversal que atraviesa todos los servicios de AWS, y las decisiones del primer día se arrastran años. Las cinco etiquetas obligatorias de MercadoFresco, que ya vienes usando desde el módulo 1, ahora con su justificación completa:

Clave Valores permitidos Para qué sirve Obligatoria en
Proyecto mercadofresco Separar del resto si algún día conviven varios proyectos en una cuenta Todo
Entorno produccion, preproduccion, desarrollo Reparto por entorno; base de los presupuestos Todo
Componente tienda, catalogo, pedidos, reparto, analitica La dimensión que más se consulta Todo
Propietario marta, luis, sara Saber a quién preguntar y a quién avisar Todo
CentroCoste operaciones, marketing Imputación contable Todo

Además de estas cinco hay cuatro opcionales, que se permiten pero no se exigen y que resuelven problemas concretos: Temporal=si y FechaBaja=yyyy-mm-dd marcan recursos de vida corta que se pueden borrar sin preguntar; Cumplimiento=rgpd señala los que contienen datos personales; y Automatizacion=apagado-nocturno selecciona qué apaga el planificador.

Una regla de oro sobre la que conviene ser inflexible: una etiqueta que no tiene un consumidor no se crea. Si nadie va a filtrar, agrupar o automatizar por ella, no debe existir, porque cada etiqueta añade fricción a cada creación de recurso y ruido a cada informe.

Convenciones de nombres y valores cerrados

Las etiquetas de AWS distinguen mayúsculas de minúsculas. Entorno=Produccion, entorno=produccion y Entorno=produccion son tres etiquetas distintas que producen tres líneas distintas en el informe de costes. Es la causa número uno de informes de asignación inservibles.

Las convenciones de MercadoFresco, escritas en mercadofresco-infra/docs/etiquetado.md:

Regla Correcto Incorrecto
Claves en PascalCase, sin espacios CentroCoste centro coste, centro_coste
Valores en minúsculas, sin espacios ni acentos preproduccion Preproducción, Pre Produccion
Sin datos personales ni secretos marta marta.gomez@...
Guiones, no guiones bajos apagado-nocturno apagado_nocturno
Valores cerrados, de una lista publicada catalogo catálogo-v2, cat
Prefijo aws: prohibido Lo reserva AWS y no se puede usar

El punto que más discusión genera y que más importa es el de los valores cerrados. Una etiqueta con valores libres es un campo de texto, y un campo de texto siempre acaba con pedidos, Pedidos, pedido, pedidos-nuevo y pedidos2, es decir, con cinco componentes donde hay uno. La lista de valores permitidos se publica, se versiona y se impone técnicamente en las secciones siguientes.

Tres límites técnicos que conviene recordar: 50 etiquetas por recurso, clave de hasta 128 caracteres y valor de hasta 256, y —el que más coste deja sin asignar— las etiquetas no se heredan: una instancia EC2 etiquetada no etiqueta sus volúmenes EBS salvo que se pida explícitamente.

Cuántas etiquetas son demasiadas y qué recursos no admiten etiquetas

Cuántas. La experiencia del sector es consistente: entre 4 y 8 etiquetas obligatorias es el rango que funciona. Por debajo de 4 no se puede repartir el coste con criterio; por encima de 8, la gente copia y pega valores sin pensar y la calidad del dato se desploma. MercadoFresco tiene cinco, que es un buen sitio donde estar. La señal de que sobra alguna es fácil de reconocer: cuando alguien crea un recurso y tiene que preguntar qué valor poner, esa etiqueta sobra o está mal definida.

Qué no se puede etiquetar. Esta es la parte que rompe las expectativas y hay que conocer antes de prometer una cobertura del 100 %:

Concepto ¿Etiquetable? Consecuencia para el coste
EC2, EBS, Lambda, DynamoDB, Aurora, SQS, SNS, EventBridge
Bucket S3 Sí, a nivel de bucket, no de objeto ni prefijo Un bucket compartido entre componentes no se reparte con etiquetas
Transferencia de datos entre AZ No Aparece sin etiqueta; hay que repartirla
Peticiones a KMS La clave sí, el uso no siempre Importe pequeño; se asume compartido
Soporte de AWS y Marketplace No Cargo de cuenta; se reparte por categoría de coste
Créditos, descuentos e impuestos No Se aplican al total, no a la etiqueta

De ahí sale un principio que ahorra frustraciones: el objetivo no es etiquetar el 100 % del coste, sino etiquetar el 100 % de lo etiquetable y repartir el resto con una regla escrita.

Activar las etiquetas de asignación de costes

Aquí está el paso que MercadoFresco no había dado y que convierte una etiqueta en una dimensión de la factura. Poner una etiqueta a un recurso no hace que aparezca en los informes de coste. Hay que activarla explícitamente como etiqueta de asignación de costes, y solo se puede hacer desde la cuenta de gestión (999988887777), en Billing → Cost allocation tags. Hay dos familias: las generadas por AWS, con prefijo aws:, y las definidas por el usuario, que son las tuyas. Las primeras son gratis y sorprendentemente útiles: aws:createdBy registra qué identidad creó el recurso y responde a «¿quién ha encendido esto?» aunque nadie pusiera Propietario; aws:cloudformation:stack-name da el coste por pila, que con la IaC del módulo 9 es un reparto casi gratuito y muy preciso; y aws:ecs:serviceName separa el coste de Fargate entre svc-mercadofresco-tienda-fg y svc-mercadofresco-trabajadores sin etiquetar nada.

Activarlas desde la CLI, en la cuenta de gestión:

# Ver que etiquetas conoce el sistema y cual es su estado
aws ce list-cost-allocation-tags --status Inactive --output table

# Activar las cinco obligatorias y tres generadas por AWS
aws ce update-cost-allocation-tags-status --cost-allocation-tags-status \
  'TagKey=Proyecto,Status=Active' \
  'TagKey=Entorno,Status=Active' \
  'TagKey=Componente,Status=Active' \
  'TagKey=Propietario,Status=Active' \
  'TagKey=CentroCoste,Status=Active' \
  'TagKey=aws:createdBy,Status=Active' \
  'TagKey=aws:cloudformation:stack-name,Status=Active' \
  'TagKey=aws:ecs:serviceName,Status=Active'

# Comprobar el resultado
aws ce list-cost-allocation-tags --status Active --output table

Cuatro detalles de este bloque. El comando vive en el espacio de nombres ce aunque la funcionalidad esté en la consola de facturación, y solo funciona en la cuenta de gestión. Una etiqueta solo aparece en la lista si AWS la ha visto al menos una vez en algún recurso: primero se etiqueta algo, luego se activa. Tras activarla tarda hasta 24 horas en aparecer en los informes. Y hay un límite de 500 etiquetas activas por organización, irrelevante con cinco y muy relevante en organizaciones que las dejaron crecer sin control.

El detalle crítico: las etiquetas no son retroactivas

Esta es la frase que hay que subrayar en toda la lección:

Una etiqueta de asignación de costes solo se aplica al coste generado a partir del momento en que se activa. Nunca hacia atrás.

Consecuencias con los números de MercadoFresco: Marta activa las etiquetas el 12 de agosto, y en Cost Explorer todo el coste del 1 al 11 aparece como (sin etiquetar) en las cinco dimensiones aunque los recursos llevaran etiquetados desde marzo. El primer mes con datos limpios es septiembre; agosto es mixto y no sirve para comparar. Y si dentro de tres meses se añade una sexta etiqueta, las comparaciones interanuales por esa dimensión no existirán hasta el año siguiente.

De ahí tres consejos que ahorran meses: activa las etiquetas el día que definas la convención, aunque la cobertura sea baja, porque el reloj empieza al activar y no al terminar de etiquetar; activa también las generadas por AWS desde el principio, que son gratis y no dependen de nadie; y anota la fecha de activación en el documento de etiquetado, para que dentro de seis meses la respuesta a «¿por qué julio no tiene reparto?» esté escrita.

Hay una excepción parcial: el informe de costes y uso puede regenerarse hacia atrás hasta cierto punto al activar etiquetas nuevas, y algunos meses anteriores llegan a rellenarse. No es fiable ni universal, así que la regla mental sigue siendo: no son retroactivas.

Imponer el etiquetado en lugar de pedirlo

MercadoFresco lleva meses con una convención de etiquetado escrita y una cobertura real del 68 %. No es falta de voluntad: es que pedir por correo que la gente etiquete no funciona nunca, en ninguna empresa. Lo que funciona es hacer que no se pueda crear un recurso mal etiquetado, o que si se crea, se corrija solo.

graph TD
  DEV["Luis crea un recurso"] --> Q1{"¿Por CDK<br/>o pipeline?"}
  Q1 -->|Si| CDK["Aspecto del CDK<br/>etiqueta toda la pila"]
  Q1 -->|No, a mano| IAM["Politica IAM con<br/>aws:RequestTag"]
  IAM -->|Falta etiqueta| DENY["AccessDenied"]
  IAM -->|Etiquetas OK| CREA["Recurso creado"]
  CDK --> CREA
  CREA --> TPOL["Politica de etiquetas<br/>de Organizations"]
  CREA --> CFG["Regla required-tags<br/>de Config + remediacion"]

Los cinco mecanismos son complementarios, no alternativos. Uno a uno.

Políticas de etiquetas de Organizations

Una política de etiquetas (tag policy) se define en la cuenta de gestión y se adjunta a la raíz o a una OU. Define qué claves existen, con qué grafía exacta y qué valores admiten, y puede además impedir operaciones que incumplan la política para tipos de recurso concretos.

{
  "tags": {
    "Entorno": {
      "tag_key": { "@@assign": "Entorno" },
      "tag_value": { "@@assign": ["produccion", "preproduccion", "desarrollo"] },
      "enforced_for": {
        "@@assign": ["ec2:instance", "ec2:volume", "rds:db", "rds:cluster",
                     "s3:bucket", "lambda:function", "dynamodb:table",
                     "elasticache:replicationgroup"]
      }
    },
    "Componente": {
      "tag_key": { "@@assign": "Componente" },
      "tag_value": {
        "@@assign": ["tienda", "catalogo", "pedidos", "reparto", "analitica"]
      }
    },
    "CentroCoste": {
      "tag_key": { "@@assign": "CentroCoste" },
      "tag_value": { "@@assign": ["operaciones", "marketing"] }
    }
  }
}

Las claves Proyecto y Propietario se declaran igual, con su lista cerrada de valores.

Qué hace cada parte. tag_key con @@assign fija la grafía canónica: a partir de ahí, un recurso etiquetado con entorno=produccion en minúscula se marca como no conforme. tag_value con @@assign define la lista cerrada. enforced_for es la parte con dientes: para los tipos listados, la creación o el etiquetado que incumpla la política se rechaza; sin él, la política solo informa. Y el operador @@assign sobrescribe lo heredado, con @@append y @@remove disponibles para combinar políticas de distintos niveles de la jerarquía.

Cómo se aplica:

# Crear la politica en la cuenta de gestion 999988887777
aws organizations create-policy --name "etiquetas-mercadofresco" \
  --type TAG_POLICY --content file://etiquetas-mercadofresco.json

# Adjuntarla a la OU Cargas (produccion, preproduccion y desarrollo)
aws organizations attach-policy \
  --policy-id p-mfetiq0001 --target-id ou-a1b2-cargas001

# Comprobar el cumplimiento en toda la organizacion
aws resourcegroupstaggingapi get-compliance-summary \
  --target-id-filters 111122223333 222233334444 333344445555 \
  --group-by RESOURCE_TYPE

Advertencia sobre enforced_for: se despliega en modo informativo primero, porque activarlo de golpe en producción puede romper el pipeline, el autoescalado o cualquier automatización que cree recursos sin las cinco etiquetas. MercadoFresco lo aplica en tres fases: informativo dos semanas, enforced_for en desarrollo dos semanas más, y solo entonces en producción.

Condiciones IAM: aws:RequestTag y aws:TagKeys

La política de etiquetas gobierna valores; IAM gobierna quién puede hacer qué, y permite exigir etiquetas en el momento de la creación con dos claves de condición: aws:RequestTag/<Clave>, el valor que se intenta poner en la petición, y aws:TagKeys, la lista de claves presentes en ella. Esta política exige las cinco etiquetas al crear instancias y volúmenes, y además impide cambiar Entorno después:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ExigirLasCincoEtiquetasAlCrear",
      "Effect": "Allow",
      "Action": ["ec2:RunInstances", "ec2:CreateVolume"],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/Proyecto": "mercadofresco",
          "aws:RequestTag/Entorno": ["produccion", "preproduccion", "desarrollo"],
          "aws:RequestTag/Componente": [
            "tienda", "catalogo", "pedidos", "reparto", "analitica"
          ]
        },
        "ForAllValues:StringEquals": {
          "aws:TagKeys": [
            "Proyecto", "Entorno", "Componente", "Propietario", "CentroCoste",
            "Temporal", "FechaBaja", "Cumplimiento", "Automatizacion"
          ]
        },
        "ForAnyValue:StringEquals": {
          "aws:TagKeys": ["Propietario", "CentroCoste"]
        }
      }
    },
    {
      "Sid": "ProhibirCambiarElEntornoDeUnRecursoExistente",
      "Effect": "Deny",
      "Action": ["ec2:CreateTags", "ec2:DeleteTags"],
      "Resource": "*",
      "Condition": {
        "ForAnyValue:StringEquals": { "aws:TagKeys": ["Entorno", "CentroCoste"] }
      }
    }
  ]
}

Los operadores de conjunto de IAM son la parte que más se equivoca. StringEquals sobre aws:RequestTag/X exige que la etiqueta X esté presente y tenga uno de esos valores: si falta, la acción se deniega, y esa es la forma de hacer obligatoria una etiqueta. ForAllValues:StringEquals sobre aws:TagKeys significa «todas las claves que envíes deben estar en esta lista», es decir, una lista blanca que impide inventarse etiquetas. ForAnyValue:StringEquals significa «al menos una de estas claves debe estar presente». Y el segundo Statement es un Deny explícito sobre el cambio posterior de Entorno y CentroCoste: sin él, cualquiera podría crear un recurso bien etiquetado y cinco minutos después mover su coste a otro centro de coste.

Una advertencia práctica: exigir etiquetas por IAM en ec2:RunInstances es más complicado de lo que parece, porque una sola llamada crea la instancia, sus volúmenes y sus interfaces de red. Por eso este mecanismo se reserva para la creación manual y el grueso del etiquetado se resuelve en el CDK.

Reglas de Config con remediación

IAM impide crear mal. Config detecta lo que ya está mal, incluido lo creado antes de poner las reglas. La regla gestionada required-tags acepta hasta seis claves con sus valores.

aws configservice put-config-rule --config-rule '{
  "ConfigRuleName": "mercadofresco-etiquetas-obligatorias",
  "Description": "Las cinco etiquetas obligatorias del proyecto",
  "Source": {
    "Owner": "AWS",
    "SourceIdentifier": "REQUIRED_TAGS"
  },
  "InputParameters": "{\"tag1Key\":\"Proyecto\",\"tag1Value\":\"mercadofresco\",\"tag2Key\":\"Entorno\",\"tag2Value\":\"produccion,preproduccion,desarrollo\",\"tag3Key\":\"Componente\",\"tag3Value\":\"tienda,catalogo,pedidos,reparto,analitica\",\"tag4Key\":\"Propietario\",\"tag5Key\":\"CentroCoste\",\"tag5Value\":\"operaciones,marketing\"}",
  "Scope": {
    "ComplianceResourceTypes": [
      "AWS::EC2::Instance", "AWS::EC2::Volume", "AWS::RDS::DBInstance",
      "AWS::S3::Bucket", "AWS::Lambda::Function", "AWS::DynamoDB::Table",
      "AWS::ElasticLoadBalancingV2::LoadBalancer"
    ]
  }
}'

Sobre la remediación, hay que tener criterio y no automatizarlo todo igual:

Etiqueta ¿Remediable automáticamente? Cómo
Proyecto Siempre vale mercadofresco; se aplica sin preguntar
Entorno Se deduce de la cuenta: 111122223333produccion
Propietario Semi Se deduce de aws:createdBy de CloudTrail y se propone
CentroCoste Parcialmente Por defecto operaciones; se revisa a mano
Componente No Nadie salvo el creador sabe si es catálogo o pedidos

MercadoFresco automatiza las dos primeras con un documento de Systems Manager y avisa en las otras tres, con una notificación diaria a alertas-mercadofresco. La regla que evita el desastre: la remediación automática nunca inventa un valor de negocio. Etiquetar todo como Componente=tienda por defecto produciría un informe de costes precioso y completamente falso.

Aspectos del CDK, ECS y el pipeline

El mecanismo que en la práctica resuelve el 90 % del problema no es ninguno de los anteriores: es etiquetar en el origen, en la infraestructura como código del módulo 9. En CDK, los aspectos (Tags) aplican etiquetas a todo el árbol de constructos de una pila, incluidos los recursos que crea un constructo de nivel 3 sin que tú los escribas:

from aws_cdk import App, Stack, Tags

app = App()
pila = PilaTiendaMercadoFresco(app, "MercadoFrescoTiendaProd", entorno="produccion")

# Un solo bloque etiqueta TODOS los recursos de la pila, incluidos los que crea
# ApplicationLoadBalancedFargateService por debajo sin que tu los escribas.
for clave, valor in {
    "Proyecto": "mercadofresco", "Entorno": "produccion",
    "Componente": "tienda", "Propietario": "luis",
    "CentroCoste": "operaciones",
}.items():
    Tags.of(pila).add(clave, valor)

# Excepciones puntuales sin renunciar al aspecto global
Tags.of(pila).add("Automatizacion", "apagado-nocturno",
                  exclude_resource_types=["AWS::ECS::Service"])

app.synth()

Tags.of(pila).add(...) recorre todo el árbol de la pila, que es la diferencia con etiquetar recurso a recurso, y exclude_resource_types permite excepciones. Como la pila la crea CloudFormation, además queda automáticamente aws:cloudformation:stack-name, que da un reparto por pila gratis.

En ECS, dos opciones concretas evitan que el mayor coste de cómputo se quede sin asignar:

aws ecs create-service \
  --cluster ecs-mercadofresco --service-name svc-mercadofresco-tienda-fg \
  --task-definition mercadofresco-tienda \
  --propagate-tags TASK_DEFINITION \
  --enable-ecs-managed-tags \
  --tags key=Proyecto,value=mercadofresco key=Entorno,value=produccion \
         key=Componente,value=tienda key=Propietario,value=luis \
         key=CentroCoste,value=operaciones

--propagate-tags TASK_DEFINITION hace que cada tarea herede las etiquetas: sin esta opción el coste de Fargate no lleva tus etiquetas, que es exactamente lo que le pasaba a MercadoFresco con 262,40 USD mensuales sin repartir. Y --enable-ecs-managed-tags añade aws:ecs:clusterName y aws:ecs:serviceName, útiles para separar la tienda de los trabajadores.

Y en el pipeline (08-04), el paso de despliegue exporta las etiquetas como variables y cdk deploy las recibe. La comprobación se hace además en build-mercadofresco-puerta-calidad: si cdk synth genera una plantilla con algún recurso etiquetable sin las cinco claves, el pipeline falla. Esa es la barrera definitiva.

Qué mecanismo cubre qué: la tabla de decisión

Mecanismo Momento Qué garantiza Qué NO cubre Coste
Aspectos del CDK (09-02) Al desplegar Todo lo creado por IaC lleva las 5 etiquetas Lo creado a mano o por otro medio 0
Puerta de calidad del pipeline (08-02) Antes de desplegar Ninguna plantilla mal etiquetada llega a AWS Lo que no pasa por el pipeline 0
Condiciones IAM (04-01) Al crear a mano No se puede crear sin etiquetas correctas Recursos ya existentes; servicios sin soporte 0
Política de etiquetas (09-04) Al crear y al etiquetar Grafía y valores canónicos en toda la organización Ausencia total de la etiqueta si no hay enforced_for 0
Regla required-tags de Config (05-04) Continuo, tras el hecho Detecta todo lo no conforme, incluido lo antiguo No impide crear; remedia solo lo deducible ~0,001 USD por evaluación
--propagate-tags de ECS Al crear el servicio Que el coste de las tareas lleve etiquetas Otros servicios con el mismo problema 0

La estrategia completa de MercadoFresco es la suma: CDK y pipeline para el camino normal, IAM para el camino manual, política de etiquetas para la grafía, y Config como red de seguridad que audita todo. Ninguno de los cinco sobra y ninguno basta por sí solo.

Auditar lo que no está etiquetado y fijar un objetivo realista

Con los mecanismos activos, queda el trabajo de arqueología: los recursos creados antes. 1. Tag Editor (dentro de Resource Groups) busca recursos por región, tipo y etiqueta —incluida la búsqueda por ausencia de etiqueta— y permite etiquetar en bloque cientos de recursos de una vez: es el atajo para arreglar el histórico. 2. La API de etiquetado de recursos, para automatizarlo:

# Recursos SIN la etiqueta Componente en la cuenta de produccion
aws resourcegroupstaggingapi get-resources --region eu-west-1 \
  --query 'ResourceTagMappingList[?!(Tags[?Key==`Componente`])].ResourceARN' \
  --output text | tr '\t' '\n' | sort

# Etiquetar en bloque un conjunto de ARN conocidos
aws resourcegroupstaggingapi tag-resources \
  --resource-arn-list \
    arn:aws:sqs:eu-west-1:111122223333:cola-mercadofresco-almacen \
    arn:aws:sns:eu-west-1:111122223333:mercadofresco-pedido-confirmado \
  --tags Proyecto=mercadofresco,Entorno=produccion,Componente=pedidos,Propietario=luis,CentroCoste=operaciones

3. Un informe de cobertura que se ejecuta cada lunes y publica el resultado como métrica:

import boto3
from collections import defaultdict

OBLIGATORIAS = {"Proyecto", "Entorno", "Componente", "Propietario", "CentroCoste"}

def cobertura(sesion, entorno):
    api = sesion.client("resourcegroupstaggingapi", region_name="eu-west-1")
    cw = sesion.client("cloudwatch", region_name="eu-west-1")
    total, completos, faltan, huerfanos = 0, 0, defaultdict(int), []

    # El paginador es obligatorio: sin el, solo se miran los primeros 100 recursos
    for pagina in api.get_paginator("get_resources").paginate(ResourcesPerPage=100):
        for recurso in pagina["ResourceTagMappingList"]:
            total += 1
            ausentes = OBLIGATORIAS - {t["Key"] for t in recurso["Tags"]}
            if not ausentes:
                completos += 1
                continue
            for clave in ausentes:
                faltan[clave] += 1
            if len(ausentes) == 5:              # sin ninguna etiqueta obligatoria
                huerfanos.append(recurso["ResourceARN"])

    pct = round(100 * completos / total, 1) if total else 100.0
    print(f"[{entorno}] {completos}/{total} completos = {pct} % "
          f"| huerfanos: {len(huerfanos)}")
    for clave, n in sorted(faltan.items(), key=lambda x: -x[1]):
        print(f"    falta {clave}: {n} recursos")

    cw.put_metric_data(Namespace="MercadoFresco/Tienda", MetricData=[{
        "MetricName": "CoberturaEtiquetado",
        "Dimensions": [{"Name": "Entorno", "Value": entorno}],
        "Value": pct, "Unit": "Percent"}])
    return huerfanos

# En la practica se asume un rol en cada cuenta con sts:AssumeRole
cobertura(boto3.Session(), "produccion")

Dos decisiones del script merecen comentario. Separa recursos incompletos (les falta alguna etiqueta) de huérfanos (no tienen ninguna): los huérfanos son casi siempre restos abandonados y son el mejor sitio por donde empezar a borrar. Y publica CoberturaEtiquetado como métrica personalizada en MercadoFresco/Tienda con dimensión Entorno, para que la cobertura viva en el mismo panel que el negocio y admita una alarma cuando baje.

Un objetivo del 100 % es una forma elegante de garantizar que nadie se tome el indicador en serio, porque hay coste que no se puede etiquetar por definición. Los objetivos de MercadoFresco, escritos y acordados:

Indicador Inicial A 3 meses Estable
Recursos con las 5 etiquetas 68 % 90 % 95 %
Coste asignado a un Componente 61 % 92 % 97 % de lo etiquetable
Recursos sin ninguna etiqueta 47 0 0
Coste con Entorno asignado 74 % 98 % 99 %

Conviene medir los dos indicadores por separado, porque son muy diferentes: cincuenta funciones Lambda sin etiquetar bajan mucho la cobertura por recursos y casi nada la de coste, y un clúster de Aurora sin etiquetar hace lo contrario. El que importa para la factura es la cobertura por coste; el que importa para la higiene operativa, la de recursos.

La cuenta como dimensión de coste

Después de todo el trabajo de etiquetado conviene reconocer algo: para la dimensión Entorno, MercadoFresco no necesitaba etiquetas. La estructura de cuentas del módulo 9 ya separa los entornos de forma perfecta e imposible de falsear:

Cuenta Entorno Coste mensual %
111122223333 producción 1.482,10 USD 66,2 %
222233334444 preproducción 402,80 USD 18,0 %
333344445555 desarrollo 246,50 USD 11,0 %
555566667777 herramientas (pipeline, ECR) 62,40 USD 2,8 %
444455556666 seguridad (trail, Config, GuardDuty) 38,90 USD 1,7 %
999988887777 gestión 4,90 USD 0,2 %
Total 2.237,60 USD 100 %

Este reparto tiene tres propiedades que ninguna etiqueta puede igualar: es completo, porque todo el coste pertenece a una cuenta, incluido el no etiquetable —transferencia entre AZ, soporte, Marketplace—; no se puede falsear ni olvidar, porque nadie crea un recurso «sin cuenta»; y es retroactivo, porque existe desde el primer día sin haber activado nada. De ahí la regla práctica, que es una de las conclusiones del módulo 9 vista desde el dinero:

Lo que quieras separar de verdad, sepáralo por cuenta. Lo que quieras analizar dentro de un mismo ámbito, sepáralo por etiqueta.

Se separa por cuenta cuando además del coste se quiere aislar el radio de daño, el acceso o las cuotas: entornos, unidades de negocio, clientes grandes. Se separa por etiqueta cuando conviven en el mismo ámbito y solo hay que atribuir: componentes, propietarios, centros de coste.

Categorías de coste: del recurso al concepto de negocio

Queda un salto. El gerente no pregunta por cuentas ni por etiquetas: pregunta por conceptos de negocio. «Plataforma», «producto» y «analítica» no son ni una cuenta ni una etiqueta: son combinaciones.

Las categorías de coste (Cost Categories) son reglas definidas en la cuenta de gestión que crean una dimensión nueva a partir de cuentas, etiquetas, servicios o tipos de cargo. Se comportan como una etiqueta más en Cost Explorer, Budgets y el CUR.

aws ce create-cost-category-definition \
  --name "AreaNegocio" \
  --rule-version CostCategoryExpression.v1 \
  --default-value "plataforma" \
  --rules '[
    {
      "Value": "analitica",
      "Rule": {
        "Or": [
          {"Tags": {"Key": "Componente", "Values": ["analitica"], "MatchOptions": ["EQUALS"]}},
          {"Dimensions": {"Key": "SERVICE", "Values": ["Amazon Redshift", "Amazon Athena"], "MatchOptions": ["EQUALS"]}}
        ]
      },
      "Type": "REGULAR"
    },
    {
      "Value": "producto",
      "Rule": {
        "And": [
          {"Dimensions": {"Key": "LINKED_ACCOUNT", "Values": ["111122223333"], "MatchOptions": ["EQUALS"]}},
          {"Tags": {"Key": "Componente", "Values": ["tienda", "catalogo", "pedidos", "reparto"], "MatchOptions": ["EQUALS"]}}
        ]
      },
      "Type": "REGULAR"
    }
  ]'

Una tercera regla, entornos-no-productivos, agrupa por LINKED_ACCOUNT las cuentas 222233334444 y 333344445555.

Cuatro detalles hacen que esto funcione. El orden de las reglas importa: se evalúan de arriba abajo y gana la primera que coincide, por eso analitica va antes que producto. --default-value "plataforma" captura todo lo que no encaja —NAT, ALB, endpoints, observabilidad, gobierno— y es la clave para que no quede coste sin clasificar, que es justo el problema de las etiquetas. Se pueden combinar And, Or y Not mezclando cuentas con etiquetas. Y las categorías sí se aplican con cierto efecto retroactivo al crearse, a diferencia de las etiquetas.

Resultado para MercadoFresco, con las categorías mutuamente excluyentes:

Área de negocio Coste mensual % Interpretación
producto 1.129,80 USD 50,5 % Lo que el cliente usa directamente
entornos-no-productivos 585,80 USD 26,2 % Preproducción + desarrollo
plataforma 306,10 USD 13,7 % Red, observabilidad, gobierno, CI/CD
analitica 215,90 USD 9,6 % Redshift, Athena, informes
Total 2.237,60 USD 100 %

Esta tabla abre la conversación que Marta llevaba meses sin poder tener: más de una cuarta parte de la factura son entornos donde no hay ni un cliente. No es necesariamente malo —hacen falta— pero es un número que merece una decisión, no un accidente.

El informe de costes y uso (CUR)

Cost Explorer, que veremos en 11-03, es una herramienta de análisis interactivo con límites: cuando la pregunta es demasiado específica —«coste por componente y por hora cruzado con los pedidos de cada ciudad»— hace falta el dato en bruto. El AWS Cost and Usage Report (CUR) es el registro más detallado que AWS publica: una fila por recurso, por tipo de uso y por hora, con más de 150 columnas. Contiene:

Grupo de columnas Contenido Ejemplo
lineItem/* Cuenta, servicio, tipo de uso, cantidad, coste no combinado line_item_unblended_cost
product/* Atributos del producto: región, tipo de instancia, familia product_region
pricing/* Modelo de precio aplicado pricing_term: OnDemand, Reserved, SavingsPlan
reservation/*, savingsPlan/* Cobertura y coste amortizado de los compromisos savings_plan_effective_cost
resourceTags/* Tus etiquetas, una columna por etiqueta activada resource_tags_user_componente
costCategory/* Tus categorías de coste cost_category_area_negocio

Configuración de MercadoFresco, creada desde la cuenta de gestión:

aws cur put-report-definition --report-definition '{
  "ReportName": "cur-mercadofresco-horario",
  "TimeUnit": "HOURLY",
  "Format": "Parquet",
  "Compression": "Parquet",
  "AdditionalSchemaElements": ["RESOURCES", "SPLIT_COST_ALLOCATION_DATA"],
  "S3Bucket": "mercadofresco-informes-analitica",
  "S3Prefix": "cur",
  "S3Region": "eu-west-1",
  "AdditionalArtifacts": ["ATHENA"],
  "RefreshClosedReports": true,
  "ReportVersioning": "OVERWRITE_REPORT"
}'

Cada opción y por qué:

  • TimeUnit: HOURLY: sin granularidad horaria no se ve el pico del viernes ni se puede calcular la base estable para los Savings Plans de 11-05.
  • Format: Parquet: columnar y comprimido. Athena cobra por bytes escaneados, y Parquet reduce el escaneo entre 10 y 30 veces frente a CSV: la diferencia entre pagar 0,60 USD al mes y pagar 18.
  • AdditionalSchemaElements: RESOURCES: añade line_item_resource_id. Sin ella el informe no dice a qué recurso corresponde cada línea, que es la mitad de la utilidad. SPLIT_COST_ALLOCATION_DATA reparte además el coste de una tarea de ECS entre sus contenedores.
  • AdditionalArtifacts: ATHENA: genera el manifiesto y el CREATE TABLE sin escribir el esquema a mano. Y RefreshClosedReports reprocesa meses cerrados cuando llegan ajustes o créditos tardíos.
  • Destino mercadofresco-informes-analitica, el mismo bucket de Sara, con una política que permita escribir a billingreports.amazonaws.com.

Advertencia de volumen: este CUR genera unos 1,8 GB al mes en Parquet. Con ciclo de vida a Glacier Instant Retrieval a los 90 días cuesta céntimos; sin él, en tres años son decenas de GB que nadie consulta.

Consultar el CUR con Athena

Con la tabla creada por el artefacto de Athena, la pregunta que abría la lección se responde por fin. Esta consulta reparte el coste por Componente y calcula el porcentaje sobre el total:

-- Coste del mes por Componente, con el porcentaje sobre el total,
-- separando lo compartido y lo no asignado.
WITH lineas AS (
  SELECT
    CASE
      WHEN resource_tags_user_componente IS NULL
        OR resource_tags_user_componente = '' THEN 'sin-etiquetar'
      ELSE resource_tags_user_componente
    END                                   AS componente,
    line_item_usage_account_id            AS cuenta,
    line_item_product_code                AS servicio,
    line_item_unblended_cost              AS coste
  FROM cur_mercadofresco_horario
  WHERE year  = '2026'
    AND month = '8'
    AND line_item_line_item_type IN ('Usage', 'DiscountedUsage', 'SavingsPlanCoveredUsage')
),
totales AS (
  SELECT SUM(coste) AS total FROM lineas
)
SELECT
  l.componente,
  ROUND(SUM(l.coste), 2)                            AS coste_usd,
  ROUND(100 * SUM(l.coste) / MAX(t.total), 1)       AS porcentaje,
  COUNT(DISTINCT l.servicio)                        AS servicios_implicados
FROM lineas l
CROSS JOIN totales t
GROUP BY l.componente
ORDER BY coste_usd DESC;

Las partes que no son evidentes. WHERE year AND month usa las particiones: sin ese filtro Athena escanea todo el histórico y una consulta de 0,05 USD pasa a costar varios euros, que es la primera fuente de facturas sorpresa de Athena. line_item_line_item_type filtra el tipo de línea; sin él se mezclan Tax, Credit, Refund y RIFee con el uso real y las sumas no cuadran con nada. line_item_unblended_cost es el coste no combinado, el que corresponde al uso de esa hora —en 11-03 veremos cuándo conviene el amortizado—. Y el CASE convierte nulos y cadenas vacías en 'sin-etiquetar': sin él, el coste no asignado desaparece del informe, que es la forma más habitual de engañarse a uno mismo.

Resultado sobre el mes de MercadoFresco:

Componente Coste USD % Servicios
pedidos 604,30 27,0 % 11
tienda 512,40 22,9 % 9
compartido (sin componente propio) 452,30 20,2 % 14
catalogo 286,70 12,8 % 8
analitica 214,50 9,6 % 6
reparto 118,90 5,3 % 7
sin-etiquetar 48,50 2,2 % 5
Total 2.237,60 100 %

Y la segunda consulta, la del coste por ciudad, que ilustra el reparto de lo que no se puede etiquetar. Cruza el coste con una tabla de pedidos que Sara ya tiene en Redshift y que se exporta a S3:

-- Coste imputado por ciudad: lo directo no existe, todo es reparto por pedidos.
WITH coste_variable AS (            -- computo, base de datos, colas y red de la tienda
  SELECT SUM(line_item_unblended_cost) AS total
  FROM cur_mercadofresco_horario
  WHERE year = '2026' AND month = '8'
    AND line_item_usage_account_id = '111122223333'
    AND line_item_line_item_type IN ('Usage', 'SavingsPlanCoveredUsage')
    AND resource_tags_user_componente IN ('tienda', 'catalogo', 'pedidos', 'reparto')
),
pedidos_ciudad AS (
  SELECT ciudad, COUNT(*) AS pedidos FROM pedidos_agosto_2026 GROUP BY ciudad
),
total_pedidos AS (SELECT SUM(pedidos) AS n FROM pedidos_ciudad)
SELECT
  p.ciudad,
  p.pedidos,
  ROUND(100.0 * p.pedidos / MAX(t.n), 1)                   AS pct_pedidos,
  ROUND(MAX(c.total) * p.pedidos / MAX(t.n), 2)            AS coste_imputado_usd,
  ROUND(MAX(c.total) / MAX(t.n), 5)                        AS coste_por_pedido_usd
FROM pedidos_ciudad p
CROSS JOIN total_pedidos t
CROSS JOIN coste_variable c
GROUP BY p.ciudad, p.pedidos
ORDER BY p.pedidos DESC;
Ciudad Pedidos % Coste imputado Coste por pedido
Madrid 75.600 42,0 % 639,30 USD 0,00846 USD
Barcelona 50.400 28,0 % 426,20 USD 0,00846 USD
Valencia 30.600 17,0 % 258,70 USD 0,00846 USD
Sevilla 23.400 13,0 % 197,90 USD 0,00846 USD
Total 180.000 100 % 1.522,10 USD 0,00846 USD

Y aquí hay que ser honesto con el resultado, porque es una trampa clásica: el coste por pedido sale idéntico en las cuatro ciudades porque lo hemos repartido proporcionalmente a los pedidos. La tabla no descubre nada sobre las ciudades; solo traduce el coste a un lenguaje que el negocio entiende. Para que dijera algo real habría que separar lo que sí difiere —por ejemplo, si Sevilla tuviera su propio almacén con infraestructura dedicada, o si el reparto de una ciudad usara más llamadas a la API de mapas—. Un reparto lineal es una convención contable, no un descubrimiento. Decirlo en voz alta cuando se presenta la tabla es lo que separa un análisis de una ilusión.

Costes compartidos y costes no asignables

Los 452,30 USD de la fila compartido son la parte más interesante del informe, porque es donde vive el gasto que nadie reclama:

Concepto compartido Coste Por qué no tiene componente
NAT Gateway (3 entornos) 243,80 USD Lo usan todos los componentes a la vez
Endpoints de VPC y transferencia entre AZ 118,60 USD Infraestructura de red común
ALB alb-mercadofresco-tienda 47,20 USD Un solo balanceador para toda la tienda
CloudTrail, Config, GuardDuty (cuenta de seguridad) 38,90 USD Gobierno de toda la organización
Route 53 y certificados 3,80 USD Servicio global

Hay tres formas de tratarlo y conviene elegir una y escribirla: dejarlo como «compartido» y presentarlo aparte, que es lo más honesto y lo que hace MercadoFresco porque nadie discute una cifra que no se le imputa; repartirlo proporcionalmente al coste directo de cada componente, sencillo y defendible; o repartirlo por uso real —bytes por el NAT, peticiones por el ALB—, el más justo y el más caro de calcular.

La regla de Marta: repartir solo cuando el reparto cambie una decisión. Si nadie va a hacer nada distinto según cómo se imputen 3,80 USD de Route 53, repartirlos es trabajo perdido. Con los 243,80 USD de NAT sí cambia algo —lleva directamente a la optimización de 11-03— y por eso se analiza en detalle.

Y luego está el coste no asignable de verdad: los 48,50 USD de sin-etiquetar, donde el objetivo no es repartirlos sino que desaparezcan. Cada mes la revisión toma esa lista, identifica los recursos y los etiqueta o los borra: media hora de trabajo que después se mantiene sola.

Coste unitario: el coste por pedido de MercadoFresco

Todo lo anterior desemboca aquí. El coste absoluto es una métrica pobre: si la factura sube de 2.237 a 2.600 USD, ¿es una mala noticia? Depende por completo de si el negocio ha crecido más o menos que eso. La métrica que sí dice algo es el coste unitario: coste dividido por unidad de valor de negocio. Para MercadoFresco, la unidad natural es el pedido.

Coste total mensual (agosto) ... 2.237,60 USD
Pedidos servidos en agosto ..... 180.000
Coste por pedido ............... 0,01243 USD (1,24 centavos de dolar)

Y el desglose por componente del coste por pedido, que es donde aparecen las conversaciones útiles:

Componente Coste mensual Coste por pedido Comentario
pedidos 604,30 USD 0,00336 USD Aurora, colas, Step Functions, Lambdas
tienda 512,40 USD 0,00285 USD Fargate, ALB, CloudFront
compartido 452,30 USD 0,00251 USD El segundo mayor: NAT y red
catalogo 286,70 USD 0,00159 USD ElastiCache, S3, CloudFront
analitica 214,50 USD 0,00119 USD No escala con los pedidos
reparto 118,90 USD 0,00066 USD DynamoDB, Lambdas
sin-etiquetar 48,50 USD 0,00027 USD A eliminar
Total 2.237,60 USD 0,01243 USD

Por qué esta métrica es mejor que el total, con tres ejemplos concretos. Crecimiento sano: si los pedidos suben un 30 % y la factura un 18 %, el coste por pedido baja, la factura ha subido y es una buena noticia; con el total absoluto, esa noticia parece mala. Detección de ineficiencia: si el coste por pedido sube dos meses seguidos sin cambio de arquitectura, hay algo mal —un recurso encendido, una consulta que se ha vuelto cara, un registro que crece sin control—. Y decisiones de producto: permite responder «¿sale rentable el pedido de 12 euros?» y «¿cuánto costará abrir en Portugal?» con un número en lugar de una intuición.

MercadoFresco publica CostePorPedido como métrica personalizada en MercadoFresco/Tienda y la pone en el panel mercadofresco-negocio, al lado de PedidosConfirmados. Es la primera vez que un dato de la factura vive junto a un dato de negocio, y esa vecindad es la mitad del valor. Como apoyo se calculan otras tres unidades: coste por cliente activo (0,048 USD/mes), coste por ciudad servida (559,40 USD) y coste de infraestructura sobre ingresos (0,42 %).

Mostrar y reintegrar costes: showback y chargeback

Con el reparto hecho, queda la parte organizativa, que es donde estas iniciativas mueren o arraigan. Dos modelos:

Modelo Qué es Efecto Riesgo
Showback (mostrar) Se informa a cada equipo de lo que gasta, sin mover dinero Crea conciencia; casi sin fricción Se puede ignorar
Chargeback (reintegrar) El coste se imputa al presupuesto del equipo Cambia comportamientos de verdad Discusiones sobre el reparto; puede incentivar decisiones malas

MercadoFresco, con tres personas, hace showback: un informe mensual automático por Propietario el día 3, que no es una lista de números sino la variación frente al mes anterior, el coste por pedido y una sola recomendación concreta; y con la regla de que nadie se lleve una sorpresa en público, porque si el gasto de Luis se ha disparado, Marta se lo dice antes de la reunión y no durante.

El chargeback llega cuando hay presupuestos departamentales de verdad. Y conviene conocer el efecto perverso que produce mal aplicado: si a un equipo se le cobra cada entorno de pruebas, deja de crear entornos de pruebas y empieza a probar en producción. Un modelo de costes que empeora la ingeniería no es un ahorro.

Errores Comunes y Consejos

Error: creer que poner etiquetas basta para verlas en la factura. El recurso está etiquetado desde marzo y el informe sigue diciendo «sin etiquetar». Consejo: hay que activar cada etiqueta como etiqueta de asignación de costes en la cuenta de gestión, y hasta 24 horas después no aparece.

Error: esperar que las etiquetas sean retroactivas. Se activan en agosto y alguien pide el reparto de mayo. Consejo: no existe y no va a existir. Actívalas el día que definas la convención, aunque la cobertura sea del 40 %.

Error: valores libres. A los seis meses hay Componente con los valores tienda, Tienda, tienda-web, web y front. Consejo: lista cerrada, publicada e impuesta con política de etiquetas de Organizations. Un informe con cinco valores donde hay uno es inservible y ya no se puede arreglar hacia atrás.

Error: pedir el etiquetado por correo. La cobertura sube al 70 % y ahí se queda para siempre. Consejo: el 90 % se resuelve en el CDK con Tags.of(pila) y en la puerta de calidad del pipeline; lo que llega por otra vía se cubre con IAM y se audita con Config.

Error: olvidar --propagate-tags en ECS. El servicio está etiquetado pero las tareas no, y el mayor coste de cómputo aparece sin repartir. Consejo: --propagate-tags TASK_DEFINITION y --enable-ecs-managed-tags en todos los servicios.

Error: perseguir el 100 % de cobertura. Se pierden semanas intentando etiquetar cosas que no admiten etiquetas. Consejo: el objetivo es el 95 % de los recursos y el 97 % del coste etiquetable; el resto se deja como «compartido» a la vista.

Error: escanear todo el CUR en cada consulta de Athena. Una consulta que debía costar 0,03 USD acaba costando varios euros y se repite cada hora en un panel. Consejo: filtra siempre por year y month, usa Parquet y pon un límite de bytes escaneados en el grupo de trabajo de Athena.

Error: presentar el coste absoluto en la reunión mensual. La factura sube, todo el mundo se alarma y nadie sabe si es bueno o malo. Consejo: presenta el coste por pedido primero y el total después: cambia la conversación de «gastamos mucho» a «gastamos bien o mal».

Consejo: activa aws:createdBy desde el primer día. Es gratis, no depende de la disciplina de nadie y responde a la pregunta más frecuente del análisis de costes: «¿quién ha encendido esto?».

Consejo: etiqueta también lo que no cuesta dinero y guarda la convención en mercadofresco-infra, no en un documento suelto. Un grupo de seguridad etiquetado con Proyecto=mercadofresco es la diferencia entre borrar con confianza y no atreverse el día de la limpieza; y una convención versionada se revisa por pull request y no se bifurca.

Ejercicios

Ejercicio 1: diseñar el etiquetado de una empresa nueva

GranjaDigital vende cajas de verdura por suscripción. Tiene una cuenta única de AWS con dos entornos mezclados (producción y pruebas), cuatro personas y cuatro sistemas: web de suscripción, motor de facturación recurrente, planificador de rutas de reparto y panel de informes. Imputa sus costes a dos áreas: «tecnología» y «logística».

  1. Propón un conjunto de etiquetas obligatorias con sus valores permitidos, justificando cada una.
  2. Indica qué separarías por cuenta en lugar de por etiqueta y por qué.
  3. Escribe la política de etiquetas de Organizations para la etiqueta de entorno.
  4. ¿Qué mecanismo de imposición recomiendas primero, teniendo en cuenta que aún no usan IaC?

Ejercicio 2: leer un informe de asignación

Parte del reparto por componente que has visto en la lección: factura total de 2.237,60 USD con 180.000 pedidos, de los cuales analitica supone 214,50 USD. Al mes siguiente, los pedidos suben a 234.000 (+30 %) y la factura total a 2.594,00 USD.

  1. Calcula el coste por pedido de los dos meses.
  2. ¿Es una buena o una mala noticia? Justifícalo.
  3. Si analitica ha pasado de 214,50 a 361,00 USD y el resto ha crecido proporcionalmente a los pedidos, ¿qué investigarías?
  4. Escribe la consulta SQL sobre el CUR que te permitiría confirmar tu sospecha.

Ejercicio 3: costes compartidos y decisiones

De los 452,30 USD compartidos de MercadoFresco, 243,80 USD son NAT Gateway repartidos así: 100,00 USD en producción, 74,00 en preproducción y 69,80 en desarrollo.

  1. Propón dos formas distintas de imputar ese coste a los componentes y di cuál elegirías.
  2. Sin entrar todavía en optimización, ¿qué dos preguntas te sugiere ese reparto?
  3. ¿Cambiaría tu recomendación de imputación si el NAT costara 12 USD al mes en lugar de 243,80? ¿Por qué?

Soluciones

Solución al ejercicio 1

(1) Etiquetas obligatorias propuestas — cuatro, no cinco:

Clave Valores Justificación
Entorno produccion, pruebas Comparten cuenta: es la única forma de separarlos
Componente web, facturacion, rutas, informes Los cuatro sistemas; «qué me cuesta cada cosa»
Area tecnologia, logistica La imputación contable que ya usan
Propietario Nombre de las cuatro personas Saber a quién avisar

No se incluye Proyecto: con un solo proyecto y una sola cuenta sería una etiqueta de un único valor, es decir, ruido.

(2) Qué separar por cuenta. Producción y pruebas deben estar en cuentas separadas, y esta es la recomendación más valiosa del ejercicio: aislamiento del radio de daño, cuotas independientes, permisos limpios y —lo que aquí importa— reparto de coste completo, retroactivo e imposible de falsear, incluido el coste no etiquetable. Con esa separación, Entorno pasa a ser redundante y quedan tres etiquetas obligatorias.

(3) Política de etiquetas para el entorno:

{
  "tags": {
    "Entorno": {
      "tag_key": { "@@assign": "Entorno" },
      "tag_value": { "@@assign": ["produccion", "pruebas"] },
      "enforced_for": { "@@assign": ["ec2:instance", "ec2:volume", "rds:db",
                                     "s3:bucket", "lambda:function"] }
    }
  }
}

Con la advertencia de desplegar enforced_for en dos fases: primero sin él para ver qué se rompe.

(4) Qué imponer primero sin IaC, de mayor a menor retorno inmediato: Config con required-tags y notificación diaria, que no impide nada pero da la foto real en 24 horas; Tag Editor para etiquetar en bloque lo existente, un trabajo de una tarde; la política de etiquetas en modo informativo, para fijar la grafía antes de que se bifurque; y las condiciones IAM solo cuando lo anterior esté estable. Con la recomendación de fondo de empezar a usar IaC: cuatro personas creando recursos a mano garantizan que la cobertura nunca pasará del 80 %.

Solución al ejercicio 2

(1) Coste por pedido:

Mes 1: 2.237,60 / 180.000 = 0,012431 USD · Mes 2: 2.594,00 / 234.000 = 0,011085 USD · variación −10,8 %.

(2) Es una buena noticia, y clara. La factura ha subido un 15,9 % mientras el negocio crecía un 30 %. El coste por pedido baja un 10,8 %, lo que significa que la arquitectura está escalando mejor que linealmente: hay componentes con coste fijo —NAT, ALB, observabilidad, gobierno— que se reparten entre más pedidos. Presentar solo el total («la factura ha subido 356 USD») daría la impresión contraria y podría llevar a frenar el crecimiento por miedo.

(3) Qué investigar en analitica. Ha crecido un 68,3 % mientras el negocio crecía un 30 %: es la única partida que se desvía. Hipótesis por orden de probabilidad: una carga o consulta programada que se ejecuta más veces de las necesarias, o un grupo de trabajo de Redshift que no se pausa; consultas de Athena sin filtrar por partición, que escanean todo el histórico del CUR cada vez; crecimiento real por más datos históricos, que justificaría un 30 % pero no un 68 %; o un informe nuevo que nadie ha dimensionado.

(4) Consulta para confirmarlo:

-- Desglose diario del componente analitica, por servicio y tipo de uso,
-- comparando los dos meses.
SELECT
  month,
  line_item_product_code                                AS servicio,
  line_item_usage_type                                  AS tipo_uso,
  ROUND(SUM(line_item_unblended_cost), 2)               AS coste_usd,
  ROUND(SUM(line_item_usage_amount), 2)                 AS cantidad
FROM cur_mercadofresco_horario
WHERE year = '2026'
  AND month IN ('8', '9')
  AND resource_tags_user_componente = 'analitica'
  AND line_item_line_item_type = 'Usage'
GROUP BY month, line_item_product_code, line_item_usage_type
ORDER BY month, coste_usd DESC;

La columna line_item_usage_type es la que resuelve el caso: distingue entre RPU-Hours de Redshift y DataScanned-Bytes de Athena. Si el crecimiento está en DataScanned-Bytes, la causa es una consulta sin particionar; si está en RPU-Hours, es un grupo de trabajo que no se pausa. Añadir line_item_resource_id al GROUP BY señalaría el recurso exacto.

Solución al ejercicio 3

(1) Dos formas de imputar los 243,80 USD de NAT. La opción A, proporcional al coste directo de cada componente, es trivial de calcular, estable y comprensible, pero asume que un componente caro usa más red, lo que no siempre es cierto: analitica es caro y usa poca salida por NAT, mientras que reparto es barato y llama constantemente a una API externa de mapas. La opción B, por bytes procesados reales según los registros de flujo de la VPC, es el reparto justo, pero exige activar y almacenar esos registros —que cuestan dinero—, procesarlos y aceptar que el resultado varíe cada mes.

Elección: la opción A, y presentar el NAT también como línea propia. Con 243,80 USD, la diferencia entre un reparto y otro es de decenas de dólares y el coste de calcular la opción B se come el beneficio. Lo importante no es a quién se imputa, sino que el número esté a la vista.

(2) Dos preguntas que sugiere el reparto. Primera: ¿por qué desarrollo y preproducción pagan 143,80 USD de NAT, casi tanto como producción, si no hay ni un cliente dentro? Casi todo ese importe es el cargo fijo por hora de cada NAT Gateway, no el tráfico, y si hay dos por redundancia cabe preguntarse si un entorno de desarrollo necesita alta disponibilidad de salida a internet. Segunda: ¿qué tráfico sale por el NAT que podría no salir? Imágenes de ECR, llamadas a S3, registros a CloudWatch: todo eso puede ir por endpoints de VPC. Es el análisis que 10-02 dejó abierto y que 11-03 cierra con números.

(3) ¿Cambiaría con 12 USD? Sí, completamente. Con 12 USD al mes, la respuesta correcta es no imputarlo en absoluto: dejarlo en la bolsa de «compartido» y no dedicarle ni una hora. El criterio no es la pureza contable, sino si el análisis puede cambiar una decisión. Doce dólares no cambian ninguna decisión; 243,80 USD al mes son casi 2.926 USD al año y sí la cambian. El esfuerzo de asignación debe ser proporcional al importe en juego, y esa es la diferencia entre gestión de costes y contabilidad ritual.

Conclusión

MercadoFresco ya no tiene una factura: tiene un modelo de costes. Sabes por qué el problema no era la cifra sino su forma: la factura viene organizada según la estructura de AWS —servicios— y el negocio pregunta según la suya —entornos, componentes, ciudades, pedidos—. Y sabes que el orden correcto para resolverlo es escribir primero las preguntas y solo después decidir las etiquetas, porque las estrategias de etiquetado diseñadas «por si acaso» acaban con veinte claves que no responden a nada.

Tienes la estrategia de etiquetado completa: las cinco obligatorias del curso con su justificación, las opcionales con su consumidor concreto, las convenciones de grafía —valores en minúsculas, sin acentos, claves en PascalCase, prefijo aws: prohibido— y, sobre todo, los valores cerrados, porque una etiqueta de texto libre siempre acaba con cinco variantes del mismo componente. Con el rango que funciona en la práctica, de 4 a 8 etiquetas obligatorias, y la señal inequívoca de que sobran: cuando alguien tiene que preguntar qué valor poner.

Tienes claro que una etiqueta no es una dimensión de coste hasta que se activa en la cuenta de gestión, que las generadas por AWS —aws:createdBy, aws:cloudformation:stack-name, aws:ecs:serviceName— son gratis y no dependen de la disciplina de nadie, y el detalle que arruina a mucha gente: la activación no es retroactiva. El coste anterior queda como «sin etiquetar» para siempre, aunque los recursos llevaran etiquetados meses.

Tienes los cinco mecanismos para imponer el etiquetado en lugar de pedirlo, con lo que cubre cada uno: aspectos del CDK con Tags.of(pila), que resuelven el 90 % del problema de golpe; la puerta de calidad del pipeline, que impide que una plantilla mal etiquetada llegue a AWS; las condiciones IAM con aws:RequestTag y aws:TagKeys, incluido el Deny explícito que evita que alguien cambie el centro de coste después; las políticas de etiquetas de Organizations con enforced_for desplegado por fases; y las reglas de Config como red de seguridad, con la regla que evita el desastre: la remediación automática nunca inventa un valor de negocio. Más el --propagate-tags TASK_DEFINITION de ECS, sin el cual el mayor coste de cómputo aparece sin repartir. Y la auditoría que lo cierra: Tag Editor para la arqueología, la API de etiquetado para automatizarla, el informe semanal publicado como métrica CoberturaEtiquetado, y los dos indicadores que hay que medir por separado —cobertura por recursos y por coste—, con objetivos del 95 % y el 97 % en lugar de un 100 % imposible.

Tienes las dos dimensiones que las etiquetas no cubren. La cuenta, que separa mejor que cualquier etiqueta porque es completa, retroactiva e imposible de falsear —de ahí la regla: lo que quieras separar de verdad, sepáralo por cuenta; lo que quieras analizar dentro de un ámbito, sepáralo por etiqueta—. Y las categorías de coste, que traducen cuentas y etiquetas a conceptos de negocio con un valor por defecto que garantiza que no quede coste sin clasificar, y que revelaron el dato incómodo: más de una cuarta parte de la factura son entornos sin un solo cliente dentro. Todo ello apoyado en el informe de costes y uso configurado con criterio —horario, en Parquet, con RESOURCES y artefacto de Athena, entregado en mercadofresco-informes-analitica— y en las consultas SQL que reparten el coste por componente y por ciudad, con los dos detalles que separan una consulta de 0,03 USD de una de varios euros: filtrar siempre por partición y por line_item_line_item_type. Y con la honestidad de reconocer que un reparto lineal por pedidos es una convención contable, no un descubrimiento.

Y tienes la métrica que cambia la conversación: el coste por pedido, hoy 0,01243 USD sobre 180.000 pedidos y 2.237,60 USD de factura, publicado en el panel mercadofresco-negocio junto a PedidosConfirmados. Con la razón por la que es mejor que el total: cuando el negocio crece un 30 % y la factura un 16 %, el total dice «malas noticias» y el coste unitario dice la verdad. Más el modelo organizativo elegido —showback, no chargeback— y el efecto perverso que conviene tener presente: un modelo de costes que hace que la gente deje de crear entornos de pruebas no es un ahorro.

Con la factura por fin legible, la siguiente pregunta ya no es quién gasta, sino en qué, y sobre todo qué de todo esto no hacía falta. Los 243,80 USD de NAT Gateway, los 214,90 de CloudWatch y esos 452,30 de coste compartido piden a gritos que alguien los abra y los mire por dentro.

En 11-03, «AWS Cost Explorer», se abre la factura de verdad: el desglose completo por servicio, los conceptos de facturación que hay que entender antes de mirar un solo gráfico —coste no combinado, amortizado y neto—, las tres sorpresas clásicas que casi siempre están ahí, la detección automática de anomalías, y las diez optimizaciones concretas que MercadoFresco va a ejecutar con su ahorro calculado una por una.

Curso de AWS

Módulo 1: Introducción a AWS

Módulo 2: Servicios principales de AWS

Módulo 3: Redes y entrega de contenido

Módulo 4: Seguridad e identidad

Módulo 5: Monitorización y gestión

Módulo 6: Bases de datos

Módulo 7: Integración de aplicaciones

Módulo 8: Herramientas para desarrolladores

Módulo 9: Infraestructura como código y gobierno de cuentas

Módulo 10: Contenedores en AWS

Módulo 11: Mejores prácticas y gestión de costos

© Copyright 2026. Todos los derechos reservados