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
- El problema real: una factura que nadie puede leer
- Qué preguntas debe poder responder MercadoFresco
- Diseñar la estrategia de etiquetado
- Convenciones de nombres y valores cerrados
- Cuántas etiquetas son demasiadas y qué recursos no admiten etiquetas
- Activar las etiquetas de asignación de costes
- El detalle crítico: las etiquetas no son retroactivas
- Imponer el etiquetado en lugar de pedirlo
- Políticas de etiquetas de Organizations
- Condiciones IAM:
aws:RequestTagyaws:TagKeys - Reglas de Config con remediación
- Aspectos del CDK, ECS y el pipeline
- Qué mecanismo cubre qué: la tabla de decisión
- Auditar lo que no está etiquetado y fijar un objetivo realista
- La cuenta como dimensión de coste
- Categorías de coste: del recurso al concepto de negocio
- El informe de costes y uso (CUR)
- Consultar el CUR con Athena
- Costes compartidos y costes no asignables
- Coste unitario: el coste por pedido de MercadoFresco
- Mostrar y reintegrar costes: showback y chargeback
- Errores comunes y consejos
- Ejercicios
- 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 | Sí | — |
| 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 tableCuatro 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_TYPEAdvertencia 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 |
Sí | Siempre vale mercadofresco; se aplica sin preguntar |
Entorno |
Sí | Se deduce de la cuenta: 111122223333 → produccion |
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=operaciones3. 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ñadeline_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_DATAreparte además el coste de una tarea de ECS entre sus contenedores.AdditionalArtifacts: ATHENA: genera el manifiesto y elCREATE TABLEsin escribir el esquema a mano. YRefreshClosedReportsreprocesa 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 abillingreports.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».
- Propón un conjunto de etiquetas obligatorias con sus valores permitidos, justificando cada una.
- Indica qué separarías por cuenta en lugar de por etiqueta y por qué.
- Escribe la política de etiquetas de Organizations para la etiqueta de entorno.
- ¿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.
- Calcula el coste por pedido de los dos meses.
- ¿Es una buena o una mala noticia? Justifícalo.
- Si
analiticaha pasado de 214,50 a 361,00 USD y el resto ha crecido proporcionalmente a los pedidos, ¿qué investigarías? - 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.
- Propón dos formas distintas de imputar ese coste a los componentes y di cuál elegirías.
- Sin entrar todavía en optimización, ¿qué dos preguntas te sugiere ese reparto?
- ¿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
- ¿Qué es AWS?
- Configuración de tu cuenta de AWS
- Infraestructura global de AWS
- Consola de administración de AWS
- AWS CLI y SDKs
Módulo 2: Servicios principales de AWS
Módulo 3: Redes y entrega de contenido
- Amazon VPC
- Grupos de seguridad y listas de control de acceso
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Módulo 4: Seguridad e identidad
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager y Parameter Store
- AWS Shield
- AWS WAF
Módulo 5: Monitorización y gestión
- Amazon CloudWatch
- AWS X-Ray y trazabilidad distribuida
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Módulo 6: Bases de datos
- Cómo elegir la base de datos adecuada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Módulo 7: Integración de aplicaciones
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrones de integración: idempotencia, reintentos y colas de mensajes fallidos
