DEV Community

Cover image for IAM Principal Cost Allocation para Amazon Bedrock (Novedad)

IAM Principal Cost Allocation para Amazon Bedrock (Novedad)

Hace unos meses inicie una serie de posts sobre como gobernar el uso de IA en AWS. En la primera entrega de esta serie hablamos de cómo dar acceso gobernado a LLMs en AWS desde el día 0: IAM Policies, Guardrails, Inference Profiles y un mecanismo de corte de presupuesto por equipo. Si no la leíste, te recomiendo empezar por ahí.
Podcast: UNA API KEY para LLMs


Pero había un punto que me quedaba pendiente: no podíamos saber quién dentro de un equipo estaba generando el consumo. Cortábamos al equipo entero, y después había que armar una solución custom con Model Invocation Logging para identificar al responsable.

AWS acaba de resolver la parte de visibilidad de forma nativa.

¿Bloquear todo el acceso? Eso sigue siendo otro tema, y creo que de forma nativa sería complejo de implementar un proxy (por ahora).

¿Qué anunció AWS?

El 8 de Abril de 2026, Amazon Bedrock lanzó soporte para asignación de costos por IAM principal, hablando en criollo: por usuario o rol de IAM, directamente en Cost Explorer y en CUR 2.0 (Cost and Usage Report).

¡BIEN! Ahora podemos ver el costo desglosado de Bedrock en Cost Explorer sin muchas vueltas.
Anuncio oficial

Ahora, algo que debes tener claro desde ya: esta funcionalidad es de billing, no de enforcement. Los datos llegan a CUR 2.0 y Cost Explorer con 24-48 horas de latencia. Eso significa que puedes saber quién gastó cuánto, pero no puedes bloquear el acceso en tiempo real con esta data. Para eso, el Budget Cut Lambda de la Parte 1 sigue siendo necesario.

¿Qué cambia respecto a la Parte 1 que habíamos hablado?

En la primera publicación usamos Inference Profiles con tags (CostCenter, Team) para atribuir costos por equipo. Eso sigue siendo válido para agrupar por carga de trabajo. Pero ahora hay una capa adicional: la identidad del que hace la llamada.

Mecanismo Granularidad ¿Qué resuelve?
Inference Profile + Resource Tags Por equipo / carga de trabajo "¿Cuánto gastó el equipo backend en Haiku?"
IAM Principal Cost Allocation (NUEVO) Por usuario / rol "¿Cuánto gastó user@empresa.com en todos los modelos?"

Juntando todo esto, tenemos: quién gastó cuánto, en qué modelo, para qué equipo. Pero recuerda: esta foto la ves con 24-48h de retraso. Es para análisis y chargeback, no para corte en tiempo real.

Pre-requisitos

Todo lo de la Parte 1 más (te invito a leerla)

  • Tags en tus IAM users/roles con atributos de negocio (team, business-unit, project)
  • Acceso a la consola de Billing and Cost Management (si es por Organizations, lo haces desde la cuenta management)
  • CUR 2.0 habilitado (el CUR legacy no soporta esto)

Paso 1: Taggear los IAM Principals

Si usas SSO, cada Permission Set genera un rol en la cuenta target con formato AWSReservedSSO_{PermissionSetName}_{hash}. Estos roles se pueden taggear.

"""
Taggea los roles SSO con atributos de negocio.
Estos tags son los que aparecerán en Cost Explorer y CUR 2.0.
"""
import boto3

iam = boto3.client("iam")

# Mapeo: role SSO -> tags de negocio
SSO_ROLES = {
    "AWSReservedSSO_BackendDev_a1b2c3d4": {
        "team": "backend",
        "business-unit": "BU-ENG-001",
        "department": "engineering",
        "environment": "development",
    },
    "AWSReservedSSO_FrontendDev_e5f6g7h8": {
        "team": "frontend",
        "business-unit": "BU-ENG-002",
        "department": "engineering",
        "environment": "development",
    },
    "AWSReservedSSO_DataTeam_i9j0k1l2": {
        "team": "data",
        "business-unit": "BU-DATA-001",
        "department": "data-science",
        "environment": "development",
    },
}

for role_name, tags_dict in SSO_ROLES.items():
    tags = [{"Key": k, "Value": v} for k, v in tags_dict.items()]

    iam.tag_role(RoleName=role_name, Tags=tags)
    print(f"{role_name} taggeado con: {tags_dict}")
Enter fullscreen mode Exit fullscreen mode

Si además tienen IAM Users (para casos legacy o service accounts), se taggean igual:

aws iam tag-user \
  --user-name "superuser-pipeline-sa" \
  --tags Key=team,Value=data Key=business-unit,Value=BU-DATA-001 Key=project,Value=recommendation-engine
Enter fullscreen mode Exit fullscreen mode

Nota importante de AWS: Los tags solo aparecen para activación en la consola de Billing después de que el principal haya hecho al menos una llamada a Bedrock. Si taggeaste un rol pero nadie lo ha usado aún, no vas a verlo en Cost Allocation Tags.

Paso 2: Activar los tags como Cost Allocation Tags

Activar allocation tag

Billing and Cost Management Console
    → Cost Organization 
        → Cost Allocation Tags (Etiquetas de asignación de costos)
            → Filtrar por "IAM principal type"
            → Seleccionar: team, business-unit, department
            → Click "Activate"
Enter fullscreen mode Exit fullscreen mode

Después de activarlos, los tags tardan hasta 24 horas en estar disponibles para filtrado en Cost Explorer y CUR.

# Verificar qué tags están activos via CLI
aws ce list-cost-allocation-tags \
  --status Active \
  --tag-keys "team" "business-unit" "department" \
  --type "iamPrincipal"
Enter fullscreen mode Exit fullscreen mode

Paso 3: Habilitar IAM Principal en CUR 2.0

Si hay un paso para que todo esto funcione, es este.

Activa la columna line_item_iam_principal en los reportes de costos.

Billing and Cost Management Console
    → Data Exports
        → Create export → Standard data export (CUR 2.0)
            → Additional export content:
                ✅ Include caller identity (IAM principal) allocation data
            → Destino: S3 bucket + Athena integration
        → Save
Enter fullscreen mode Exit fullscreen mode

¿Qué genera esto? Cada línea del CUR 2.0 ahora incluye el ARN exacto del principal que hizo la llamada a Bedrock. Y los tags del principal aparecen con prefijo iamPrincipal/ para no colisionar con resource tags.

¿Cómo se ve la data?

En Cost Explorer: filtrar por equipo/usuario

Una vez activados los tags, Cost Explorer permite agrupar directamente:

Cost Explorer
    → Filtro: Service = "Amazon Bedrock"
    → Group by: Tag → "iamPrincipal/team"
Enter fullscreen mode Exit fullscreen mode

Vas a ver algo como:

iamPrincipal/team    | Costo (USD)
---------------------|------------
backend              | $142.30
data                 | $89.50
frontend             | $23.10
(sin tag)            | $5.40
Enter fullscreen mode Exit fullscreen mode

¿Y si quieres ver quién dentro del equipo backend está consumiendo más? Cambias el Group by:

Cost Explorer
    → Filtro: Service = "Amazon Bedrock"
    → Filtro: Tag "iamPrincipal/team" = "backend"
    → Group by: Tag → "iamPrincipal/business-unit"
Enter fullscreen mode Exit fullscreen mode

BONUS

(Opcional) CUR 2.0 + Athena: análisis avanzado por usuario individual

Si Cost Explorer no te da suficiente granularidad y necesitas ver por usuario individual, puedes consultar CUR 2.0 directamente con Amazon Athena.

Sobre costos de Athena: Athena cobra $5 USD por TB escaneado (con el modo on-demand). Para CUR de organizaciones pequeñas/medianas esto suele ser centavos por consulta. Si quieres reducir costos, activa la compresión del CUR (formato Parquet) y particiona por mes. También existe el modo Provisioned Capacity para uso intensivo.

-- ¿Quiénes son los top 10 consumers de Bedrock este mes?
SELECT
    line_item_iam_principal AS iam_principal,
    tags['iamPrincipal/team'] AS team,
    tags['iamPrincipal/business-unit'] AS business_unit,
    line_item_product_code,
    SUM(line_item_unblended_cost) AS total_cost,
    SUM(line_item_usage_amount) AS total_usage
FROM cur_2_0.bedrock_usage
WHERE
    line_item_product_code = 'AmazonBedrock'
    AND EXTRACT(MONTH FROM billing_period) = EXTRACT(MONTH FROM CURRENT_DATE)
GROUP BY 1, 2, 3, 4
ORDER BY total_cost DESC
LIMIT 10;
Enter fullscreen mode Exit fullscreen mode

Resultado:

iam_principal                                          | team     | business_unit | total_cost
-------------------------------------------------------+----------+---------------+-----------
arn:aws:sts::123456:assumed-role/BackendDev/javier@...  | backend  | BU-ENG-001    | $67.30
arn:aws:sts::123456:assumed-role/DataTeam/maria@...     | data     | BU-DATA-001   | $52.10
arn:aws:sts::123456:assumed-role/BackendDev/luis@...    | backend  | BU-ENG-001    | $41.20
...
Enter fullscreen mode Exit fullscreen mode

Sin proxies, sin Lambdas, sin CloudTrail scraping: sabes exactamente que Javier del equipo backend gastó $67.30 este mes.

Recuerda que cada ejecución tiene un costo mínimo en Athena, pero para un CUR particionado en Parquet suele ser menos de $0.01 por consulta.

Recap ¿Cómo se complementa con la Parte 1?

Componente Parte 1 Parte 2 (lo nuevo)
¿Quién puede usar qué modelo? IAM Policy per-team Sin cambios
¿Se protege PII? Bedrock Guardrails Sin cambios
¿Cuánto gastó cada equipo? Inference Profile tags → Cost Explorer IAM Principal tags → Cost Explorer (más granular)
¿Cuánto gastó cada usuario? ❌ No disponible line_item_iam_principal en CUR 2.0
¿Se corta al exceder presupuesto? Budget Cut Lambda (per-team, ~10 min) ⚠️ CUR tiene 24h de delay. NO sirve para bloquear. El Budget Cut Lambda sigue siendo el único mecanismo de corte

Lo que sigue sin resolver nativamente

Seamos honestos: hay cosas que necesitan un proxy real, y para dar gobierno a la IA es lo mejor que puedes pensar.

  • Corte per-user en tiempo real: CUR tiene 24h de delay. Si necesitas cortar a un usuario específico en minutos, necesitas Model Invocation Logging + Lambda + iam:PutRolePolicy dirigido al session principal
  • Caching de respuestas: No hay caching nativo de Bedrock. Un proxy como LiteLLM puede cachear respuestas repetitivas y ahorrar costos
  • Multi-provider routing: Si quieres probar OpenAI y Anthropic directamente (no vía Bedrock), necesitas una capa de abstracción
  • Observabilidad semántica: Para ver el árbol de razonamiento de un agente, necesitas OTel + Langfuse. CloudTrail te dice "quién llamó", pero no "por qué razonó así"

La clave es que los tags del principal (quién) y los tags del recurso (qué) se complementan en CUR 2.0. No se pisan porque CUR los distingue con prefijos: iamPrincipal/team vs resourceTag/Team.


Conclusiones

Con este anuncio, el modelo de gobierno nativo que planteamos en la Parte 1 gana una pieza que faltaba: visibilidad de costos por usuario, sin añadir infraestructura, sin scraping de CloudTrail, sin Lambdas custom para atribución. Es una herramienta de análisis y chargeback, no de enforcement. El bloqueo en tiempo real sigue dependiendo del Budget Cut Lambda y CloudWatch.

Siempre pensemos en etapas:

  • Día 0: IAM Policies + Guardrails + Inference Profiles → acceso gobernado
  • Día 1: Budget Cut Lambda, protección contra gastos descontrolados
  • Ahora (Día 2): IAM Principal Cost Allocation, saber exactamente quién gasta qué

¿Falta mucho por hacer? Sí. Un proxy real (LiteLLM), observabilidad semántica (Langfuse), y caching siguen siendo evoluciones deseables si la organización escala. Pero la base está puesta, y es 100% nativa.

Lo bueno de todo esto: AWS sigue mejorando sus productos, pero nosotros como arquitectos debemos de saber identificar el uso para nuestras necesidades.

Hoy es cost allocation por principal. Mañana quizás sea throttling per-user nativo o guardrails a nivel de servicio que no requieran guardrailConfig en el código del dev.

Lo que conoces hoy en día en cloud no queda de un lado, todo ese conocimiento es MUY necesario para disponibilizar IA de forma responsable y sobre todo cuantificable.

¿Te gustaría que estemos en 📩 contacto?
Te espero en LinkedIn o desde el Podcast: Cloud para Todos

Héctor Fernández
AWS Community Builder

https://podcast.hectorfernandez.dev

Top comments (0)