Módulo 6: Finops For Tokens
2. Por qué un token es el recurso *usage-based* más extremo del ecosistema
Descripción
finops-and-cost-guardrails-guide ya enseñó la distinción central entre dos formas de facturar: PROVISIONED (reservas capacidad de antemano, pagas exista o no la uses) y PAY_PER_REQUEST (pagas por lo que de verdad ocurre, sin reserva previa) — aplicada, en esa guía, a Shipments, la tabla DynamoDB de Andes Cargo. Esta lección retoma exactamente esa distinción, y da un paso que ninguna guía anterior necesitó dar: en PAY_PER_REQUEST, todavía hay un modo declarado en el HCL (billing_mode = "PAY_PER_REQUEST"), un atributo real que Terraform sabe leer. Con Bedrock on-demand, no hay ni eso — ningún atributo de bedrock.tf representa, ni de lejos, "una invocación al modelo". Es el punto exacto donde el espectro usage-based de este ecosistema termina.
Conexión con el módulo
La lección 1 adelantó esta idea en su sección final, sin desarrollarla. Esta lección la desarrolla con precisión técnica completa, verificada contra el mismo terraform providers schema que el Módulo 3 de esta guía ya usó para extraer el schema real de aws_bedrock_guardrail. La lección 3, inmediatamente después, escribe la política que hace cumplir el único punto de control que sí existe para una carga de tokens: el tag de asignación de costo, no un límite de capacidad.
Analogía: el taxímetro que ni siquiera sabe cuántos viajes harás
Un taxi con taxímetro ya es usage-based — pagas por el viaje que hiciste, no por tener el auto reservado todo el día. Pero el taxímetro, dentro de un viaje, sabe algo: sabe que estás en un viaje, sabe cuándo empezó, sabe la distancia recorrida en tiempo real, y puede mostrarte un número que sube mientras avanzas. Ahora imagina un sistema de transporte más extremo: no hay taxímetro visible en ningún momento, no hay ningún contador que puedas consultar durante el viaje, y ni siquiera existe un registro de "cuántos viajes vas a hacer este mes" hasta que, al final del mes, llega una sola factura con el total. Eso es Bedrock on-demand, comparado con PAY_PER_REQUEST de DynamoDB: DynamoDB, al menos, declara en su configuración que "cobra por request" —el HCL tiene un billing_mode que lo dice—; Bedrock ni siquiera tiene ese modo declarado en ningún lugar de bedrock.tf, porque la invocación no es un atributo de un recurso — es un evento que ocurre completamente fuera de lo que Terraform administra.
El espectro completo, de "prendido = cuesta" a "no hay ni un modo declarado"
MENOS USAGE-BASED MÁS USAGE-BASED
EC2 reservada DynamoDB DynamoDB Bedrock
(PROVISIONED, PROVISIONED PAY_PER_REQUEST on-demand
siempre) (read/write_ (billing_mode
capacity en HCL) en HCL, volumen
en usage file)
read_capacity=5 read_capacity=5 billing_mode = (ningún atributo
en el HCL -- en el HCL -- "PAY_PER_REQUEST" en bedrock.tf
representa la representa la -- el HCL declara representa una
CAPACIDAD, un CAPACIDAD, un el MODO, pero invocación --
número que existe número que existe ningún atributo la invocación NO
antes de cualquier antes de cualquier declara CUÁNTAS ES UN RECURSO,
invocación invocación requests van a es un EVENTO
ocurrir en tiempo de
ejecución
│ │ │ │
▼ ▼ ▼ ▼
Infracost lee el Infracost lee el Infracost necesita Infracost NO
número directamente número directamente infracost-usage.yml TIENE NINGÚN
del HCL del HCL (Módulo 2, lección CAMPO QUE LEER
6 de finops) -- no existe
el recurso
Fíjate en la progresión: en los dos primeros casos (PROVISIONED), el número que determina el costo es un atributo del recurso mismo — read_capacity = 5 es, literalmente, una línea de HCL que Infracost puede leer sin necesitar nada más. En PAY_PER_REQUEST, el modo sigue siendo un atributo del recurso (billing_mode = "PAY_PER_REQUEST"), pero el volumen ya no vive en el HCL — vive en infracost-usage.yml, un archivo separado que alguien tiene que escribir a mano, con datos que ningún plan puede inferir (finops-and-cost-guardrails-guide, Módulo 2, lección 6). Bedrock on-demand da el último paso: ni siquiera hay un modo declarado, porque no hay ningún recurso de Terraform cuyo propósito sea representar "una llamada a InvokeModel". La invocación ocurre completamente fuera del control plane que Terraform administra — vive en el código de la aplicación (handler.py, el boto3.client("bedrock-runtime").invoke_model(...) que ninguna lección de esta guía llega a ejecutar de verdad), no en ninguna declaración de infraestructura.
Verificación real: ¿qué recursos de Bedrock existen, y cuáles representan capacidad frente a invocación?
Vale la pena confirmar esto con evidencia, no solo con la explicación en prosa — exactamente la misma disciplina que el Módulo 3, lección 2 de esta guía ya aplicó al extraer el schema completo de aws_bedrock_guardrail de terraform providers schema -json. Corrida contra el mismo provider hashicorp/aws que el resto de esta guía usa:
terraform providers schema -json | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for provider, pdata in data['provider_schemas'].items():
if 'aws' not in provider:
continue
for name in pdata.get('resource_schemas', {}):
if 'bedrock' in name.lower():
print(name)
"
Qué esperar (literal — ejecutado en este mismo entorno, provider hashicorp/aws v6.x):
aws_bedrock_guardrail
aws_bedrock_guardrail_version
aws_bedrock_provisioned_model_throughput
Tres recursos, ninguno de los cuales representa "una invocación". aws_bedrock_guardrail/aws_bedrock_guardrail_version son los que el Módulo 3 y 4 de esta guía ya declararon — configuración de un guardrail, no de una llamada. aws_bedrock_provisioned_model_throughput es el más interesante para esta lección: sí existe, y sí representa un compromiso de capacidad —el equivalente, en Bedrock, del read_capacity/write_capacity de DynamoDB PROVISIONED—. Su schema real, confirmado con el mismo comando:
terraform providers schema -json | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for provider, pdata in data['provider_schemas'].items():
if 'aws' not in provider:
continue
block = pdata['resource_schemas']['aws_bedrock_provisioned_model_throughput']['block']
for name, attr in block.get('attributes', {}).items():
print(name, '| required:', attr.get('required', False))
"
Qué esperar (literal):
commitment_duration | required: False
id | required: False
model_arn | required: True
model_units | required: True
provisioned_model_arn | required: False
provisioned_model_name | required: True
region | required: False
tags | required: False
tags_all | required: False
model_units es exactamente el equivalente conceptual de read_capacity — un número, declarado en el HCL, que representa cuánta capacidad reservas, sin importar cuántas invocaciones reales hagas con ella. Esto confirma la distinción precisa de esta lección: Provisioned Throughput sí tiene un recurso de Terraform, porque reservar capacidad es un acto declarativo (decides, de antemano, cuántas Model Units comprar) — pero on-demand, la opción que esta guía usa por defecto (Módulo 2, lección 2), no tiene ningún recurso equivalente, porque no hay nada que reservar de antemano. La Módulo 6, lección 7 retoma aws_bedrock_provisioned_model_throughput con números reales aplicados a Andes Cargo.
Por qué esto le importa específicamente al cost gate heredado
cost-tags (heredado, Módulo 4 de finops-and-cost-guardrails-guide) evalúa resource_changes de un tfplan.json — su unidad de trabajo es, siempre, un recurso de Terraform. Cualquier política Rego de cost-policy/, incluida la que la lección 3 de este módulo escribe, solo puede evaluar lo que existe en ese JSON: recursos, sus type, sus change.after. Como ninguna invocación de Bedrock aparece jamás en ese JSON —porque ninguna invocación es un recurso—, ninguna política Rego, sin importar qué tan bien escrita esté, puede limitar cuántas veces extract-shipment-manifest-fields llama al modelo. bedrock-budget.rego (lección 3) hace lo único que sí puede hacer desde ese ángulo: verificar que el recurso que sí existe (aws_bedrock_guardrail) esté correctamente etiquetado para asignación de costo. El presupuesto de volumen en sí —cuántas invocaciones al mes— necesita un mecanismo completamente distinto, fuera del tfplan.json: la calculadora de la lección 4, corrida como step independiente del pipeline, no como una política Rego más.
LO QUE cost-policy/ PUEDE VER LO QUE cost-policy/ NUNCA PUEDE VER
aws_bedrock_guardrail Una llamada a InvokeModel
(existe en tfplan.json, (no existe en tfplan.json --
tiene tags, tiene un ARN) no existe en NINGÚN plan,
│ porque no es un recurso)
▼ │
bedrock-budget.rego SÍ puede ▼
exigir Workload=GenAIExtraction bedrock_cost_estimate.py
sobre este recurso (lección 3) (Python, no Rego) es el
único mecanismo que puede
razonar sobre un supuesto
de volumen (lección 4)
Errores comunes
Buscar un atributo de "volumen esperado" en bedrock.tf, esperando que exista igual que billing_mode en DynamoDB (de expectativa). Qué pasa: alguien, familiarizado con PAY_PER_REQUEST, abre bedrock.tf buscando algo parecido a un modo declarado. Cómo detectarlo: si tu búsqueda en el HCL de esta guía por cualquier campo relacionado con "volumen" o "requests" en bedrock.tf no encuentra nada. Cómo corregirlo: no lo vas a encontrar, y esta lección explica por qué — a diferencia de PAY_PER_REQUEST, que al menos declara el modo, Bedrock on-demand no tiene ningún atributo equivalente en el recurso, porque la invocación nunca es un recurso de Terraform en absoluto.
Confundir aws_bedrock_provisioned_model_throughput con "el recurso que Infracost podría usar para calcular el costo on-demand" (de leer el nombre sin el schema). Qué pasa: alguien, al ver que sí existe un recurso llamado "Bedrock ... Throughput", asume que resuelve el problema de estimar el costo on-demand. Cómo detectarlo: si esperas que declarar este recurso te dé una cifra de costo por invocación on-demand. Cómo corregirlo: aws_bedrock_provisioned_model_throughput representa Provisioned Throughput —el modelo de precio opuesto al on-demand que esta guía usa por defecto (Módulo 2, lección 2)—. Declararlo significa comprar capacidad reservada, no medir un volumen on-demand; los dos modelos de precio son mutuamente excluyentes para el mismo modelo en el mismo momento.
Concluir que, como cost-policy/ no puede limitar el volumen de invocaciones, el gate de costo de este módulo "no sirve para nada" (de sobre-generalización). Qué pasa: alguien, al entender el límite de esta lección, descarta el valor de bedrock-budget.rego y de la calculadora integrada. Cómo detectarlo: si tu conclusión es "entonces este módulo no puede prevenir nada real". Cómo corregirlo: al revés — precisamente porque cost-policy/ no puede resolver esto sola, este módulo construye un segundo mecanismo, complementario, fuera de Rego: la calculadora de la lección 4, corrida como step del pipeline con un umbral declarado. Las lecciones 4 y 6 demuestran, con evidencia ejecutada, que ese mecanismo sí corta antes del apply — un tipo de gate distinto al de Rego, no un gate débil.
Ejercicios
Ejercicio 1 — Ubica, en el espectro de esta lección, dónde caería un hipotético "Bedrock Reserved Capacity Contract" que AWS anunciara mañana, con un precio fijo mensual sin importar el uso. ¿Se parecería más a read_capacity de DynamoDB PROVISIONED, o a billing_mode = "PAY_PER_REQUEST"?
Ver solución
Se parecería a read_capacity — un número fijo, declarado de antemano en el HCL, independiente del uso real. De hecho, ese producto hipotético ya existe hoy: es exactamente lo que aws_bedrock_provisioned_model_throughput y su atributo model_units representan, verificado en esta misma lección. Provisioned Throughput cae en el extremo "menos usage-based" del espectro — el costo depende de cuánta capacidad reservaste, no de cuántas veces la usaste, la misma propiedad que hace a read_capacity de DynamoDB PROVISIONED predecible desde el primer día.
Ejercicio 2 — Explica por qué cost-tags (heredado) puede evaluar aws_bedrock_guardrail (con la política de la lección 3) pero nunca podría evaluar "cuántas veces se invocó el modelo este mes", sin importar cuántas políticas Rego nuevas se escriban. Usa el diagrama de "lo que cost-policy/ puede/no puede ver" de esta lección para responder con precisión.
Ver solución
Porque cost-tags corre conftest test tfplan.json -p cost-policy/ — su única fuente de información es tfplan.json, un archivo que Terraform genera a partir de recursos declarados, nunca de eventos que ocurren en tiempo de ejecución. aws_bedrock_guardrail existe en ese archivo porque es un recurso declarado en bedrock.tf; una invocación real a InvokeModel nunca aparece ahí, porque ocurre después del apply, dentro del código de la aplicación, completamente fuera del control plane que Terraform administra. Ninguna cantidad de políticas Rego nuevas puede cambiar esto — el límite no es de la política, es del propio archivo de entrada que Rego evalúa.
Ejercicio 3 — Si Andes Cargo decidiera, en el futuro, comprar Provisioned Throughput para extract-shipment-manifest-fields, ¿tendría sentido que bedrock-budget.rego (lección 3) también exigiera Workload=GenAIExtraction sobre aws_bedrock_provisioned_model_throughput? Justifica tu respuesta con lo que ya sabes sobre el alcance de esa política.
Ver solución
Sí tendría sentido, y de hecho sería una extensión natural: aws_bedrock_provisioned_model_throughput sí es un recurso real en tfplan.json, con un atributo tags real (confirmado en el schema de esta lección) — exactamente el mismo tipo de superficie que aws_bedrock_guardrail ya tiene, y que bedrock-budget.rego ya evalúa. Si Andes Cargo migrara a Provisioned Throughput, extender ai_workload_resource_types (el conjunto que la lección 3 declara) para incluir también aws_bedrock_provisioned_model_throughput sería el mismo tipo de cambio incremental que el Ejercicio 3 del Módulo 5, lección 3 de esta guía ya practicó con bedrock-least-privilege.rego — agregar un tipo de recurso al conjunto existente, sin reescribir la lógica de la política.
Resumen y siguiente paso
Esta lección desarrolló, con evidencia verificada en vivo contra terraform providers schema, la diferencia exacta entre tres puntos del espectro usage-based: PROVISIONED (capacidad declarada en el HCL), PAY_PER_REQUEST (modo declarado, volumen fuera del HCL), y Bedrock on-demand (ningún atributo declarado en absoluto, porque la invocación nunca es un recurso). Confirmaste que Bedrock sí tiene un recurso que representa capacidad (aws_bedrock_provisioned_model_throughput, con model_units requerido) — pero ninguno que represente una invocación individual, la razón técnica exacta por la que ningún gate basado en tfplan.json puede, por sí solo, limitar el volumen de una carga on-demand.
Antes de avanzar deberías poder: dibujar el espectro completo de las cuatro posiciones (PROVISIONED de EC2/DynamoDB, PAY_PER_REQUEST, Bedrock on-demand) sin ayuda; nombrar los tres recursos de Bedrock que sí existen en el provider de AWS, y explicar cuál de ellos representa capacidad; y explicar por qué cost-tags puede proteger un guardrail pero nunca un volumen de invocaciones.
La lección 3 escribe la primera pieza de código de este módulo: bedrock-budget.rego, la política que hace cumplir lo único que cost-policy/ sí puede ver — el tag de asignación de costo sobre el recurso que sí existe.
Recursos
- Terraform Registry —
aws_bedrock_provisioned_model_throughput— el recurso que sí representa capacidad reservada, contrastado en esta lección con la ausencia total de un recurso para invocaciones on-demand. finops-and-cost-guardrails-guide, Módulo 1, lección 5 — el origen dePAY_PER_REQUESTvs.PROVISIONEDpara DynamoDB, la distinción que esta lección extiende un paso más.finops-and-cost-guardrails-guide, Módulo 2, lección 6 — el origen deinfracost-usage.yml, el mecanismo que resuelve el volumen para recursos usage-based que sí tienen un recurso de Terraform detrás.- Este curso, Módulo 3, lección 2 — el precedente exacto de extraer un schema real con
terraform providers schema -json, reaplicado aquí.