Estructura básica de una política IAM: Effect, Action, Resource y Condition explicados

La primera vez que abres una política IAM en JSON y ves cuatro claves que controlan todo el acceso a tu infraestructura, la tentación es asumir que son intercambiables o que alguna tiene prioridad implícita sobre otra. No es así. Cada elemento resuelve una pregunta distinta, y entender exactamente qué pregunta responde cada uno es lo que separa una política que funciona de una que abre brechas silenciosas.

TL;DR — Estructura básica de una política IAM

ElementoPregunta que respondeObligatorio
Effect¿Se permite o se deniega?
Action¿Qué operación de API?
Resource¿Sobre qué recurso específico?Sí (en la mayoría de tipos de política)
Condition¿Bajo qué circunstancias adicionales?No

Cómo evalúa AWS una política IAM

Antes de diseccionar cada elemento, es importante entender el modelo de evaluación. AWS no lee una política IAM como un programa secuencial. Cuando una entidad (usuario, rol, servicio) realiza una llamada a la API, el motor de autorización de IAM evalúa todas las políticas aplicables simultáneamente y aplica una lógica de precedencia bien definida: un Deny explícito siempre gana, independientemente de cuántos Allow existan. Si no hay ningún Allow explícito, el resultado por defecto es Deny implícito.

Un statement dentro de una política es la unidad mínima de evaluación. Cada statement contiene los cuatro elementos que vamos a analizar.

graph TD A["Llamada a la API"] --> B["Recolectar todas las políticas aplicables"] B --> C["Evaluar cada Statement"] C --> D{"¿Hay Deny explícito?"} D -- Sí --> E["DENEGADO — Deny explícito"] D -- No --> F{"¿Hay Allow explícito?"} F -- Sí --> G["PERMITIDO"] F -- No --> H["DENEGADO — Deny implícito"]
  1. Llamada a la API: una entidad IAM intenta ejecutar una operación (por ejemplo, s3:GetObject).
  2. Recolección de políticas: IAM reúne todas las políticas aplicables — identity-based, resource-based, SCPs, permission boundaries.
  3. Evaluación de statements: para cada statement, IAM verifica Effect, Action, Resource y Condition.
  4. Deny explícito: si cualquier statement con Effect: Deny coincide, la solicitud se rechaza inmediatamente.
  5. Allow explícito: si al menos un statement con Effect: Allow coincide y no hay Deny, se permite.
  6. Deny implícito: si ningún Allow coincide, se deniega por defecto.

El elemento Effect — la única decisión binaria

Effect acepta exactamente dos valores: Allow o Deny. No hay gradaciones, no hay prioridades numéricas, no hay 'permitir con advertencia'. Es la declaración de intención del statement completo.

Lo que muchos ingenieros subestiman al principio: un Deny explícito en una política de bajo nivel puede bloquear un Allow concedido por una política de administrador. Esto no es un bug — es el diseño intencional del modelo de seguridad de IAM. Si tienes un SCP a nivel de organización con un Deny sobre ec2:TerminateInstances, ningún Allow en ninguna política de usuario o rol lo va a superar.

{
  "Effect": "Deny",
  "Action": "ec2:TerminateInstances",
  "Resource": "*"
}
Piensa en Effect como el interruptor de un circuito eléctrico: no importa cuántos cables de Allow hayas conectado, si el Deny está cerrado, no pasa corriente.

El elemento Action — qué operación de API está en juego

Action especifica una o más operaciones de la API de AWS que el statement cubre. El formato es siempre servicio:Operación, donde el prefijo del servicio coincide con el namespace del servicio en IAM (no necesariamente con el nombre del servicio en la consola).

{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:ListBucket"
  ],
  "Resource": "*"
}

El comodín * en Action ("Action": "s3:*") cubre todas las operaciones del servicio S3. El comodín puede aplicarse también a nivel de operación: "s3:Get*" cubre todas las operaciones que empiezan con 'Get' en S3. Esto es potente pero requiere cuidado — "s3:Get*" incluye operaciones como s3:GetBucketPolicy que pueden no ser tu intención.

Un detalle que no está siempre visible en la consola: algunas acciones de IAM no corresponden directamente a llamadas de API REST. Por ejemplo, s3:ListAllMyBuckets es la acción IAM que controla la operación ListBuckets de la API de S3. Siempre verifica el nombre exacto de la acción en la Service Authorization Reference, no lo asumas desde el nombre de la API.

El elemento Resource — sobre qué actúa la política IAM

Resource define el ARN del recurso o recursos a los que aplica el statement. Este es el elemento donde más errores de alcance ocurren en producción.

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::mi-bucket-produccion/*"
}

Hay un error clásico aquí que vale la pena documentar explícitamente porque aparece constantemente en revisiones de seguridad:

graph LR A["s3:ListBucket"] -->|"Resource correcto"| B["arn:aws:s3:::mi-bucket"] A -->|"Resource incorrecto"| C["arn:aws:s3:::mi-bucket/*
AccessDenied"] D["s3:GetObject"] -->|"Resource correcto"| E["arn:aws:s3:::mi-bucket/*"] D -->|"Resource incorrecto"| F["arn:aws:s3:::mi-bucket
AccessDenied"]
  1. Síntoma: el usuario tiene permiso s3:ListBucket pero recibe AccessDenied al listar el bucket.
  2. Diagnóstico incorrecto: 'falta el permiso s3:ListBucket'.
  3. Causa real: s3:ListBucket requiere que el Resource apunte al ARN del bucket (arn:aws:s3:::mi-bucket), no a los objetos dentro de él (arn:aws:s3:::mi-bucket/*). Son dos ARNs distintos.
  4. Corrección: incluir ambos ARNs en el mismo statement o en statements separados.
{
  "Effect": "Allow",
  "Action": [
    "s3:ListBucket"
  ],
  "Resource": "arn:aws:s3:::mi-bucket-produccion"
},
{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": "arn:aws:s3:::mi-bucket-produccion/*"
}

Algunas acciones IAM no soportan restricción a nivel de recurso específico — requieren "Resource": "*". Esto no es una elección de diseño tuya, es una limitación del servicio. La Service Authorization Reference indica explícitamente qué acciones soportan ARNs específicos y cuáles requieren *. Nunca asumas que puedes restringir una acción a un ARN específico sin verificarlo.

El elemento Condition — el contexto que refina la política IAM

Condition es el elemento opcional que añade restricciones contextuales al statement. Un Allow con Condition solo se activa si todas las condiciones se cumplen simultáneamente. Si alguna condición falla, el statement no aplica — no genera un Deny, simplemente no contribuye a la evaluación.

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::mi-bucket-produccion/*",
  "Condition": {
    "StringEquals": {
      "aws:RequestedRegion": "eu-west-1"
    },
    "Bool": {
      "aws:SecureTransport": "true"
    }
  }
}

La estructura de Condition tiene tres capas: el operador de condición (como StringEquals, IpAddress, Bool), la clave de condición (como aws:RequestedRegion o claves específicas del servicio como s3:prefix), y el valor contra el que se compara.

graph TD A["Bloque Condition"] --> B["Operador de condición
StringEquals / Bool / IpAddress"] B --> C["Clave global aws:*
Región, IP, MFA, Hora"] B --> D["Clave de servicio
s3:prefix / ec2:Region"] C --> E["Valor de comparación"] D --> E E --> F{"¿Todas las condiciones
se cumplen?"} F -- Sí --> G["Statement aplica"] F -- No --> H["Statement no contribuye"]
  1. Operador: define el tipo de comparación — igualdad de string, rango de IP, valor booleano, fecha, etc.
  2. Clave global (aws:*): disponible en todas las solicitudes — región, IP de origen, MFA, hora, tipo de credencial.
  3. Clave de servicio: específica del servicio — por ejemplo, ec2:Region, s3:prefix, iam:PassedToService.
  4. Evaluación AND: múltiples claves dentro del mismo bloque de operador se evalúan con AND lógico.
  5. Evaluación OR: múltiples valores para la misma clave se evalúan con OR lógico.

Un caso de uso operacional frecuente: forzar MFA para operaciones destructivas sin bloquear operaciones de lectura.

{
  "Effect": "Deny",
  "Action": [
    "ec2:TerminateInstances",
    "rds:DeleteDBInstance"
  ],
  "Resource": "*",
  "Condition": {
    "BoolIfExists": {
      "aws:MultiFactorAuthPresent": "false"
    }
  }
}

Nota el uso de BoolIfExists en lugar de Bool. La diferencia es importante: Bool falla si la clave no está presente en la solicitud (por ejemplo, cuando se usa un rol asumido con credenciales temporales que no incluyen contexto MFA). BoolIfExists solo evalúa la condición si la clave existe; si no existe, la condición se considera cumplida. Elegir el operador incorrecto puede resultar en un Deny inesperado o en una condición que nunca se activa.

Política completa — los cuatro elementos juntos

Una política realista de acceso a S3 para una aplicación de solo lectura con restricción de red:

🔽 Ver política IAM completa de ejemplo
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListarBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::mi-bucket-produccion",
      "Condition": {
        "StringLike": {
          "s3:prefix": "datos/publicos/*"
        }
      }
    },
    {
      "Sid": "LeerObjetos",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::mi-bucket-produccion/datos/publicos/*",
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "true"
        }
      }
    },
    {
      "Sid": "DenegarFueraDeRegion",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::mi-bucket-produccion",
        "arn:aws:s3:::mi-bucket-produccion/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": "eu-west-1"
        }
      }
    }
  ]
}

Para verificar el comportamiento de una política antes de aplicarla en producción, usa el simulador de políticas IAM desde la CLI:

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/MiRolAplicacion \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::mi-bucket-produccion/datos/publicos/archivo.csv \
  --context-entries ContextKeyName=aws:SecureTransport,ContextKeyType=boolean,ContextKeyValues=true

Wrap-up y próximos pasos con la estructura de política IAM

Los cuatro elementos no son configuración opcional — son el vocabulario completo con el que IAM expresa cualquier decisión de acceso. Effect define la intención, Action delimita el alcance de operación, Resource lo ancla a infraestructura concreta, y Condition añade el contexto que hace las políticas realmente precisas.

El siguiente nivel natural es entender cómo interactúan múltiples políticas: identity-based policies, resource-based policies, permission boundaries y SCPs. Cada capa adicional puede restringir lo que las otras permiten, y el orden de evaluación importa. La documentación oficial de lógica de evaluación de políticas IAM es la referencia definitiva para ese análisis.

Glosario de términos clave

TérminoDefinición operacional
StatementUnidad mínima de una política IAM. Contiene Effect, Action, Resource y opcionalmente Condition y Sid.
ARN (Amazon Resource Name)Identificador único de un recurso AWS. Formato: arn:aws:servicio:región:cuenta:recurso.
Deny explícitoStatement con Effect: Deny que coincide con la solicitud. Tiene precedencia absoluta sobre cualquier Allow.
Deny implícitoResultado por defecto cuando ningún Allow coincide con la solicitud. No requiere un statement Deny.
Clave de condiciónVariable contextual evaluada en el bloque Condition. Puede ser global (aws:*) o específica del servicio.
Service Authorization ReferenceDocumentación oficial de AWS que lista todas las acciones, tipos de recursos y claves de condición por servicio.

Comentarios

Entradas populares de este blog

EC2 sin acceso a Internet en VPC personalizada: Internet Gateway y Route Table

Actualizar Contenido en CloudFront: Cómo Crear una Invalidación para Limpiar el Caché del Edge

Aumentar el Timeout de Lambda: Configuración, Límites y Diagnóstico en Producción