El motor de autorización de AWS es una función booleana. Recibe un request context y devuelve Allow o Deny, sin estados intermedios. Todo lo que escribís en JSON — políticas de identidad, políticas de recurso, boundaries, SCPs — son entradas de esa función. Si entendés el orden en que se evalúan, dejás de adivinar por qué falla un AccessDenied y empezás a predecir qué puede crear cada principal en tu cuenta. Y lo que se puede crear es, literalmente, lo que se factura.
Qué es un request context
Cuando alguien llama a la API — desde la consola, el AWS CLI o un SDK — AWS arma un objeto interno con seis campos: el principal que hace el pedido, la acción (ec2:RunInstances), el recurso, los datos del entorno (IP de origen, hora, si hay MFA), los datos del recurso (tags, dueño) y la política de recurso si existe.
Ese contexto es el único input. Las condition keys de un Condition no consultan nada externo: leen campos de ese objeto. Por eso aws:RequestedRegion funciona en cualquier servicio con endpoint regional, y ec2:InstanceType solo existe cuando la acción es de EC2. Una key que el servicio no popula se evalúa como ausente, y en un Deny con StringNotEquals eso cambia el resultado.
El orden de evaluación, y por qué Deny gana siempre
La documentación de IAM lo dice sin ambigüedad: por defecto, todos los requests se deniegan implícitamente, con la única excepción del root user de la cuenta, que tiene acceso completo. El resto tiene que ser habilitado explícitamente.
El motor corre en este orden:
- Barrido de Deny. Evalúa todas las políticas aplicables a la vez — SCPs, RCPs, políticas de recurso, políticas de identidad, permissions boundaries y session policies — buscando un statement con
Effect: Deny. Si encuentra uno solo que aplique, devuelveDenyy termina. - RCPs de Organizations. Si no hay un
Allowaplicable,Deny. - SCPs de Organizations. Si no hay un
Allowaplicable,Deny. - Políticas de recurso.
- Políticas de identidad. Si ninguna permite la acción,
Denyimplícito. - Permissions boundary. Si no permite la acción,
Denyimplícito. - Session policy. Si hay una y no permite la acción,
Denyimplícito.
El barrido de Deny corre antes que todo lo demás, sobre todas las capas a la vez. Un Allow más específico no lo levanta, porque no hay nada que desempatar.
Esa es la diferencia con los sistemas de permisos que ordenan reglas por especificidad. Acá no hay especificidad: hay un OR de todos los Deny y después un AND de las capas de Allow. La consecuencia práctica es que un Deny en un SCP es un techo que ningún administrador de la cuenta miembro puede levantar — y eso lo convierte en la herramienta de control de costos más contundente que tenés.
Identity-based y resource-based: unión, no intersección
Una política identity-based cuelga de un user, un group o un role: describe qué puede hacer esa identidad. Una resource-based cuelga del recurso — un bucket policy, una queue policy de SQS, una key policy de KMS — y tiene un elemento Principal que las identity-based no tienen.
Dentro de la misma cuenta, los permisos resultantes son la unión de las dos. Si la acción está permitida por la política de identidad, por la de recurso, o por ambas, AWS la permite. Hay excepciones documentadas: las trust policies de roles IAM y las key policies de KMS tienen que permitir explícitamente al principal, no alcanza con la política de identidad.
Cruzando cuentas la lógica cambia: hacen falta las dos. La cuenta A tiene que permitirle a su principal la acción, y la cuenta B tiene que permitir a ese principal en la política de recurso. Ninguna cuenta puede otorgar acceso a la otra unilateralmente.
Los boundaries y los SCPs se comportan al revés: son intersección. Un permissions boundary no otorga nada — recorta. Los permisos efectivos son la intersección entre la política de identidad y el boundary. Con SCP y RCP, la intersección es de tres.
| Combinación | Operación | Otorga permisos |
|---|---|---|
| Identity + resource (misma cuenta) | Unión | Sí, cualquiera de las dos |
| Identity + resource (cross-account) | Intersección | Hacen falta las dos |
| Identity + permissions boundary | Intersección | Solo la identity policy |
| Identity + SCP / RCP | Intersección | Solo la identity policy |
| Cualquiera + un Deny explícito | Deny gana | — |
Las credenciales de larga vida y el threat model que publica AWS
El access key de un IAM user con vida indefinida — pegado en un .env, en un repo, en una variable de CI o en un notebook — tiene una particularidad: AWS mantiene una política administrada dedicada a contenerlo. Se llama AWSCompromisedKeyQuarantineV3, su ARN es arn:aws:iam::aws:policy/AWSCompromisedKeyQuarantineV3, y AWS la aplica automáticamente cuando detecta credenciales expuestas en público. Existe porque el caso ocurre con frecuencia suficiente como para automatizar la respuesta.
Leé el JSON de esa política como lo que es: un inventario de lo que un atacante hace con una key robada, mantenido por el proveedor que ve los incidentes. Hay verbos de creación de capacidad, los esperables. Pero una porción grande del documento son acciones de IAM orientadas a persistencia — iam:CreateAccessKey, iam:AttachUserPolicy, iam:UpdateLoginProfile, iam:PassRole — más bloques de S3 y de KMS. La lectura correcta no es "el atacante mina cripto". Es: el atacante primero se atornilla a la cuenta, después se lleva o cifra los datos, y recién ahí escala capacidad. Cualquier control que solo mire el gasto llega tarde a los dos primeros pasos.
La alternativa está resuelta desde hace años y no cuesta nada: roles con credenciales temporales. Para EC2, instance profiles. Para EKS, IRSA o Pod Identity. Para GitHub Actions, un OIDC provider. Para las personas, IAM Identity Center. El objetivo es llegar a cero access keys de larga vida en la cuenta, y auditarlo:
# usuarios IAM con access keys activas — el objetivo es lista vacía
aws iam list-users --query 'Users[].UserName' --output text \
| tr '\t' '\n' \
| while read u; do
aws iam list-access-keys --user-name "$u" \
--query "AccessKeyMetadata[?Status=='Active'].[UserName,AccessKeyId,CreateDate]" \
--output text
done
En el nivel de la cuenta, MFA para el root ya no es opcional. La documentación de IAM es explícita: es requerida para el root user de cuentas standalone, management y member, con una ventana de registro de 35 días desde el primer intento de sign-in en la consola. Si administrás la organización, lo que corresponde es centralizar el root access y eliminar las credenciales root de las cuentas miembro.
OIDC en lugar de un access key en el CI
El CI es donde más viven las keys de larga vida, porque un secret de repositorio se configura en dos minutos y no vence nunca. El reemplazo es un OIDC provider: GitHub emite un JWT de corta vida por cada job, el job lo presenta con sts:AssumeRoleWithWebIdentity, y STS devuelve credenciales temporales. No hay secret que rotar porque no hay secret.
Toda la seguridad del esquema vive en el bloque Condition de la trust policy. El claim aud fija para quién se emitió el token; el claim sub fija qué repositorio, branch o environment lo pidió.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:mi-org/mi-repo:ref:refs/heads/main"
}
}
}
]
}
Sin ese bloque Condition, la trust policy le confía a la federación entera: cualquier repositorio de GitHub del planeta puede pedir un token y asumir el role. Con StringLike y repo:mi-org/mi-repo:* le confiás a cualquier branch y a cualquier pull request de ese repo, lo cual es aceptable para un job de plan y no lo es para uno de apply. Para deploys, anclá el sub a un environment (repo:mi-org/mi-repo:environment:production) y aprovechá las reglas de aprobación del environment como segunda barrera.
IAM no factura, pero fija el techo
IAM es gratuito. Lo dice AWS textual: "IAM is offered at no additional charge". Los SCPs tampoco tienen cargo. Lo que sí cuesta es lo que IAM permite crear.
Poné un número concreto. Una p5.48xlarge — ocho GPUs H100 — cuesta USD 55,04 la hora on-demand en us-east-1 según el pricing público al momento de escribir esto. Una sola instancia encendida una semana son USD 9.246,72. Dos instancias, o una sola olvidada tres semanas, ya es una factura de cinco cifras.
Ahora el matiz que hace creíble el escenario: en una cuenta nueva la cuota on-demand de instancias P es 0 vCPU por defecto. Sin un aumento de cuota aprobado de antemano, ese RunInstances falla aunque la política lo permita. El peor caso realista de una cuenta sin preparación no es la instancia más cara del catálogo — es una flota de la familia más cara para la que la cuenta ya tiene cuota, multiplicada por el vCPU disponible y por todas las regiones habilitadas. El atacante no elige el precio máximo; elige el máximo que la cuota le deja lanzar, y la región que vos no mirás.
Ese es exactamente el patrón que documentó AWS en la campaña de cryptomining sobre EC2 y ECS que GuardDuty detectó a partir de noviembre de 2025: arranca con credenciales IAM comprometidas y termina configurando Auto Scaling groups de hasta 999 instancias, mezclando spot y on-demand. AWS no publica la cifra en dólares de esos incidentes. El volumen de instancias sí está publicado, y con el precio por hora de la familia que la cuenta tenga habilitada la aritmética la hacés vos.
El control es un SCP en la OU de workloads. No otorga permisos: recorta el techo para todos los principals de las cuentas miembro, incluido el administrador de esa cuenta.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInstanciasFueraDeCatalogo",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"ec2:InstanceType": ["t3.*", "t4g.*", "m7g.*", "c7g.*", "r7g.*"]
}
}
},
{
"Sid": "DenyRegionesNoUsadas",
"Effect": "Deny",
"NotAction": ["iam:*", "organizations:*", "route53:*", "cloudfront:*",
"support:*", "budgets:*", "ce:*", "sts:*"],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["us-east-1", "sa-east-1"]
}
}
}
]
}
Fijate que el primer statement es un allowlist: deniega todo lo que no esté en una lista corta de familias aprobadas. La alternativa que se ve más seguido es un denylist — negar p*, g6*, trn*, x2* — y envejece mal por una razón estructural: AWS lanza familias nuevas de forma continua, y las nuevas suelen ser las caras. Un denylist escrito hace un año ya deja pasar g5, gr6, p6, dl1 y f2. Un allowlist falla del lado seguro: cuando alguien necesita una familia que no está, abre un ticket. El denylist falla del lado que aparece en la factura.
Dos detalles más del mecanismo:
- El
NotActioncon servicios globales es obligatorio solo si us-east-1 no está entre las regiones aprobadas. IAM, Organizations, Route 53, CloudFront y STS tienen endpoints que se resuelven ahí; si negás us-east-1 sin exceptuarlos, rompés la cuenta. En el ejemplo de arriba us-east-1 sí está permitida, así que eseNotActiones defensa en profundidad para el día que la lista cambie. - Los SCPs no aplican a la management account. Los workloads no van ahí.
- Desde mayo de 2026 el quota subió: hasta 10 SCPs por nodo (antes 5) y 10.240 caracteres por policy (antes 5.120). Alcanza para separar el deny de regiones del deny de instance types en policies distintas.
Para recursos caros que no querés bloquear del todo — NAT gateways, RDS multi-AZ, Redshift, SageMaker endpoints — el patrón es exigir tags en el request con aws:RequestTag/* y una condición Null en el SCP. Sin CostCenter y Owner, no se crea. El tagging deja de ser una convención y pasa a ser precondición de la API.
Proteger los propios controles
Un control que el atacante puede apagar no es un control. La primera acción de una credencial comprometida con permisos amplios suele ser callar la telemetría: detener el trail, borrar el detector de GuardDuty, borrar el budget que iba a avisar. El tercer SCP de la OU cierra eso.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ProtegerElPlanoDeControl",
"Effect": "Deny",
"Action": [
"cloudtrail:StopLogging",
"cloudtrail:DeleteTrail",
"cloudtrail:UpdateTrail",
"guardduty:DeleteDetector",
"guardduty:UpdateDetector",
"budgets:DeleteBudget",
"budgets:ModifyBudget"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/org/OrgSecurityAdmin"
}
}
}
]
}
La excepción por aws:PrincipalArn es necesaria — alguien tiene que poder mantener esos recursos — y es el punto débil del statement si la dejás así. Un administrador de la cuenta miembro que pueda crear un role llamado OrgSecurityAdmin se auto-excepciona en un CreateRole. Por eso el ARN de arriba usa un path reservado (/org/) y el SCP se acompaña con un Deny sobre iam:CreateRole, iam:DeleteRolePermissionsBoundary y iam:PutRolePolicy para todo lo que cuelgue de ese path. El nombre del role protegido no puede ser creable por quien está siendo restringido.
El billing alarm va antes que el primer recurso
Este es el punto donde el mecanismo tiene una trampa concreta. La métrica EstimatedCharges del namespace AWS/Billing se publica solo en us-east-1, y solo después de habilitar billing alerts en las preferencias de facturación. Si creás la alarma en tu región de trabajo, no vas a encontrar la métrica y vas a asumir que no existe.
# después de habilitar billing alerts en la consola de Billing
aws cloudwatch put-metric-alarm \
--region us-east-1 \
--alarm-name billing-total-estimado-50-usd \
--namespace AWS/Billing \
--metric-name EstimatedCharges \
--dimensions Name=Currency,Value=USD \
--statistic Maximum \
--period 21600 \
--evaluation-periods 1 \
--threshold 50 \
--comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:us-east-1:111122223333:alertas-billing
Maximum como estadística, porque EstimatedCharges es un contador acumulado del mes. Y --period 21600 son seis horas, que es un período de alarma razonable para una métrica de facturación: la doc de AWS dice que los datos de costo se actualizan varias veces por día — hasta tres, en el caso de Budgets — sin comprometerse a una cadencia fija. Diseñá asumiendo horas de retraso, no minutos.
La consecuencia de eso es que la alarma sobre gasto incurrido siempre llega tarde. Lo que llega a tiempo es el budget sobre forecast: AWS Budgets proyecta el cierre del mes y dispara cuando la proyección cruza el umbral, no cuando el gasto real lo cruza. Un budget con alerta sobre gasto real al 80% te avisa después de gastar 80. Uno sobre forecast te avisa el día que la pendiente cambió.
Cuánto cuesta el control, y a partir de cuándo se paga solo
Estos son los precios de us-east-1 verificados el 2026-08-10.
| Control | Precio | Nota |
|---|---|---|
| CloudWatch alarm standard resolution | USD 0,10 / alarma-métrica / mes | 10 gratis en el free tier |
| AWS Budgets — monitoreo y notificaciones | Sin cargo | |
| AWS Budgets — action-enabled budgets | 2 gratis / mes, después USD 0,10 / día | El budget action puede aplicar una policy Deny |
| AWS Budgets — reports por email | USD 0,01 por report entregado | |
| AWS Cost Anomaly Detection | Sin cargo | Detección por ML sobre patrones de gasto |
| Cost Explorer UI | Sin cargo | |
| Cost Explorer API | USD 0,01 por request paginado | Se acumula rápido si lo automatizás |
| CloudTrail — primera copia de management events | Sin cargo | Por región y por cuenta; el segundo trail sí se cobra |
| IAM Access Analyzer — external access | Sin cargo | Findings de acceso público y cross-account |
| IAM Access Analyzer — unused access | USD 0,20 / role o user analizado / mes | Un analyzer alcanza para todas las regiones |
| IAM Access Analyzer — internal access | USD 9,00 / recurso / región / mes | Se activa por recurso, no por cuenta |
| IAM Access Analyzer — custom policy checks | USD 0,0020 por llamada a la API | Para el gate en CI |
Fuentes de la tabla, en orden: CloudWatch pricing, AWS Budgets pricing, AWS Cost Anomaly Detection, AWS Cost Explorer pricing, CloudTrail pricing, IAM Access Analyzer pricing. El precio por hora de EC2 sale de EC2 On-Demand pricing y la gratuidad de IAM está declarada en la página de IAM. Todo esto caduca: la fecha de verificación está arriba y conviene rehacerla antes de citar cualquier número.
Con esos números, cada control tiene un punto de cruce explícito contra el recurso más caro que tu cuenta puede lanzar. Tomando la p5.48xlarge a USD 55,04 la hora como unidad de comparación:
| Control | Costo mensual | Se paga solo si evita |
|---|---|---|
| Una billing alarm | USD 0,10 | 6,5 segundos de una p5.48xlarge |
| Un action-enabled budget (el tercero en adelante) | USD 3,00 | 3,3 minutos |
| Unused access analyzer, 300 roles y users | USD 60,00 | 1 hora y 5 minutos |
| Internal access analysis, 20 recursos en 2 regiones | USD 360,00 | 6 horas y 32 minutos |
El unused access analyzer es la decisión fácil: 300 principals × USD 0,20 son USD 60 al mes, y ese gasto se justifica si en todo el mes evita poco más de una hora de una sola instancia grande encendida por error. Cualquier organización con esa cantidad de roles tiene ese incidente más de una vez al año, y el analyzer además reduce la superficie que hace posible el incidente.
El internal access analysis no se activa igual, porque el precio es por recurso y por región. A USD 9,00 mensuales cada uno, 20 recursos en dos regiones cuestan USD 360 al mes: la barra de justificación sube a más de seis horas de la instancia más cara, o a lo que valga para vos saber exactamente quién dentro de la organización alcanza esos recursos. La regla operativa es la misma que con cualquier control caro: no lo prendas transversalmente. Elegí la lista corta de recursos que guardan lo que no podés perder — los buckets con datos regulados, las keys de KMS que los cifran, las tablas de auditoría — y activalo solo ahí. Diez recursos bien elegidos cuestan USD 90 al mes y responden la pregunta que importa; ochenta mal elegidos cuestan USD 720 y responden la misma pregunta con más ruido.
El AWS Budgets action es el punto donde el loop se cierra: un budget que al superar el umbral adjunta una policy de Deny a un grupo o role. El barrido de Deny corre antes que todo, así que el efecto sobre los requests siguientes es inmediato y no hay política de identidad que lo contrarreste.
El loop mensual
Los controles de arriba son estáticos. Lo que los mantiene vivos es una rutina de cuatro pasos que corre una vez por mes y siempre en la misma dirección: de la línea de la factura hacia la política que la permitió.
- Cost Explorer, agrupado por usage type. No por servicio: el servicio te dice "EC2", el usage type te dice
BoxUsage:p5.48xlargeoNatGateway-Hours. Anotá las tres líneas que más crecieron contra el mes anterior. - CloudTrail, filtrado por la acción que creó ese recurso.
RunInstances,CreateNatGateway,CreateDBInstance. Lo que buscás es el campouserIdentity: qué principal firmó. - La política que lo permitió. Con el principal en la mano, andá a qué le da permiso: policy administrada, inline, o herencia por un role asumido. Preguntá si ese principal tenía que poder hacer eso. La mitad de las veces la respuesta es no y nadie lo había mirado.
- Access Analyzer unused access, más Access Advisor. El primero lista los roles y users que no se usaron en la ventana configurada; el segundo, por principal, muestra qué servicios tocó y cuándo. Los dos juntos te dan la lista de permisos que podés borrar sin romper nada.
Los pasos 1 a 3 explican la factura del mes pasado. El paso 4 achica la del mes que viene. La rutina completa lleva menos de una hora si el tagging está puesto, que es otra razón para tratarlo como precondición de la API y no como convención.
Verificar antes de aplicar
El policy simulator evalúa políticas de identidad, permissions boundaries, SCPs y políticas de recurso que le pases. La documentación aclara que no soporta RCPs, y que los resultados pueden diferir del entorno real en configuraciones avanzadas — VPC endpoint policies, role chaining, múltiples políticas de recurso sobre un mismo recurso.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:role/app-runtime \
--action-names ec2:RunInstances iam:CreateUser \
--resource-arns "arn:aws:ec2:us-east-1:111122223333:instance/*" \
--query 'EvaluationResults[].{accion:EvalActionName,decision:EvalDecision}'
El resto del toolkit, todo gratis salvo donde se aclara:
| Herramienta | Para qué |
|---|---|
| IAM Access Analyzer | Genera políticas a partir de la actividad real de CloudTrail y valida con más de 100 checks antes de guardar. Generación, validación y external access no se cobran; unused e internal access sí, con los precios de la tabla. |
| Prowler | Auditoría de la cuenta contra benchmarks tipo CIS. Corrélo el día uno: te dice si el root tiene MFA y qué keys llevan años sin rotar. |
| Cloudsplaining | Escanea las políticas existentes y devuelve un reporte priorizado por riesgo: wildcards en Resource, acciones de escalada de privilegios, modificación de datos sin restringir. |
| Policy Sentry | Genera políticas de mínimo privilegio desde un YAML con ARNs y nivel de acceso. Ataca la causa real del Resource: "*", que es lo tedioso que resulta a mano. |
| AWS Budgets | Alerta sobre gasto real o proyectado y ejecuta budget actions que adjuntan una policy Deny. Los datos se actualizan hasta tres veces por día: no es una barrera en tiempo real. |
| aws-nuke | Borra todos los recursos de una cuenta sandbox. Es el complemento del SCP: el SCP evita que se cree, aws-nuke borra lo que se creó igual. Usá el fork de ekristen, que es el mantenido. |
Los límites que vas a chocar
Los quotas de IAM son ajustables hacia arriba y la aprobación es automática hasta el máximo. Los de caracteres, no:
| Límite | Default | Máximo |
|---|---|---|
| Managed policies por role | 10 | 25 |
| Managed policies por user | 10 | 20 |
| Customer managed policies por cuenta | 1.500 | 10.000 |
| Roles por cuenta | 1.000 | 10.000 |
| Tamaño de una customer managed policy | 6.144 caracteres | No ajustable |
| Políticas inline agregadas por role | 10.240 caracteres | No ajustable |
| Trust policy de un role | 2.048 caracteres | 8.192 caracteres |
| Duración máxima de una sesión de role | 12 horas | No ajustable |
El límite de 6.144 caracteres por managed policy y el de 10 policies por role son los que fuerzan la arquitectura real: no vas a poder describir cada permiso individual de un servicio grande en una sola policy. La salida es ABAC — condiciones sobre aws:PrincipalTag y aws:ResourceTag en lugar de enumerar ARNs — que además hace que el tagging sea el mismo mecanismo que usás para atribuir costos en Cost Explorer.
Cuatro preguntas para responder con números
Antes de dar la cuenta por configurada, respondé estas cuatro con un valor concreto, no con una impresión.
¿Cuál es el recurso más caro por hora que un principal de esta cuenta puede crear hoy? No el más caro del catálogo de AWS: el más caro que el cruce de tus SCPs y tus cuotas efectivamente permite lanzar. Multiplicalo por 24 y por 7. Ese número es tu blast radius por semana.
¿Cuánto tarda la cuenta en avisarte, y cuánto cuesta esa demora? Convertí la latencia a dólares: horas hasta la alerta × costo por hora del recurso de la pregunta anterior. Si tu peor recurso corre a USD 55,04 la hora y tu detección tarda doce horas, la latencia vale USD 660,48 por incidente. Esa cifra es la que decide si te alcanza con un budget sobre gasto real o necesitás uno sobre forecast.
¿Cuántas credenciales de larga vida quedan, y qué antigüedad tiene la más vieja? El comando de más arriba lo responde en un minuto. La meta es cero, y el número intermedio sirve para saber si estás bajando o subiendo.
¿Quién puede apagar los controles? Listá los principals que hoy pueden ejecutar cloudtrail:StopLogging o budgets:DeleteBudget. Si la lista tiene más de un role, el SCP de la sección anterior todavía no está puesto.
Ahí está la simetría que vale la pena retener. El tag que decide si un principal puede tocar un recurso es el mismo tag que decide en qué línea de la factura cae ese recurso. Una sola taxonomía, dos usos: control de acceso y atribución de costo.
Para seguir
Lecturas
- Policy evaluation logic — La fuente canónica del orden de evaluación, el deny implícito por defecto y los diagramas de decisión dentro de una cuenta y cross-account.
- Identity-based policies and resource-based policies — El elemento Principal, y el matiz que rompe la intuición: dentro de la cuenta alcanza con que una permita, entre cuentas hacen falta las dos.
- Security best practices in IAM — Recomendaciones ordenadas por impacto. Aclara explícitamente que SCPs y RCPs no otorgan permisos por sí solas.
- Service control policies (SCPs) — El mecanismo del techo organizacional: qué alcanza, qué no, y por qué la management account queda afuera.
- Techniques for writing least privilege IAM policies — El puente entre la teoría y escribir JSON: condiciones y restricción por recurso en lugar de wildcards.
- GuardDuty Extended Threat Detection uncovers cryptomining campaign on Amazon EC2 and Amazon ECS — Campaña activa desde noviembre de 2025 que arranca con credenciales IAM comprometidas y termina en Auto Scaling groups de hasta 999 instancias. AWS no publica cifras en dólares.
Videos
- AWS re:Inforce 2023 - A first-principles approach: AWS Identity and Access Management (IAM201) — Reconstruye IAM desde primeros principios en lugar de enumerar features. Para ver antes de meterse en la evaluación de políticas.
- AWS re:Inforce 2022 - AWS Identity and Access Management (IAM) deep dive (IAM301) — Nivel 300 sobre tipos de política, cómo se combinan y cómo se resuelve la decisión final. El complemento en video de la página de policy evaluation logic.
- AWS re:Invent 2023 - Use new IAM Access Analyzer features on your journey to least privilege (SEC238) — Generar políticas desde la actividad real de CloudTrail y validarlas antes de desplegar.